Changelog Workmanship
Use this skill to write or repair a CHANGELOG.md so a user deciding whether to
upgrade can read one section and know what changed, what broke, and what to do.
A changelog is a product surface, not a git log dump.
⚠️ Critical Constraints
- Write user-facing change, not commit subjects. A changelog answers "what
does this mean for me?", not "what did the author touch."
Why: raw commit messages describe implementation; users need outcomes.
- WRONG:
- refactor auth middleware to use jwt-decode v4 (a1b2c3d)
- CORRECT:
- Fixed: expired sessions now log out cleanly instead of looping
- Never invent or backdate entries. Every entry must trace to real shipped
change. Why: a fabricated history is worse than none — it destroys trust
and misleads upgraders.
- Keep an
## [Unreleased] section at the top, always. Why: the file
stays current only if entries land with the change, not at release time when
context is gone.
- WRONG: writing the whole changelog the day you cut the release from
git log.
- CORRECT: each PR adds its line to
[Unreleased]; release moves the block.
- Version headings must match real tags and semver. Why: a user pins a
version and expects the heading, the tag, and the breaking-change signal to
agree; a Patch bump that hides a breaking change is a workmanship failure.
Why This Exists
Most projects either have no changelog or have an autogenerated commit dump that
no human reads. Both fail the one job: helping a person decide whether and how to
upgrade. Generation defaults to either over-terse ("bug fixes") or commit-shaped
noise. This skill is the forcing function for the disciplined middle: structured,
grouped, audience-facing, semver-aligned, and maintained continuously.
Quick Start
- Locate or create
CHANGELOG.md at the repo root.
- Reconstruct history from tags + the commits/PRs/issues between them:
git tag --sort=-creatordate
git log --oneline v1.1.0..v1.2.0
- For each release, group changes under the standard headings (below).
- Translate each entry from commit language into user-facing language.
- Confirm version order, dates, and semver alignment.
- Run
bash scripts/validate.sh and report the verdict.
The Keep-a-Changelog Structure
A conforming file has this shape:
# Changelog
All notable changes to this project are documented here.
The format is based on Keep a Changelog, and this project adheres to
Semantic Versioning.
## [Unreleased]
### Added
- New thing users can now do.
## [1.2.0] - 2026-06-06
### Added
- ...
### Changed
- ...
### Fixed
- ...
## [1.1.0] - 2026-05-01
### Removed
- ...
[Unreleased]: https://example.com/compare/v1.2.0...HEAD
[1.2.0]: https://example.com/compare/v1.1.0...v1.2.0
Rules that hold every time:
- Releases are listed newest first, each with
## [version] - YYYY-MM-DD.
[Unreleased] sits at the top with no date.
- Dates are ISO 8601 (
YYYY-MM-DD), unambiguous across locales.
The Six Groups
Group every entry under exactly one of these standard headings, omitting any
group with no entries:
- Added — new features or capabilities.
- Changed — changes to existing behavior.
- Deprecated — soon-to-be-removed features (warn before removal).
- Removed — features taken out this release.
- Fixed — bug fixes.
- Security — vulnerability fixes (call these out explicitly).
Semver Alignment
The release type the changelog implies must match the version bump:
- Anything under Removed, or a breaking Changed entry → Major bump.
- Anything under Added (backward-compatible) → Minor bump.
- Only Fixed / Security → Patch bump.
Mark breaking changes loudly — a **BREAKING:** prefix on the entry, plus the
Major bump. A changelog that buries a breaking change under a Patch heading is
the most damaging failure mode.
Audit Flow (for an existing changelog)
- Confirm the structural skeleton: title,
[Unreleased], newest-first
releases, dated headings, comparison links.
- Read each entry as a user: does it describe an outcome, or an implementation
detail / bare commit subject? Rewrite the latter.
- Check grouping: is every entry under a correct standard heading? Are empty
groups omitted?
- Cross-check each version heading against the real tag and the bump type the
entries imply.
- Verify comparison/diff links resolve and the
[Unreleased] link points at
the latest tag.
- Confirm the maintenance discipline exists (a CONTRIBUTING note or release
step that adds to
[Unreleased]); if not, recommend one.
Keeping It Current (the release flow)
The changelog stays good only if it is edited continuously, not reconstructed:
- Every change-bearing PR adds a line under the right group in
[Unreleased].
- Cutting release
X.Y.Z is a mechanical move: rename [Unreleased] to
[X.Y.Z] - <today>, add a fresh empty [Unreleased], update the diff links,
tag the commit.
- Never let "write the changelog" become a release-day archaeology task.
Output Specification
Return a workmanship report (also written to CHANGELOG.md when repairing):
- Verdict: pass, needs work, or blocked.
- Findings: ordered by severity, each tied to a changelog property.
- Changes: what was edited and why.
- Verification: commands run and their results.
- Residual risk: history gaps, unverifiable entries, missing tags.
Quality Rubric
- Every entry reads as a user-facing outcome, with zero raw commit hashes or
subjects left in prose form.
- Every entry sits under a correct standard group; no empty groups are emitted;
releases are newest-first with ISO dates.
- The latest release heading matches a real tag, and the implied bump type
(Major/Minor/Patch) matches the actual version, with breaking changes marked.
Examples
Rewriting a commit dump into a release entry:
Troubleshooting
| Symptom |
Likely cause |
Fix |
| Changelog is a commit dump |
Generated from git log at release time |
Adopt the [Unreleased] continuous-edit flow; rewrite entries as outcomes. |
| Users surprised by a breaking change |
Breaking change under a Patch/Minor bump |
Re-bump to Major; add **BREAKING:** prefix and a migration note. |
| Dates ambiguous or missing |
Locale-specific or absent dates |
Use ISO 8601 YYYY-MM-DD on every dated release. |
| Comparison links 404 |
Stale diff links after a tag |
Update [version]: <compare-url> and the [Unreleased] link to the latest tag. |
| Empty headings everywhere |
Boilerplate template not pruned |
Omit any group (Added/Changed/...) with no entries this release. |
See Also
- Keep a Changelog conventions (structure + group names).
- Semantic Versioning (Major/Minor/Patch bump rules).
scripts/validate.sh — structural self-check for this skill.
1---2name: changelog-quality-pass-23description: Use when writing or auditing changelogs and release notes for user-facing, semver-aware clarity. Triggers:4---5
6# Changelog Workmanship
7
8Use this skill to write or repair a `CHANGELOG.md` so a user deciding whether to
9upgrade can read one section and know what changed, what broke, and what to do.
10A changelog is a product surface, not a `git log` dump.
11
12## ⚠️ Critical Constraints
13
14- **Write user-facing change, not commit subjects.** A changelog answers "what
15 does this mean for me?", not "what did the author touch."
16 **Why:** raw commit messages describe implementation; users need outcomes.
17 - WRONG: `- refactor auth middleware to use jwt-decode v4 (a1b2c3d)`
18 - CORRECT: `- Fixed: expired sessions now log out cleanly instead of looping`
19- **Never invent or backdate entries.** Every entry must trace to real shipped
20 change. **Why:** a fabricated history is worse than none — it destroys trust
21 and misleads upgraders.
22- **Keep an `## [Unreleased]` section at the top, always.** **Why:** the file
23 stays current only if entries land with the change, not at release time when
24 context is gone.
25 - WRONG: writing the whole changelog the day you cut the release from `git log`.
26 - CORRECT: each PR adds its line to `[Unreleased]`; release moves the block.
27- **Version headings must match real tags and semver.** **Why:** a user pins a
28 version and expects the heading, the tag, and the breaking-change signal to
29 agree; a Patch bump that hides a breaking change is a workmanship failure.
30
31## Why This Exists
32
33Most projects either have no changelog or have an autogenerated commit dump that
34no human reads. Both fail the one job: helping a person decide whether and how to
35upgrade. Generation defaults to either over-terse ("bug fixes") or commit-shaped
36noise. This skill is the forcing function for the disciplined middle: structured,
37grouped, audience-facing, semver-aligned, and maintained continuously.
38
39## Quick Start
40
411. Locate or create `CHANGELOG.md` at the repo root.
422. Reconstruct history from tags + the commits/PRs/issues between them:
43 ```bash
44 git tag --sort=-creatordate
45 git log --oneline v1.1.0..v1.2.0
46 ```
473. For each release, group changes under the standard headings (below).
484. Translate each entry from commit language into user-facing language.
495. Confirm version order, dates, and semver alignment.
506. Run `bash scripts/validate.sh` and report the verdict.
51
52## The Keep-a-Changelog Structure
53
54A conforming file has this shape:
55
56```markdown
57# Changelog
58
59All notable changes to this project are documented here.
60The format is based on Keep a Changelog, and this project adheres to
61Semantic Versioning.
62
63## [Unreleased]
64
65### Added
66- New thing users can now do.
67
68## [1.2.0] - 2026-06-06
69
70### Added
71- ...
72### Changed
73- ...
74### Fixed
75- ...
76
77## [1.1.0] - 2026-05-01
78
79### Removed
80- ...
81
82[Unreleased]: https://example.com/compare/v1.2.0...HEAD
83[1.2.0]: https://example.com/compare/v1.1.0...v1.2.0
84```
85
86Rules that hold every time:
87- Releases are listed **newest first**, each with `## [version] - YYYY-MM-DD`.
88- `[Unreleased]` sits at the top with no date.
89- Dates are ISO 8601 (`YYYY-MM-DD`), unambiguous across locales.
90
91## The Six Groups
92
93Group every entry under exactly one of these standard headings, omitting any
94group with no entries:
95
96- **Added** — new features or capabilities.
97- **Changed** — changes to existing behavior.
98- **Deprecated** — soon-to-be-removed features (warn before removal).
99- **Removed** — features taken out this release.
100- **Fixed** — bug fixes.
101- **Security** — vulnerability fixes (call these out explicitly).
102
103## Semver Alignment
104
105The release type the changelog implies must match the version bump:
106
107- Anything under **Removed**, or a breaking **Changed** entry → **Major** bump.
108- Anything under **Added** (backward-compatible) → **Minor** bump.
109- Only **Fixed** / **Security** → **Patch** bump.
110
111Mark breaking changes loudly — a `**BREAKING:**` prefix on the entry, plus the
112Major bump. A changelog that buries a breaking change under a Patch heading is
113the most damaging failure mode.
114
115## Audit Flow (for an existing changelog)
116
1171. Confirm the structural skeleton: title, `[Unreleased]`, newest-first
118 releases, dated headings, comparison links.
1192. Read each entry as a user: does it describe an outcome, or an implementation
120 detail / bare commit subject? Rewrite the latter.
1213. Check grouping: is every entry under a correct standard heading? Are empty
122 groups omitted?
1234. Cross-check each version heading against the real tag and the bump type the
124 entries imply.
1255. Verify comparison/diff links resolve and the `[Unreleased]` link points at
126 the latest tag.
1276. Confirm the maintenance discipline exists (a CONTRIBUTING note or release
128 step that adds to `[Unreleased]`); if not, recommend one.
129
130## Keeping It Current (the release flow)
131
132The changelog stays good only if it is edited continuously, not reconstructed:
133
134- Every change-bearing PR adds a line under the right group in `[Unreleased]`.
135- Cutting release `X.Y.Z` is a mechanical move: rename `[Unreleased]` to
136 `[X.Y.Z] - <today>`, add a fresh empty `[Unreleased]`, update the diff links,
137 tag the commit.
138- Never let "write the changelog" become a release-day archaeology task.
139
140## Output Specification
141
142Return a workmanship report (also written to `CHANGELOG.md` when repairing):
143
144- **Verdict:** pass, needs work, or blocked.
145- **Findings:** ordered by severity, each tied to a changelog property.
146- **Changes:** what was edited and why.
147- **Verification:** commands run and their results.
148- **Residual risk:** history gaps, unverifiable entries, missing tags.
149
150## Quality Rubric
151
152- Every entry reads as a user-facing outcome, with zero raw commit hashes or
153 subjects left in prose form.
154- Every entry sits under a correct standard group; no empty groups are emitted;
155 releases are newest-first with ISO dates.
156- The latest release heading matches a real tag, and the implied bump type
157 (Major/Minor/Patch) matches the actual version, with breaking changes marked.
158
159## Examples
160
161Rewriting a commit dump into a release entry:
162
163- Input commits: `fix: npe in retry`, `chore: bump deps`, `feat: add --json`.
164- Output:
165 ```markdown
166 ## [2.3.0] - 2026-06-06
167 ### Added
168 - `--json` flag for machine-readable output.
169 ### Fixed
170 - No longer crashes when a request is retried after a timeout.
171 ```
172 (`chore: bump deps` is dropped — not user-facing unless it changes behavior.)
173
174## Troubleshooting
175
176| Symptom | Likely cause | Fix |
177| --- | --- | --- |
178| Changelog is a commit dump | Generated from `git log` at release time | Adopt the `[Unreleased]` continuous-edit flow; rewrite entries as outcomes. |
179| Users surprised by a breaking change | Breaking change under a Patch/Minor bump | Re-bump to Major; add `**BREAKING:**` prefix and a migration note. |
180| Dates ambiguous or missing | Locale-specific or absent dates | Use ISO 8601 `YYYY-MM-DD` on every dated release. |
181| Comparison links 404 | Stale diff links after a tag | Update `[version]: <compare-url>` and the `[Unreleased]` link to the latest tag. |
182| Empty headings everywhere | Boilerplate template not pruned | Omit any group (Added/Changed/...) with no entries this release. |
183
184## See Also
185
186- Keep a Changelog conventions (structure + group names).
187- Semantic Versioning (Major/Minor/Patch bump rules).
188- `scripts/validate.sh` — structural self-check for this skill.