docs/book/src/maintainers/changelog-generation.md
The authoritative procedure for assembling CHANGELOG-next.md between stable releases. This page is loaded by the changelog-generation skill and read by maintainers running a release manually — both consume the same protocol.
The release workflows (release-stable-manual.yml) automatically use CHANGELOG-next.md as the GitHub Release body if it's at the repo root when a release fires. After the stable release ships, the workflow deletes the file. No manual cleanup needed.
PREV_TAG=$(git tag --sort=-creatordate | grep -vE '\-beta\.' | head -1)
echo "Range: ${PREV_TAG}..HEAD"
Accept any of the following and normalize to <from>..<to>:
| Input | Interpretation |
|---|---|
v0.7.2 | v0.7.2..HEAD |
v0.7.1..v0.7.2 | Exactly as given |
v0.7.1 v0.7.2 | v0.7.1..v0.7.2 |
Verify both refs exist before proceeding:
git rev-parse --verify <ref> 2>/dev/null || echo "ERROR: ref not found"
git log <from>..<to> --pretty=format:"%H %h %s" --no-merges
Save full SHAs for the contributor resolution step:
git log <from>..<to> --pretty=format:"%H" --no-merges > /tmp/zc-commits.txt
Map each commit to a section by its conventional commit prefix. Commits without a recognized prefix must still be read and categorized by content — never silently drop them.
| Prefix | Section |
|---|---|
feat:, feat(*) | What's New |
fix:, fix(*) | Bug Fixes |
refactor:, perf: | What's New (group as "Improvements") |
security:, fix(*security*) | What's New → Security |
docs:, docs(*) | What's New → Documentation (omit trivial typo fixes) |
chore:, ci:, build: | Omit unless user-visible (new install path, dropped platform, etc.) |
breaking: or ! suffix | Breaking Changes — always surface |
| No prefix | Read body; categorize by content; note in review |
Do not use git log --pretty=format:"%an" alone — it misses everyone listed in Co-Authored-By trailers. Use the GitHub GraphQL authors field, which resolves direct authors and co-authors.
Paginate in batches of 100 commits. Use pageInfo.endCursor while hasNextPage is true.
{
repository(owner: "zeroclaw-labs", name: "zeroclaw") {
ref(qualifiedName: "refs/heads/master") {
target {
... on Commit {
history(first: 100) {
pageInfo { hasNextPage endCursor }
nodes {
oid
authors(first: 10) {
nodes {
user { login }
email
}
}
}
}
}
}
}
}
}
Run via gh:
gh api graphql -f query='<query>'
By login pattern:
[bot]web-flowdependabotgithub-actionsblacksmithBy email pattern:
*@noreply.github.com*@noreply.anthropic.com*noreply*AI model names appearing as author names (not logins):
Claude, Copilot, ChatGPT, Codex, Gemini, GitHub Copilot^(gpt|claude|gemini|copilot)-Cross-reference each oid from the GraphQL response against /tmp/zc-commits.txt to include only commits within the release range. Collect unique logins, sort case-insensitively, prefix each with @.
Two or three sentences. Describe the release theme, scale, and anything a reader skimming the title needs before reading on. Write for a non-technical reader.
Four to six bullets. Lead with user-visible impact, not implementation detail. Each bullet should answer: "What can I do now that I couldn't before?" or "What just got better?"
Group entries by area. Use only groups that have content.
Suggested groups (add or omit freely):
Write each entry as a sentence for a human reader — not a raw commit message. Reference PR numbers with (#NNNN) where available.
A summary table. Columns: Area | Fix. Collapse multiple fixes for the same feature into one row when that reads more clearly than separate rows.
Call out every breaking change with a migration path. Look for:
If there are no breaking changes, omit this section entirely.
@login handles from step 3, sorted case-insensitively, one per line.
*Full diff: `git log <prev-tag>..<next-tag> --oneline`*
Write to CHANGELOG-next.md at the repository root — that's the path the release workflows look for. A copy also lands at tmp/CHANGELOG-next.md for in-session review before committing.
git add CHANGELOG-next.md
git commit -m "chore(release): add CHANGELOG-next.md for vX.Y.Z"
Replace vX.Y.Z with the next release version. Ask the user for confirmation before committing.
Push to the open release PR branch on zeroclaw-labs/zeroclaw:
git push upstream <branch>
Don't push directly to master.
release-stable-manual.yml checks for CHANGELOG-next.md at the start of the release job. If found, its content becomes the GitHub Release body. If not found, the workflow falls back to auto-generated feat:-only notes.
After a successful stable release, the workflow automatically deletes CHANGELOG-next.md and commits the removal. No manual cleanup is required.