Skip to main content

03.14Practice

Harness engineering for AI coding agents

Harness engineering at AR Labs: each recurring AI coding agent mistake becomes a hook or check, so rules that matter are enforced, not remembered.

A bench vice clamps a block marked MODEL; bolts on its jaws are labelled hook, guard and check.A bench vice clamps a violet block marked model between yellow jaws; bolts on the vice are labelled hook, guard and check, and a turned handle tightens the screw.PRACTICE03.13TURN EACHMISTAKE INTOA CHECKDRAWINGENG-03.13SUBJECTharness engineeringSHEET13 OF 26 · AR LABSMODELHOOKGUARDCHECKENFORCED BY THE ENVIRONMENT, NOT MEMORY
On this page
  1. The problem it addresses
  2. Where the term comes from
  3. The layers
  4. Three guards, each from a recorded problem
  5. How we write a guard
  6. Proportional checks
  7. Adopting it

Harness engineering is how AR Labs keeps AI coding agents (AI agents that write code, such as Claude Code or Codex) from repeating their mistakes. An agent's harness is everything around the model that shapes what an agent does: the agent program, its instructions, skills, hooks, tools and checks. Harness engineering is the practice of changing that environment whenever an agent makes a mistake, so the mistake cannot recur. At AR Labs a rule that matters starts as written guidance and, once it has been broken, moves into a script that enforces it. The anatomy of a coding agent harness takes the harness apart piece by piece; this page is about changing it.

The problem it addresses

An instruction in an agent's context is not the same as an instruction the agent follows. Under pressure to finish, a trained habit can beat a written rule, and in unattended runs nobody is watching to notice. We found this directly: the coding pipeline's stage gates (spec before implementation, cross-model review before close) once lived only in prose, and unattended runs skipped them without any error.

Where the term comes from

Mitchell Hashimoto named the practice in his February 2026 post "My AI Adoption Journey", describing it as the idea that "anytime you find an agent makes a mistake, you take the time to engineer a solution such that the agent never makes that mistake again", through better standing instructions and through tools that make the wrong path unavailable. Vivek Trivedy of LangChain gave it a formula in "The Anatomy of an Agent Harness": "Agent = Model + Harness." Simon Willison, Anthropic's engineering blog and Birgitta Böckeler on Martin Fowler's site have written about the same discipline.

The layers

FIG. 01
flowchart TD
  I["Rules loaded by file type"] --> C["Command files, run by reference"]
  C --> H["Pre-action hooks"]
  H --> F["Floors on every step"]
  F --> R["Cross-model review"]
  R --> P["A person merges"]
  1. Instructions. Short rule files that load when an agent touches matching files, so a coding rule loads for source files and not for prose. They explain why a rule exists.
  2. Command files, executed by reference. Each pipeline step has a written procedure, and agents are required to open and follow it, never to work from a remembered version. When agents reconstructed a procedure from memory, steps fell out silently; in recorded cases the walkthrough, the proof manifest and a status transition went missing.
  3. Hooks. Scripts run automatically around an agent's action. The ones here run before a tool does, and can block the action and say why.
  4. Floors. Scripted proofs that a step's work exists before the step may close, for example that a reviewed commit has really landed on its target branch (see the pipeline).
  5. Independent review by a different model in a fresh context.
  6. A person, at the plan and at the merge.

The diagram above orders the layers as a change meets them, from guidance an agent can skip to a person's merge. The figure, shared with the anatomy page, shows the parts of the harness those layers are built from: rules and commands entering the context window, hooks around every tool call, and floor scripts that check the world without a model.

