Changelog Writer
Writes external, end-user-facing changelog entries with personality — modeled on what
Slack, Basecamp, Linear, Raycast, and Tumblr do well: voice and confidence first, jokes
used sparingly and only in service of the fact, never replacing it.
This skill is for external/end-user changelogs. If the audience is internal engineering
or the changelog needs to double as a searchable technical record (breaking changes, API
versioning, migration notes), this tone is the wrong trade — use a precise, Stripe-style
format instead and skip this skill.
Reference files:
references/voice-examples.md — annotated examples of the tone done well (Slack, Basecamp,
Tumblr, Raycast, Linear) and done badly, for calibration
references/draft-format.md — the full spec for the intermediate draft format the scripts parse
Scripts (in scripts/) — run these against every draft before presenting a final version:
check-all.mjs — runs everything below and gives one pass/fail report
check-structure.mjs, check-sass-budget.mjs, check-voice.mjs, check-length.mjs, check-cadence.mjs
Workflow
- Gather the raw material. Get the list of what actually shipped — feature names, what
changed, any tickets/PRs. If it's vague ("we improved search"), ask what specifically changed
before writing anything. A joke can't rescue an entry with no real content behind it.
- Group by theme, not by ticket. Five related tweaks become one entry. Assign each entry
a
category (New, Improved, or Fixed) and a stable id (a slug from the feature name,
not a sequence number — this is what makes the rule IDs and scripts useful over time, since
entries can be tracked and diffed release over release).
- Draft every entry in the structured format described in
references/draft-format.md.
Write the whole draft this way first — plain field-by-field — before making it pretty.
This is what makes the entry checkable by script instead of just vibes.
- Run
node scripts/check-all.mjs <draft-file>. Fix every FAIL. Read every WARN and use
judgment — WARNs are things worth a second look, not always things to change.
- Re-run until clean, then convert the structured draft into the final published prose
(see "Rendering the final version" below).
- Present the finished changelog, not the structured intermediate format — the field
labels are a drafting tool, not something an end user should ever see.
The building blocks (rule reference)
Every rule below has a stable ID. The scripts check the ones marked [scripted]; the rest
are judgment calls a script can't reliably make — apply them yourself when drafting.
Entry skeleton — STRUCT-*
- STRUCT-01 (scripted) — Headline is mandatory. Plain statement of what shipped, no pun
required. Whoever's skimming has to get this in one glance.
- STRUCT-02 (scripted) — So-what is mandatory for
New/Improved entries (optional,
can collapse into the headline for Fixed). One sentence on why this matters to the reader,
written like you're telling a friend, not pitching a feature.
- STRUCT-03 (scripted) — Receipt is mandatory for
New/Improved entries. The actual
detail: what changed, what to click, what's different. Always plain English, never vague
("various improvements" fails this check).
- STRUCT-04 — Hook is optional and the only piece allowed to be cut. Better no joke than
a forced one.
Sass budget — SASS-*
- SASS-01 (scripted) — One bit per entry, max. If the headline already lands a joke, the
hook stays quiet.
- SASS-02 (scripted) — Zero jokes near security, billing, data loss, outages, or anything
users would be upset about. Straight talk only. This is a hard rule, not a style preference.
- SASS-03 (scripted, batch-level, warn only) — Not every entry needs a bit. If most entries
in one release have a hook, personality has stopped being special.
Voice — VOICE-*
- VOICE-01 (scripted) — No corporate buzzwords or press-release phrasing ("we are pleased
to announce," "leverage," "seamless experience," "blazing fast" — say the actual number instead).
- VOICE-02 (scripted, warn only) — Use contractions. Formal constructions read stiff.
- VOICE-03 (scripted) — No clichéd pun templates (egg-cellent, purr-fect, fin-tastic,
and the like — swap-a-word-for-a-similar-sounding-word puns that are worn out regardless of
the feature) and no lampshading ("get it?", "pun intended", a winking emoji). If a joke has
to announce itself, it already failed.
- VOICE-04 (judgment only, not scripted) — The "two truths, one phrase" test: a pun is
only good if its second meaning is also literally true of what shipped, not just wordplay
that happened to be available. A script can't know what's true about the feature, so this
one's on you. See
references/voice-examples.md for the full breakdown.
Length — LEN-*
- LEN-01 (scripted) —
Fixed entries: ~30 words total across all filled fields. Should
read in one breath.
- LEN-02 (scripted) —
New/Improved entries: 4 sentences max combined across all fields.
- LEN-03 (scripted) — No single field over 40 words. If it needs a paragraph, the feature
is confusing, not the changelog.
Cadence / metadata — CAD-*
- CAD-01 (scripted) — Category must be
New, Improved, or Fixed. Nothing fancier —
the categories aren't where the personality goes.
- CAD-02 (scripted, warn only) — Every entry needs a unique, stable id (a slug, not a
sequence number), so entries can be tracked release over release.
- CAD-03 (judgment only) — Ship an entry every release, even a boring one. Consistency is
what earns the reader's trust that the funny ones are worth reading.
Rendering the final version
Once a draft passes check-all.mjs clean, convert each entry from the structured fields into
one flowing changelog entry:
## [Headline, possibly rewritten as a title]
[Hook, if present — one line, woven in naturally] [So-what, one line] [Receipt, the actual detail]
[CTA, if present]
Group entries under New, Improved, Fixed headers, in that order. Don't show the reader
headline: / hook: / etc. labels — those are scaffolding for the check, not part of the
output.
Example — structured draft:
### ENTRY id=bulk-actions category=New sass=hook
headline: Bulk actions have entered the chat.
hook: You can now select every unread thread at once.
sowhat: Stop clicking the same checkbox forty times.
receipt: Select multiple conversations from the inbox and archive, tag, or reassign them in one action.
cta: none
### END
Rendered as:
Bulk actions have entered the chat. Select every unread thread at once instead of
clicking the same checkbox forty times — archive, tag, or reassign a whole batch in one go.
Common failure mode to watch for
The most common way this goes wrong isn't "not funny enough" — it's overwritten. Every entry
trying too hard, reader fatigue setting in by the third one. When in doubt, cut the hook before
you cut the receipt. The fact always survives; the bit is expendable.
1---2name: changelog-writer3description: Write external-facing changelog and release note entries in a pithy, tastefully sassy, occasionally clever voice that makes software updates fun to read without burying what actually shipped. Use this skill whenever someone asks to write, draft, or review a changelog entry, release notes, "what's new" post, or product update announcement for end users. Also trigger when someone pastes a list of shipped features/fixes and wants them turned into public-facing copy, or asks to make a changelog "less boring," "more fun," or "not sound like corporate speak."4---56# Changelog Writer78Writes external, end-user-facing changelog entries with personality — modeled on what9Slack, Basecamp, Linear, Raycast, and Tumblr do well: voice and confidence first, jokes10used sparingly and only in service of the fact, never replacing it.1112**This skill is for external/end-user changelogs.** If the audience is internal engineering13or the changelog needs to double as a searchable technical record (breaking changes, API14versioning, migration notes), this tone is the wrong trade — use a precise, Stripe-style15format instead and skip this skill.1617**Reference files:**18- `references/voice-examples.md` — annotated examples of the tone done well (Slack, Basecamp,19 Tumblr, Raycast, Linear) and done badly, for calibration20- `references/draft-format.md` — the full spec for the intermediate draft format the scripts parse2122**Scripts** (in `scripts/`) — run these against every draft before presenting a final version:23- `check-all.mjs` — runs everything below and gives one pass/fail report24- `check-structure.mjs`, `check-sass-budget.mjs`, `check-voice.mjs`, `check-length.mjs`, `check-cadence.mjs`2526---2728## Workflow29301. **Gather the raw material.** Get the list of what actually shipped — feature names, what31 changed, any tickets/PRs. If it's vague ("we improved search"), ask what specifically changed32 before writing anything. A joke can't rescue an entry with no real content behind it.332. **Group by theme, not by ticket.** Five related tweaks become one entry. Assign each entry34 a `category` (`New`, `Improved`, or `Fixed`) and a stable `id` (a slug from the feature name,35 not a sequence number — this is what makes the rule IDs and scripts useful over time, since36 entries can be tracked and diffed release over release).373. **Draft every entry in the structured format** described in `references/draft-format.md`.38 Write the whole draft this way first — plain field-by-field — before making it pretty.39 This is what makes the entry checkable by script instead of just vibes.404. **Run `node scripts/check-all.mjs <draft-file>`.** Fix every FAIL. Read every WARN and use41 judgment — WARNs are things worth a second look, not always things to change.425. **Re-run until clean**, then convert the structured draft into the final published prose43 (see "Rendering the final version" below).446. **Present the finished changelog**, not the structured intermediate format — the field45 labels are a drafting tool, not something an end user should ever see.4647---4849## The building blocks (rule reference)5051Every rule below has a stable ID. The scripts check the ones marked **[scripted]**; the rest52are judgment calls a script can't reliably make — apply them yourself when drafting.5354### Entry skeleton — `STRUCT-*`5556- **STRUCT-01** *(scripted)* — Headline is mandatory. Plain statement of what shipped, no pun57 required. Whoever's skimming has to get this in one glance.58- **STRUCT-02** *(scripted)* — So-what is mandatory for `New`/`Improved` entries (optional,59 can collapse into the headline for `Fixed`). One sentence on why this matters to the reader,60 written like you're telling a friend, not pitching a feature.61- **STRUCT-03** *(scripted)* — Receipt is mandatory for `New`/`Improved` entries. The actual62 detail: what changed, what to click, what's different. Always plain English, never vague63 ("various improvements" fails this check).64- **STRUCT-04** — Hook is optional and the only piece allowed to be cut. Better no joke than65 a forced one.6667### Sass budget — `SASS-*`6869- **SASS-01** *(scripted)* — One bit per entry, max. If the headline already lands a joke, the70 hook stays quiet.71- **SASS-02** *(scripted)* — Zero jokes near security, billing, data loss, outages, or anything72 users would be upset about. Straight talk only. This is a hard rule, not a style preference.73- **SASS-03** *(scripted, batch-level, warn only)* — Not every entry needs a bit. If most entries74 in one release have a hook, personality has stopped being special.7576### Voice — `VOICE-*`7778- **VOICE-01** *(scripted)* — No corporate buzzwords or press-release phrasing ("we are pleased79 to announce," "leverage," "seamless experience," "blazing fast" — say the actual number instead).80- **VOICE-02** *(scripted, warn only)* — Use contractions. Formal constructions read stiff.81- **VOICE-03** *(scripted)* — No clichéd pun templates (egg-cellent, purr-fect, fin-tastic,82 and the like — swap-a-word-for-a-similar-sounding-word puns that are worn out regardless of83 the feature) and no lampshading ("get it?", "pun intended", a winking emoji). If a joke has84 to announce itself, it already failed.85- **VOICE-04** *(judgment only, not scripted)* — The "two truths, one phrase" test: a pun is86 only good if its second meaning is also literally true of what shipped, not just wordplay87 that happened to be available. A script can't know what's true about the feature, so this88 one's on you. See `references/voice-examples.md` for the full breakdown.8990### Length — `LEN-*`9192- **LEN-01** *(scripted)* — `Fixed` entries: ~30 words total across all filled fields. Should93 read in one breath.94- **LEN-02** *(scripted)* — `New`/`Improved` entries: 4 sentences max combined across all fields.95- **LEN-03** *(scripted)* — No single field over 40 words. If it needs a paragraph, the feature96 is confusing, not the changelog.9798### Cadence / metadata — `CAD-*`99100- **CAD-01** *(scripted)* — Category must be `New`, `Improved`, or `Fixed`. Nothing fancier —101 the categories aren't where the personality goes.102- **CAD-02** *(scripted, warn only)* — Every entry needs a unique, stable id (a slug, not a103 sequence number), so entries can be tracked release over release.104- **CAD-03** *(judgment only)* — Ship an entry every release, even a boring one. Consistency is105 what earns the reader's trust that the funny ones are worth reading.106107---108109## Rendering the final version110111Once a draft passes `check-all.mjs` clean, convert each entry from the structured fields into112one flowing changelog entry:113114```115## [Headline, possibly rewritten as a title]116[Hook, if present — one line, woven in naturally] [So-what, one line] [Receipt, the actual detail]117[CTA, if present]118```119120Group entries under `New`, `Improved`, `Fixed` headers, in that order. Don't show the reader121`headline:` / `hook:` / etc. labels — those are scaffolding for the check, not part of the122output.123124**Example — structured draft:**125```126### ENTRY id=bulk-actions category=New sass=hook127headline: Bulk actions have entered the chat.128hook: You can now select every unread thread at once.129sowhat: Stop clicking the same checkbox forty times.130receipt: Select multiple conversations from the inbox and archive, tag, or reassign them in one action.131cta: none132### END133```134135**Rendered as:**136> **Bulk actions have entered the chat.** Select every unread thread at once instead of137> clicking the same checkbox forty times — archive, tag, or reassign a whole batch in one go.138139---140141## Common failure mode to watch for142143The most common way this goes wrong isn't "not funny enough" — it's overwritten. Every entry144trying too hard, reader fatigue setting in by the third one. When in doubt, cut the hook before145you cut the receipt. The fact always survives; the bit is expendable.