Skip to main content

Harness: Controlling an Agent’s Execution

When a model says “run npm test”, no test runs. What actually runs it is the software wrapped around the model: the harness, or control layer. Suppose a harness handles that request like this. It first checks whether npm test is on its list of allowed commands, then runs it in a subprocess with a five-minute limit. When the run ends, it hands the exit code and the last 200 lines of output back to the model and writes the same record to its log. If the process crashes midway, the next attempt resumes from that log entry.

In that one step the model only proposed the next move; the harness made the move executable, kept it within bounds, left a record to inspect afterwards, and made it recoverable after failure. A harness is therefore more than a chat interface: it connects the model, repository, terminal, permissions, and verification workflow.

A Harness Owns at Least Eight Things​

The table below places all eight responsibilities on the same npm test request.

LayerResponsibilityWhat it does for npm test
LoopAlternate model turns, tool calls, results, and stopping conditionsHand the test result back so the model can choose the next step; stop when tests pass or the step budget runs out
ContextSelect instructions, history, files, and retrieved evidencePut only the last 200 lines of output into context, not the whole log
ToolsValidate arguments, execute calls, and return structured resultsCheck that the command and working directory are valid, return the exit code and output; a subprocess inherits its parent’s environment variables and open resources (see Processes, Address Spaces, and Memory), so decide first what it may carry
PolicyEnforce scope, permissions, approvals, budgets, and safety boundariesCheck the allowlist: npm test may run directly, while a command that deletes a directory or pushes code must stop and ask a person
StatePreserve task progress, artifacts, and resumable checkpointsRecord that this step ran the tests and what the result was, so work can resume here after a crash
ReliabilityHandle timeouts, retries, idempotency, cancellation, and failuresEnd the process when five minutes are up; rerunning tests changes nothing, so a retry is safe
ObservabilityRetain traces, logs, token/cost data, and tool evidenceLog the command, duration, and exit code, so anyone can later see what ran and when
EvaluationTest outcomes on representative tasks instead of judging prose aloneCheck whether the tests actually passed, not whether the model sounds finished

“All tests pass” from the model does not count: a dependable harness reads the exit code of npm test itself and then decides whether to stop. How to wire that kind of external acceptance into the loop is covered in Bounded Agent Loops.

Design a Harness in This Order​

  1. Define the outcome and an external success check, such as “npm test exits with 0”.
  2. Expose the smallest useful tool set. Small local models depend on this step even more: iterations should be short, the tool set narrow, and output length capped.
  3. Separate read-only actions from writes and irreversible actions.
  4. Preserve artifacts and evidence outside the conversational transcript.
  5. Add time, token, retry, and concurrency limits; stop conditions and the circuit breaker are detailed in Bounded Agent Loops.
  6. Trace model turns and tool calls with sensitive-data controls.
  7. Replay representative tasks before changing the default model, harness, or reasoning level.

The checks that decide whether a result can ship, such as tests, permissions, and end-to-end acceptance, belong to the harness and workflow; the model is replaceable. Fast-changing model and pricing choices belong in the Model & API Radar, and public test results and tool-specific failure modes live in Choosing an AI Coding Agent. A stronger model may reduce retries, but it removes none of the need for evidence, permissions, and end-to-end checks.

Where These Controls Live in Claude Code, Codex, and Pi​

A coding agent you install already implements most of the responsibilities above; what you adjust are its switches. The table below follows the official documentation as of 2026-10-29, and Pi 1.4.0.

ControlClaude CodeCodexPi
Project instructionsCLAUDE.md; it can also read the repository’s AGENTS.mdAGENTS.md: ~/.codex/AGENTS.md first, then each level from the repository root downAGENTS.md and CLAUDE.md always load, regardless of project trust (security doc)
Permissions and approval/permissions, or permissions.allow / ask / deny in settings.json, evaluated deny → ask → allowapproval_policy (such as on-request or never) and /permissions; approvals_reviewer = "auto_review" hands approvals to a reviewer agentNo prompt before tool calls; --approve / --no-approve only decide whether project settings and extensions load
SandboxOff by default; turn it on with /sandbox or sandbox.enabled; it wraps shell commands onlysandbox_mode: read-only, workspace-write, danger-full-access; workspace-write has no network by defaultNo built-in sandbox; run the whole of Pi in a container or virtual machine
Skills folder.claude/skills/<name>/SKILL.md; personal skills in ~/.claude/skills/.agents/skills, scanned from the current directory up to the repository root; personal skills in $HOME/.agents/skills.agents/skills/, ~/.agents/skills/, and .pi/skills
MCP configclaude mcp add; to share with a team, put it in .mcp.json at the project rootcodex mcp add, or an [mcp_servers.<name>] table in ~/.codex/config.tomlpi mcp add; ~/.pi/agent/mcp.json, and .pi/mcp.json for the project
Hookshooks in settings.json; for example, PreToolUse can block a tool call~/.codex/hooks.json or [hooks] in config.toml; the project’s .codex/ must be trusted firstNo separate hook config; an extension uses pi.on("tool_call", …) to block or rewrite calls
Unattended runsclaude -p "…"codex exec "…", which runs in a read-only sandbox by defaultpi -p "…", or --mode json and --mode rpc
Resume a sessionclaude -c continues the most recent one; claude -r <ID or name> resumes a specific sessioncodex resume --last; for non-interactive runs, codex exec resume --lastpi -c continues the most recent one; pi -r opens the session list

The same word “sandbox” covers different ground in each of the three; Claude Code’s sandbox, for example, can still read ~/.ssh once it is on. Before changing anything, read the comparison and starter configuration in Agent Security.

Explore connectionsOpen network