The layer everyone skips
The AI-engineering conversation has moved from prompts to loops — agents that run on a schedule, fix their own errors, and never stop. But there is a layer underneath the loop that decides whether any of it is safe to run in production: the harness.
A harness is the environment a single agent runs in — its model, its tools, its permissions, and its context. If a loop is the schedule that keeps an agent working, the harness is the sandbox that determines what that agent can touch on each turn. Loop an unsafe harness and you have automated a liability. So before you schedule anything, engineer the harness the way you'd engineer any production system: least privilege, guardrails as code, and a bounded blast radius.
This is a DevOps/SRE guide to doing exactly that. The examples use the Claude Code harness (the .claude/ directory) because it is concrete and widely used, but the principles map to any agent runtime.
What's actually in a harness
A production harness has a handful of components, and each maps cleanly to an infrastructure concept you already know:
| Harness component | Infra equivalent |
|---|---|
Standing context (CLAUDE.md) | Runbook / system-of-record |
Permissions (settings.json) | RBAC / IAM policy |
Tools (.mcp.json, MCP servers) | Service accounts + API scopes |
| Subagents | Isolated workloads / namespaces |
| Hooks | Admission control / policy-as-code |
| Memory | State / datastore |
Treating the harness as config-as-code — versioned, reviewed, diffable — is the first move. A prompt lives and dies in a chat window; a harness is committed to the repo and changes go through review, like any other infrastructure.
Least privilege: the agent's IAM
The single highest-leverage control is the permission layer. In Claude Code that is settings.json; conceptually it is the agent's IAM policy. Default-deny anything destructive, and pre-approve only the narrow, safe operations the agent actually needs:
{
"permissions": {
"allow": ["Bash(kubectl get:*)", "Bash(kubectl describe:*)", "Read"],
"deny": ["Bash(kubectl delete:*)", "Bash(rm:*)", "Bash(terraform apply:*)"]
}
}
The same instinct that makes you scope a Kubernetes RBAC role to the verbs a service actually uses applies here: an agent that only reports should never hold write verbs. And when the agent needs real tools, give it a scoped interface, not a shell — a read-mostly MCP server that exposes typed, allow-listed operations instead of raw kubectl. The tool boundary is where you decide what "Act" can possibly mean.
Guardrails as code: hooks are admission control
Permissions decide what an agent may call. Hooks decide, deterministically, what actually happens around each call — and they are the closest thing an agent runtime has to admission control. A hook is a script that runs before or after a tool use; a non-zero exit (exit code 2 in Claude Code) blocks the action.
# pre-tool hook: block writes to protected paths, deterministically
case "$TOOL_INPUT" in
*"/etc/"*|*"prod-values.yaml"*)
echo "blocked: protected path" >&2
exit 2 ;; # exit 2 = deny the tool call
esac
This is the same idea as a Kyverno policy rejecting a non-compliant manifest at the admission webhook: a deterministic gate the model cannot talk its way past. Guardrails you can reason about belong in hooks, not in the prompt — a prompt is a suggestion, a hook is enforcement.
Blast radius: isolate with subagents
The third control is containment. When one agent does everything in one context, a single confused turn can touch everything it has access to. Subagents split the work into isolated contexts, each with its own tools and permissions — the agent equivalent of putting workloads in separate namespaces with their own quotas.
Two patterns pay off immediately:
- The fresh-context reviewer. The agent that writes a change should not be the one that approves it — give the review to a subagent with a clean context and read-only tools. This is the maker/checker split that makes a multi-agent panel trustworthy, applied at the harness level.
- Scoped workers. A subagent that summarizes logs needs log-read access and nothing else. Don't hand every subagent the full toolset; scope each to its job so a failure in one can't cascade.
Context is a resource — budget it
Context is not free, and a bloated one degrades quality (the "context rot" problem where the model loses the thread in a wall of text). Treat the standing context like a capacity concern: keep CLAUDE.md tight — a few hundred tokens of durable, high-signal facts, not a dumping ground. Push situational detail into skills and memory that load on demand. A lean, well-curated harness context is the difference between an agent that stays on task and one that wanders.
Observability: trace the harness like a service
You would never run a production service blind, and an autonomous agent is a production service. Log every tool call — the command, the arguments, the result, the cost. When the loop does something at 03:00, you want to answer "what did it do and why" as easily as you would for a human on-call. The action stream is real telemetry; pipe it somewhere you can query, the same way you'd instrument any system with OpenTelemetry. Unobservable automation is just an incident you haven't noticed yet.
The order that matters: harness → loop → self-improving
These controls are also a sequence, and the order is the whole point:
- Harness first. Get least-privilege, hooks, and isolation right while a human is still in the loop for every turn.
- Then test it. Run evals against the agent before it touches prod — does it stay inside its permissions under adversarial inputs?
- Then loop it. Only once the harness is provably safe do you put it on a schedule.
- Then add memory. A persistent memory layer turns a loop into a self-improving system — output becomes a lesson becomes a skill. Powerful, but only worth doing on top of a harness you already trust; a system that compounds on an unsafe base compounds the danger too.
Skipping straight to step 3 — looping an unscoped agent because the demo looked magic — is how you get the 2 a.m. horror story. The harness is the boring work that makes the impressive part safe.
Takeaway
Prompts and loops get the attention, but the harness is where production reliability actually lives. Engineer it like infrastructure: default-deny permissions, guardrails as deterministic hooks, blast-radius isolation via subagents, a budgeted context, and full observability of every action. Get that foundation right and looping the agent becomes a scaling decision instead of a gamble. Get it wrong and no prompt, however clever, will save you.