changelog-armed.sh does not notice a deleted release heading — a PR can erase a shipped section and CI stays green #122
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:cli
scope:drill
scope:host
scope:installer
scope:templates
scope:tiers
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/box#122
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Found in review of #118 (both
claude-bot-andresmgslandgrok-bot-andresmgslcaught it; CI did not).What happened
#118 added a
### Fixedentry under## Unreleased. The edit replaced the line## 0.8.0 — 2026-07-19instead of inserting above it. Net effect on that branch: the 0.8.0 heading was gone, and its entire### Addedbody — the whole shipped 0.8.0 record — was silently absorbed into## Unreleased.Caught in review and fixed. Nothing shipped. But nothing in CI objected.
The gap
.github/scripts/changelog-armed.shwas run against that exact tree and exited 0:It is correct on its own terms — it asks only whether the top section agrees with
VERSION. Deleting## 0.8.0leaves## Unreleasedon top, so the invariant it checks still holds. The guard is not wrong; it is narrow.release-notes.shwould then have failed to extract 0.8.0 — but only at the next release that asked for it, long after the merge, with no obvious link back to the PR that caused it.Why it matters more than one near-miss
This is the same failure shape the changelog work has been chasing (#108, and heavy-duty/rig#66): a changelog edit that git merges cleanly, that no human diff-reads carefully, and that no check objects to — where the damage only surfaces at release time. #108 was about a missing
## Unreleased; this is about a vanished release section. Same class, opposite end.Consequences if it had merged:
release-notes.shextracts by heading.Suggested fix
A monotonicity assert: the set of
^## X.Y.Zheadings on a PR head must be a superset of the set on the merge base. Release headings are append-only — one is added by the ceremony (#96) and none is ever legitimately removed, so the rule has no false positives to tune. It also naturally covers the rename case (a stamp rewriting## Unreleased→## X.Y.Zadds a heading, removes none).This is a class check in the same shape as
eof_guard_sweep(#112) and #118's own sweep-totality assert: guard the state, not the instance.Worth checking whether the siblings want the same guard — heavy-duty/rig and heavy-duty/cast both extract release bodies by heading the same way.
Refs