test(repin): no test runs rt repin's realBake, so a re-pin that bakes the wrong ref passes go test #1563
Labels
No labels
bump
major
bump
minor
bump
patch
kind
bug
kind
chore
kind
docs
kind
epic
kind
feature
loop/paused
mirror-drift
priority
critical
priority
high
priority
low
priority
medium
size
L
size
M
size
S
size
XL
status
blocked
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
frankenbit/release-toolkit#1563
Loading…
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?
#1561 found this while measuring what guards the bake.
rt repin's production bake step,realBake, is never run by a test. A re-pin that passed the wrong ref into the bake would passgo testand push an rc tag baked for something else.Measured
All reads are on live
origin/main6a36090, 2026-10-06.What
realBakedoes.cmd/rt/repin.go:64-74. It callsselfboot.CommitBakewithToolkitRef: refandPhase: bake.PhaseRepin, and returns the detached commit's SHA. Its only reference is the production wiring in the cobra command,repinDeps{... bake: realBake}atcmd/rt/repin.go:180.What the tests run instead.
cmd/rt/repin_test.gocallsrunRepinseven times (lines 182, 225, 259, 277, 311, 355, 410). Each injectsstubBake(line 291), which returns a fixed SHA without touching git. TheTestValidateRepin_*tests do not bake. No test runs therepincommand end to end:git grep '"repin"'over*_test.gofinds only the C5 exclusion entry incmd/rt/main_test.go:276.The injection is deliberate. The
repinDepscomment (cmd/rt/repin.go:52-56) says the bake does real git work that a scripted runner cannot fake. Injecting it made the orchestration testable, but no test exercises the real step on its own.Mutants. Each was applied to an LF clone of
6a36090and graded withgo test ./cmd/rt/ ./internal/selfboot/ ./internal/bake/. The baseline is ok on all three packages.ToolkitRef: refbecomesToolkitRef: "main"cmd/rt/repin.go:67ToolkitRef: refbecomesToolkitRef: "v0.57.0"cmd/rt/repin.go:67CommitBakebakes'main'instead of the requested refinternal/selfboot/selfboot.go:74TestCommitBake_TreeCarriesRewrittenRef'main'whatever ref it is giveninternal/bake/marker.go,rewriteMarkerTestRewriteMarker_CanonicalM2 and M3 were re-run with byte-exact LF writes and gave the same kills. So the kernel and
CommitBakeare pinned, andrt repin's call into them is not.Why it matters
rt repinis dormant for toolkit-self while the wrappers float at@main(AGENTS.md §2.2). It is still the tool for a deliberate tag pin, and the first run after a long dormancy is when an untested wiring line is most likely to have drifted.If it regressed like M1 or M4, the re-pin would push an rc tag that does not carry its own ref. Only two mechanisms would then react:
build-ref-checkwould refuse the tag, but only after it exists. It has never graded an rc tag on the forge (#1561).'main'at run time. It does not catch M4's stale value, which wins as baked. That is howv0.57.1tov0.57.4shipped a stalereusable-recover-pending-cut.yml(#1173).AGENTS.md §2.5 says "The bake is pinned in Go tests", naming
cmd/rt/build_bake_test.go,internal/bakeandinternal/selfboot. That holds for the kernel andCommitBake, but not for the steprt repinactually runs.Acceptance criteria
realBakeagainst a scratch repository carrying the canonical reusables. It asserts that the returned SHA is a commit, that its tree carries'<rc-tag>'at every marker-carrying canonical reusable, and that its parent is the scratch repository's HEAD.rt repin's own call into the bake" is updated. That sentence lands with #1562, so update it there or after it.go test ./...passes, and so does the CI linter (golangci-lint).Out of scope
rt repincommand, which pushes a tag and a branch and opens a PR. The orchestration tests withstubBakealready cover that sequence.rt repinfor a tag whose tree does not carry its own ref. Today the only check on the baked value isbuild-ref-check, after the push. Whether to refuse earlier is a separate design question.