---
name: prepare
description: >
Draft a release's changelog entry from the changes since the last tag:
take the note each change declared, rule on the ones that declared none,
and collate them into the section for the version being released. Use
before tagging a release, when asked what changed since the last release,
or as a step in a repository's own release procedure. Triggers on:
prepare the release, release notes for x.y.z, changelog entry for the
release, what changed since the last release.
---
# prepare: the release's entry
A release is the notes its changes declared, in one section, in the order a
reader scans. What is not declared is ruled from the diff, which is the
fallback rather than the method.
Read the [release-notes skill](../release-notes/SKILL.md) first. It carries
the one test, the block, the shape of an entry and what never goes in one.
Nothing below repeats it.
## The three passes
**Pass 1, scope.** One call, which also clears the scratch file:
```bash
node "${CLAUDE_PLUGIN_ROOT}/bin/agent-notes.mjs" commits
```
It prints the previous tag, the range, and a row per commit. When
`firstRelease` is true nothing has been released yet, the range is the whole
history, and the entry describes what the software does rather than what
changed in it.
Add `--path
` when the thing being released is a subtree rather than the
repository: a plugin in a monorepo, a package in a workspace. The range then
covers only the commits that touched it, the entry lands in
`/CHANGELOG.md`, and the scratch file is its own. A subtree is usually
versioned on its own rather than tagged, so pass `--since` with it.
A repository can keep both: one changelog per released thing and another for
itself. They are drafted separately and nothing reconciles them. The
repository's entry can summarise what four subtree entries said, or say
something none of them did.
**Pass 2, rule on each commit.** One call per commit:
```bash
node "${CLAUDE_PLUGIN_ROOT}/bin/agent-notes.mjs" evidence
```
Then one row per commit, and show the table:
| Field | What goes in it |
| ------------- | --------------------------------------------------------------- |
| `sha` | as printed |
| `source` | `note`, `NONE` or `diff`, from what the change declared |
| `observable` | yes or no, against the one test |
| `category` | the section its type asks for, or one of the six |
| `breaking` | yes or no |
| `entry` | one sentence: what a person running it sees differently |
| `evidence` | the paths in the diff that show it |
| `discrepancy` | where the message and the diff disagree, empty when they do not |
A commit whose `note` is declared needs no ruling: the row is the note, and
`source` is `note`. One declaring `NONE` is a ruled row with no entry. Only
the rest cost a reading of the patch, and for those `discrepancy` is the row
that earns the diff: a commit saying "fix typo" that also moves a default is
caught here or nowhere.
`patch` comes back null on a large commit, with `patchOmitted` giving its
length. Rule from the stat and the file list; a diff that long is a rewrite,
a generated file or a first commit, and reading it line by line changes
nothing.
Add `--pulls` to fetch the pull requests a commit landed through, each with
its own block. Try it on one commit first: under squash merges the commit
body already is the pull request body, and then it costs an API call per
commit and adds nothing.
**Under a merge workflow**, a merge commit and the commits it brought in are
one change, and the merge commit's body is where the block is. Rule the merge
commit and skip what is under it, or the same note is counted twice.
**Pass 3, write.** Only from the rows where `observable` is yes, and from
`entry` as the table holds it.
Collapse a class only when a member of it is observable. Nine dependency
bumps that change no behaviour are not one shorter entry, they are no entry.
Several commits producing one observable change are one entry.
Write the body to the `scratchFile` path from pass 1, show it to the user,
then:
```bash
node "${CLAUDE_PLUGIN_ROOT}/bin/agent-notes.mjs" section
```
That puts it in `CHANGELOG.md` under `## - `, above every
older release and below the preamble, and leaves the same bytes in the
scratch file for whatever publishes the release. It refuses a version the
changelog already carries. Pass the same `--path` you passed to `commits`, or
the entry lands in the wrong file. `--at ` dates the entry from that
commit rather than today, for a release written up after the fact.
## A repository that writes its entries per change
Where `.releasetools.yaml` does not except `changelog-per-change`, every
change wrote its own entry as it merged and the section is already there.
Then this pass is an audit: read the section against the range and say what
is missing, rather than writing it again.