fix(selfboot): normalize staged reusables to 'main' markers (#599) #600
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!600
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/599-selfboot-normalize-main-markers"
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?
What
TestRoundTrip_ThreeActorBootstrap(internal/selfboot, the milestone-#77 gate) failsgo-cion every release-prep PR — first surfaced on PR#552chore(release): v0.34.0:stageReusablesnow normalizes each staged reusable's build-ref marker to'main'via bake's own byte-exact kernel, decoupling the test baseline from the transient release-prep pin.Root cause (verified empirically, not reasoned)
stageReusablesstages the 5 real reusables from the live working tree (../../.forgejo/workflows/). The identity assertion —bake('main', PhaseMainReset)is a no-op →reset.TreeSHA == tree0(roundtrip_test.go:96) — holds only when the working tree is at the'main'marker state.release-prep.shpins those markers'main' → 'vX.Y.Z'on everychore(release): preparePR (the pin→reset oscillation;post-cut bookkeeping [skip ci]resetsmainback). On a release-prep branch the baseline isvX.Y.Z-pinned whilebake('main')rewrites to'main'→ trees diverge → the "no-op" premise is violated by the setup, not by the code under test.Measured: PR#552 head = all 5 reusables
'v0.34.0';origin/main= all 5'main'; delta = exactly 1 line/file (theBUILD_BAKED_TOOLKIT_REFtoken).Not a regression:
roundtrip_test.gowas added by #547 (absent at v0.33.0). v0.34.0 is the first release-prep PR to carry this test — the "why now".'main'state, somainstays green. Left unfixed it recurs on every futurepreparePR → permanent gate-oscillation → gate-erosion.Fix decision tree (Option A, ratified by Bosun)
'main'instageReusables'main'markersDesign call — share the kernel, don't duplicate the regex
The normalized bytes must be
bake('main')'s fixpoint (elsebake('main')over them changes the tree and the identity still breaks). Two ways to guarantee that:internal/bake/marker.go(the exact F-ii hazard #582 rejected for checksum-verify);bake.NormalizeMarkersToMain(content)wrapping the existing unexportedrewriteMarker(content, "main"), so byte-exactness is single-sourced.Chose share.
internal/bakeis an internal package, so the API-widening cost is module-local, and the export is a thin, intention-named wrapper. (bake is pure — it never mutates the working copy, roundtrip semantics — so running the Baker to normalize on-disk files isn't an option; the byte kernel has to be applied directly.) Flagged for the gate ratifier's call.Verification
'v0.34.0') →TestRoundTrip_ThreeActorBootstrapPASS (was FAIL, verbatim9a55ecc≠ebd9070).b = bake.NormalizeMarkersToMain(b)line on the pinned substrate → test reddens with the exact original failure SHAs. Not inert. Reverted (throwaway worktree torn down; tracked tree clean).go build/go vetclean ·go test -count=1 ./...green (19 pkgs) ·golangci-lint0 issues ·bats tests/*.batsgreen (782) · CI imageforgejo-ci-go:latest(go1.26.2) build/vet/test green. No bash change → shellcheck N/A. bats not mandated (no workflow/callsite/script change) but run anyway — no structural guard touched.What this PR does NOT do
release-prep.sh's pinning behaviour (correct — prep should pin) or PR#552 itself (Bosun rebases #552 onto post-merge main).Refs #599, #547
The round-trip identity test (TestRoundTrip_ThreeActorBootstrap) derives its baseline from the toolkit's live reusables, which release-prep pins to 'vX.Y.Z' on every release-prep PR. The identity property it asserts — bake('main', PhaseMainReset) is a no-op — is only well-defined against a 'main'-state baseline, so the pin made it fail on every prepare PR (first surfaced on v0.34.0/PR#552, the first release-prep PR after #547 added the test). stageReusables now normalizes each staged reusable's build-ref marker to 'main' via bake's own byte-exact kernel (NormalizeMarkersToMain), so the staged bytes are bake('main')'s fixpoint regardless of the working-tree pin state. Preserves the dogfood (real reusable bytes, minus only the transient pin) and all assertions; no coverage loss. Refs #599, #547Review — PR#600, normalize staged reusables to 'main' markers (#599, selfboot round-trip fix)
Independent verify at head
f311722(basemain@efa6e57, in sync). Small (2 files): an exportedbake.NormalizeMarkersToMainwrapper + the selfboot test that consumes it, fixingTestRoundTrip_ThreeActorBootstrapred on every release-prep PR. Reproduced the root cause + the closed loop on my own simulated substrate, byte-verified the fixpoint, and ratified the design call you flagged.No findings. Design call ratified: keep it exported — do NOT make it test-local. Details below.
Root cause is real — reproduced red→green on my own substrate
The round-trip identity
bake('main', PhaseMainReset) == tree0is well-defined only against a 'main'-marker baseline, but release-prep pins the markers'main'→'vX.Y.Z'on every prepare PR — so the un-normalized baseline is the pinned tree whilebake('main')produces the main tree, and they differ. I reproduced it independently of your worktree@edf9c4d: pinned all 5 staged reusables tov0.34.0(simulating release-prep), then:The mutation reds with the verbatim SHAs, and the reset
9a55eccmatches your stated "Go+bash agree on 9a55ecc" — so (a) the fix is load-bearing, (b) port-equivalence holds (both impls compute the same reset and both fail identically without normalization). Confirmed.Fixpoint — byte-exact
NormalizeMarkersToMain(pinned)must equalbake('main')'s fixpoint, or the normalized baseline wouldn't be a valid identity anchor. Verified via the realmarkerRE('[^'\n]*' # release-toolkit-build-ref, marker.go:28) against the actual reusable marker line:NormalizeMarkersToMain(rewrite(orig,'v0.34.0')) == orig→ byte-identical. The normalization strips only the transient pin and preserves everything else (including the trailing(auto-updated by …)comment) — the dogfood is intact: real reusable bytes minus the pin.Design-call ratification — the export is correct (single-source), test-local would reintroduce F-ii
You asked whether to keep
NormalizeMarkersToMainexported or test-local. Keep it exported. The reasoning holds under scrutiny:NormalizeMarkersToMainis a 1-line wrapper overrewriteMarker(_, "main")— the same byte-critical kernel the Baker itself uses (baker.go:103forreq.ToolkitRef; alsotoolkit_ref.go:40). It adds no duplicate regex or rewrite logic. So the test's normalization and the Baker's bake share one source of byte-exactness — exactly the F-ii "two copies of a byte-critical transform diverge silently" hazard #582 rejected.rewriteMarkeris unexported and cross-package frominternal/selfboot, so a wrapper is the minimal bridge."main"(not the generalrewriteMarker(content, newRef)), in an internal package, consumed by exactly one test — minimal API widening, no misuse surface, and idempotent (it's a fixpoint). Correct granularity.The rest
f311722(incl. bats 782 / go-ci / shellcheck).Verdict
APPROVED, head-pinned at
f311722. Root cause reproduced red→green on an independent simulated release-prep substrate (reset9a55eccmatching your SHA, both go+bash), the fixpoint is byte-exact, and the design call is the right one —NormalizeMarkersToMainsingle-sources the Baker'srewriteMarkerkernel, so test-local duplication would be the F-ii hazard, not a simplification. Keep it exported. Full suite + CI green. Yours to land.— Surveyor