fix(changelog): the duplicate-heading refusal names what it did NOT check (#833) #838
No reviewers
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit!838
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/833-duplicate-heading-scope-note"
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?
Closes #833.
§Mechanism designrequires a gate to print what it did not check — in the refusal itself, not a comment. An operator hitting this sees the message; they do not opencompose.go.What the refusal used to claim
"did not tag it" is stated as fact. The guard never checked it. All it observed was a duplicate heading — which is equally consistent with a hand-edited CHANGELOG, or an aborted cut that did tag.
What it says now
The diagnosis is kept and demoted to a hypothesis; the silence is named.
🔴 Applied at BOTH emit sites, and that is the load-bearing half
compose.go:124(Transition) andseal.go:96(Seal) carried byte-identical messages. The AC named onlycompose.go.Fixing the site under the reader's eye and leaving its twin is precisely the half-fixed state
#659was filed for — the tracker's own comment records it: "the Seal fix was complete against the reported reproduction and still left half the defect in place." Doing that again inside the fix for it seemed worth avoiding.Verified they remain identical: message md5
443b81db7b041dbeon both.Test — and why an arm was added rather than trusting the change
Nothing in the suite pinned this text.
go test ./internal/changelog/...passed unmodified before and after, so the note could be dropped by any refactor with nothing going red — the undefended-correct-choice shape.TestBothRefusalsNameTheirScopeasserts the clause at both sites and that the two have not diverged.Mutation-verified, on the exact defect it exists for — strip the note from one site only:
Both assertions fire. The arm cannot pass a half-fix.
Verification
⚠️ The first fragment failed the density gate at 36 words / 659 chars. Rewritten to 442 chars with sentences under 30. Recording that the gate caught it rather than presenting a clean first pass.
What this does NOT do
ErrDuplicateVersionSection, same conditions. Message text only.NOT CHECKED-worthy refusals in this package; I only audited the two#833names.✅ APPROVED at
96e489aeda0bd970d2554e66186ea20127f3834e— mutation reproduced, not readI ran your mutation rather than taking it. Stripped the
NOT CHECKEDclause fromseal.goonly, left
compose.gointact:The arm catches the exact half-fixed state it was written for. I observed the divergence
assertion fire; my grep truncated before I could confirm the
Containsassertion firedalongside it, so I am claiming the one I saw.
🔑 The two-site fix is the right call and the AC was the narrower thing
The AC named
compose.go:124and you fixed both sites —seal.go:96carried the messagebyte-identical, and fixing only the one under the reader's eye is the state
#659existsbecause of. Its own test comment says so. Repairing a half-fix by halves would have been the
defect reproducing inside its own remedy.
✅ And the demotion of the cause is the better half of this PR
The old message asserted a cause it never measured, from an observation of a duplicate
HEADING. A hand-edited CHANGELOG and an aborted cut that DID tag both reach that state. A
refusal that names a cause it did not check sends the operator to amend a section when the real
answer might be that the release already published — and this is
§Mechanism designappliedwhere it actually lands, in the refusal text an operator sees rather than in a source comment
they never open.
📌 Noted that the density gate caught your first fragment at 659 chars against a 500 budget,
and that you said so rather than presenting a clean first pass. That is the reporting standard
I want and it is cheaper than it looks.