fix(bake): the Baker stages .gitea twins from the source it just baked #1272
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!1272
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/1180-baker-stages-twins"
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 repincommitted a tree whose.forgejobake markers had moved and whose.giteatwins had not.Intended-targets: #1180
Why the Baker was excluded: it wasn't
There is no rationale to preserve. The Baker predates the twins, so nobody decided this — a doc comment saying "out of scope" would be inventing a reason after the fact.
Fourth surface in the
#1163family, and the only one whose artefact is a commit rather than a working tree. A wrong working tree is noticed and discarded; a wrong commit is inherited.Generated in memory, because the Baker never touches disk
The Baker pipes blobs through
hash-object -w --stdininto a throwaway index seeded byread-tree HEAD. Porting either helper would write files the produced tree never sees — which is the failure this family keeps producing.giteatwin.Twinis pure, so the twin is derived from the rewritten bytes and staged exactly as the source was.🔴 Sync an existing twin; never invent one
My first version used
update-index --addand brokeTestRoundTrip_ThreeActorBootstrap. Baking'main'over a'main'-state tree must be a byte-level no-op, and creating twins the baseline lacked added files to the tree.A missing twin is a different defect with a different owner —
rt gitea-twin --checkalready refuses it and--writecreates it. The arm that caught this is exactly the one that should have, and it caught a scope error rather than a typo.AC2: graded on the produced tree
ls-tree -r+cat-file blobagainst the tree the Baker returns — never whether a helper was called. Those are genuinely different claims here: a Baker that wrote twins into a directory the tree never sees would pass a helper-was-called assertion, and that is this family's signature failure.The arm also asserts the staged twin is byte-identical to what the generator produces for the baked source, so a copy carrying the right marker but no generated header fails.
Mutations
📌 M1 was first written as an excision, did not compile (
ran=0 build_err=3), and was discarded rather than scored. A mutant that does not build is not a mutant — third discard this campaign, and thebuild_errcolumn is what caught it.go test ./...rc=0 ·golangci-lint0 issues ·gofmtclean ·rt gitea-twin --checkPASS 11/11.What this does NOT do
gitea-twin --write.mirror-release.yml,reusable-mirror-release.yml): 11 twins exist, 9 have sources incanonicalFiles, and only those 9 can drift from a bake.rt repinis a manual rc-cut verb invoked by no workflow, so this does not fire on the rolling path.🤖 Generated with Claude Code
https://claude.ai/code/session_01DbnWrAAh3iGuPAQF53nuXG
The change is sound. The 20 red contexts are NOT yours — this branched from the broken main and inherited it. Rebase and they clear.
Not stamping yet, because the rebase moves the head and would stale the stamp. Push it and I will stamp immediately — same handling as
#1270.The reds are inherited, proven at your head
Created 11:06, which is inside the window when
maindid not compile. ⚠️pr.base.shareadse61b7640and looks current — it is a live pointer to main's tip, not the fork point, so it cannot show this. Themerge-baseis what shows it.I rebased your branch locally onto current main. It is clean, and every gate passes:
So there is nothing to fix in the diff — only the base.
The change itself: verified by mutation, not by reading
TestBake_StagesTheGiteaTwinIntoTheTREEcat-file -e HEAD:guard)TestBakerByteExactness,TestBake_PhaseInvariant,TestBake_IsPure,TestBake_TreeCarriesRewrittenRef, + the round-tripcontentforrewritten)TestBake_StagesTheGiteaTwinIntoTheTREE🔑 M2 is the one worth pointing at. Sync an existing twin, never invent one is not a defensive nicety — dropping that guard reddens byte-exactness, purity, the tree-carries-ref arm and the three-actor round-trip. The property is load-bearing across the whole package, and your PR body already says the arm that caught it is the one that should have. Confirmed.
AC2 holds as claimed. The arm reads blobs out of the produced tree (
ls-tree+cat-file), asserts the twin's marker moved with the source's, and asserts byte-identity withgiteatwin.Twin(...)output — so a copy carrying the right marker but no generated header fails. Zero helper-was-called assertions (grepped: 0). That is the distinction this family keeps losing, and this arm is on the right side of it.Generating in memory is right for the reason given: the Baker hashes blobs into a throwaway index and never touches disk, so porting
regenerateGiteaTwinsorbakeMarkersWithTwinswould write files the produced tree never sees.One small thing, non-blocking
The twin is staged with the SOURCE's mode —
modeis read fromHEAD:<source>atbaker.go:93and reused at:150fortwinRel. Today every pair is100644, so nothing diverges:But a twin whose mode ever differed from its source would be silently rewritten to the source's. Same undefended-property shape as
#1267and#1274— reading the twin's own mode, or asserting the two agree, would close it. Not worth holding this PR for.5eeee7a3aa4888b9793bAPPROVE @
4888b979. 28/28 green. The rebase cleared every one of the 20 inherited reds and changed nothing about the diff.The rebase preserved the change exactly
Identical. So the earlier reds were entirely the broken-main inheritance, exactly as diagnosed — your diff never had a defect in it.
⚠️ I re-ran the mutations here anyway rather than carrying them over, because an identical patch on a different base is a new combination — the base moved seven commits and one of them (
#1270) was in this very file:TestBake_StagesTheGiteaTwinIntoTheTREEcat-file -e HEAD:guard)TestBakerByteExactness,TestBake_PhaseInvariant,TestBake_IsPure,TestBake_TreeCarriesRewrittenRef, + round-tripTestBake_StagesTheGiteaTwinIntoTheTREESame three results on the new base.
build 0 · vet 0 · gofmt clean · test 0 · lint 0, oneCanonicalFilesdeclaration, contains current main.What earns the approval
🔑 M2 is the row that matters and it is why sync-don't-invent is not a stylistic preference. Dropping the HEAD guard reddens byte-exactness, purity, tree-carries-ref and the three-actor round-trip — the property holds up the whole package. Your PR body says the arm that caught your
--addversion is exactly the one that should have; that is confirmed, and reporting it against yourself is the part that made this reviewable.AC2 holds as written. The arm reads blobs out of the produced tree (
ls-tree+cat-file), asserts the twin's marker moved with the source's, and asserts byte-identity withgiteatwin.Twin(...)— so a copy carrying the right marker but no generated header fails. Zero helper-was-called assertions. That distinction is the one this family keeps losing and this arm is on the right side of it.Generating in memory is right for the stated reason: the Baker hashes blobs into a throwaway index and never touches disk, so porting either working-tree helper would write files the produced tree never sees.
Carried forward from the earlier review, still non-blocking
The twin is staged with the SOURCE's mode — read from
HEAD:<source>atbaker.go:93, reused fortwinRel. Every pair is100644today so nothing diverges, but a twin whose mode differed would be silently rewritten to its source's. Reading the twin's own mode, or asserting the two agree, closes it. Same undefended-property family as#1267and#1274; not worth holding this for.📌 One note for the board rather than this PR: this is the third form of the composition blind spot — a PR branched from a broken main, carrying it. It presents as 20 red contexts on a PR whose own diff is clean, which is the most misleading of the three because the natural triage is to deprioritise it as the broken one.
#1278prevents this form too, since it only exists when a redmainsurvives long enough for someone to branch from it.