Skip to content

fix: stamp release notes at release time, so users actually see them - #34

Merged
ThorSPB merged 1 commit into
mainfrom
fix/stamp-release-notes
Sep 8, 2026
Merged

ThorSPB merged 1 commit into
mainfrom
fix/stamp-release-notes

Conversation

@ThorSPB

@ThorSPB ThorSPB commented Sep 8, 2026 •

Copy link
Copy Markdown
Owner

The problem

changelog.py deliberately ignores non-version headings, so anything sitting
under ## Unreleased is invisible to users — no error, it simply never
appears. 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 ## Unreleased and showed its
users 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.py renames ## Unreleased → ## v<version> in both
language files:

  • idempotent — a re-run must not stamp twice, which would swallow the next
    version's notes into an already-released section
  • leaves an empty Unreleased alone — a release with no user-visible change
    must not ship a bare heading
  • refuses a non-x.y.z version; exit 1 means "nothing to stamp", a normal
    outcome that must not fail the release

release.yml runs it in create-release before the tag is cut and commits
the result, so the notes actually leave ## Unreleased rather than being
re-shipped verbatim under the next version too. That commit carries a skip-CI marker,
or the push to main would 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 works
once #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, a
bad version, and the point of the whole exercise — that notes_since() returns
nothing 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 main too.
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.

`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
ThorSPB force-pushed the fix/stamp-release-notes branch from ba850f2 to e386e0e Compare September 8, 2026 15:36
@ThorSPB
ThorSPB merged commit bf42f74 into main Sep 8, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant