---
name: worca-changelog
description: Build a "What's new in Worca" changelog page for @worca/app — a scroll-revealed, screenshot-led ship log in the established Worca design (hero ribbon, kicker/headline/chips per feature, live demo cards, receipts) — from the merged PRs since a reference version, preview it as an Artifact, and record it for docs.worca.dev/changelog. Triggers on "changelog", "what's new", "ship log", "release notes", "worca-changelog", or any request to write up what shipped since a version.
---

# Worca changelog entry

A changelog entry is an **HTML page** in the ship-log design, built from the
PRs merged since a reference version. It is served for good at
`https://docs.worca.dev/changelog/<VERSION>/` and previewed first as a private
Artifact. It is not a bullet list of commits: every section sells one
user-facing capability with a headline, a screenshot, and a small live demo
of the mechanism.

The output is written into `docs/changelog/` **on the current branch, left
uncommitted**. This skill never branches or opens a PR, and it commits,
pushes and publishes only when the user says so at the end — see Step 7.

The design, layout and writing rules live next to this file — read both
before writing a line of copy or markup:

- `DESIGN.md` — tokens, components, section anatomy, demo recipes, voice.
- `template.html` — the page skeleton with every component wired up. Copy
  it; never restyle it.
- `inline-assets.mjs` — turns local `<img src>` paths into data URIs so the
  page publishes as one file.

**Usage:**

- `/worca-changelog` — offer the last stable release as the delta start
- `/worca-changelog --from:1.1.1` — delta since that version (tag or bare version)
- `/worca-changelog --from:1.1.1 --to:worca-app-v1.2.0-rc.3` — explicit end ref (default `HEAD`)
- `/worca-changelog --no-publish` — build the file, skip the Artifact preview

---

## Step 1: Resolve the range

The delta runs from a **reference version** to an **end ref**. A stable
release is the natural reference: it is the last thing a `latest` user has,
so "what's new since it" is exactly the RC's story.

```bash
# Last stable release: highest worca-app-v tag with no pre-release suffix.
git fetch --quiet --tags origin
STABLE=$(git tag --sort=-v:refname | grep -E '^worca-app-v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
LAST_RC=$(git tag --sort=-v:refname | grep -E '^worca-app-v[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$' | head -1)
echo "stable: $STABLE   npm latest: $(npm view @worca/app version)   last rc: $LAST_RC"
node -p "require('./package.json').version"     # the version this entry describes
```

If `--from:` was **not given**, do not pick silently: **offer the last stable
release** with `AskUserQuestion`. Put it first and mark it recommended; the
other options are the last RC and "other" (free text). If the stable tag and
`npm view @worca/app version` disagree, say so in the question — one of them
is stale.

A bare version (`1.1.1`) means the tag `worca-app-v1.1.1`. Verify the tag
exists (`git rev-parse -q --verify refs/tags/<tag>`) before using it; stop on
a miss rather than guessing at a nearby tag.

The end ref defaults to `HEAD`. The entry's **version label** is
`package.json`'s version at the end ref (`git show <to>:package.json`).

Print the resolved range and continue:

```
Changelog range:  worca-app-v1.1.1  →  HEAD  (1.2.0-rc.3)
```

---

## Step 2: Collect the delta

Both views, always — PRs give the story, the first-parent log catches what
landed without a PR.

```bash
FROM=worca-app-v1.1.1; TO=HEAD
SINCE=$(git log -1 --format=%cI "$FROM")
gh pr list --state merged --base dev --search "merged:>=${SINCE%%T*}" --limit 200 \
  --json number,title,body,mergedAt,url,labels \
  --jq "[.[] | select(.mergedAt > \"$SINCE\")] | sort_by(.mergedAt) | .[] | \"#\(.number)\t\(.mergedAt[:10])\t\(.title)\""
git log "$FROM".."$TO" --first-parent --format='%h %s'
```

Read every PR body (`gh pr view <n> --json body --jq .body`). The body is
where the user-facing framing lives; the title is often internal.

Then **cluster into 4–7 themes**. A theme is something a user can point at
in the product: a new view, a new command, a behaviour that changed. Fold
these in rather than giving them a section:

- `chore(release)` bumps, CI, test-only and Windows-path fixes → the
  receipts section, as numbers.
- Refactors and internal plumbing → mention in the sub of the feature they
  enable, or drop.
- Bug fixes → one "Fixes" theme only if there are several visible ones;
  otherwise a chip on the relevant feature (`<span class="chip">no more X</span>`).

Order themes by how much a user will care, biggest first. The hero's chips
and ribbon list them in that order.

---

## Step 3: Collect the receipts

The closing section carries six numbers. Compute them; never estimate.

```bash
git diff --shortstat "$FROM".."$TO"                      # files, +lines, -lines
git rev-list --count --first-parent "$FROM".."$TO"      # commits on dev
gh pr list --state merged --base dev --search "merged:>=${SINCE%%T*}" --json mergedAt \
  --jq "[.[] | select(.mergedAt > \"$SINCE\")] | length"  # PRs merged
git diff --stat "$FROM".."$TO" -- '*.test.mjs' 'test/' | tail -1   # test lines
npm test 2>&1 | grep -E '^# (tests|pass|fail) '           # suite size, must be 0 fail
```

Pick the six that tell the story (tests green, lines landed, PRs merged,
schema versions bumped, a security property, a round-trip proven). Two of
the six may be a cropped screenshot instead of a number — see `DESIGN.md`.

---

## Step 4: Capture screenshots

Every feature section wants a real screenshot; a section with no UI surface
(a CLI change, a policy) gets two demo cards instead, as `template.html`
shows for `s3`.

Follow the sandbox recipe in `docs/screenshots.md` — `HOME` and `WORCA_HOME`
both set to a throwaway dir, seeded mock runs, demo projects with plausible
names — so nothing private lands in a published page. Run the mock server
from the current checkout; the sandbox is runtime state only and produces
no branch, worktree, or commit of its own. Then, with the UI running (the
`run` skill knows how), capture at **1440 × 900**, light theme, and save as
PNG under:

```
docs/changelog/shots/<VERSION>/<section-id>.png     e.g. shots/1.2.0-rc.3/s1.png
```

Crop in markup, not in the file: the `.shot .crop` container takes
`max-height`, and the `<img>` takes `width:130%;max-width:none;margin:…` to
zoom on the part that matters. Keep the original so a later re-crop is free.

Compress before inlining — screenshots dominate the page weight:

```bash
sips -s format jpeg -s formatOptions 82 s1.png --out s1.jpg      # macOS; ~5× smaller
```

---

## Step 5: Write the copy, then the page

Read `DESIGN.md` → *Voice* before writing. The shape of every section is
fixed; only the words and the demo change:

| Slot | Rule |
| --- | --- |
| kicker | 1–2 words naming the area: `Budget`, `Routing`, `Team`. Dot colour from the palette table. |
| h2 | ≤ 7 words, a full sentence with a period. Obvious before clever: a reader must know what the feature is from the headline alone (*New to Worca? Follow the checklist.*), never a riddle that needs the sub to decode. |
| sub | **Problem first, in bold, then the answer.** Line 1 is one plain question or pain the reader recognises, wrapped in `<b>…</b>` and followed by `<br>`. Line 2 starts with *Now …* and says what Worca does and how, in words a first-time user understands. ≤ 52ch measure, no "we". |
| chips | 2–4 plain phrases a user understands at a glance, and only ones that matter (`highlights the real button`, `stored in your git remote`). Numbers go in `<b>`. No tool names, flags, schema versions, internal terms, or shorthand a reader would have to decode. |
| demo | A 10–20 line CSS/JS loop that shows the *mechanism* — precedence, masking, a flag appearing. Reuse a recipe from `DESIGN.md` before inventing one. Its label, steps and note follow the same plain-words rule as the chips. |
| shot | The screenshot in a `.shot` frame with a one-line `.shot-cap` that says what the reader is looking at. |

Build the page:

1. Copy `template.html` to `docs/changelog/worca-app-v<VERSION>.src.html`.
2. Fill every `{{…}}` slot; delete the sections you don't need, duplicate
   the ones you do. Keep ids sequential (`s0` hero … `sN` receipts) — the
   dot rail is built from them.
3. Set the hero ribbon text to the section names, uppercased, `·`-separated,
   **repeated twice** so the drift never shows the end.
4. Point every `<img src>` at the local file (logo: `ui/public/assets/worca-logo.png`).
5. Inline and check weight:

```bash
node .claude/skills/worca-changelog/inline-assets.mjs \
  docs/changelog/worca-app-v<VERSION>.src.html \
  docs/changelog/worca-app-v<VERSION>.html
```

The script fails above 15 MB (the Artifact ceiling is 16 MB) and warns
above 4 MB — the sibling ship logs weigh ~1.6 MB. Over 4 MB means a
screenshot went in as PNG; go back to Step 4.

6. Open the built file in a browser and check, in this order: reveals fire
   as you scroll; every demo loops; the rail dot follows the section; no
   horizontal scroll at 390 px wide; `prefers-reduced-motion` shows every
   demo in its final state. Fix in the `.src.html`, rebuild.

