Most “AI, write the changelog” sessions produce a bulleted dump of commit messages that nobody finishes reading. The reason is structural: git log is written for the author at commit time, and a release note is written for the reader at release time. Different audiences, different framing. This workflow takes you from raw commits to a two-tier release note (one section users read, one section developers read) without losing the technical accuracy that makes the note trustworthy.
TL;DR
- Deterministic tools like
git-cliffand GitHub’s built-in release notes group commits and PRs by type for free. They are fast and never invent. Use them as your floor. - An LLM (Claude Sonnet 4.6 or GPT-5.5, as of June 2026) earns its keep on exactly one job: rewriting the user-facing tier into plain language a non-engineer finishes.
- The workflow is: deterministic grouping first, then AI rewrite of the user tier only, then a human review pass focused on breaking changes.
- A 45-commit release takes 20-30 minutes this way, versus roughly 90 minutes by hand.
- Never paste only
git log. PR titles and labels carry the intent commits leave out.
Where AI fits (and where it doesn’t)
Changelog tooling is a spectrum from fully deterministic to fully generative. Each rung does one thing well.
| Approach | What it does | Invents? | Best for |
|---|---|---|---|
git-cliff | Parses Conventional Commits into Keep a Changelog sections via Tera templates; ~120ms for 10k commits | No | Library/CLI repos with strict commit discipline |
release-please / semantic-release | Bumps version from commit types, opens release PR, writes CHANGELOG | No | CI-driven semver automation |
GitHub auto-notes (gh release create --generate-notes) | Lists merged PRs by label, credits contributors, links full diff | No | Quick GitHub-native release pages |
| AI rewrite (this workflow) | Translates grouped commits into reader-facing prose by audience tier | Yes, if unchecked | Mixed-audience notes where users, support, and engineers read the same doc |
The deterministic tools never hallucinate but also never explain why a user should care. The AI does the explaining, which is the part a git-cliff template cannot do. So the strong move is to layer them: let a deterministic tool produce the grouped skeleton, then have the model rewrite only the user tier.
Who this is for
Maintainers who cut releases weekly or biweekly, indie devs shipping product updates, and tech leads who own the release note but resent spending an hour reading 45 commits to write it.
When to reach for it
Releases with 20-100 commits where the surface area is large enough that no single person remembers everything that changed. Semver-disciplined repos where commit messages follow at least loose Conventional Commits convention (feat:, fix:, chore:). End-of-sprint notes that go to a mixed audience.
When this is NOT the right tool
- Security releases. The wording matters too much to delegate, and you do not want the model paraphrasing a CVE description. Write
Security:entries by hand. - Bare tags without commit discipline (one-line “wip” messages). The AI has nothing to ground on and will invent.
- Tiny patch releases (1-3 commits). Write it yourself in 2 minutes.
Before you start
- Pick the tag range.
git log v1.2.0..HEAD --onelineis the canonical input. If you do not tag, use the last release SHA. - Pull PR titles and labels too. They often carry more user-facing intent than the commit messages.
gh pr list --state merged --search "merged:>2026-05-01"works. - Decide the audience tiers in advance. Most projects need exactly two: “For users” (behavior changes, new features, breaking changes) and “For developers” (internal refactors, dependency bumps, infra). One tier is fine for libraries.
- Have the previous changelog open. Tone, section order, and category names should match. Readers learn your format and you should not break it without reason. If you have not picked a format, the six Keep a Changelog sections (Added, Changed, Deprecated, Removed, Fixed, Security) are the default everyone recognizes.
Step by step
- Generate the raw inputs. A typical mid-size release: 45 commits across 3 weeks.
git log v1.2.0..HEAD --oneline > /tmp/commits.txt gh pr list --state merged --base main --search "merged:>=2026-05-01" \ --json number,title,labels > /tmp/prs.json - Group deterministically first if you can. Run
git-cliff v1.2.0..HEAD(it follows Conventional Commits and emits Keep a Changelog sections), orgh release create v1.3.0 --generate-notes --draftfor the GitHub-native PR list. This skeleton is your grounding truth so the model has less room to drift. - Paste the skeleton (or the raw inputs) into one message. Tell the model the audience tiers and the section names you want.
- Ask for grouping first, no prose. “Group these into Added / Changed / Fixed / Removed. One line per commit. Keep PR numbers.” This surfaces mis-categorized commits early and is cheap to review.
- Review the grouping. Move anything mis-bucketed. A
fix:that is actually a feature gets moved to Added. Arefactor:that changes a user-visible default gets moved to Changed. Anything with a!orBREAKING CHANGE:footer is a major-version signal; make sure it surfaces. - Ask the model to rewrite each line for the user-tier section. “Rewrite the Added / Changed / Removed sections for non-engineer readers. Skip the internal-refactor items.” This is the step that separates a release note from a commit dump.
- Ask for a separate developer-tier section with the internal items left intact. Dependency bumps, refactors, test-only changes go here.
- Read both sections out loud. If you trip on a sentence, rewrite it. Then check every breaking change is called out explicitly with a migration note.
A prompt that produces honest output
Use [bracketed placeholders] so nothing reads as a literal template token.
Here are 45 commits from v1.2.0..v1.3.0:
[paste git log --oneline output]
Here are the merged PR titles with labels:
[paste gh pr list JSON]
Produce a release note with two sections:
1. "For users" — only user-visible changes (new features, behavior
changes, bug fixes that affect users, breaking changes with migration
notes). Plain language. No internal jargon.
2. "For developers" — internal refactors, dependency bumps, infra,
test-only changes, anything not user-visible.
Within each section, group as: Added / Changed / Fixed / Removed.
Keep PR numbers in parentheses.
Rules:
- Do NOT invent features. If a commit message is unclear, mark it
"[unclear - needs review]" and I will check.
- Do NOT soften breaking changes. Call them out with a "Breaking:" prefix
and a one-line migration note.
- Do NOT merge unrelated commits into one line.
Any current frontier model handles this well. As of June 2026, Claude Sonnet 4.6 and GPT-5.5 both have a 1M-token context standard (full 1M in the ChatGPT app needs the $200 Pro tier), so even a 200-commit release pastes in whole. The model choice matters less than the review pass.
Quality check
- Every breaking change is labeled “Breaking:” with a migration note. AI tends to bury these. Re-grep the diff for API changes and confirm each one shows up. Cross-reference the
!markers andBREAKING CHANGE:footers fromgit log. - No invented features. Cross-check anything that sounds new against the actual PR. The model will sometimes elaborate a one-line commit into a paragraph that promises more than was shipped.
- User-tier section has no internal jargon. No “refactored the FooService”; that belongs in the developer tier.
- Counts roughly match. 45 commits in, somewhere between 20 and 35 lines out. If the AI gave you 12 lines, it collapsed too much.
- Tone matches the previous changelog. The same person could have written both.
How to reuse this workflow
- Save the two-tier prompt as a snippet. The same template works release to release; only the input changes.
- Keep a “Breaking change checklist”: API removals, default changes, migration steps required. Re-check every release.
- Wire
git-cliff(orgh release --generate-notes) into a script that dumps the grouped skeleton plus rawgit logandgh pr listinto one file ready to paste. That removes most of the friction and gives the model a deterministic grounding layer. - After each release, note any mis-categorized items and add an example to the prompt for next time. The prompt gets sharper.
Recommended workflow
git-cliff (or git log + PR titles) produces grouped sections → AI rewrites the user tier in plain language → AI emits the developer tier verbatim → human reviews breaking changes and reads aloud → ship. For a 45-commit release this takes 20-30 minutes, versus roughly 90 minutes by hand. For a 7-section note, expect 3-4 rounds of “this line is wrong” corrections.
Common mistakes
- Pasting only
git logwithout PR titles. Commits are written tersely; PR titles carry the intent. You lose half the signal. - Letting AI write the user-tier section in one shot, no grouping pass. Mis-categorized items survive into the final note.
- Skipping the read-aloud pass. Awkward AI phrasing slips through and makes the whole note feel machine-generated.
- Trusting the AI on breaking changes. It will soften “Removed X” into “Updated X.” Always re-verify each removal against the diff.
- Using it on security releases. Wording matters too much; do those by hand.
FAQ
- Why not just use git-cliff or GitHub auto-notes and skip AI?: Do use them for the grouping. They are deterministic and never invent. But they output the commit subject verbatim, so a user reads “fix: null guard in token refresh” instead of “Fixed a sign-out bug on slow networks.” The AI’s only job is that translation. Pair the two.
- What if commit messages are bad?: AI can still produce a draft from PR titles plus issue references, but quality drops fast. Long term, the fix is commit discipline (Conventional Commits, PR templates), which also lets the deterministic tools work.
- Can the AI tag PRs by section automatically?: Yes, if PRs carry labels (
type:feature,type:fix). Labels are a stronger signal than the message. GitHub’s auto-notes group by label too, so set them once and both layers benefit. - Should I let it write the headline summary?: Draft yes, ship no. The one-sentence summary at the top of a release note is the part users actually read. Write it yourself once the body is final.
- What about per-commit attribution?: If your project credits contributors, ask the model to keep authors.
git log --pretty='%h %an %s'gives the input. Verify the names against the real list before publishing. GitHub auto-notes credit contributors automatically.
Related
Tags: #AI coding #Workflow