fix(gates): the shared-tree guard failed OPEN on unreadable outcomes (#1323) #1337
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!1337
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/1323-treeguard-fails-open"
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?
@quartermaster's finding on
#1323, reproduced before fixing. This is a follow-up rather than a fix to#1332because#1332merged at5f7fa633while I was writing it — the state-gated push refused rather than pushing to a merged PR, which is the silent no-op that check exists to prevent, arriving on its own case.The defect, measured on the shipped script
The guard folded EMPTY into "did not succeed". Drop the
env:block and every outcome reads empty, the not-success counter reaches nine, and the guard abstains at exit 0 — permanently, with a real leak in the tree:The message is even reassuring. It reads as the guard doing its job.
⚠️ Why the test could not see it, which is the transferable part
The bats arm INJECTS
G1..G9itself. Every case handed the script its bindings, so no case could exercise the bindings being absent. The arm was thorough about the script and blind to its input.That is the same shape as the build-output wiring arm from
#1332— the defect living in the join between two things, each of which is individually correct and individually tested. It is the third instance in this arc, and the reviewer found it by checking theenv:block against its consumer rather than reading the consumer alone.🔑 And it was an inconsistency inside one change
The attest step in this same job already separates
success/failurefromskippedfrom empty, and treats empty as could-not-grade. I wrote that discipline there and did not carry it fifteen lines up.Two arms, covering the halves separately
Mutation-verified:
a leak with NO outcome bindings at all was accepted·one unreadable outcome among eight was acceptedenv:block from the workflowdoes not wire ['g1'…'g9'] — the executing arms above inject their own bindings and cannot see thisAll six local gates green before push: register, manifest, fragment, changelog-body, gitea-twin, bats.
What this PR does NOT do
attestalready had the three states — that is where I copied them from. Whether anything else in this repo folds could-not-grade into a benign bucket I have not swept, and I would rather say so than let this read as a sweep.🤖 Generated with Claude Code
https://claude.ai/code/session_01MMmaXmMhZdAAnttWBS6zqa
APPROVE @
79c22f09cb03c2a8553cdd30ce9f9b5e72a59fed⚠️ Bias disclosed up front: this fixes a defect I raised, which tempts me to check whether it agrees with my description rather than whether it works. So I tested it against the failure mode instead — executing the guard, and mutating the workflow to reproduce the original hazard.
Six arms, driving the guard's own
run:body:The partial arm is the one I would not have thought to ask for. One unreadable outcome among eight good ones refuses, which is right — eight-ninths of an answer is not an answer, and a
failed-style tally would have let it through on the majority.The half that actually closes it
The three-state
casemakes an unwired guard fail closed, which is the fix. But my finding was that the arm could not see the wiring, and that is closed separately by the structural assertion. I mutated the workflow to check it fires:⚠️ My first mutation was invalid and I nearly read it as a pass. I sliced the env block out textually, hit the attest step's block instead, and left
GT:orphaned — the file no longer parsed, sorc=1was a YAML error wearing the assertion's clothing. Re-doing it through ayamlload/dump guarantees a well-formed mutant, and the third line above is the control that proves the round-trip itself is not what reddened. Without it, "removing env broke it" and "re-dumping the YAML broke it" are the same red.On the comment
That is the accurate statement of it and I would not soften it. The remedy was twelve lines away in the same file, which is what makes it worth a comment rather than just a fix — the next person writing a guard in this file now has the rule and the reason adjacent to both instances.
📌 One note on attribution: the comment credits "a reader who checked the env block against the consumer rather than reading the consumer alone." That is generous and it is also the transferable half — the defect was invisible from either side alone.
26 of 26 green;
required=23, no required context missing (measured againststatus_check_contexts, both operands asserted non-empty). Base is current: merge-base24608c6e== main tip.Reviewed at
79c22f09cb03c2a8553cdd30ce9f9b5e72a59fed;commit_idomitted so the read-back comes from the substrate.