fix: the release suite accepts the ceremony's own tree #109
Labels
No labels
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-triage
ready
release
scope:apply
scope:capture
scope:coolify-api
scope:fleet
scope:manifest
scope:secrets
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/cast#109
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/release-suite-stamped-tree"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #108. The cast twin of heavy-duty/rig#45 (rig#44) — same disease, same cure.
What
test/release.test.ts:148-152demanded the REALCHANGELOG.md's literalUnreleasedsection extract non-empty and contain#96. The ceremony's ownrelease: X.Y.ZPR stamps that heading into## X.Y.Z — date, so the first real release PR turned CI red by construction — and post-release main stays red until the next feature PR re-adds the section. Invisible to both drill rehearsals: a fork rehearsal tags a branch, which runsrelease.ymland neverci.yml.How
The test now reads the top
##heading's token —Unreleasedbetween releases, the stampedX.Y.Zon and right after one — and asserts the exactrelease-notes.shthe workflow runs extracts it non-empty. The hardcoded issue-number grep is gone (no format coverage, rots per release).Tests
Verified on both legitimate tree states: main's shape and a stamped copy (
## 0.1.0 — 2026-07-19) — 24/24 both ways; full suite 606/606; biome clean.Unblocks the
release: 0.1.0PR, which follows once this lands.🤖 Generated with Claude Code
Verdict: I agree with everything and have no additional feedback.
Verdict: approve.
The diagnosis in #108 checks out against the code.
release.ymlstamps## X.Y.Z — dateover## Unreleasedvia the ceremony's own PR, and the old test (test/release.test.ts, pre-diff lines 148-152) hardcoded both the literalUnreleasedtoken and a#96grep — false by construction on the release tree and on post-release main. The fix targets exactly that and nothing else.Why the new acceptance stays narrow:
/^## (\S+)/m(test/release.test.ts:159) yields the first whitespace-delimited token of the top heading, andrelease-notes.shcompares awk's$2of^##lines whole (.github/scripts/release-notes.sh:25-28). Same delimiter, same anchor —Unreleasedand0.1.0 — 2026-07-19both round-trip correctly.##heading throws explicitly (test/release.test.ts:160); an empty top section still fails, because the script exits 1 on empty extraction (release-notes.sh:30) and the test pinscode === 0plus non-empty output (test/release.test.ts:162-163). I verified this: stamping the real changelog's top heading to## 0.1.0 — 2026-07-19passes 24/24, and an empty stamped top section fails the test.#96grep loses no coverage. The#96ritual citation is still pinned by the missing-version test (test/release.test.ts:126), where it belongs — in the script's own error message, not in rotting changelog content.release.ymlextracts by$GITHUB_REF_NAMEafter asserting tag ==package.jsonversion, and a section missing for that version fails beforegh release createruns. The test's job is drift detection on the extraction tool against the real file, which it still does.Ran the suite in a worktree at head
d7d715a: build clean, 606/606 tests pass,biome check --error-on-warningsclean, and the stamped-tree state passes as claimed in the PR body.One optional tightening for a future pass, not blocking: the test accepts any
\S+top token, so a top heading that is neitherUnreleasednorpackage.json's version would still pass. Assertingtop[1]is one of those two would close that last sliver, at the cost of coupling the test to the version-bump ordering inside the release PR — reasonable to leave as is.— automated review by
claude-bot-andresmgsl· heavy-duty-review-botVerdict: Approve — I agree with this as-is.
Why
Unreleasedplus a rotting issue-number grep (#96), both false by construction on the ceremony'srelease: X.Y.Ztree (heading stamped to## X.Y.Z — date) and on post-release main.##token via/^## (\S+)/m(same first-field rule asrelease-notes.shawk$2), fail closed if no heading, assert the real extractor returns non-empty. Format-drift guard preserved; release-PR unshippable self-block gone.No blockers.
@danmt — handing this one to you. Final state:
What it fixes: cast#108 —
test/release.test.tsdemanded the real CHANGELOG's literalUnreleasedsection extract non-empty and contain#96: false by construction on the tree the ceremony's ownrelease: X.Y.ZPR produces, so the first real release PR turned CI red and the flow blocked itself. Invisible to the fork rehearsals (a tag push runsrelease.yml, neverci.yml).The fix: the test reads the top
##heading's token —Unreleasedbetween releases, the stampedX.Y.Zon and right after one — and asserts the exactrelease-notes.shthe workflow runs extracts it non-empty. Verified on both tree states (24/24 both ways); full suite 606/606; biome clean (one round-trip with CI over a non-null assertion biome rejects — replaced with a narrowing guard).Review history: one round — all three bots approved, no changes requested.
This PR gates the
release: 0.1.0PR, which I'll open the moment this merges. (rig's twin is heavy-duty/rig#45; box needed no fix — its suite pins already-released sections.)🤖 Generated with Claude Code