--- name: release-notes description: Write consistent, hand-written-looking changelog entries for a Poracode release. Use when the user wants to "add release notes", "update the changelog", "write the changelog for vX.Y.Z", "cut a release entry", or has just tagged/shipped a release and wants the in-app + website changelog updated. Audits release metadata, every commit, and the full previous-release diff—including direct-commit releases with sparse PR coverage—then distills the user-facing changes and maintainer highlights into one curated entry prepended to website/public/changelog.json. allowed-tools: Bash(gh:*), Bash(git:*), Bash(pnpm:*), Bash(node:*), Read, Edit, Write, Grep, Glob --- # Release Notes — Poracode Turn a release's PRs, complete commit range, and actual code diff into a **curated, human-readable changelog entry** that reads like a person wrote it — and keep every release in the changelog consistent in voice, shape, and length. Poracode's changelog is **curated data**, not auto-generated. GitHub already auto-lists merged PRs on the Release page; this skill produces the _hand-written_ layer that ships inside the app (Settings → Changelog, the "What's New" dialog) and on the marketing site. ## When to use - The user asks to **add/write release notes**, **update the changelog**, or **"do the changelog for vX.Y.Z"**. - A release was just tagged/published and the in-app + website changelog need an entry. - Backfilling missing releases into the changelog. ## When NOT to use - Editing the _mechanics_ of the changelog feature (the React surfaces, the seen-state gate) — that's normal code work, not this skill. - Writing a single commit message or a GitHub Release body (GitHub generates that). ## The one file you edit There is a **single source of truth**: **`website/public/changelog.json`** on master. The marketing site serves it at `https://www.poracodeapp.com/changelog.json` and the desktop app fetches + caches it at runtime, so editing this one file (and pushing to master, which redeploys the site) updates both surfaces **without an app rebuild**. Shape — an object with a newest-first `releases` array (do not invent fields): ```json { "releases": [ { "version": "1.3.1", "date": "2026-06-17", "title": "Short punchy headline, NO version number", "summary": "One or two sentences — the release's elevator pitch.", "changes": [ { "kind": "added", "label": "Projects", "text": "One complete, user-facing sentence." }, { "kind": "fixed", "text": "A mixed-scope fix that needs no forced label." } ] } ] } ``` `kind` is one of `added` | `improved` | `fixed`. Release-note text stays English (it is not localized — only the surrounding app UI chrome is). `label` is an optional short feature or product prefix; labeled and unlabeled changes may appear in the same release. ## Step 1 — Gather source material Resolve the repo slug from the remote (default `Porabuild/Poracode`): ```bash gh repo view --json nameWithOwner -q .nameWithOwner ``` Then collect, for the target version: - **The release date** and the **auto-generated PR list** (the body): ```bash gh api repos///releases/tags/v --jq '{date: .published_at, body: .body}' ``` - **The complete git range**, even when a GitHub release and PR list exist. PRs are supplementary, not authoritative: direct commits, local merges, and squash details may be absent. Read every commit subject and body, inspect the range-level file list and diffstat, then inspect the substantive patches and nearby tests before writing: ```bash git log --date=iso-strict --pretty='format:%H%n%ad%n%s%n%b' .. git diff --stat .. git diff --name-status .. git diff .. ``` Do not infer behavior from commit titles alone — open the patch when the subject is ambiguous (especially `feat`/`fix` that touch UI, settings, i18n, or provider adapters). - **An unpublished target boundary**, when no target tag exists. Use the intended release commit (usually `HEAD`), confirm its package version matches the requested version, and state this boundary in the handoff rather than pretending the tag exists. - **The maintainer's highlight notes**, if they pasted any (Telegram/X-style bullets, "now with: …"). These are authoritative for _what matters most_ — lead with them. If the user didn't provide notes and the release is large, ask once whether they have highlights; otherwise proceed from the collected release metadata and git evidence. ### Step 1b — Commit disposition ledger (mandatory) Before drafting prose, walk **every** non-merge commit in `..` and assign one disposition. Keep this ledger in your working notes (it does not ship in `changelog.json`, but you must not skip it): | Disposition | When | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | **include** | User-facing on its own — gets its own bullet (or is the seed of one). | | **fold into \** | Related to a larger change already included; note which bullet absorbs it. | | **omit** | Implementation-only, test-only, merge-only, build, chore, pure rename, or internal refactors with no visible behavior change. Write a one-line reason. | **Always-include signals** (treat as `include` or `fold`, never silent `omit`): - New or changed **user-visible copy** — especially commits that add msgids / touch `src/renderer/locales/**/messages.po` (or mobile/website locale catalogs). New strings almost always mean a user can see something new. - **Discoverability / selection-state UI**: mode-specific icons, badges, trigger labels, selected-state highlights, status chips, empty states, onboarding hints. These stay even when the underlying feature shipped in an earlier release — users notice the clarity, and "polish on an existing feature" is still `improved`, not noise. - Settings, menus, tooltips, toasts, dialogs, composer controls, sidebar chrome, Git/PR controls, provider pickers — anything the user clicks or reads in the app. - Behavior fixes the user would have hit (done-state, wrong window size, broken launch). **Safe to omit** (with an explicit reason): - Tests, type-only refactors, CI, lockfile-only, version bumps, pure renames without product behavior, dependency upgrades with no user-visible effect. - Micro-animation / fade / layout-pixel tweaks that do not change what the control _means_ or how the user finds it. - Marketing-site-only changes (`website/**` that do not affect the shared `changelog.json` data or in-app surfaces) — mention only when they are a real product surface (e.g. a new About page, pairing flow). Sticky nav on the public changelog page is optional; prefer app-facing notes when distilling. **Anti-pattern that caused a real miss (1.6.1):** a small `feat(pr-watch)` that only swapped the PR automation trigger icon/label per Auto Fix vs Auto Merge was dropped while larger sidebar/workspace bullets dominated. It touched every locale catalog and changed what users see on every automated PR — that is `include` as `improved`, not "too small to list." **Reconciliation gate:** every `feat/*` and `fix/*` commit that touches `src/renderer/**`, `src/mobile/**`, `src/shared/messages.ts`, or locale catalogs must end as `include` or `fold into …`. If you cannot fold it cleanly, give it a bullet. Quick helpers when auditing: ```bash # Commits that added/changed localized strings (strong user-facing signal) git log --pretty='%h %s' --name-only .. -- 'src/renderer/locales/**' # UI / settings / PR surfaces in the range git log --pretty='%h %s' --name-only .. -- \ 'src/renderer/**' 'src/mobile/**' 'website/src/**' ``` ### Step 1c — Prior Art & History Cross-Check (Mandatory Anti-Hallucination Gate) **Never assume a feature is brand new.** Before writing bullets, cross-check previous releases in `website/public/changelog.json`: ```bash # Search existing changelog for mentions of the area or feature grep -in "" website/public/changelog.json ``` 1. **Verify Lineage**: - If GitHub Actions, Plugins, Workspaces, Archived Threads, or Provider Switching are in the commit range, check what already existed in prior versions. - If the capability already existed, do **NOT** write `"You can now "` as an `added` bullet. That is a hallucination of novelty. - Accurately describe the **delta / upgrade** as `improved` (e.g. _"GitHub Actions now supports multiple signed-in accounts"_, _"Switching providers now continues inside the same thread (Handoff 2.0)"_, _"Archived thread management is redesigned for multi-workspace and remote filtering"_). 2. **Differentiate Plumbing from User Benefits**: - Do **NOT** list internal architecture or IPC/notification mechanics as user bullets (e.g. avoid _"task notifications and background task updates"_ or _"added database sync migrations and Zod schemas"_). - Distill the user outcome: what can the user now do or see? (e.g. _"Run Antigravity via first-class ACP runtime support with live usage discovery and machine-scoped settings"_). 3. **Check Environment & Scope Support**: - Check if changes expanded platforms (e.g. WSL, remote SSH, Windows shell resolution, multi-machine settings). Mention platform support explicitly when added. ## Step 2 — Distill into ONE curated entry (house style) This is the consistency contract. Match the voice of the existing entries (read a couple from `website/public/changelog.json` first). **title** - Short, punchy, **no version number**, sentence case. ~3–8 words. - Name the 2–3 headline features, joined with commas / `&`. E.g. `\"Antigravity ACP, Handoff 2.0 & thread mentions\"`. **summary** - 1–2 sentences, benefit-first, the release's "elevator pitch". - Big releases may open with `\"A big feature release: …\"`. Patches stay factual and short. **changes** - Each is **one complete, user-facing sentence**. Prefer second person and present tense: _\"You can now…\"_, _\"Sessions are saved automatically…\"_. - `kind`: `added` (new capability) · `improved` (better/faster/refined existing) · `fixed` (bug fix). Order them added → improved → fixed. - `label`: optional short feature/product prefix. Add it only when the whole sentence belongs to one obvious surface such as `Remote`, `Claude`, `Plugins`, or `Security`. Omit it for mixed-scope sentences or whenever the right label is uncertain; never force every change to have one. - **Distill, don't dump.** Merge related work into one bullet when it serves the same user outcome. A 70-PR major may land ~6–14 bullets; a busy patch may land ~6–14. Tiny hotfixes stay short (~2–5). Distillation means folding related commits, **not** dropping discoverability improvements or new user-visible labels/icons because a larger headline feature is already listed. - Lead the `added` bullets with the maintainer's highlights when present. - After drafting, re-read the disposition ledger: every `include` must map to a bullet (alone or folded); every `omit` must still look like noise on a second look. **Hard rules (what makes it look hand-written)** - ❌ No PR numbers, no `by @handle`, no `dependabot`/CI/chore/`build(deps)` items, no raw PR-title phrasing. - ❌ No version number inside `title`. - ❌ Do not claim existing features are newly added — verify with `grep` against `website/public/changelog.json` and mark upgrades as `improved`. - ❌ Do not list internal protocol/plumbing mechanisms (heartbeats, task notifications, IPC schemas) in place of user outcomes. - ❌ Do not drop mode icons, selection labels, badges, or status presentation just because the capability already existed — those are user-facing `improved` items. - ✅ Vary sentence openings — don't write "Added X. Added Y. Added Z." Describe the _benefit_, not the implementation or the commit. - ✅ Mix labeled and unlabeled changes when that best represents the release. - ✅ Keep product nouns literal: `Poracode, Claude, Codex, Gemini, Grok, Command Code, WSL, ACP, Opus 4.8, Ultracode, Fable 5, Git, GitHub, macOS, Windows, Linux`. - ✅ Each feature appears in the release that introduced it — don't repeat it in a later patch. **Refinements** of an earlier feature (clearer labels, icons, defaults, multi-account support, in-thread handoff) belong in the release that shipped the refinement. ### Good vs bad ``` ✅ { kind: "added", text: "Start a new project by cloning any GitHub repository directly from Poracode." } ✅ { kind: "improved", label: "Provider switch", text: "Switching providers or models now continues seamlessly inside the same thread while preserving your full conversation history and active MCP configuration (Handoff 2.0)." } ✅ { kind: "improved", label: "GitHub", text: "GitHub Actions now supports multiple signed-in GitHub accounts, letting you switch accounts when browsing workflows, dispatching runs, or inspecting CI statuses." } ❌ { kind: "added", label: "GitHub Actions", text: "You can now view workflow runs, inspect step logs, and monitor action statuses directly inside Poracode." } // Hallucination: GitHub Actions was added in 1.6.0; multi-account support was the 1.7.0 delta ❌ { kind: "added", text: "Add GitHub repository clone flow by @SDSLeon in #167" } // raw PR title + noise ❌ { kind: "added", text: "Added clone." } // too thin, no benefit ❌ omit "PR automation mode icons" as "too small / polish" // discoverability is user-facing ``` ### Template ```ts { version: "X.Y.Z", date: "YYYY-MM-DD", title: "Headline feature, second feature & third", summary: "One or two sentences on what this release gives the user overall.", changes: [ { kind: "added", label: "Projects", text: "You can now …" }, { kind: "improved", text: "… is now faster / clearer / smoother because …" }, { kind: "fixed", text: "… no longer … ." }, ], }, ``` ## Step 3 — Write it Prepend the new entry to the `releases` array in **`website/public/changelog.json`** (newest first; the app re-sorts defensively, but keep the source tidy). Keep valid JSON — double-quoted keys/strings, no trailing commas. When _amending_ an already-published entry (missed bullet, wrong wording), edit that release in place — do not invent a new version. Changelog data deploys with the site; the app refetches it without an app rebuild. ## Step 4 — Verify ```bash pnpm exec vitest run src/shared/changelog.test.ts # validates changelog.json: schema, sorted, unique, all fields pnpm run typecheck ``` Optionally build the marketing site to confirm the `/changelog` page renders: `pnpm --dir website build`. Then commit + push to master — Vercel redeploys the site and the desktop app fetches the new notes on its own (no app release needed for a notes-only change). ## Final checklist - [ ] `title` has no version number; `summary` is 1–2 sentences. - [ ] **History & prior art verified:** Grepped `website/public/changelog.json` to verify feature lineage and ensure existing capabilities are not falsely claimed as `added`. - [ ] **User outcome focus:** Filtered out internal protocol/plumbing mechanisms (e.g. IPC schemas, background task notifications) in favor of direct user-facing capabilities and benefits. - [ ] Changes are distilled (not one-per-PR), grouped added→improved→fixed, each a full benefit sentence. - [ ] **Disposition ledger complete:** every non-merge commit is `include`, `fold into …`, or `omit` with a reason. - [ ] Every `feat`/`fix` that touches renderer/mobile UI, settings, messages, or locale catalogs is `include` or `fold` — none silently omitted. - [ ] Discoverability work (icons, selection labels, badges, status chips for existing features) is covered as `improved` when present in the range. - [ ] Labels appear only where the category is clear; mixed or uncertain changes remain unlabeled. - [ ] No PR numbers / author handles / dependabot / CI / chore noise. - [ ] `version` (no `v`) and `date` (release date, ISO) are correct. - [ ] `website/public/changelog.json` is valid JSON; the test + typecheck pass.