Problem
When cutting the 0.26.0 release, the ## Unreleased section of CHANGELOG.md was empty despite 5 merged PRs (#17, #18, #21, #22, #23). The release notes had to be reconstructed by hand from git log v0.25.0..HEAD plus each PR's body.
This is a recurring hygiene gap: nothing enforces that a PR which changes shipped behavior also records an entry under ## Unreleased, so the changelog silently drifts out of sync with main and the release author has to back-fill it under time pressure (exactly when mistakes are most likely).
Proposed fix
Add a lightweight CI check on pull_request that fails when a PR touches source paths but does not add a line under ## Unreleased in CHANGELOG.md.
Sketch:
- Trigger:
pull_request touching src/** (and any other shipped-code paths — e.g. packages/**, installer scripts/workflows).
- Diff the PR against the base; require that
CHANGELOG.md has at least one added line within the ## Unreleased block.
- Provide an escape hatch for changes that genuinely don't warrant a changelog entry — a
no-changelog PR label or a [skip changelog] marker in the PR body.
- Keep it advisory-but-required: a clear failure message pointing at
## Unreleased so the fix is obvious.
Out of scope / notes
- Release-prep PRs (
chore(release): prepare X.Y.Z) consume ## Unreleased into a dated block and reset it to empty — the check must not fire on those (skip on the release/* head branch or the chore(release) title).
- This is not a blocker for any release; it's preventive. The release runbook already documents reconstructing notes from
git log when ## Unreleased is empty.
Surfaced during the 0.26.0 release (PRs #24 / #25).
Problem
When cutting the 0.26.0 release, the
## Unreleasedsection ofCHANGELOG.mdwas empty despite 5 merged PRs (#17, #18, #21, #22, #23). The release notes had to be reconstructed by hand fromgit log v0.25.0..HEADplus each PR's body.This is a recurring hygiene gap: nothing enforces that a PR which changes shipped behavior also records an entry under
## Unreleased, so the changelog silently drifts out of sync withmainand the release author has to back-fill it under time pressure (exactly when mistakes are most likely).Proposed fix
Add a lightweight CI check on
pull_requestthat fails when a PR touches source paths but does not add a line under## UnreleasedinCHANGELOG.md.Sketch:
pull_requesttouchingsrc/**(and any other shipped-code paths — e.g.packages/**, installer scripts/workflows).CHANGELOG.mdhas at least one added line within the## Unreleasedblock.no-changelogPR label or a[skip changelog]marker in the PR body.## Unreleasedso the fix is obvious.Out of scope / notes
chore(release): prepare X.Y.Z) consume## Unreleasedinto a dated block and reset it to empty — the check must not fire on those (skip on therelease/*head branch or thechore(release)title).git logwhen## Unreleasedis empty.Surfaced during the 0.26.0 release (PRs #24 / #25).