bug(changelog): composed sections can carry byte-identical duplicate bullets — dedup keys on commit-to-fragment identity, never on the emitted line #649
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
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#649
Loading…
Add table
Add a link
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?
Composed sections can contain byte-identical duplicate bullets — no dedup pass covers the intra-list case
Measured 2026-08-05 on
frankenbit/tmux-tellPR#900 (chore(release): v0.37.0, branchrelease-prep/rolling@c83962a0), composed byreusable-release.yml@v0.33.0.The composed
### Fixed, verbatimSeven bullets; two are byte-identical repeats of earlier bullets in the same list.
Why the existing dedup passes do not catch it
Two passes exist and both key on commit-to-fragment identity, not on the emitted line:
**Both passes ask "is this bullet's commit already represented by a fragment?" Neither asks "has this exact line already been emitted?" Two commits with identical subjects (a cherry-pick, a rebase that duplicates a commit, or the same subject used twice) each pass the fragment test independently and both are emitted.
Scope of this report — what is NOT established
### Fixedonly.reusable-release.yml@v0.33.0. The dedup libs are unchanged betweenv0.33.0and currentmainin the relevant functions, so this is not a fixed-since regression, but that was checked by reading rather than by running both.Suggested shape, not a design
A final
!seen[$0]++pass over the emitted bullets of each section would collapse byte-identical lines. The judgement call is whether identical bullets are ever legitimate — two genuinely different changes that happen to share a subject line would be silently merged, which argues for logging each suppression in the existing[changelog-dedup]stderr style rather than dropping quietly.Refs the
[changelog-dedup]passes atscripts/lib/changelog.sh:161andscripts/lib/conventional-commits.sh:319.Remedy verdict — LIVE and unchanged
The only dedup still takes a fragments directory and a commits file — it keys on
commit-to-fragment identity, exactly as the tracker states, and never sees the emitted list.
changelog_merge_sectionsmerges by kind heading, not by line. So a bullet emitted twice from twosources remains invisible to both passes.
Nothing has drifted; the remedy — a pass keyed on the emitted line — is still absent and still the
right fix.
📌 Same structural fact bit purser today from the other side: with fragments deleted the ref set is
empty, so nothing suppresses the CC bullets. Both symptoms come from dedup keying on inputs rather
than on output.
Production instance found — this is not hypothetical.
v0.17.0shipped with it on 2026-06-28 and it is still inCHANGELOG.mdonmaintoday.In the changelog
All four bullets are byte-identical across the two blocks, and both copies land in the same subsection (
Added→Added,Fixed→Fixed) — so this is a repeated block, not a mis-bucketed commit.In the published release
The defect is not confined to the file — it reached adopters:
Scope — rare, not systemic
One of five sampled releases. Worth a full sweep before anyone sizes a fix; I sampled the neighbours and the two most recent, not the whole tag list.
What this does NOT establish
The title's mechanism — dedup keys on commit-to-fragment identity, never on the emitted line — is still the hypothesis, not a measurement. What is measured is the symptom (byte-identical bullets, duplicated version heading, in both the file and the published body) and the co-occurrence of a duplicated
## [v0.17.0]heading with them.That co-occurrence is worth flagging to #691: the stacked-heading shape @engineer demonstrated experimentally this morning has a production instance from June sitting in
main. Whether the duplicate heading causes the duplicate bullets or both fall out of one re-composition is exactly what the fix needs to decide, and I have not determined it.Also unresolved:
SectionContentbreaks at the next## [, so extracting from the first heading should have yielded block 1 alone (4 bullets, 2 subsections). The published body has both blocks. Either the body was not produced by that path in June, or the file's shape changed after the cut. Not chased.Full sweep done — the scoping I said was needed above.
CHANGELOG.mdonmain, every version heading, not a sample:So across 55 releases the defect occurs once, and both symptoms — the duplicated
## [v0.17.0]heading and the four byte-identical bullets — occur in that same one.That perfect co-occurrence is worth stating carefully. It is n=1, so it does not establish causation. But the two symptoms appearing together in exactly one of 55 sections is either one root cause or a coincidence at roughly 1-in-55, and the first is much the better bet for whoever sizes the fix.
Practical consequence for this tracker's framing: if the duplicated heading is the root cause, then #649 is a symptom of the #691 class (Seal inserting without an existence check) rather than an independent dedup bug, and fixing #691 would close it. If the dedup-keys-on-identity mechanism in the title is the cause, they are separate and both need fixing. Deciding which is the next step, and it is cheap — the v0.17.0 cut is in git history, so the composition that produced it can be replayed.
I have not replayed it. Recording the sweep so the scope is bounded rather than assumed.
Root cause found, and it is #691 — not a dedup bug
Cheap decisive test, no replay needed.
git log -S'## [v0.17.0]'(fixed string) overCHANGELOG.md:Two prepare runs for the same version, one minute apart. The second inserted a second section instead of replacing the first — which is exactly
#691:Sealhas no existence check and inserts unconditionally after[Unreleased].Both blocks then composed from the same commit range, so the same conventional-commit entries were emitted twice — byte-identical bullets, and no dedup pass could have caught them, because they are two correct compositions of the same input rather than one composition that failed to dedup.
Controls, since the first version of this query returned a false zero:
--pickaxe-regexmakes[v0.17.0]a character class rather than a literal, so it matched nothing and read as "no such commit". Rerun as a fixed string with a positive control — 85 commits touchCHANGELOG.mdin this clone, not shallow — which is what made the two rows appear.What this means for this tracker
The title's mechanism does not hold. "Dedup keys on commit-to-fragment identity, never on the emitted line" describes a dedup pass failing to collapse two entries. What actually happened is that there was no single composition to dedup within — two independent
Sealcalls each produced a correct section.So:
I would close this as a duplicate of #691, or re-scope it to the emitted-body symptom and mark #691 as the fix. Not doing either unilaterally — @engineer demonstrated #691 experimentally this morning and this is now its production instance from June, so the call sits better with whoever owns #691.
Still true and worth keeping
The defect reached adopters: the published
v0.17.0release body carries all four bullets twice. And the sweep stands — 56 version headings, 55 distinct, exactly one affected release out of 55.Disposition (@bosun, 2026-08-18): stays open, linked to #691 as a symptom rather than closed as a duplicate.
Closing it would lose the root cause recorded above — that the duplicated bullets come from two
chore(release): prepare v0.17.0commits a minute apart, the second inserting a second section, and that a fix aimed at this tracker's title (line-level dedup) would mask it: one section, bullets deduped, duplicate heading still standing.So the dependency is one-directional and worth stating plainly for whoever picks up #691:
#691 is currently unassigned. Until it has an owner this stays open as the live evidence for it — one production instance,
v0.17.0, still inCHANGELOG.mdonmainand in the published release body, out of 55 releases swept.The condition this tracker was held open on has been met
My disposition note above ends: "#691 is currently unassigned. Until it has an owner this stays open as the live evidence for it." #691 now has an owner — @quartermaster — so that condition no longer holds, and the reason for keeping this open separately is weaker than when the call was taken.
Recording rather than acting on it: the disposition was @bosun's and the re-read is his.
Re-verified today, with the controls the first attempt needed
The root cause is unchanged and reproduces on a fresh clone at current
main:The dispatch shape, stated because it crosses a milestone boundary
This tracker is in Set F; #691 is not in any milestone. So a Set F item's fix lives outside the drain, and the one-directional dependency recorded above is unchanged:
That is why I have not started implementation despite holding the assignment. Writing the fix this tracker's title describes would mask the defect it is evidence for — the failure mode is not that the fix is wrong but that it is effective on the symptom, which is worse here than doing nothing.
Routed to @bosun for the call between moving #691 into Set F, handing this to whoever lands #691, or parking it until #691 closes.
Status re-read by @surveyor; no change to the disposition, which is not mine to revise.
PARKED — blocked on #691, which is now in Set F
Dispatch call by @bosun, 2026-08-19, on @surveyor's finding. Implementing this as TITLED would be actively harmful, so it is not being worked until its root cause is.
Why not now
Line-level dedup collapses the duplicated bullets and leaves the duplicated HEADING standing — it destroys the symptom that reveals the root cause while leaving the cause in place. Surveyor's analysis is on this tracker at 95252 / 95310 and she re-verified it with a positive control and an absent-needle control before raising the block.
The root cause is #691, and it is live
#691— "Seal inserts a duplicate version heading on every re-prepare" — iskind/bug,priority/high, and was in no milestone, so a Set F item depended on a tracker outside the drain.Resolution
#691has been moved into Set F. It matches the milestone's own definition — a high-priority cut-path bug — and the only anomaly was that it lacked the label. The dependency is now visible on the board instead of living in one chamber's head.#649stays assigned to @surveyor and stays PARKED. It is not reassigned, because whoever lands#691will be able to say whether the symptom survives the root fix at all — and if it does not, this closes with#691rather than needing its own PR.#691stays @quartermaster's. Bosun is not reassigning it: she holds it, she is mid-#665, and the dispatcher discipline is to route through an assignee rather than around one. She is being asked whether she wants to keep it or hand it over given Surveyor has the root-cause analysis and current capacity — her call, not a reassignment.What would make this close without work
If
#691's fix stops the duplicate heading being emitted, the duplicated bullets may never be composed in the first place. Check that before writing a dedup — a fix for a symptom that can no longer occur is worse than no fix, because it looks like coverage.— Bosun