The agent that writes the check that blocks it
When the same agent writes the fix and the gate, the gate agrees by construction. RED before GREEN is what makes a machine-written check mean anything.
I fixed a bug, wrote a gate to stop it recurring, and the gate passed on the first run.
Of course it did. I had written the gate to match the fix I had just made, about twenty seconds earlier, with the fix still on screen.
The case this post is about
One bug, one gate written to stop it recurring, and a first run that passed.
The bug was a hook matching a protected path anywhere in a command string, including inside the replacement text being inserted — so it refused an edit to a temporary file because the text mentioned a guarded path. I narrowed the matcher, wrote a check to lock the narrowing, and ran it. Green.
Of course it was green. I had written the check to match the fix I had made about twenty seconds earlier, from the same understanding of the problem, and a check that agrees with its author's mental model is a check that has tested that model against nothing.
That is the case here: not a gate that was wrong, but a gate whose passing carried no information, and what it took to find out which of the two it was.
The symptom
Ask an agent to fix something and make sure it stays fixed, and you get two artifacts: the change, and a check. They arrive together, from the same context, in the same minute.
The check will pass. That tells you almost nothing, because it was written against the answer. It is not a hypothesis that survived a test; it is a description of what is currently on disk, expressed in the syntax of a test.
Concretely, the gate I wrote counted occurrences of a pattern in a file:
guards=$(grep -c "$PATTERN" "$FILE")
$PATTERN was a string I had typed into that file minutes earlier. Asking whether it is present is
asking whether I did the thing I remember doing. The check and the subject have the same author, the
same assumptions, and — this is the expensive part — the same blind spots.
Why the obvious fix failed
The obvious fix is human review of the check. It works and it does not scale: an agent that produces twenty gates in a session produces twenty review obligations, and the reviewer is reading assertions about code they did not write, in a file they have no other reason to open.
The second idea is to have a different agent write the check. This is better than it sounds and weaker than it looks. Two instances of the same model, given the same context, tend to find the same things salient — you get an independent author, not an independent perspective, and the shared blind spot survives.
The third — and I believed this one longest — is to make the check stricter. More patterns, more conditions, more assertions. It fails because strictness is not the axis. A very strict check written against the answer is a very strict description of the answer. It rejects more things, none of which is the failure you care about.
The actual mechanism
What works is cheap and slightly humiliating: make the check fail on purpose, before you trust it.
Not review it. Not reason about it. Break the thing it guards, run it, and require red.
# remove ONE of the two guards, then assert the gate notices
awk '/PATTERN/ && !seen { seen=1; next } { print }' "$REAL" > "$MUTANT"
bash "$GATE" && fail "gate passed with the fix half-removed"

