---
name: release-notes-writer
description: Turn a range of commits into release notes written for the people affected, grouped by what changed for them rather than by commit type, with breaking changes and required actions first. Use when cutting a release, updating a CHANGELOG, writing a deploy summary, or explaining what shipped.
---
<!--
  release-notes-writer — from Toolbay
  https://toolbay.ai/product/release-notes-writer
  Free to use, modify, and share. Keep this line and others can find it too.
-->


# Release Notes Writer

## Install

Save this file as `~/.claude/skills/release-notes-writer/SKILL.md`, or
`.claude/skills/release-notes-writer/SKILL.md` to scope it to one repo. Claude
Code auto-discovers it. Invoke with `/release-notes-writer` or by asking "write
release notes for this release".

## Why this exists

Most generated release notes are a reformatted git log. They list every commit,
including the ones only the author cares about, and they bury the single line that
actually matters ("you must set this variable or deploys fail") in the middle of
forty bullets nobody reads.

Good release notes answer three questions in order: **do I have to do anything,
what is different for me, and what got fixed that was annoying me.** Everything
else is noise, and noise is why people stop reading changelogs.

Write for the reader, not for the repository.

## Step 1 — Establish the range

```
git describe --tags --abbrev=0 2>/dev/null
git log <last-tag>..HEAD --oneline
```

No tags? Ask what the range should be, or fall back to something defensible like
"since the last deploy" or a date. Never guess silently: notes covering the wrong
range are worse than none.

Then read the actual changes, not only the subjects:

```
git log <range> --format='%H%n%an%n%s%n%b%n---'
git diff <range> --stat
```

Commit bodies are where the reasoning lives. A subject says what; a body says why,
and why is what belongs in release notes.

## Step 2 — Classify by IMPACT, not by prefix

Conventional-commit prefixes describe the author's intent, not the reader's
experience. A `fix:` can be a breaking change and a `chore:` can be the most
important line in the release.

Sort every commit into:

- **ACTION REQUIRED** — the reader must do something: set a variable, run a
  migration, update a call site, rotate a key. **Always first, always explicit.**
- **BREAKING** — behaviour they depend on changed.
- **NEW** — capability they did not have.
- **IMPROVED** — something existing is better, faster, or clearer.
- **FIXED** — a bug they might have hit. Describe the SYMPTOM, because that is
  how they will recognise it.
- **INTERNAL** — refactors, tests, dependency bumps, formatting. Collapse to a
  single count. Do not enumerate.

That last rule matters. Twelve dependency bumps listed individually push the one
migration nobody can skip below the fold.

## Step 3 — Detect what commits do not say

Some of the most important entries are never written in a commit subject. Check
the diff directly:

```
git diff <range> -- '*.env.example' '*.env.sample'      # new config
git diff <range> --stat -- '*migration*' '*schema*'      # migrations
git diff <range> -- package.json | grep -E '^[-+].*"'    # dep + engine changes
git diff <range> --diff-filter=D --name-only             # deletions
```

A new key in `.env.example` is almost always ACTION REQUIRED even when its commit
says `chore: tidy config`. A deleted export is BREAKING even when its commit says
`refactor`.

## Step 4 — Write entries a stranger can act on

For each entry: what changed, who it affects, and what to do. One line each where
possible.

Bad, and typical:

> - fix: update watermark logic

Good:

> - **Downloads no longer break Claude Code skills.** The licence header was
>   placed above YAML frontmatter, so skills lost their name and description on
>   install. Re-download any skill bought before today.

The difference is that the second tells someone whether they need to act.

Rules for the prose:

- Lead with the effect, not the mechanism.
- Name the symptom for fixes. People search notes for the thing they experienced.
- Never write "various improvements" or "bug fixes". If it is not worth describing,
  it belongs in the INTERNAL count.
- Link the PR or commit at the end of the line, never at the start.
- Say the version and date. Notes without a date get quoted out of context.

## Step 5 — Assemble

```markdown
## <version> — <date>

### Action required
- <what to do, and what breaks if you skip it>

### Breaking
- <what changed, and how to adapt>

### New
- <capability, and why you would use it>

### Improved
- <what is better, and how you would notice>

### Fixed
- <symptom you may have hit, now resolved>

_Also: <n> internal changes (refactors, tests, dependency updates)._
```

Omit any empty section. An empty heading is a small lie about the release having
been bigger than it was.

## Step 6 — Verify before publishing

- Every claim traceable to a real commit in the range. **Do not invent an entry
  because a section looks thin.**
- No section silently dropped. A skipped breaking change is the one failure that
  makes notes actively harmful.
- Read the ACTION REQUIRED section as someone who has never seen this codebase.
  Could they do it from this text alone? If not, add the command.

## Rules

- Impact over prefix, always.
- Symptoms for fixes, effects for features.
- Collapse internal noise to a count; never pad a release to look bigger.
- If nothing user-visible shipped, say so plainly. "This release is internal
  maintenance" is a perfectly good changelog entry and it buys trust for the
  release where you do need attention.
