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 ...
Installs into .claude/skills of the current project.
Are you the author of Release Notes?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/sdsleon-release-notes)
---
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/<owner>/<repo>/releases/tags/v<X.Y.Z> --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' <previousTag>..<target>
git diff --stat <previousTag>..<target>
git diff --name-status <previousTag>..<target>
git diff <previousTag>..<target>
```
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 `<previousTag>..<target>` 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 \<bullet\>** | 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 <previousTag>..<target> -- 'src/renderer/locales/**'
# UI / settings / PR surfaces in the range
git log --pretty='%h %s' --name-only <previousTag>..<target> -- \
'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 "<feature-keyword>" 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 <basic action>"` 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.