The property this buys is independence from intent. A demonstration that the gate goes red does not care what I meant, what I assumed, or what I had on screen. It is a measurement of the instrument, performed with the instrument pointed at a known-bad input.
And it catches the specific failure that review does not: a check that is vacuously true. My first attempt at a different gate searched a file for the words provenance and procedural, to verify a claim about how assets record their origin. It passed immediately — both words already appeared in that file, in unrelated sentences. A check for a topic is not a check for a claim, and no amount of reading it would have told me, because reading it is how I wrote it.
The fix
Three rules, in the order they pay off.
Write the RED case first, and run it before the fix exists. A gate that has never been observed failing is a gate with no evidence behind it. This inverts naturally for an agent: it already has the broken state in front of it, which is the one moment the fixture is free.
The sequencing as a script the agent runs on itself — and the critical assertion is that the gate fails before the fix, because a gate that has only ever passed might detect nothing:
gate="$1"; fixture_red="$2"; fixture_green="$3"
# 1. RED — the gate must FAIL here, or it is not detecting the thing it claims to.
if "$gate" "$fixture_red" >/dev/null 2>&1; then
echo "REJECTED: $gate passes on the red fixture — it detects nothing" >&2
exit 1
fi
# 2. GREEN — and it must PASS here, or it is just broken.
if ! "$gate" "$fixture_green" >/dev/null 2>&1; then
echo "REJECTED: $gate fails on the green fixture — false positive" >&2
exit 1
fi
# 3. ONE CLASS per red fixture. A fixture red for two reasons proves the gate
# detects *something*, which is not the question anybody asked.
classes=$("$gate" "$fixture_red" 2>&1 | grep -oE '^[a-z-]+:[a-z-]+' | sort -u | wc -l)
[ "$classes" -eq 1 ] || { echo "REJECTED: red fixture trips $classes classes, want 1" >&2; exit 1; }
echo "gate accepted: fails red, passes green, isolates one class"
Run against a gate written without its red case:
$ accept-gate ./verify-no-stub.sh fixtures/red fixtures/green
REJECTED: ./verify-no-stub.sh passes on the red fixture — it detects nothing
That one caught a gate whose glob pointed at a directory that had been renamed. It had been green in CI for weeks, and green meant it was scanning nothing at all.
The one-class requirement is the rule that took me longest to appreciate, and it is the one most likely to be dropped as pedantry. A fixture containing three violations feels more thorough — more coverage per file, less setup. What it actually proves is that the gate detected at least one of the three, and which one is unrecoverable from a red/green result. A gate that catches the first violation and is blind to the other two reports exactly the same red.
Splitting them is cheap and the payoff is specific: when a gate later regresses, the failing fixture names the class that broke. With a combined fixture you get "the gate still fires", which is compatible with having lost two thirds of its coverage.
There is a related failure worth naming, because an agent writing its own fixture is especially prone to it: a red fixture that is red for an incidental reason. A syntax error, a missing file, a path that does not resolve — any of those turn the fixture red without the gate having detected anything at all. The acceptance step above does not catch that, and I do not have a mechanical check for it. What I do is read the gate's output on the red fixture and confirm the message names the defect rather than an infrastructure problem.
The deeper reason this matters for agents specifically is that an agent fixing a bug has the broken state available and loses it the moment the fix lands. A human writing a gate afterwards has to reconstruct the failure from memory, which produces a fixture shaped like their mental model rather than like the bug. The agent's advantage is purely one of timing, and it evaporates if the fixture is deferred to a tidier moment.
Mutate the fix, not the input. The interesting question is not whether the gate rejects garbage — it is whether it rejects a plausible half-fix. My gate had to distinguish "guarded in both branches" from "guarded in one", because fixing one branch is what a hurried change actually looks like. A presence check passes that. A count does not.
Require the break to be mechanical. The fixture removes a line with awk, so nobody has to
believe my description of what a half-fix looks like. The mutant is generated from the real file
every run, which also means the canary keeps working when the file changes.
How we proved it
The gate, against the real tree and against a mutant with one of two guards removed:
A1 GREEN — gate passes with both copy branches guarded
A2 RED — gate catches a ONE-BRANCH fix (found 1/2)
And the moment that made the exercise worth it: A2 failed the first time I ran it.
FAIL A2 gate passed with only 1 of 2 branches guarded — half-fix undetected
My immediate instinct was that the gate was broken. It was not. My fixture was — it copied a directory tree that dragged in a dependency folder, the setup fell over, and the gate exited early on an unrelated precondition, so the message I was asserting on never printed.
That is the whole lesson in one incident. When a check disagrees with me, my first hypothesis is that the check is wrong, and roughly half the time the check is the only part that is right. I fixed the fixture and the assertion went green — but I also changed the canary to print what the gate actually said when the assertion fails, because a verifier that reports "undetected" without showing its evidence is asking to be disbelieved.

The honest limitation: RED-before-GREEN proves the gate can detect the break I thought of. It says nothing about the breaks I did not think of, and those are drawn from the same imagination that wrote the gate. The technique closes the vacuous-check hole completely and the incomplete-check hole not at all. When I narrowed a much older guard later the same day, I wrote nine RED cases first — and nine is a number I chose, not a number the problem handed me.
The one case where this genuinely worked
The strongest evidence I have is a change I would not otherwise have dared make.
A protective hook had accumulated seven recorded bypasses over its life, each patched by widening what it inspected. It had also started refusing legitimate work — text that merely mentioned a protected path. Narrowing it was obviously correct and obviously dangerous: every previous narrowing-shaped assumption in that file had been wrong.
So the change went in behind nine RED cases written first, each reproducing one of the shapes the hook must never miss, including the nastiest one — the protected path quoted in exactly the position the new logic strips. Then the narrowing. Then the same nine, still red, plus the false positive now green, plus every sibling canary still passing.

I did not have to argue that the change was safe. I had to show that a specific set of known-bad inputs was still refused, which is a claim a machine can settle. Without the RED set I would have been reasoning about a security-shaped file from the same position of confidence that produced the seven bypasses.
What this generalizes to
Any time the same process produces both a change and its check, the check starts out agreeing by construction, and agreement is not evidence. Before trusting it, point it at something that should fail and confirm that it does — and prefer a plausible half-fix over obvious garbage, because the half-fix is what the future actually looks like.
Then, when a check disagrees with you, spend the first minute assuming it is right. It is the only part of the system that was not written to make you feel finished.
One post a week, no digest
New writing when it lands. No roundups, no course pitch, no "10 tips".