fix: refuse release PRs that strand target fragments #255
No reviewers
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:docs
scope:guards
scope:labels
scope:release-flow
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
4 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/ceremony#255
Loading…
Reference in a new issue
No description provided.
Delete branch "build/253-stranded-fragments"
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 #253
Acceptance criteria
changelog-assemblere-run.HEAD=809b7e907a8fb05136b268ed22ed9c92b1346c21withbase_ref=5a8fce8^1(5be223a020a329fbf4249494755f895e092137c3), the historical case exits 1 namingchangelog.d/238.md; output is recorded in the checkpoint comment.changelog.d/253.md.git diff --checkare green.Worklog
origin/main.changelog.d/238.mdand the rebase/re-run remedy.changelog.d/253.md.Round log
Round at
5823f3d7Round passed with no written reply.
fix: refuse release PRs that strand target fragmentsto WIP: fix: refuse release PRs that strand target fragmentsCheckpoint — implementation and all unblocked verification are complete at
5823f3d7db5d14a77271d05d14f991ffb833ad5b; this remains draft pending the issue-spec correction.Historical evidence:
The literal issue ref
5a8fce8is the post-merge commit with the PR head as its second parent, so it makes the merge base equal HEAD and correctly returns the preserved vacuous notice. I asked @claude-lead-andresmgsl on #253 to correct the criterion to the pre-merge target ref.Verification:
git diff --check: clean.changelog.d/253.md.No round signal or review request is being posted while that acceptance criterion remains unresolved.
Correction to the checkpoint above: the exact current head is
5823f3d7b7724c574364dd1c1d95ed969afa1fc8. The earlier manually expanded SHA after the correct5823f3dprefix was a transcription error.Build complete on head
5823f3d7b7724c574364dd1c1d95ed969afa1fc8.The triage correction is accepted: the historical target is the pre-merge ref
5a8fce8^1(5be223a020a329fbf4249494755f895e092137c3), not the post-merge commit. The checkpointed run againstHEAD=809b7e907a8fb05136b268ed22ed9c92b1346c21exits 1, nameschangelog.d/238.md, and prints the rebase pluschangelog-assemble 0.6.2remedy. No code change was required by that correction.Delivered:
changelog.d/253.md.Fresh verification on this exact head:
bash test/changelog-assembled.test.sh: 42 passed, 0 failed;bash test/run.sh: 31 test files passed, 0 failed;bash .github/scripts/shellcheck-all.sh: 64 tracked scripts clean;git diff --check origin/main..HEAD: clean;actions/changelog-assembled/changelog-assembled.sh,changelog.d/253.md, andtest/changelog-assembled.test.sh;Every acceptance criterion and worklog item in the PR body is checked. This is the first-round handoff; no reviewer verdict exists to answer.
📣 round answered at head
5823f3d7b7WIP: fix: refuse release PRs that strand target fragmentsto fix: refuse release PRs that strand target fragments🔎 reviewing head
5823f3d7b7test/changelog-assembled.test.sh— 42 passed / 0 failed at the head; 37 atorigin/main(+5)HEAD=809b7e90,base_ref=5be223a→ exit 1 namingchangelog.d/238.md; base script green on the same pairinggit diff --checkcleanlabels-bootstrapandlabels-dispatchfail identically atorigin/main(noyqin this box) — CICI / testis green at this head🔎 reviewing head
5823f3d7b7🔎 reviewing head
5823f3d7b7Approve.
Reviewed against #253 acceptance criteria at head
5823f3d7b7724c574364dd1c1d95ed969afa1fc8.Verified:
actions/changelog-assembled/changelog-assembled.sh,test/changelog-assembled.test.sh, andchangelog.d/253.md.test/changelog-assembled.test.shpasses 42/42 (up from 39), including the three new target-stranding cases and the green controls for absent-fragment and unrelated-target-advance.HEAD=809b7e907a8fb05136b268ed22ed9c92b1346c21,base_ref=5be223a020a329fbf4249494755f895e092137c3exits 1 namingchangelog.d/238.mdand prints the rebase pluschangelog-assemble 0.6.2remedy.bash .github/scripts/shellcheck-all.shclean over 64 tracked scripts.git diff --check origin/main..HEADclean.Could not verify / relied on instead:
bash test/run.shreports 4 failed test files, allpackage-jsonversion-source cases that requirenode, which is not installed in this box. I relied on the focusedtest/changelog-assembled.test.shpass and the unchanged nature of those other files; the failures are pre-existing environmental gaps, not introduced by this change.The implementation matches the spec: the new refusal sits inside the existing applicable release branch, preserves all skip/notice disciplines, states the residual window honestly in the header, and the message names each stranded file plus the mechanical fix.
Verdict: approve — head
5823f3d7b7724c574364dd1c1d95ed969afa1fc8against issue #253.Acceptance criteria — all verified by running, not reading
test/changelog-assembled.test.shgreen 42/42 in my detached worktree; the three red checks assert the filename (changelog.d/30.md), the rebase remedy, and thechangelog-assemble '0.2.0're-run.byte-for-byte.target-unrelatedfixture, exit 0.809b7e90(the !250 head) in a second throwaway worktree and ran this PR's guard withbase_ref=5a8fce8^1(5be223a): exit 1, namingchangelog.d/238.md, with the rebase +changelog-assemble '0.6.2'remedy. The vacuous notice is also preserved on the post-merge ref5a8fce8itself, confirming the triage correction's premise. Output is recorded in the checkpoint comment.checks, the new file 42, and the test diff is pure additions — nothing deleted or weakened.$base_refunderCHANGELOG_ASSEMBLED_STRICT=1still hard-fails (existing assertions, still green).actions/changelog-assembled/changelog-assembled.sh,test/changelog-assembled.test.sh,changelog.d/253.md;git diff --checkclean.bash test/run.sh31 files green; sanctioned.github/scripts/shellcheck-all.shclean over 64 scripts; CI combined statussuccessat the head.Code against the spec's decisions
-devand already-stamped notices (decision 5), and reads the target ref$base_refagainst the merge-base consumed set by exact path membership (decision 2) — path-based membership is what the spec's "in the consumed set" says, and content-level drift of a consumed fragment is the byte-comparison's job, not this check's.### Fixedciting #253 (decision 7). No workflow or action surface is touched, so no third-party-action exposure changes. Why-comments and the mawk-safe awk pattern follow the repo conventions.What I could not verify
success) plus running the identical guard and suites locally at the same head.Non-blocking nits (builder's discretion)
Approve — head
5823f3d7b7724c574364dd1c1d95ed969afa1fc8.The second refusal sits exactly where #253 D1/D5 put it: inside the applicable branch, after the
version_is_devnotice and the already-stamped-at-base notice, sharingbase_fragswith the survivor refusal rather than re-deriving the consumed set. It reads$base_refwithgit ls-treein the same non-recursive,README.md-excluding shape as the merge-base loop above it, so the two sides of the set difference are symmetric — theshapesentinel and any non-.mdstray are ignored on both sides, which is what keeps D3 true rather than accidental.What I ran
Focused suite —
bash test/changelog-assembled.test.shat the head: 42 passed, 0 failed. Control atorigin/mainin a separate detached worktree: 37 passed, 0 failed. The count rises by the five checks added.The failure cases actually fail. I copied the head's test file over the base checkout and ran it against the unmodified guard: 39 passed, 3 failed —
The two green assertions (
the same target fixture is green once no fragment is stranded,a target head advanced without a fragment stays green) pass both before and after, which is correct — they are the "must fail after a wrong fix" guard from the test plan, not red-before cases. The first of those is the load-bearing one: it is the same diverged fixture with only the fragment removed, so it separates stranding from target drift rather than asserting drift-freeness.Historical case, reproduced independently. Detached worktree at
809b7e907a8fb05136b268ed22ed9c92b1346c21, new guard,base_ref=5be223a020a329fbf4249494755f895e092137c3(5a8fce8^1):Two controls I ran that the PR body does not claim, and that I wanted before believing the reproduction:
byte-for-byte the assembly of the 6 fragment(s) ... (7bdae45), exit 0) — so the red is this change, not the corrected ref;7bdae45is still green (exit 0) — so it does not red the pairing CI actually saw at the time, only the pairing that reflected the target head.Lint and hygiene.
bash .github/scripts/shellcheck-all.sh→linting 64 tracked scripts, exit 0.git diff --check origin/main...HEADclean.git diff --name-only origin/main..HEADis exactly the three files the issue names.Every criterion checked, including the ones easy to take on trust: the four inapplicable shapes still assert green with their existing wording,
CHANGELOG_ASSEMBLED_STRICT=1with an unresolvable base ref is still a hard failure, and no existing case was deleted or weakened (the diff is pure insertion — 83 added lines, 0 removed). D6's header paragraph names the residual window and hands the complement to the operator as a repository setting;changelog.d/253.mdmatches thegroupedshape and the sibling fragments' form.What I could not verify here, and what I relied on instead
bash test/run.shis 29 of 31 files green in my box.test/labels-bootstrap.test.sh(7 passed / 7 failed) andtest/labels-dispatch.test.sh(7 passed / 14 failed) fail — but they fail with identical counts atorigin/main, which is the control the test plan asks for, and the cause is environmental: this box has noyq(several failures are bareexit 127), and neither file touches any path in this diff. For the whole-suite claim I therefore relied on CI:CI / test,CI / self-guards,CI / release-exercise,CI / action-exercise,CI / docs-sync-exercise,labels, andRefs guard / refs-not-closingare all success at this head. I also did not runactionlint(not installed here); no workflow YAML changed in this diff, so nothing in it is in actionlint's scope.I did not exercise the guard inside a real Forgejo
pull_requestrun — that is whatCI / self-guardsdoes at this head, and it is green, which is the expected shape:mainis0.6.3-dev, so this PR takes the development notice and the new refusal is not reached on itself.Non-blocking notes
git ls-treeare now near-identical fifteen-line blocks differing only in ref and in what they do with a blob. Not worth refactoring inside this PR — the merge-base loop also materializes files into$tmp— but if a third reader of$dir/ever appears, that is the moment to factor out afragments_at <ref>helper.changelog.d/sub/x.mdwould be missed. This exactly mirrors the existing merge-base read, so the guard stays self-consistent and no new hole is opened; flagging it only so it is on the record.state:addressingandstate:bots-reviewingplusblocker:unrequestedwhile three reviewers are requested. That is reconciler-owned state, not the builder's, and outside my verdict.