Lesson

A guard that does nothing looks like a guard with nothing to do

By (LinkedIn)Published 4 min read

A guard that silently does nothing looks exactly like a guard with nothing to do. Some of ours failed that way: a check that resolved nothing and still passed, and a push guard that never saw the push. Test that a guard fires on the real failure and stays quiet on normal work, reintroduce the bug before you trust it, and make it say what it cannot check.

Quiet is not the same as working

When an AI agent edits files or pushes code in our environment, small automatic checks watch what it does: a linter on a file it wrote, a refusal to push to work that has already been merged. When a check finds nothing, it says nothing. That is also exactly what it says when it is broken.

It happened to us more than once:

  • A check that routes each file edit to the right project’s rules shipped resolving nothing, and it looked fine. A small parsing mistake meant every rule was skipped. Nothing failed. Nothing ran. It happened a second time, for a different reason, with the same silence.
  • A guard that stops pushes to already merged work never saw the push at all, because a bug cut the command short before the guard read it. It had been put in place precisely because this kind of mistake had happened before.
  • A website’s syntax check lost its registration and sat there, silently doing nothing, until someone looked.
  • Setup scripts kept printing “success” while another running process quietly undid what they wrote.
Four ways a guard can behaveA 2 by 2 grid. Columns: fires on the real failure, or does not. Rows: stays quiet on normal work, or does not. Fires and stays quiet: it works. Fires and cries wolf: it gets switched off. Silent and quiet: the dangerous one. Silent and noisy: useless.Fires on the real failureDoes not fireQuiet on normalworkWorksthe only quadrant you wantSilentlooks like it works, and protects nothingNoisy on normalworkCries wolfsomeone switches it off by the afternoonUselessnoise, and no protectionFour ways a guard can behaveA 2 by 2 grid. Columns: fires on the real failure, or does not. Rows: stays quiet on normal work, or does not. Fires and stays quiet: it works. Fires and cries wolf: it gets switched off. Silent and quiet: the dangerous one. Silent and noisy: useless.Fires on the realfailureDoes not fireQuiet onnormalworkWorksthe only quadrantyou wantSilentlooks like itworks, and protectsnothingNoisy onnormalworkCries wolfsomeone switches itoff by theafternoonUselessnoise, and noprotection
The dangerous quadrant looks exactly like the one that works, until the failure it was meant to catch arrives.
Read the diagram as text
  1. Quiet on normal work, Fires on the real failure: Works (the only quadrant you want)
  2. Quiet on normal work, Does not fire: Silent (looks like it works, and protects nothing)
  3. Noisy on normal work, Fires on the real failure: Cries wolf (someone switches it off by the afternoon)
  4. Noisy on normal work, Does not fire: Useless (noise, and no protection)

Test both directions, then break it on purpose

A guard needs two tests, not one: that it fires on the failure it exists for, and that it stays quiet on ordinary work. Then it needs a third: put the original bug back and watch it get caught.

Calibrating a guardA loop of four steps: write the guard, reintroduce the bug it is meant to catch, check whether it fires, and if it does not, fix the guard and start again.Write the guardReintroduce the bug it isforDoes it fire?and is it quiet on normalwork?No: fix the guardCalibrating a guardA loop of four steps: write the guard, reintroduce the bug it is meant to catch, check whether it fires, and if it does not, fix the guard and start again.Write the guardReintroduce the bug it is forDoes it fire?and is it quiet on normal work?No: fix the guard
Read the diagram as text
  1. Write the guard
  2. Reintroduce the bug it is for
  3. Does it fire? (and is it quiet on normal work?)
  4. No: fix the guard, then back to: Write the guard

What that looks like in practice:

  • The routing check now has a set of test cases that assert exactly which rules each case reaches. It never checks only that the script finished, because “reached nothing” and “nothing to do” both finish cleanly. One live case writes a deliberately broken file and requires the guard to reject it.
  • On another project, a check against committing secrets was first run across the whole codebase to count false alarms, and then known bugs were put back one at a time. Some of the checks that looked fine did not fire. Without that step, we would have trusted them.

Keep the rule narrow, and say what it cannot see

A guard that blocks too much does not survive. Two comments from our own checks say it best: “ALLOW everything else… or this gets switched off and then it catches nothing”, and “a guard that cries wolf on prose gets switched off”. Narrow rules stay switched on.

Some guards should let work through when they cannot decide, and say so. Our push guard asks GitHub whether the work is still open. If that lookup fails, it lets the push through, for a reason written in the file: a safety net that stops all work whenever the network hiccups is a safety net somebody removes the same afternoon. The price is that a successful push is not proof, and the documentation says that too.

For the checks that check the checks, we added a read-only health report: one line per check, where every failure names the script that fixes it.

The rules we kept, as a checklist:

  • Decide whether the guard refuses, or adds a check and lets the work through. Write the choice down.
  • Write down what it cannot see. Ours cannot see files written by a build tool, a formatter or a copy command, and the documentation says so.
  • Test that it fires, and test that it stays quiet.
  • Reintroduce the original bug before you trust it.
  • Keep the rule narrow enough that nobody wants to switch it off.
  • Check that the checker exists and is switched on.

If you are putting guards around AI agents or automated processes and want a second pair of eyes on them, book a call.

Book a 30-min scoping call

Bring one process. We will tell you whether automation changes it.