7. After Step 6 has added the entry to `entries.json`, check that the docs
   site builds with it:

```bash
node docs-site/build.mjs     # fails on a missing image, a page without an entry, or the reverse
```

The `.src.html` and `shots/<VERSION>/` are the entry: they are what gets
committed, and what docs.worca.dev serves (`docs-site/build.mjs` gives the
page a document shell and serves the screenshots as files). The built
`worca-app-v<VERSION>.html`, with the screenshots embedded, exists only to
publish the Artifact preview. It is git-ignored and never committed.

---

## Step 6: Publish the preview and record the entry

The Artifact is a **private preview**, for reviewing the page before it goes
out. The public, permanent address is the docs site (Step 7).

Unless `--no-publish`:

1. Load the `artifact-design` skill (the Artifact tool requires it before
   any publish; the page's design is already settled, so the pass is
   confirmation, not redesign).
2. Publish `docs/changelog/worca-app-v<VERSION>.html` with:
   - title: `Worca — What's new in <VERSION>` (the `<title>` tag already says this)
   - favicon: `🚢` on first publish, omitted on redeploys
   - description: one sentence — the hero sub is usually right.

Always, with or without `--no-publish`, record the entry in
`docs/changelog/entries.json`, newest first (see `DESIGN.md` → *Index*):

```json
{ "version": "1.7.0", "since": "1.6.0", "date": "2026-10-02", "artifact": "https://claude.ai/artifact/…" }
```

Leave `artifact` out when nothing was published. The docs site's release list
reads its summary and headlines from the page itself, so nothing else goes
into the record.

Redeploys of the same version go to the same URL: edit the source, rebuild,
publish the same path again. A new version is a new file, a new URL and a new
record.

---

## Step 7: Leave it on the current branch, then ask

The entry stays **in the working tree of the branch that is checked out**.
Do not create a branch, a worktree or a PR for it — a changelog is release
material, and the release engineer decides when and how it lands. One PR per
changelog is exactly the churn this rule avoids. It goes live on
docs.worca.dev only once it is on `dev` **and** `docs-live` has moved.

Show what is waiting, then stop:

```bash
git status --short docs/changelog/
```

Finish with the summary:

```
Changelog entry ready

  Range:      worca-app-v1.1.1 → HEAD (1.2.0-rc.3)
  Sections:   6 features + receipts
  Page:       docs/changelog/worca-app-v1.2.0-rc.3.src.html  (+ 6 screenshots)
  Preview:    <artifact url>   |   not published (--no-publish)
  Docs:       https://docs.worca.dev/changelog/1.2.0-rc.3/  (live once published — see the question below)
  Files:      uncommitted on <branch> — 8 files under docs/changelog/
```

Then, always, print a **release announcement** in one fenced block the user
can copy straight into a team chat:

````
```
Worca <VERSION> released

Changes:
1. <one sentence per section, in the page's order>
2. …

npm install -g @worca/app@<VERSION>

https://docs.worca.dev/changelog/<VERSION>/
```
````

- One numbered item per feature section (not the receipts), in page order.
  Fold the small changes that only got a chip into the closest item or one
  last "also" item, never a list of their own.
- Each item is one sentence (two at most) that says what a teammate can do now
  and why it matters, in the same plain words as the page: *Your team can
  share one hosted Worca while each person's runs are charged to their own
  key or Claude subscription. Keys are kept in a separate key service, so
  neither Worca nor its agents can read them, which is a strong protection
  against prompt injection.* No PR numbers, internal names or flags. A
  command the reader types, such as `worca runs`, stays in backticks.
- The install line pins the exact version, even for an RC:
  `npm install -g @worca/app@1.2.0-rc.3`.
- The link is the docs page, not the Artifact: the docs page is public and
  permanent, while the Artifact is private until it is shared. Below the block,
  say that the link works once the entry is published, and give the Artifact
  URL as the preview to review before then.

Last, ask with `AskUserQuestion` whether to commit and publish now:

- **Commit and publish now (Recommended)** — commits the entry to `dev`,
  pushes, and puts it on docs.worca.dev.
- **Not yet** — leaves it uncommitted to review or change first.

On *Commit and publish now*, read `.claude/skills/worca-release/SKILL.md` →
*Changelog mode* and follow C1–C6 for this version. That is the same
procedure `/worca-release --publish-changelog` runs, so there is one
definition of it. The answer covers this one entry only.

On *Not yet*, stop and say how to finish later, after any edits:
`/worca-release --publish-changelog` (it takes the newest entry; add
`--version:<VERSION>` to name it). Skip the question entirely when the
current branch is not `dev`, and say the entry has to reach `dev` first.
