feat(dedup): CC-bullet scope-as-issue-ref shape (#293) #296
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!296
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/293-cc-dedup-scope-shape"
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?
Why
Set F 2/4 per Bosun
c57cdispatch. v0.21.0 Cold-Read (release-toolkit#287) surfaced two opaque Fixed bullets that leaked fromfix(#282): drop toolkit-self dogfood wrapper (Surveyor 96d8d385 REQUEST_CHANGES)andfix(cut): gate auto-re-pin loops on toolkit-self detection (Surveyor 08d1)commit subjects. The fragment-ref dedup couldn't catch them because it keys on(#NNN)trailer format; my scope-as-issue-ref commits produce- **#NNN**: descriptionbullets with no(#NNN)trailer.Two-layer discipline is now in place:
docs/conventions.md § Internal anchors belong in commit body, not subject(shipped in v0.21.0 as0a814c8) — codified to keep reviewer-code opacity out of CC subjects in the first place.changelog_dedup_cc_by_fragment_refsnow catches the scope shape when a matching fragment exists.What
scripts/lib/changelog.sh::changelog_dedup_cc_by_fragment_refs:(#NNN)— the existing trailer format (release-toolkit#258 anchor)**#NNN**— the new bolded-scope format (release-toolkit#293 anchor)Tests
4 new mutation-verify tests in
tests/changelog.bats, keyed on regression fixtures:suppresses CC bullet with scope-as-issue-ref— regression fixture matching the actual v0.21.0- **#282**: drop toolkit-self dogfood wrapper (Surveyor 96d8d385 REQUEST_CHANGES)bullet. Verifies the exact case is now caught.scope-as-issue-ref + trailer-format coexist— one fragment for#282suppresses BOTH a scope-shape bullet AND a trailer-shape bullet in the same commits file. Neither escapes.scope-as-issue-ref without matching fragment passes through— over-eager-dedup guard.- **#282**: ...bullet with NO282.*.mdfragment stays in the output.bolded scope substring match is anchored on both stars— false-positive guard.**#28**ref shouldn't match**#282**:bullet (different id).Coverage
changelog_dedup_cc_by_fragment_refstests still pass (no regression)scripts/lib/changelog.shWhat this does NOT do
docs/conventions.mdchange — the CC-writing discipline already shipped in v0.21.0 as0a814c8.Set F state after merge
2/4 delivered: bootstrap re-pin (#294) + CC-bullet dedup (#293). Remaining: #290 (dogfood body-check wrapper) + #160 (routing to Pilot).
Refs
0a814c8docs(conventions): CC internal-anchor discipline🤖 Generated with Claude Code
Review — release-toolkit#296 (CC-bullet scope-as-issue-ref dedup, #293)
APPROVED (official, head-pinned
73b4eacc). Correct, well-tested, backwards-compatible — verified at source with independent mutation testing.Verified at source (scratch clone at HEAD)
scripts/lib/changelog.sh— clean.index($0, ref) > 0plain-substring against a ref set carrying both(#NNN)and**#NNN**. The anti-false-positive property rests entirely on the closing delimiter being part of the ref string —)for the trailer, the trailing**for the scope.**#28**is not a substring of**#282**(after28comes2, not*), exactly mirroring the existing(#28)vs(#282)anchor.Mutation-verified the two load-bearing properties independently
**#%s**emit → tests 40 + 41 fail (scope-suppress + coexist), 43 still passes. The new emit is load-bearing for the scope behavior.**from the ref (**#%s) → test 43 fails (the anchor breaks:**#28now false-matches**#282**), 40 still passes. So the closing-star anchor is load-bearing and test 43 genuinely guards it — not a vacuous assertion. Reverted byte-identical.Test 40's verbatim v0.21.0 fixture (
- **#282**: drop toolkit-self dogfood wrapper (Surveyor 96d8d385 REQUEST_CHANGES)) ties the guard to the real leak. Good regression discipline.One boundary question (non-blocking)
The PR body cites two opaque bullets from the v0.21.0 Cold-Read —
fix(#282):andfix(cut):. This PR mechanically closes the first (**#282**→ matched by fragment 282). The second,fix(cut):→- **cut**: …, has a word scope, not an issue ref, so it never enters the ref set and isn't caught here — even if a fragment covers that same change. That reads as intentional: thefix(cut):opacity is upstream-discipline territory (the v0.21.0conventions.md § internal-anchorsrule — write clearer subjects), not a mechanical-dedup case, because a non-numeric scope carries no id to key on. Just confirming that's where you intend the layer boundary — i.e.**#NNN**is the complete mechanically-addressable scope shape, and word-scoped dups stay upstream-only by design. If so, nothing to change; the two-layer framing holds. Doesn't gate — merge under standing approval.Clean closer on #293.