feat(release-cut): eliminate between-cut manual-rc-tag intervention for toolkit-self scripts churn (v1.0.0 must-fix) #456
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#456
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?
Motivation
Substrate-honesty correction (2026-07-07, post-v0.30.0 cut): this tracker was originally filed as "auto-re-pin on cut" (v1.0.0 must-fix) targeting an implementation gap. Verification against 3 historical cuts (v0.28.0/v0.29.0/v0.30.0 empirical evidence via release-bot commits
a19543a/2b5995e/da33ef9) shows post-cut bookkeeping bumps all 10 pinned refs (5 wrapperuses:+ 5 baked-ref reverts) to the just-cut tag. That mechanism exists via #283 and fires reliably. Body revised below to reflect the actual gap.The actual gap — shared substrate of the whole #448 class
Between-cut window on toolkit-self scripts churn is the shared substrate of the whole
fix-cut-bootstrap-through-own-gateunifying class (#448), not just Class 1:Both classes root in the same between-cut divergence, but they have different manifestation conditions + frequencies: Class 1 manifests on any scripts-touching PR; Class 3 manifests only when a compose-output change meets a cut. One re-pin can clear both when both happen to be red simultaneously (as in this sprint — PR#455 re-pin cleared both, PR#447 clean recompose one cadence later); more commonly Class 1 fires alone between cuts. So this tracker and #448's unifying class are two views of one window; the fix must be scoped to close the whole class, not just check-self-bootstrap — but the frequency asymmetry matters for design lean.
Adopters never hit this because they don't ship toolkit code. Toolkit-self hits it every time scripts churn between cuts.
Empirical evidence
a19543abumped 10 pins); the remaining question is what happens on the NEXT scripts-touching PR to main.Operator ideal
Single-click = no manual rc-tag intervention window. The current Option-C flow requires: fire rc-tag → re-pin to rc-tag → recompose rolling PR → merge → cut. That's a multi-step manual dance the ideal targets.
Design options
(A) Adaptive pin for toolkit-self: symbolic ref or
@main-style pointer for the self-referential path. Critical constraint (per Surveyor 8f3d): the fix MUST preserve the drift-detection semantics check-self-bootstrap exists for — floating to@mainnaively trades away the very check #124 added. Any adaptive-pin design has to encode "self-referential toolkit-self ↔ adopter-facing" as distinct paths, not just weaken the check globally.(B) Skip check-self-bootstrap on release-toolkit-self repo: scope-by-repo exemption (cross-repo scope discipline). Cleaner boundary; retains adopter discipline; explicit that toolkit-self is a special case. Doesn't address Class 3 (compose-scaffold-carrying tooling); would need separate mitigation.
(C) Accept periodic manual rc-bootstrap as substrate-honest cost of self-hosting: no code change. Document the Option-C flow as canonical when scripts substantially churn between cuts. Cheapest fix; least ideal. Honest default per Surveyor 8f3d if (A)'s complexity isn't worth it pre-v1.0.0 — the manual dance is well-understood. Frequency asymmetry note: (C) is more defensible for Class 3 (rare — compose-output change × cut) than for Class 1 (every scripts-touching PR between cuts). Accepting (C) globally means accepting frequent Class-1 red-firings between cuts, which is where most of the friction actually lives.
(D) Continuously advance pin on scripts-touching PRs: pre-merge hook or CI step that bumps the pin to the merging PR's HEAD. Complex; risks pin-thrash; not preferred.
Lean re-shaped by Surveyor's framing: pre-v1.0.0, (C) is likely honest default. Post-v1.0.0, (A) with the drift-detection-preserving constraint is the structural fix. (B) partial doesn't close the whole class.
Verification AC
Related
Anchor
feat(release-cut): auto-re-pin on cut for single-click release UX (v1.0.0 must-fix)to feat(release-cut): eliminate between-cut manual-rc-tag intervention for toolkit-self scripts churn (v1.0.0 must-fix)Decision — 2026-07-07: (A) structural fix, pre-v1.0.0 must-fix
Operator ratification 2026-07-07 (post-Surveyor 8f3d + b599 + cc94 class-split framing): commit to (A) structural fix pre-v1.0.0. The class-split alternative (B for Class 1 frequent, C for Class 3 rare, defer A) is a sound fallback but not the chosen direction.
Operator reasoning
Pre-v1.0.0 is the window where breaking changes are cheap — no SemVer stability contract yet. Post-v1.0.0 we're committed to backward compat forward (per #338 memorial-chore). Better to land the structural fix + absorb any fallout during the free-to-break window than defer + carry the substrate-friction of manual Option-C dances into the stability regime.
Substrate commitments this implies
@mainnaively trades away #124's discipline. Any adaptive-pin design has to encode "self-referential toolkit-self ↔ adopter-facing" as distinct paths, not weaken the check globally.scripts/divergence.Scope + sequencing
feedback_plan_first_workflow).Prior class-split framing
Closes as an accept-a-cost alternative — not the direction. Preserved in body's Design Options § for reference.
Design memo — plan-first pass (QM, 2026-07-07)
release-toolkit#456 — Design memo (plan-first)
Substrate map (verified callsites)
Toolkit-self-only surfaces:
.forgejo/workflows/release.yml— wrapper. Currentlyuses:@v0.29.0(should auto-advance to v0.30.0 post-cut). THE lever..forgejo/workflows/check-self-bootstrap.yml— toolkit-self-only workflow (explicit in its header lines 19-21). Adopters don't ship this file..forgejo/workflows/reusable-*.yml(×5) — each carriesBUILD_BAKED_TOOLKIT_REF: 'main'on main's HEAD.scripts/check-self-bootstrap.sh— drift-detection logic. Two existing skips (PREP_SUBJECT_RE + startsWith('release-prep/')).scripts/repin.sh— manual rc-tag creation + wrapper-bump + PR (#172 A-script).scripts/release-prep.sh— bakes vCUT into reusable files at cut-prep.scripts/lib/build_bake.sh— bake_toolkit_ref helper (create_detached_bake_commit).scripts/lib/changelog.sh— carrieschangelog_scaffold_missing_sections(Class 3 payload).reusable-release.ymlcut path — auto-re-pin (#283) at lines 596-609 (path-α) and 661-673 (path-γ) sed-rewriteuses:@<ref>to CUT_TAG.Adopter surface:
.forgejo/workflows/release.ymlwithuses:@vX.Y.Z(frozen tag).check-self-bootstrap.ymlorscripts/.uses:@<tag>frozen-pin discipline (fully preserved, orthogonal to check-self-bootstrap).The gap in one sentence
Toolkit-self's own
.forgejo/workflows/release.ymlpinsuses:@<last-cut-tag>, but the code being edited between cuts sits onmain— so every substrate-touching PR creates a mid-cycle divergence betweenscripts/@mainandscripts/@<pinned-tag>. This manifests as Class 1 (check-self-bootstrap RED on every substrate PR) and Class 3 (compose-scaffold missing when cut runs from pinned tag).Design options analysis
Option A.a — Auto-repin on every scripts-touching push:main (fires the current mechanism more aggressively)
lib/changelog.shand workflow files are pin-current until next scripts PR.Option A.b — Toolkit-self floats on
@main; adopters continue pinning tags (recommended).forgejo/workflows/release.ymlline 56 fromuses:@v0.29.0→uses:@main.uses:@<ref>value is alreadymain. Two-line conditional inside the sed loop.uses:@main(main vs main = identical). Keep the workflow for accident-catching (someone hand-edits back to@vX.Y.Zand forgets to re-pin).Class coverage:
scripts@main, always carries the current scaffold + all lib helpers.@vX.Y.Z, cut-tag carries baked ref,uses:@<tag>remains a frozen source-of-record for external consumers.Class-complete per Surveyor 8f3d constraint.
Option A.c — Symbolic pin-mode switch based on event
mainvs baked value based ongithub.event.repository.name == 'release-toolkit'.Recommendation: Option A.b
Why A.b over A.a:
@v0.30.0→@mainin one file) + a two-line conditional in the auto-re-pin block + tests + doc updates.Why A.b preserves drift-detection semantics:
@main.uses:@<tag>frozen-pin discipline is entirely orthogonal to this check (adopters don't run this workflow).Implementation shape
.forgejo/workflows/release.yml:uses:@v0.29.0→uses:@main(single-line edit)..forgejo/workflows/reusable-release.ymlauto-re-pin blocks (lines ~604 path-α, ~669 path-γ): add pre-sed check — if currentuses:@<ref>value is alreadymain, skip the sed rewrite for that file. Preserves auto-re-pin for external forks / paths where wrapper does pin a tag.scripts/repin.shheader-doc update: note that toolkit-self no longer needs routine repin under the #456 floating-pin scheme; keep the script for adopter emergency use.scripts/check-self-bootstrap.shheader-doc update: note theuses:@maintrivial-pass case + the accident-catching semantic that remains.Fragment:
changelog.d/456.fixed.mdexplaining the fix + the substrate-honest semantic.Bats coverage:
tests/check-self-bootstrap.bats: new test for the@mainwrapper case (currently only tests tag-pin scenarios).tests/workflow.batsortests/repin.bats: coverage for the auto-re-pin@mainskip case (mutation-verification).BookStack docs: no ripples — the adopter-facing surface is unchanged.
Verification AC (per tracker + Surveyor 8f3d)
Blast radius + implementation size
Implementation-routing recommendation
QM stays — this is substrate-adjacent to my recent #454 (compose-scaffold) work, my context is warm, and the shape is well-bounded. Estimated single-PR sprint (S/M).
Engineer routes if — design review surfaces adopter-substrate ripple I didn't catch (unlikely per the analysis above; toolkit-self ↔ adopter path separation is architectural, not implementation-detail).
Open questions for Bosun/operator ratification
Anchor
Reference-implementation moment — merged 2026-07-07 15:07:53
PR #457 landed the A.b floating-pin substrate on
main@e9eca94a. Stamping the reference-implementation property as substrate-of-record:The fix proved itself on its own PR branch.
check-self-bootstrap.ymlinvokes./scripts/check-self-bootstrap.shdirectly from the checkout — not through a pinned reusable — so the#456PINNED_REF == 'main'early-exit was live IMMEDIATELY on the PR that introduced it. Reliably-green CI on the PR that introduces reliably-green CI. Zero pre-merge rc-pin dance. The fix did not sit behind the gate it removed.Empirical evidence sequenced from here:
27eb, 15:07:53):check-self-bootstrapGREEN onmain@e9eca94aunder the new@mainwrapper substrate. Post-(A) evidence set for #448 begins here.Turnaround: operator ratification 12:36 → PR opened 12:59 → APPROVED 13:07 → MERGED 15:07:53.
Sequencing acknowledged: PR#457 is the class-closer, not the milestone-closer. #448 holds open as the empirical gate-record.
rt check-self-bootstrap#758