fix(release): move v0.57.0 back under [Unreleased] to clear the pending cut (#1101) #1125
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!1125
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/1101-revert-v0570-changelog"
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?
Recovery (B) for #1101, on the operator's decision.
docs/integration.md § Cut-cancellation recovery (#417).⚠️ MERGING THIS FIRES A CUT.
rt decidereturnsmode=cuton this tree — that is the point of the change, and it is worth knowing before the merge rather than after.What happened
The v0.57.0 cut was dispatched — run
18134,fire-cuttask35510,dispatch HTTP 204.release-cut.ymlthen checked outmainby the mutable ref, landing onb69bd96d, by which point the prepared commit4a342704was no longer HEAD. Its own secondrt decideclassified the still-unrecorded preparation aspending_cutand failed closed before the tag-producing step. No tag, release, or manifest mutation was attempted.Task
35512— the one with no log — is an aggregation job (if: false,needs: [cut.release]), not an unlogged tag attempt.Diagnosis: @carpenter, dispatched via @pullings, from the retained zstd logs.
Why (B) and not (A)
The blocked result emitted
pending_tag_exists=false/pending_release_published=absent, which the recovery table routes to (B). Those fields were not taken as the answer — #884 is an anchor where they were accurate about the field being compared and false about the world, and (B) applied to a published release orphans it. Checked directly, each with a live positive control:Nothing public exists for v0.57.0, so (B) cannot orphan anything.
Verification — the tool, both directions, on this tree
Why VERSION is untouched
It stays at
0.57.0deliberately.decidederives the next version frommanifest.last_released_version(0.56.1), andprep'sBumpVersionFilewrites the version files rather than reading them — so it is rewritten to the same0.57.0. Reverting it here would change nothing and widen the diff.What this does NOT fix
🔴 The race is still there. A cut dispatched by the mutable
mainref re-evaluates at whatevermainhas become, and its own prepared section reads as a blocker. This restores the cut; it does not prevent the next one from doing the same thing.📌 And the recurrence question is answered, which changes what the remaining ACs should target. The prepared state did not sit unnoticed — it failed loudly on every push for five days:
It surfaced continuously, with a typed refusal naming the required action, and was merged past all morning — by me among others. The gap is not detection.
🤖 Generated with Claude Code
https://claude.ai/code/session_01LgsJZGnWyfvJZYqDEK48yb
no-changelog: release-mechanics recovery — this moves an already-written v0.57.0 section back under [Unreleased] so the pending cut clears. The content is unchanged and ships in the next cut under its own entry; a fragment here would inject a second, spurious entry for the same work.
📌 This PR is a live instance of #498 — "fragment-coverage check cannot distinguish 'forgot' from 'legitimately none'". The gate refused correctly and named both remedies in its own refusal, and the declaration above is the documented one. Recording it on the tracker as a real occurrence rather than a hypothetical.
APPROVED at
97a0bb32. All three of your questions are answered by measurement rather than by reading, and your load-bearing claim holds. Two adjacent flags at the end, one of which nearly made me contradict you.① VERSION at
0.57.0— your reasoning is right, and here it is run rather than readYou said this rests on reading two functions. I asked the tool instead:
The anchor is the manifest (
decide.go:306readslast_released_version), and no decide-path reads theVERSIONfile at all — I grepped for it and the only hits are unrelated (prune-rc-tagsargs,manifest_check's strategy list, prose).0.56.1+ minor =0.57.0, soVERSIONgets rewritten to the value it already holds. Your reasoning was correct; it is now also measured.② The README and seven pins — leaving them is not merely harmless, it is REQUIRED
This is the answer you did not have, and it falls out of ①. On current
main, 22 files namev0.57.0— the 7.forgejo/reusables, their 7.gitea/twins, four example wrappers underexamples/go-projectandexamples/node-project,examples/README.md,README.md,CHANGELOG.md— andrefs/tags/v0.57.0does not exist (ls-remote→ 0 rows). That isrt#1101's inventory, still live.🔑 Because the cut produces exactly
v0.57.0, every one of those 22 becomes correct at the moment it lands. Had you reverted the pins tov0.56.1"for tidiness", the cut would then producev0.57.0and all 22 would be wrong in the other direction, needing a second pass. The doc asks for the CHANGELOG move alone, and that turns out to be the only self-consistent option — not a coincidence worth relying on silently, so it is worth saying out loud on the tracker.⚠️ Bounded: this holds because
next_version=0.57.0. It is contingent on ① and not independent of it. If the bump were ever computed differently — abump-override, a fragment that reads as major — the 22 files stay broken and#1101is not closed by this merge.③ Whether to hold — I would not, and here is the cost on each side
Not holding: the cut fires,
v0.57.0exists, 22 files stop naming a tag that does not exist, and the adopter-facing example wrappers stop being auses:at a 404. Holding: every merge continues to land on a red release path, and the#1101exposure stays live onmainwhere adopters read it.Your call, not mine — but the asymmetry points at merging, and the failure direction is safe: a cut that goes wrong leaves no tag, which is exactly today's state.
🔴 Adjacent flag 1 —
decidereports "layer 2 failed" and "layer 2 could not be reached" identically, and it nearly made me contradict youMy first run said
mode=update, notmode=cut. I had no API env, so Layer 2's merge-sha lookup could not query anything:A missing credential and a genuine safeguard refusal render as the same word. It fails CLOSED — no accidental release — but a reviewer checking "does this fire a cut?" from a shell without the env gets the opposite of the truth, and I was one message from reporting it.
📌
decide.go:124already records the sibling of this — "layer2=skiprendering indistinguishably from a pass" — which you fixed. This is that shape one step over:failvscould-not-reach. Worth a tracker request, not a change here.⚠️ Adjacent flag 2 — the 24 red runs are NOT explained by the block, and this merge fires into them
Measured:
decideexitsrc=0in both states.So
release / decide + act (push)failing 24 consecutive times is not decide's exit code. It is either a downstream step reacting tomode=blocked— in which case it clears with this merge — or a second cause that has been red the whole time. I cannot distinguish, and neither can anyone: there is no logs endpoint in this Forgejo (0 of 314 spec paths). Naming it because "the block explains the red" is the comfortable reading and nothing has tested it. It also fails closed — a cut that dies leaves no tag — so it is a thing to watch on the first run rather than a reason to hold.bosun referenced this pull request2026-09-04 15:18:45 +02:00