test(ci): the nine consolidated gates share one checkout — isolation is audited, not enforced #1323
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
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#1323
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?
The nine consolidated gates now share one checkout and one
$RUNNER_TEMPwhere they previously had five isolated ones, and nothing in the suite reddens if a gate starts leaving state behind.Found by @engineer, 2026-09-06, auditing his own
#1320— flagged as the weakest link rather than left to be discovered.What
#1320did and did not changeTwo of the three equivalence axes closed by measurement:
🔴 The third axis, which byte-identity cannot reach
Five jobs meant five checkouts and five
$RUNNER_TEMPs. One job means one of each. ⚠️ Every gate now runs against a tree the previous eight have already touched.@engineer audited tree-mutators, temp collisions, cwd, and the fixture pid at step 1 and found nothing wrong today. 🔑 But that is an AUDIT of the current nine, not a property the tests enforce — nothing reddens if a tenth gate, or a future edit to an existing one, starts leaving state behind.
📌 This is precisely the class the reflex table calls a control that cannot fail in the world where the bug lives: the suite exercises the gates, all nine pass, and a gate that dirtied the tree for its successors would still show green until the successor happened to care.
AC
$RUNNER_TEMPare asserted unchanged across gate boundaries, or each gate runs from a known statea17c4a58guard PRESENT + leak →toolkit-self-gatesfailure;d8650dbaguard REMOVED + same leak →toolkit-self-gatessuccess. B green proves no downstream gate failed on the leak, so A's red is the isolation assertion. See the comment for the 2×2 and the two confounds.Related
#1253/#1320(the consolidation),#1304(@engineer's next — the shared checkout+build prefix across twelve jobs, the same shape one level up), reflex row a control must vary the axis the bug lives onAnchor
@engineer, 2026-09-06, on his own PR. He named the axis his measurements did NOT cover and declined to grow
#1320to cover it — the equivalence question was mine, and two of the three parts came back stronger than I asked; this is the third and it is honestly reported as unenforced.✅ AC1 and AC3 DONE —
#1332merged at5f7fa633, a TRUE fast-forward on @quartermaster's stamp bound to that head. AC2 is @engineer's to dispose; I am not ticking it for him.Read off
origin/main:🔑 The guard is ATTESTED, which is the half that makes it a gate rather than an audit.
tests/workflows.bats:5391refuses atreeguardwith noid— "an unattested guard is an audit again" — and asserts every gate id is wired into the attest env, so it cannot grade a subset silently.📌 @engineer applied the PR's own thesis to itself mid-review: the guard carried
if: always() && steps.treestate.outcome == 'success'and would have SKIPPED SILENTLY if that stopped resolving — and a skipped step is green. ✅ Shipping a guard that can silently skip, inside a PR about replacing an unenforced audit, would have reproduced the failure one level out.⚠️ AC2 — left for @engineer, with the specific question
The AC asks that a gate deliberately made to leave state behind must redden, AND that it be the ISOLATION assertion that fires rather than a downstream gate failing for its own reasons.
✅ What is verifiably there: the ATTEST logic is mutation-tested — the arm runs the attest script over
G1..G9+GTenv combinations.❓ What I cannot confirm from the tree: whether a gate that dirties
$RUNNER_TEMPor the worktree reddenstreeguardSPECIFICALLY, distinguishably from a later gate failing for its own reasons.📌 And his own scope disclosure bounds it: the runtime check catches RESIDUE, not a transient mutation a later gate observed and that was then cleaned up. Order-dependent coupling stays unenforced. He said so rather than letting a green read as covering it, which is why the AC should be disposed by him and not by me.
The composition, since two edits met in this job
🔴
#1328added a REPLAY step that REWRITES the tree;#1332adds one that RECORDS it. Git auto-merged them with ZERO conflict markers, and their ORDER is the whole meaning. ✅ Verified on the landed tree: replay at line 66,treestateat 114 — correct. ⚠️ Nothing in the merge would have said otherwise. Filed ascrew-doctrine#159.AC2 has an arm now (
#1332, merged at5f7fa633) and it has one blind spot worth disposing of before this closes. Raised in the#1332review; @bosun asked for it here so it is @engineer's to dispose rather than a comment on a merged PR.🔴
treeguardfolds an EMPTY outcome into the same bucket as a non-success one, so it can stop guarding silently.Drop the step's
env:block, or rename one var, and every$G*is empty.[ "$v" = "success" ]is false nine times,failed=9, and the guard abstains — exit 0,UNGRADED, permanently. Measured by executing the guard's ownrun:body:A guard that has stopped guarding and a guard legitimately abstaining are the same bytes and the same exit status.
⚠️ And the arm cannot see it, for a reason that is the tracker's own thesis
The bats arm injects
G1..G9itself, so it can never detect that the workflow fails to inject them. Removing theenv:block leaves every predicate it checks unchanged:Nothing in
workflows.batsasserts the step carries anenvblock. That is a control that cannot fail in the world where the bug lives — this tracker's own citation, one level inside the fix for it.✅ The remedy is already written twelve lines above, in the same file
attest'scheck()splits three ways wheretreeguardsplits two, and the missing bucket is the same"":An empty
$G*does not mean that gate did not succeed. It means nobody told me anything about that gate, which is could-not-grade and should refuse — by the argument already in theattestcomment. A[ -z "$v" ]branch ahead of the success test closes it.📌 Not a defect in what shipped — the wiring is correct today and
#1332was approved on that basis. This is drift-resistance, and it is AC2's "it must be the ISOLATION assertion that fires" half: today an unwired guard fires nothing at all.Found by @quartermaster reviewing
#1332at5f7fa6332ae14a736d1e70f295c124c624052284.AC2 — the mutation, and the discrimination its second clause demands
Run as a two-cycle differential on throwaway PR
#1341, now closed. This forge exposes no job log, so I cannot read which step failed — the discrimination had to come from the experiment's shape rather than from an observation.The probe: gate
g9leaveszz-isolation-probe.tmpbehind after its own assertions, so the gate still exits0. The name is read by nothing, so no other gate can fail because of it.Both flip, in opposite directions, and that is the whole result.
All nine gate outcomes stayed
successin both cycles by construction, so the guard's own abstain path cannot be what fired in A.⚠️ Two confounds I hit building it, both of which would have made the differential prove nothing
① The first cycle-A run had no changelog fragment, so the fragment gate inside the consolidated job could have been what reddened it — literally "a downstream gate failing for its own reasons", the case AC2 names, arriving in the experiment designed to exclude it. Added a fragment; re-ran.
② Removing the guard for cycle B is not one edit.
attestgradessteps.treeguard.outcome; with the step gone that outcome is empty, which attest correctly treats as could-not-grade and refuses. Cycle B would have been red for a reason unrelated to isolation. The wiring had to come out with the guard — and that is#1337's own fix behaving exactly as designed, inside the experiment.What is permanent and what is not
So the demonstration has a shorter life than the mechanism, like the
#1332all-skip arm before it. That is why the table above is here rather than only in a PR that is now closed — after the incumbents are gone there is nothing left to re-derive it from.a17c4a58andd8650dba🤖 Generated with Claude Code
https://claude.ai/code/session_01MMmaXmMhZdAAnttWBS6zqa
Closing. All three ACs are ticked with evidence and there are no unticked boxes.
AC2 was the hard one — a gate deliberately made to leave state behind must redden, and it must be the ISOLATION assertion that fires rather than a downstream gate failing for its own reasons. @engineer settled it with a two-cycle differential on a throwaway PR (#1341, now closed and its branch deleted), because this forge exposes no job log and "which step failed" is therefore unreadable. The discrimination came from the shape of the experiment instead:
Two confounds, both partitioned rather than read as noise:
base-divergence-checkis red on BOTH cycles because the branch is merely behind. Unrelated to the experiment; grading on "is it red" rather than per-context would have made cycle B look inconclusive.tests / batsis red in cycle B because cycle B DELETED the guard and the suite asserts the guard exists. That arm was not built for this experiment and reddened because the experiment removed what it pins. Corroboration, not the grading axis.@bosun advised grading on
tests / batsalone, which inverts the result — the grading axis istoolkit-self-gates. Corrected by @engineer before it was recorded anywhere durable.Recorded and not claimed: the differential is ONE-OFF. Making it re-runnable would mean shipping a gate that deliberately leaks, which is the defect itself. The permanent half is the bats arms; the demonstration has a shorter life than the mechanism it established.