Repository navigation
fix: stamp release notes at release time, so users actually see them - #34
Merged
Merged
Conversation
`changelog.py` ignores non-version headings, so anything under `## Unreleased` is invisible to users - and nothing ever moved those notes out. v1.6.1 shipped with both its notes still unreleased and showed its users no release notes at all. Nobody was stamping them by hand, so the release has to. - `scripts/stamp_changelog.py` renames `## Unreleased` to `## v<version>` in both language files. Idempotent (a re-run must not swallow the next version's notes into a released section), refuses a non-x.y.z version, and leaves an empty Unreleased alone so a release with no user-visible change cannot ship a bare heading. Exit 1 means "nothing to stamp", which is a normal outcome. - `release.yml` runs it in `create-release` **before the tag is cut** and commits the result, so the notes leave `## Unreleased` instead of being re-shipped verbatim under the next version too. That commit carries a skip-CI marker, since a push to main would otherwise start a second Release run. - All four build jobs now check out **the tag** rather than the triggering commit - the stamp only exists on the tag, so otherwise they would each build the pre-stamp changelog. They are already gated on a non-empty version, and the tag exists by then on both trigger paths (push+bump, and tag push). Also corrects the history the missing stamp left behind: the icon fix did ship in v1.6.1 and now sits under that heading, while the Made-for-Kids fix stays under Unreleased, since it only actually works once the solver-script fix lands. A user going 1.6.1 -> 1.6.2 then sees exactly the one note that is new to them. Tests cover the rename, idempotency, the empty section, a bad version, and the point of the whole thing - that `notes_since()` returns nothing before stamping and the note afterwards. Idempotency and the empty-section guard were both mutation-checked. Learned the hard way while writing this: the first version of this very commit message quoted the skip-CI token verbatim to explain it, which skipped CI on the PR. CLAUDE.md now warns about it - a squash merge would carry it to main too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ThorSPB
force-pushed
the
fix/stamp-release-notes
branch
from
September 8, 2026 15:36
ba850f2 to
e386e0e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
changelog.pydeliberately ignores non-version headings, so anything sittingunder
## Unreleasedis invisible to users — no error, it simply neverappears. Notes get written under that heading as work lands, and nothing ever
moved them out.
v1.6.1 shipped with both its notes still under
## Unreleasedand showed itsusers no release notes at all. You said you never move them by hand, so the
release has to do it.
The fix
scripts/stamp_changelog.pyrenames## Unreleased→## v<version>in bothlanguage files:
version's notes into an already-released section
must not ship a bare heading
x.y.zversion; exit 1 means "nothing to stamp", a normaloutcome that must not fail the release
release.ymlruns it increate-releasebefore the tag is cut and commitsthe result, so the notes actually leave
## Unreleasedrather than beingre-shipped verbatim under the next version too. That commit carries a skip-CI marker,
or the push to
mainwould start a second Release run.All four build jobs now check out the tag rather than the triggering commit.
This is the subtle half: the stamp only exists on the tag, so without it every
build would package the pre-stamp changelog and the fix would do nothing
visible. They are already gated on a non-empty version, and the tag exists by
then on both trigger paths (push + bump tag, and a direct tag push).
Also corrects the history the missing stamp left behind
The icon fix did ship in v1.6.1, so it now sits under that heading. The
Made-for-Kids note stays under
## Unreleased, because it only actually worksonce #33's solver-script fix lands. So a user going 1.6.1 → 1.6.2 sees exactly
the one note that is new to them, and a user on 1.6.0 sees both.
Tests
308 passed. Five new tests cover the rename, idempotency, the empty section, abad version, and the point of the whole exercise — that
notes_since()returnsnothing before stamping and returns the note after. Idempotency and the
empty-section guard were both mutation-checked.
Note
The first push of this branch got no CI at all: my commit message quoted the
skip-CI token verbatim while explaining it, and GitHub honours that token
anywhere in the message. A squash merge would have carried it onto
maintoo.Scrubbed from both the message and this body, and CLAUDE.md now warns about it.
No bump tag in this title, so merging it cuts no release on its own.