Skip to content
Agentic Control Plane

Harness control patterns

Harness engineering became a named discipline this year — Martin Fowler’s site, Databricks, Augment Code, and a run of arXiv papers all converged on the same decomposition: an agent is a model plus everything around it — 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 and maintain the survey of what each harness ships natively; 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 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 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?”). 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 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, 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 — 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, 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). 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 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 (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. 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: 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, 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 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 is the pre-flight check for any unattended job, including every agent a fleet orchestrator spawns.

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, 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, or a 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 · what our layer intercepts per harness · the same survey for MCP servers · what is an agent harness.