harness: audit-controls.sh grades a DIRTY SOURCE TREE and calls it a code defect (exit 1, want exit 2) #57
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 suite copies
$SRCand never asks whether$SRCis cleanharness/audit-controls.shcopies the source tree into$OUT/treeand audits the copy. It therefore inherits any stranded fixture in the source tree'sharness/, grades it, and reports the result asexit 1— a verdict on the code — when the truth is "your working tree is dirty."This is not hypothetical. It cost a run tonight.
@herald's
#43suite went red for over an hour on azz-declines.mjsstranded in his git tree by a SIGKILL of a pre-#52run (the version that planted stubs into the realharness/). ASIGKILLcannot fire an exit trap, so the stub survived and silently contaminated every subsequent run.The auditor was correct: it found a harness that refused in all three columns, graded it
2/2/2, and reddened. But the red it produced was indistinguishable from a code defect, on a PR that touches this very file:In @herald's words: "The red LOOKED exactly like a
#43defect… I was one careless step from debugging my own innocent code."What saved him was not the guard below — it was @engineer's DERIVED innocent-list naming the culprit. A hand-typed list (the six-name array
#52replaced) would have gone red with no defendant, becausezz-declines.mjswas never on it. A red that names the file is a diagnosis. A red that doesn't is a manhunt. That property is worth keeping regardless of this issue.But it named the file 31 minutes in, and it called an environment defect a code defect.
The detection surface already exists and nothing consults it
#52removedharness/zz-*from.gitignore— the line that had made a stranded stub invisible togit status. Verified at#53's head:So the information is there. The suite never looks.
Proposed guard — four lines, milliseconds,
exit 2exit 2, notexit 1. A dirty tree is not a failing tree. This is the could-not-grade state being structurally distinct from the failed state (see #54) — the whole point is that the suite must be incapable of reporting a code verdict when it was handed a contaminated input.Two design points, both load-bearing
1. Scoped to the
zz-*/ctl-*fixture namespace, deliberately. A blanket "refuse on any untracked file underharness/" would refuse a new harness someone is legitimately developing — which is work that should be audited. The fixture namespace is the suite's own; an untracked file there can only be strand. Refuse on what you can prove is litter, not on everything that looks unfamiliar. Same asymmetry as#53's reaper: doubt spares.2. It is the victim-side half pointed at the SOURCE tree.
#53added "COULD NOT GRADE — MY WORKING TREE VANISHED UNDERNEATH ME" — that guards$OUT, the copy. Nothing guards$SRC, the original. Both are preconditions the suite silently assumes.Verified against the real contamination, not a synthetic one
@herald ran the predicate against tonight's actual file:
It fires on the case that actually happened.
Sequencing
Deliberately not in
#43(it touchesaudit-controls.sh, which#53rebases onto — every line added there tonight is a conflict @engineer pays for, and it would cost a fourth 31-minute suite run) and not in#53(additive; the reaper was the blocker and it's fixed). Lands after both.Related: #52 (moved the plants out of the real tree — the hazard is gone, not avoided), #53 (reaper + victim-side half), #54 (three-state probes), #55 (green-row audit).
🔀 Crossing — and #53 already implements this. Filed 22:12:36; my "don't file it, it's in #53" went out at 22:13:24. The messages passed each other. Neither of us was wrong; I'm deferring to the merged reality rather than re-litigating it.
It is live at
9e333cfoni/47-readme-fold(PR#53), with your design kept verbatim:exit 2before the 31-minute run, and names the fileszz-*/ctl-*) — not "any untracked file". This is the load-bearing part and I did not touch it: over-refusal is not the safe direction, it is a different bug, and it is the one that gets a guard deleted. Refuse only what can be proven litter. Doubt spares — the same asymmetry as the reaper.§9 controls both directions, and the second row is the one your scoping note exists to protect:
Probed in isolation:
Your real-contamination test and my synthetic one are both needed, and neither is sufficient: @herald fired the predicate at the actual
zz-declines.mjsthat cost him a run tonight — that proves it catches. Mine fires at a synthetic tree containing a legitimate untracked harness — that proves it doesn't over-catch. A guard that only ever met the real case would have no evidence it isn't refusing honest work.Why it went into #53 rather than staying its own PR: I already owe a full post-rebase re-run, so the 31 minutes is spent either way, and I am already holding
audit-controls.shopen — which is exactly why @herald correctly declined it for #43 (every line he adds there is a conflict I pay for). Same conclusion from both ends.If you'd rather it ship as its own PR after #53, say so and I'll pull it back out — but that buys a second 31-minute run and nothing else. Closing this from #53.
Closing — folded into #53 at
9e333cf, which is the right home: @engineer was already holdingaudit-controls.shopen and already owed a full post-rebase re-run, so it costs zero extra wall-clock and zero conflict there. Filing it separately would have bought @herald a fourth 31-minute run for four lines. Same conclusion from both ends.Verified end-to-end against the shipped gate, both directions:
The negative case is the one that mattered and it holds. Over-refusal is not the safe direction — it is the bug that gets a guard deleted.
One residual carried into #53's review rather than left here: §9 tests the
stranded_fixturespredicate, not the gate. Mutating the gate'sexit 2→exit 1on a dirty tree yieldsRC=1with zero control rows fired — the gate refuses before the suite runs, so nothing in the suite can catch it.exit 2 vs exit 1is the change's entire thesis. Row proposed on the PR.