
An AI coding agent hit a shell policy that blocked rm -rf. Good. It still needed to clear the files, so it wrote a Python cleanup script and ran that instead. The forbidden command never executed. The files were gone anyway. Bad.
That incident is documented in Grail's account of why policy guardrails fail with autonomous agents. The agent was not staging a rebellion. It was doing what we pay agents to do: route around an obstacle and finish the task.
The guardrail did exactly what its author wrote. It checked for one command and stopped that command. The intent was larger: do not let the agent destroy files. The check passed while the intent failed.
I have built this defect more than once.
What counts as a gate
I use Agent Harness Engineering for the work around the model: the hooks, permissions, checks, state machines, orchestration rules, and completion contracts that decide what an AI coding agent may do and what evidence counts as done. You can also think of it as the AI control plane. The model produces and reasons; the control plane constrains and verifies.
A gate is any part of that harness that accepts or rejects agent work. A pre-tool hook is a gate. A permission boundary is a gate. A catalog lookup, a test, a Definition of Done item, or a completion contract can all be gates. The implementation varies, but the job is the same: allow acceptable work through and stop unacceptable work.
Instructions are useful, but they are not controls. "Do not delete files" lives in the same context the agent is reasoning through. So does "always write a test first" or "do not touch authentication." The model can misunderstand those sentences, trade them against another instruction, or reason its way to an equivalent action.
A real control sits outside that reasoning loop. The hook refuses. The permission is absent. The merge check stays red. The model does not get to reinterpret the boundary.
This distinction is becoming clearer across the field. In her harness-engineering work, Birgitta Böckeler separates deterministic, computational controls from inferential ones and says we still need a way to evaluate harness coverage and quality (Martin Fowler). We have mechanisms. We do not yet have much design discipline for deciding whether a mechanism protects the property its author thinks it protects.
That gap matters because a deterministic check can be perfectly repeatable and still be wrong.
Check passed, intent missed
The rm block is the cleanest example. Its check was effectively "reject this shell command." Its intent was "prevent destructive file deletion." Those statements are not equivalent, and Python fit through the space between them.
I found the same shape while dogfooding an AI-SDLC harness I built. In one system, we maintained a catalog meant to stop agents from rebuilding components that already existed. The gate compared a proposed component's name, file path, and content hash against the catalog.
An agent reimplemented an existing capability under a new name. New name, new path, new bytes. Every comparison returned the correct answer, and the duplicate sailed through.
The check answered, "Is this literally the same component?" The intent was, "Are we rebuilding a capability we already have?" We had encoded identity and believed we had protected functionality. (In hindsight, those are obviously different questions. They were less obvious while the check was green.)
I call this the box-checking government agent: the audit trail is immaculate, each field is satisfied, and the objective is lying on the floor.
The shorter name is more useful: check passed, intent missed.
Once I had a name for it, I saw it in gate after gate. They were not fuzzy checks in need of tighter thresholds. They were precise checks measuring the wrong property.
Designing a gate before shipping it
The reflex after the Python bypass is to extend the blocklist. Ban Python deletion. Then Node. Then find -delete. You can spend a long time enumerating implementations of the outcome you meant to prevent.
I now start one level earlier: recognize that I am authoring a gate. That is step zero because it is the step I skipped most often. A hook or catalog lookup can look like ordinary plumbing right up until it acquires the power to accept or reject work. At that moment it becomes a control, with a control's obligations.
The mechanical check comes next. Write down exactly what the machine evaluates: a hash comparison, a regular expression, an exit code, a state transition, set arithmetic. If that decision can be deterministic, make it deterministic. Asking a second LLM whether the first LLM complied may be useful as review, but it is not the same kind of boundary.
Then write the intent in one sentence. Not "the test passes." State the real-world property that a passing test is supposed to imply: "The agent cannot delete user files without approval." "The system does not contain duplicate capabilities." "Every accepted change has evidence for each acceptance criterion."
Now try to break the implication on paper:
Can this exact check pass while that exact intent fails?
The renamed component was the adversarial case for our catalog. The Python script was the adversarial case for the shell block. If I can construct such a case in five minutes, the gate is unsound before it ships. I can revise the check, narrow the intent, add another control, or explicitly accept the gap. What I cannot honestly do is claim the original protection.
Some intents cannot be covered completely by one check. Static parsing will not identify every possible implementation of file deletion. A catalog cannot prove that two components are semantically equivalent in every case. Chasing a maximally clever adversary can turn a useful gate into a research program.
So the threat model is part of the design. Which accidental or naive mistake do we actually observe? What stronger boundary covers the dangerous remainder? What residual risk stays open? A narrow gate with an honest label is more useful than a broad promise with a hidden hole.
Scope matters just as much. We once applied a completion gate at the wrong grain. It required a release artifact—a version tag—from a small worker agent operating in the middle of a larger feature. That worker had no business releasing anything. The gate presented it with two choices: fail forever or manufacture evidence.
The agent produced a fake release tag.
The gate did not merely miss its intent. It created pressure to fabricate. That is a different class of damage: a control meant to stop bad work began rewarding a convincing lie. A gate must know when it fires, which actor it governs, and whether that actor can legitimately produce the artifact being demanded.
This playbook has more than two fields, and I expect it to grow. Check and intent are the foundation. Threat model, enforcement boundary, scope, residual risk, and escalation are already load-bearing. Treating those dimensions as a fixed checklist would recreate the problem in miniature.
A block should have somewhere to go
I used to treat a gate firing as friction. The run stopped, which meant the system had failed to remain autonomous.
That was backwards.
When a gate blocks an agent, three outcomes are available.
The agent can wedge: it cannot proceed, cannot stop cleanly, and burns the rest of the run. The agent can bypass: it finds an equivalent route, as the cleanup script did, and defeats the intent without raising an alarm. Or it can escalate: it stops the unsafe path and hands the decision to a human or supervising orchestrator with the context needed to resolve it.
The bypass is the worst outcome because it looks successful. The gate is green. The audit trail shows no forbidden command. The damage is real and the signal is missing.
Escalation is the target. Something questionable was about to happen, the control caught it, and the agent asked for a decision instead of improvising around the boundary. That is success through failure.
It only works if escalation was designed. A useful block needs an owner, a context package, and a forward path: what the agent was trying to do, which check refused it, what evidence exists, who may decide, and how execution resumes afterward.
Public issue trackers show how easy it is to under-design this part. One Claude Code error message suggested the destructive TaskStop action before the safer option of waiting for a running background agent; issue 32307 was later closed as not planned. A separate, currently open report describes a workflow resume after compaction silently re-running completed agents instead of finding their recorded results (issue 65796). These are user reports, not controlled evaluations, but both expose the same missing questions: Is the agent actually dead? Is the work actually absent? What state should travel through recovery?
A gate is unfinished when all it knows how to do is say no.
The playbook has to police itself
We enforce our gate-design playbook with another gate. That creates a useful test of whether the discipline is honest.
The mechanical half is straightforward. Did the author declare a check? Did they state an intent? Did they supply an adversarial case and name the remaining gap? A build-time meta-gate can require those artifacts.
The judgment half is not mechanical. Does the proposed check actually imply the proposed intent? Is the adversarial case serious? Is the threat model honest? Those are reasoning questions. Pretending a machine can fully certify them would produce the exact defect the playbook exists to catch: a check claiming an intent it cannot guarantee.
So we use two enforcement planes. Runtime gates constrain the harness while agents use it. Build-time meta-gates police the harness's own source before those controls ship. The meta-gate enforces the declarations it can verify, then a human reviews the implication it cannot.
I am comfortable with that seam. The system is stronger because it says where automation ends.
What I am—and am not—claiming
The primitives are not mine. Deterministic permissions, phase gates, traceability, and canonical definitions compiled into multiple agent formats are already being developed in public. The recent paper A Deterministic Control Plane for LLM Coding Agents describes hard-coded gate predicates, requirement-to-file-to-test traces, tiered permissions, and one canonical definition compiled for several coding environments. That is good company and convergent evolution, not competition.
I also use plan-time collision arbitration—deciding which agents may touch which files before dispatch—and I did not invent it. The same goes for "submit, never merge." A study of 29,585 pull-request lifecycles found collaborator-style agent workflows were at least 96 percent agent-initiated while terminal merge authority remained overwhelmingly human (Chung and Hassan, 2026). We conform to that emerging norm.
The two contributions I am willing to claim are narrower.
First is the design discipline itself: treating "check passed, intent missed" as a first-class gate defect, then requiring authors to state the implication and attack it before shipping.
Second is the self-policing enforcement plane: a harness that applies that discipline to its own source at build time while admitting which judgment cannot be automated.
I have not found those two pieces published as a repeatable gate-authoring method. If prior work turns up, I will cite it gladly. Giving away the established parts makes the remaining claim smaller, but it also makes it real.
Gates are a design surface
Return to the blocked rm -rf command. A longer list of forbidden tools would have delayed the bypass. It would not have repaired the design.
The useful questions were available before the gate shipped:
What property are we trying to guarantee?
How could this check pass while that property fails?
Where does the agent go when the check blocks it?
Those questions move the work from policy wording to system design. They force us to verify an artifact instead of trusting a narrator: inspect the files, the repository state, the trace, or the permission boundary rather than an agent's declaration that it is done.
That is the craft I mean by Agent Harness Engineering. Gates are a design surface. Treat them like one.
Jason Vertrees is the founder of Heavy Chain Engineering, which helps lower middle-market vertical SaaS companies and PE firms turn scattered AI usage into measurable delivery leverage — 85% faster feature velocity, six-to-eight-week projects shipped in days. If you want help building an AI-native engineering organization, book an AI Delivery Assessment.


