# Harness Control Patterns — the Enforcement Layer of Harness Engineering

Eight patterns for controlling what a coding agent may do — prompt rules, permission prompts, pattern rules, approval classifiers, lifecycle hooks, sandboxes, proxies, and independent ledgers — with where each holds, where each fails, and how they compose.

# Harness control patterns

Harness engineering became a named discipline this year — [Martin Fowler's site](https://martinfowler.com/articles/harness-engineering.html), [Databricks](https://www.databricks.com/blog/ai-harness), [Augment Code](https://www.augmentcode.com/guides/harness-engineering-ai-coding-agents), and a run of [arXiv](https://arxiv.org/abs/2607.08028) papers all converged on the same decomposition: an agent is a model plus [everything around it](/blog/what-is-an-agent-harness) — loop, context, tools, verification, memory, and *controls*.

That last component usually gets one line ("guardrails and permissions") before the article moves on to context management. This page is the missing chapter. We run a control layer [across every major harness](/coverage) and maintain [the survey of what each harness ships natively](/controls); the same eight patterns keep appearing, in every harness, under different names. Here they are as a pattern language: what each one is, where it holds, where it fails, and how they compose.

One distinction organizes everything below. [Augment Code puts it](https://www.augmentcode.com/guides/harness-engineering-ai-coding-agents) as prompt-stated standards versus a linter that blocks the PR: the first is **probabilistic compliance**, the second is **deterministic enforcement**. Every pattern here is one or the other, and most control failures in the wild are a probabilistic pattern doing a job that needed a deterministic one.

## The eight patterns

### 1. Prompt rules

*"Never push to main. Ask before deleting files."* — in `AGENTS.md`, `CLAUDE.md`, `.cursor/rules`, or the system prompt.

**Mechanism:** the model reads the rule and (usually) follows it.
**Holds:** for style, convention, and workflow steering — the things you'd tell a new teammate. Cheap, portable, zero infrastructure.
**Fails:** whenever following the rule matters. Compliance is probabilistic by construction: a [2026 enterprise-agent benchmark](https://arxiv.org/abs/2607.08028) gave the same rule set as prompt text and as a code-owned gate — prompted, violations still reached the reader; gated, none did. Prompt rules also share the model's context with whatever an attacker managed to inject into it. Cursor's docs are honest about their own `.cursor/rules`: steering, not enforcement.

**Use for guidance, never for limits.** Every pattern below exists because this one isn't enough.

### 2. The permission prompt

The native ask: *"Agent wants to run `rm -rf ./build` — allow?"*

**Mechanism:** the harness pauses the tool call and asks a human.
**Holds:** as the backstop of last resort while someone is watching, and as the seam where several harnesses now let an external policy inject its own questions ([coverage column: "can policy ask a human?"](/coverage)).
**Fails:** on volume and on absence. The fatigue numbers are stark — Anthropic's published data has humans catching **13.6%** of dangerous commands at the prompt, versus 89% for their screening classifier. And when nobody is present, harnesses differ on what an unanswered ask *means* — deny, hang, or approve — which is the [empty-chair test](/blog/interactive-vs-autonomous-the-empty-chair-test) below.

### 3. Pattern rules (allow/deny lists)

`"rm *": "deny"` in a settings file — Claude Code's `permissions.allow/ask/deny`, Codex's TOML profiles, Gemini CLI's policy engine, opencode's last-match-wins rules.

**Mechanism:** the harness matches each tool call against declarative rules before dispatch.
**Holds:** for the crisp cases — deny always beating allow (Claude Code, Muse Code), explicit allows silencing prompts, org-managed scopes overriding local ones. Deterministic, auditable-in-principle, and the most mature rule systems (Claude Code's layered scopes, Gemini's five priority tiers) are genuinely expressive.
**Fails:** three ways, all structural. **Respelling** — a deny for `rm -rf ~/` misses `rm -r -f ~`, `bash -c "rm -rf ~"`, and the catastrophe after `&&`; this is the [documented bypass class](/blog/claude-code-deny-list-bypass), reproduced across multiple harnesses, and Cursor labels its own allowlist "best-effort, not a security boundary" after three CVEs. **Dialect drift** — a deny written in `settings.json` doesn't exist in `config.toml` or `permission.yaml`, so every added harness re-derives policy by hand. **Per-machine scope** — the rules live with each install; two engineers on one repo run whatever their machines accumulated.

### 4. The approval classifier

A model screens the tool call before (or instead of) the human: Claude Code's auto mode, Codex's Guardian reviewer, Cursor's Auto-review, Hermes's `smart` approvals, fx's model review.

**Mechanism:** unresolved-by-rules calls go to an LLM judge that allows, denies, or escalates.
**Holds:** against fatigue — 89% vs 13.6% is a real result, and every major harness shipped a variant of this in one season. Best deployments are *screens in front of* the human ask, not replacements for it.
**Fails:** two ways. First, **no ledger**: as of the August survey, [no harness keeps a queryable record of what its classifier decided or why](/controls) — the decisions exist only in session transcripts. Second, **self-judging**: the degenerate form trusts the governed model's own risk flag (Cline's safe-command tier reads the model's `requires_approval` field — the classifier *is* the thing being screened). A classifier is a probabilistic pattern wearing enforcement clothes; it needs a deterministic floor under it and a record behind it.

### 5. Lifecycle hooks — the interception seam

`PreToolUse` fires before every tool call with the full input; your process answers allow / ask / deny.

**Mechanism:** the harness calls out of itself, deterministically, on every dispatch. This is the strongest in-harness pattern and the one converging fastest: Codex's 2026 hooks are deliberately Claude Code-compatible, Cursor and Hermes honor the same decision shapes, Grok Build loads `.claude/settings.json` hooks directly, dsh ships a bridge. **The PreToolUse contract is becoming the ecosystem's POSIX.**
**Holds:** outside the model (prompt injection can't argue with it), before execution, with the full arguments. In several harnesses hooks still fire under the yolo flag — Claude Code's deny rules and hooks survive `--dangerously-skip-permissions`; Grok Build's fire even in always-approve mode. A [50-line hook is a real control](/blog/claude-code-hooks-vs-acp), today, for free.
**Fails:** at the edges you only find in production. **Fail-open timeouts** — Claude Code and Grok Build proceed if the hook doesn't answer in time (Grok's core gives it 5 seconds), which means a crashed control layer silently stops controlling; fx's ACP-server seam and dsh fail closed instead. **Dialect drift again** — compatible is not identical; Grok Build reads Claude's hook files but parses only its own decision vocabulary, so a config can *load* and silently not *enforce*. **Coverage gaps** — a hook that doesn't fire for MCP tools, subagents, or Code Mode sub-calls governs a subset and reports it as the whole ([per-harness coverage](/coverage)). And in Muse Code, the hook process itself runs *outside* the sandbox — your enforcement layer has more privilege than the agent it constrains.

### 6. The sandbox

OS-level containment: Seatbelt, bubblewrap/seccomp, Landlock, nsjail — workspace-scoped writes, network egress off by default.

**Mechanism:** the kernel, not the harness, bounds what any process in the session can touch.
**Holds:** as blast-radius control — the only pattern that constrains what happens when every other pattern has already failed. Muse Code ships it on by default and refuses shell if the sandbox can't be verified; Codex sandboxes on all three OSes.
**Fails:** as a *decision* mechanism — a sandbox can't distinguish a good `git push` from a bad one; it can only make the write set small. Defaults are the real story: most harnesses ship it off. And containment has documented holes — hooks and MCP servers outside the Muse sandbox, `--disable-sandbox` also forcing full network egress. A sandbox bounds damage; something else must still decide. [Sandboxes and control planes](/blog/sandboxes-and-control-planes) is our longer treatment.

### 7. The proxy

Move enforcement out of the harness entirely: the harness's HTTP client or tool dispatch points at a chokepoint that every call must transit.

**Mechanism:** enforcement at the network or gateway layer, below whatever the harness does or doesn't ship.
**Holds:** where in-process patterns can't — it's outside the escape hatch (no harness flag disables a proxy it must route through), it's the same policy in one dialect for every harness behind it, and it sits where the call already serializes, which is why [the proxy pattern scored highest of the three integration patterns we benchmarked](/blog/decorator-proxy-hook-three-governance-patterns) (45/48, vs 43 for hooks, ~40 for decorators). It's also the only pattern that naturally covers [the MCP server side of the wire](/mcp-controls).
**Fails:** on what it can't see — a proxy watches serialized calls, so framework-internal handoffs and local side effects that never cross the wire need the hook pattern to catch. Latency and availability become policy properties: a slow or down chokepoint must itself have a defined failure mode, and "fail open or fail closed" is now *your* design decision rather than the harness's.

### 8. The independent ledger

A record of every action that the agent's own process didn't write.

**Mechanism:** append-only, ideally off the machine that ran the agent, ideally with the decision (*allowed by which rule, denied why, approved by whom*) attached to each action.
**Holds:** as the difference between a transcript and evidence. The question that exposes the gap is the one [teams ask themselves about production agents](/blog/audit-what-your-coding-agent-runs): *could you prove exactly what every agent did and why it was allowed?* A session log written by the governed process, on the governed machine, editable by the flag that disabled the controls, cannot answer it.
**Fails:** almost universally, by omission. In the [August survey](/controls), exactly two harnesses keep a local append-only event log (dsh, Muse Code) — and both live on the machine that ran the agent. No surveyed harness ships an off-machine decision ledger, and none records what its approval classifier decided. This is the least-shipped pattern and the one audits ask for first.

## The two tests

Two questions sort any stack of these patterns faster than a feature comparison:

**The escape hatch.** Every harness has a flag — `--yolo`, `--dangerously-skip-permissions`, `--force`, a mode named after honesty. Ask of each control you rely on: *does it survive the flag?* Prompt rules: no. Pattern rules: sometimes (Claude Code's denys do; most don't). Hooks: harness-dependent. Sandbox: usually off with the same flag. Proxy and off-machine ledger: yes — they're the only patterns that don't live inside the thing the flag disables. [What survives yolo mode](/blog/what-survives-yolo-mode) walks all eleven harnesses.

**The empty chair.** Run the agent with nobody watching and let it hit an approval. Deny-and-continue (dsh, Codex `exec`, Claude Code `dontAsk`, Gemini's non-interactive resolve) is the right answer, and it's newly common — but several harnesses still hang, and a few auto-approve. [The empty-chair test](/blog/interactive-vs-autonomous-the-empty-chair-test) is the pre-flight check for any unattended job, including every agent [a fleet orchestrator spawns](/blog/who-controls-the-fleet).

## Composition: the enforcement ladder

The patterns aren't alternatives; production stacks layer them, and the layering has a shape:

| | Pattern | Kind | Survives escape hatch? | Empty chair |
|---|---|---|---|---|
| 1 | Prompt rules | probabilistic | no | n/a |
| 2 | Permission prompt | human | no | **the problem** |
| 3 | Pattern rules | deterministic | sometimes | rules answer |
| 4 | Approval classifier | probabilistic | no | judge answers, unrecorded |
| 5 | Lifecycle hooks | deterministic | harness-dependent | hook answers (check fail-open) |
| 6 | Sandbox | deterministic | usually not | bounds damage, decides nothing |
| 7 | Proxy | deterministic | **yes** | policy answers, defined |
| 8 | Independent ledger | evidence | **yes** (if off-machine) | records whatever happened |

Read bottom-up and it's a floor: the deterministic, out-of-process patterns (7, 8) hold regardless of harness state; the in-harness deterministic patterns (3, 5, 6) do the low-latency work; the probabilistic patterns (1, 4) make the whole thing *usable* — steering the model and screening the noise so the human ask (2) is rare enough to mean something.

The failure mode to design out is **inversion**: a probabilistic pattern as the only thing between the agent and a consequence. A prompt rule guarding production credentials, a self-classifying model deciding its own risk, a classifier with no floor and no ledger — each is pattern 1 or 4 doing pattern 5-or-below's job.

## What stays above the harness

Grade any single harness generously and [four gaps remain](/controls#where-every-native-model-ends), because they're structural: the record is self-authored, policy is per-machine, one flag undoes it, and rules don't travel across harness dialects. Those four are the job of a layer above the harness — which is the layer we build. But the pattern language stands on its own: whether you assemble patterns 1–8 from a harness's native surfaces, [a 50-line hook you maintain yourself](/blog/claude-code-hooks-vs-acp), or [a control plane](/what-is-an-agentic-control-plane), the same eight mechanisms with the same failure modes are what "the control layer of the harness" means.

*Companion pages: [what each harness ships natively](/controls) · [what our layer intercepts per harness](/coverage) · [the same survey for MCP servers](/mcp-controls) · [what is an agent harness](/blog/what-is-an-agent-harness).*
