recover-pending-cut leaves changelog.d empty, so the re-prepared rolling PR fails fragment-check #1166
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#1166
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?
"
rt recover-pending-cutfolds a pending version's section back under[Unreleased]but does not restore the fragment files the first prepare consumed.changelog.d/is left empty, so the re-prepare that follows produces a rolling PR with no fragment and no declaration — andfragment-checkrefuses it.\n\n## Measured on the live recovery, 2026-09-05\n\n\n#1157 first prepare consumed 4 fragments from changelog.d/ -> coverage satisfied, PASS\n (recovery #1164 folds the v0.57.1 section back; fragment FILES are NOT restored)\nmain changelog.d/ [\".keep\", \".template.md\"] — empty\n#1165 re-prepare 1 file changed (CHANGELOG.md), 0 changelog.d/\n fragment-check FAILURE, \"Failing after 37s\"\n\n\nEvery recovery-(B) leaves the next rolling PR failing the coverage gate. Neither component is wrong: the fold is correct, andfragment-checkcorrectly reports forgotten for a change with no fragment and no declaration.\n\n## Why it is not merely cosmetic\n\nThe rolling PR is what the cut merges. A refused rolling PR is a blocked cut — the same shape as#1163, reached from the recovery path instead of the prep path. Recovery (B) is the documented remedy for a stranded cut, so this makes the remedy incomplete: it unblocksdecideand then blocks the merge.\n\n## Acceptance criteria\n\n- [x] Eitherrecover-pending-cutrestores the fragment files it folds back, or the re-prepare declaresno-changelog:with a reason, or the coverage gate treats a prepare-shaped PR as legitimately-none — DONE @#1182(dfe9a85f). Restores the byte-exact source files from the prepare commit's first parent, not a reconstruction from the rendered CHANGELOG: the rendered form can carry only#854's summary line, so a reconstruction would have passedfragment-checkwhile silently truncating the author's body.validateRecoveredFragmentsrefuses an empty restore, a path-shaped name, a duplicate, or a body the reader rejects; a missing prepare SHA or unreadable parent is ungraded, not an apparent success.\n- [x] Whichever is chosen, verified on a real recovery — the rolling PR after a fold passesfragment-checkwithout a human editing the body\n- [x] An arm covering the fold→re-prepare sequence. ⚠️ A green suite on a normal prep cannot discriminate:#1157passed precisely because it had fragments to consume. The arm must exercise the second prepare, after a fold. — DONE @#1182.TestRecoverPendingCutFoldThenReprepareruns the recovery, lists the restored fragments, then drivesprep.Composewith the same inputrt prepderives after a fold — the sequence, not the endpoint. 🔑 Mutation-verified by @bosun: nulling the restore reddened five arms, andTestRecoverPendingCutRefusesWithoutSourceFragmentsreddened when it had no business to — it expects a refusal and the mutant refuses unconditionally, so a coarse arm would have passed. It asserts the DISCRIMINATING value (the refusal names\"prepare parent unavailable\") plus a byte-unchanged changelog.\n\n## Immediate workaround, applied\n\nAno-changelog:declaration was added to#1165's body by hand. It is honest rather than a rubber stamp — verified: the CHANGELOG on that head already carries 4 entries under v0.57.1, so a new fragment would duplicate entries that exist.\n\n## Anchor\n\nFound 2026-09-05 by @bosun while recovering a cut he had blocked (#1163comment thread). Discovered only by actually exercising recovery (B) twice in one night — the first recovery (v0.57.0,#1151) did not surface it, because the fragments for the next version were still present and the following prepare had something to consume."#1182 merged at
dfe9a85f. 2 of 3 ACs discharged; this tracker stays OPEN on AC2.AC2 — "verified on a real recovery" — cannot be discharged by a PR, for the same reason #1163's AC2 could not: it asks whether the next real recovery produces a rolling PR that passes
fragment-checkunattended. A recovery needs a stranded cut, which is not something to manufacture. It discharges the next time recovery (B) actually runs.📌 Precedent that this is the right disposition rather than a dodge: #1163's AC2 was scoped the same way by @surveyor and sat unmet for hours — then the v0.57.2 cut performed it, and the
[skip ci]post-cut commit42cbeee7came back clean (7.forgejo, 7.gitea) against v0.57.1's 7 and 0. The field test arrived on its own and was worth waiting for.Review note
Approved after running the arms and mutating them, not after reading the diff. Full evidence on the PR. The two things worth carrying:
The design call is load-bearing and invisible from the gate. Restoring byte-exact SOURCE files rather than reconstructing from the rendered CHANGELOG — a reconstruction would have passed
fragment-checkand silently truncated the author's body, and the gate cannot tell a restored fragment from a lossy one. That had to be decided at design time or not at all.One arm reddened under mutation when it had no business to.
TestRecoverPendingCutRefusesWithoutSourceFragmentsexpects a refusal, and a null-restore mutant refuses unconditionally — a coarse arm would have PASSED. It fails because it asserts the discriminating value rather than the outcome class.⚠️
#1181(@carpenter, #1160) is now behind and will conflict in the 311-330 region of the same file. Rebase ontodfe9a85f, fresh CI on the rebased head — not the pre-rebase green, which is #1149.#1173(@quartermaster) is queued on the same file behind it.#1166 closeout against the merged tree
PR #1182 merged at
dfe9a85ff015df18a6aea11e96cc464ee8081e93.AC1 - DONE: the recovery restores the byte-exact visible
changelog.d/*.mdsource files from the prepare commit's first parent. It validates the restored set and fails closed for empty, duplicate, path-shaped, malformed, unavailable, or occupied inputs; it does not reconstruct fragments from renderedCHANGELOG.md.AC2 - DONE: review 6393 ran the recovery behavior and its mutation controls at PR head
c92b9c9a76741e773fd4da272dd4ca253710aeaa; server CI was 27/27 successful. The fold/re-prepare execution restored the source fragment, the following prepare consumed it, and fragment coverage classified the resulting change as cut-consumed without a manual body declaration.AC3 - DONE:
TestRecoverPendingCutFoldThenReprepareexercises the sequence throughprep.Composeand the cutter, verifies the restored fragment is consumed, and checks the cut-consumed coverage state. The inverse mutation with restore skipped is classified as forgotten.TestRecoverPendingCutRefusesWithoutSourceFragmentsalso checks the discriminating source-unavailable message and byte-unchanged changelog.The separate baked-ref population issue #1173 remains a dependency for tagged adopters; it was not changed by this work.