Instructions and rules pushed into the context window, skills and commands pulled in; tool calls pass through hooks to tools and MCP servers; subagents, memory and records, and floor scripts. The harness is the runtime around the model, such as Claude Code or Codex. Instruction files and rules are pushed into the context window in every session. Skills and command files are pulled in only when the task calls for them. When the model decides to act, the tool call passes through hooks, scripts that run before and after each action and can deny it with a reason; allowed calls reach tools and MCP servers, which act on the repository, code host, issue tracker and web. The model can delegate to subagents, which run in their own context with their own model and tool list and return a summary. Memory and durable records, such as the issue tracker and notes, outlive the session and are read when needed. Deterministic scripts, the floors and gate checks, inspect the world directly to confirm that a step's work exists, with no model involved. Anatomy of a coding-agent harness EVERYTHING AROUND THE MODEL · PUSHED CONTEXT, PULLED CONTEXT, ENFORCEMENT, HELPERS, HANDS AND RECORDS FIGURE CODING-AGENT HARNESS DRAWN BY AR LABS SHEET 1 / 1 the runtime, such as Claude Code or Codex HARNESS CONTEXT WINDOW Model [e.g. Claude, GPT] Reads the window, decides the next action PUSHED every session Instructions [AGENTS.md / CLAUDE.md] A short map: what success is, where to read next Rules [standing constraints] Always, or when matching files are touched PULLED when relevant Skills [folder with SKILL.md] Specialised know-how, loaded when the task calls for it Commands [step procedures] Opened and followed for a pipeline step, never recalled Hooks [scripts at runtime events] Run before and after every action; can deny it and say why Tools and MCP servers [described by schemas] Act on the world: files, search, tracker Subagents [own context, model, tools] Isolate research, review or extraction Memory and records [issue tracker, notes, history] State that outlives the session Floors and gate scripts [deterministic] Prove a step's work exists; no model involved THE WORLD repository, code host, issue tracker, web KEY Always present, or an action Enters the window on demand tool call if allowed delegates returns a summary writes read when needed acts on inspects
The parts of a harness that the layers on this page are built from.

Three guards, each from a recorded problem

The pipeline guard. Because prose gates were skipped, a hook now watches every command that changes a bead (one tracked task in the Beads issue tracker). It refuses force-closing a parent with open children, cascading deletes, the retired custom status names, creating children outside the fan-out step, and moving a task into implementation while a task that blocks it is unmerged or while files in its spec's declared scope have changed since the spec was written. Humans have an override for repairing state; agents meeting a gate do not use it.

The enforcement-surface guard. An unattended session once met a bug in a gate script and fixed it in place. The fix was correct, but nothing recorded it, and a process whose checks can be edited by the process being checked cannot rely on those checks. We now treat the gate scripts, the pipeline manifest, the step procedures, the hooks themselves and their registration as an enforcement surface. What happens on an edit depends on who is watching. In an unattended session, an edit there is denied. In an attended session it is allowed but loudly audited, with a log line and a warning in the transcript the person is reading, because a blanket denial started blocking work a person had asked for. The executor also fingerprints the surface before and after each unattended session, so tampering shows up even if a hook is bypassed.

The worktree guard. Edits to a project's primary checkout are denied, with the correct worktree path in the denial. See worktree isolation.

How we write a guard

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Edit|Write|MultiEdit",
        "hooks": [{ "type": "command", "command": "bun .claude/hooks/guard-edits.ts" }] }
    ]
  }
}

A few design rules recur across our hooks:

  • State the problem at the top of the file. Each hook opens with the incident that produced it, what it blocks and what it deliberately does not catch.
  • Choose failure behaviour on purpose. The pipeline guard fails open: a parse error allows the command, because a guard must never break normal work, and only a confirmed missing artefact denies. The worktree guard fails closed once it has identified a protected checkout, because an edit there is the exact harm it exists to stop.
  • Match invocations, not mentions. A command is inspected only when the program it runs is the one being guarded. A commit message or a note that merely names a command is never matched.
  • Say what a heuristic misses. Screening shell commands for writes is a heuristic, and the hook's own header says what it cannot catch and which other layer covers that gap.
  • Keep one copy of the truth. The list of guarded paths lives in one place that the hook, the driver and the executor all read, so they cannot disagree.

Proportional checks

A harness can make verification so heavy that it stalls the work it protects. Our research found that large engineering organisations, including Google, Meta and Anthropic, split verification into a fast gate that runs only the tests a change can affect and a slower full run afterwards, and that the only well-sourced budget for the gate is about ten minutes. Our regression gate applies that ceiling (see test-driven development).

Adopting it

Keep a log of every agent mistake you correct by hand. When a correction recurs, ask whether a script could have refused the action. If it could, write the hook, put the incident in its header, and leave the written rule in place to explain why the hook exists. The prose explains; the hook guarantees.