An agent is a model, a loop, and a harness. The model gets all the attention and is the only part you cannot test. The loop is the program that calls it repeatedly. The harness is everything that runs between two calls, in deterministic code: the permission decision, the sandboxed execution, the truncation of the result, the verification, the hooks, the persistence.
This is the companion to my field guide on data readiness, RAG and context engineering. That guide answered one question: what enters the model. This one answers the next: how many times, under what constraints, and who decides it is finished. It condenses a 47-slide working deck and its technical reference, and it is written to be used during construction rather than read once.
Three claims organise everything below, and each one is a consequence of the model being the only non-deterministic component in the system.
Three laws
Law 1. The context decides what enters; the loop decides how many times. The model is a stateless function: a sequence of tokens in, a sequence out, nothing persists. Every "conversation", every "memory", every "agent" is reconstructed by the application on each call. The consequence for loop design: since nothing naturally connects two calls, everything that connects them is code we write, and that code has exactly the properties we give it. What changes relative to context engineering is that the context is no longer assembled once for a question; it is reassembled on every turn by the loop, from state that lives elsewhere. Prefix caching, compaction and sub-agents stop being techniques somebody applies by hand and become policies the loop applies automatically.
Law 2. Reliability multiplies. A task of n steps, each succeeding with probability p and with no verification in between, succeeds with probability p to the power n. At p = 0.95 and n = 20, global success is 36 %. At p = 0.90, it is 12 %. Per-step reliability is a base; the number of steps is an exponent. No reasonable improvement in p, a better model or a better prompt, compensates for growth in n.
| Steps without verification | p = 0.99 | p = 0.95 | p = 0.90 | p = 0.80 |
|---|---|---|---|---|
| 5 | 95 % | 77 % | 59 % | 33 % |
| 10 | 90 % | 60 % | 35 % | 11 % |
| 20 | 82 % | 36 % | 12 % | 1 % |
| 50 | 61 % | 8 % | 1 % | 0 % |

τ-bench (Yao et al., Sierra, June 2024) introduced the metric that follows from this: pass^k, the probability that a system succeeds on k independent attempts at the same task. It is not pass@k, at least one success out of k, which is the metric of demonstrations. On τ-bench's retail domain, GPT-4o scores roughly 61 % pass^1 and under 25 % pass^8. A system in production is judged in pass^k by its users whether or not anyone measures it.
METR (March 2025) gives the complementary marker: the time horizon of tasks models complete at 50 % reliability roughly doubles every seven months, but the horizon at 80 % reliability is about five times shorter. The gap between "the agent can do it" and "we can rely on it" is a factor of five in task duration.
Three responses, all of them the harness's job, and they structure the rest of this article: reduce n by moving every deterministic step into code, insert verifiers after each step rather than at the end of the task, and make steps idempotent and replayable so that retrying costs less than getting it right first time.
Law 3. The model proposes, the harness disposes. What the model decides, which tool, which arguments, when to stop, what to remember, how to decompose, is probabilistic and carries no guarantee. What the harness decides, whether the call is permitted, how it executes, what comes back into the context, when to force a stop, is code: testable and provable.
The difference is not one of degree. A rule in the prompt is tested by sampling and yields a compliance rate. A rule in the harness is tested like code and yields a property. The review test to apply to every line of a system prompt: if the model ignores this rule, what happens? If the answer is unacceptable, the rule changes sides and becomes a hook, a permission, or a verifier.
| Property you want | The "prompt" version (a wish) | The "harness" version (a guarantee) |
|---|---|---|
| No writes outside the repository | "Only modify project files" | A filesystem sandbox: the rest does not exist |
| Tests run before concluding | "Always run the tests before finishing" | A Stop hook: the stop is refused without a recent test run |
| No external send without approval | "Ask for confirmation before sending" | Action class 4, mandatory permission, journalled |
| Do not modify the tests | "Never edit the tests/ directory" | tests/ read-only; a PreToolUse hook refuses any diff on it |
The five loop failures
My previous guide had five context failures: lost in the middle, distraction, tool confusion, conflict, poisoning. The loop has its own five. The grid exists for diagnosis: faced with a failure, you identify which one before choosing a remedy.
| Failure | Mechanism | Example | Remedy |
|---|---|---|---|
| Loop without progress | Same tool, same arguments, same error; or oscillation between two states. The model does not perceive the repetition. | The same test rerun fourteen times with no code change | Hash-based call detection, turn bounds, error compaction |
| Objective drift | Every turn takes an implicit decision; after compaction, the original objective is no longer in the context. | A bug fix was requested; an architecture refactor was delivered, bug intact | The plan as an artefact reread every turn; objective restated after compaction |
| Unverified declared success | The model asserts completion; nothing verified it; the harness accepts the text as proof. | "The tests pass": never run, or run against the old code | Stop criterion is proof produced by the harness |
| Objective circumvention | The metric is satisfied, the intent is not. The shortest path to green runs through the verifier. | Tests marked skip, expected value hard-coded, assert deleted | The verifier out of reach: read-only or hidden tests, a grader with no write access |
| Uncontrolled irreversible action | A side effect executes before validation, or twice after a resume. | Deletion, send, payment, migration; replayed after an incident | Action classes and permissions, idempotency, sandbox, checkpoint before the irreversible |

Failures three and four share one remedy, and it is the highest-yield rule in this article: the verifier is out of reach of the agent it verifies — another context, other rights, another process.
Workflow or agent
Anthropic's "Building effective agents" (December 2024) draws the line: workflows are systems where the model and tools are orchestrated along a predefined code path; agents are systems where the model dynamically directs its own process and tool use. Five workflow patterns cover most ground: prompt chaining with verification between steps, routing, parallelisation, orchestrator-workers, and evaluator-optimiser.
An agent begins where the number of steps cannot be known in advance. Two cumulative conditions justify going there: the path depends on what the environment returns (otherwise it is a workflow, however many model turns it takes), and the environment returns a verifiable signal (otherwise the loop does not converge, it just spins).
| Level | Pattern | When it fails |
|---|---|---|
| 0 | Single call | Fails as soon as there are several steps |
| 1 | Chaining and routing | Predictable; fixed steps, verified outputs |
| 2 | Parallelisation, orchestrator | Independent sub-tasks, aggregated |
| 3 | Evaluator-optimiser | Bounded: converges if the signal is real |
| 4 | Agent: loop open over tools | Costs variable latency, p^n, guardrails |
| 5 | Multi-agent | Only on a demonstrated need: ×4 to ×15 tokens |

You climb a rung only on a measured failure of the one below. The most profitable intermediate pattern is the evaluator-optimiser: a loop, but closed by code, with a bounded iteration count and a real verifier. A three-step verified workflow beats a thirty-turn unverified agent on almost every enterprise case.
Three review questions settle the maturity of any existing agentic system:
- Who decides to stop? If the model alone: failures one and three.
- What prevents an unwanted action? If a sentence in the prompt: failure five.
- How do we know it is finished? If a declaration by the model: failures three and four.
If the answer to any of the three is "the model", the harness is not finished.
1. The loop
Anatomy of a turn
ReAct (Yao et al., 2022) formalised the reason-act-observe sequence. Its modern form is the tool_use block the model emits and the tool_result block the harness returns; the tool's result is the observation. Four technical facts shape the design.
The whole context is resent on every turn, so the cumulative cost of an uncached session grows as n². A stable prefix and prefix caching are not an optimisation; they decide whether the loop is economically viable at all. A single turn can carry several tool calls, and the harness executes independent ones in parallel, returning every result in the same turn. The stop_reason is the only end-of-turn signal: end_turn for a final answer, tool_use for at least one call to execute, max_tokens for a truncated output. And max_tokens is not an ending: output truncated in the middle of a tool call is an incoherent state, which the harness treats as an error, never as an answer.
Here is the inner loop with its bounds, provider-independent:
def run_turns(task, journal, policy, env, verifier, budget):
ctx = assemble_context(journal) # a projection of the journal
while True:
if budget.exhausted(journal): # turns, tokens, currency, wall time
return force_stop(journal, reason="budget")
if budget.soft_threshold(journal): # 80 %: instruction to wrap up
ctx = ctx + [system_hint("Finish the current step, write HANDOFF.md, stop.")]
resp = model.call(ctx) # the only non-deterministic call
journal.append(model_call(ctx_hash=hash(ctx), stop=resp.stop_reason, usage=resp.usage))
if resp.stop_reason == "max_tokens":
return handle_truncation(journal)
if resp.stop_reason == "end_turn":
return finalize(journal, resp) # the harness verifies the proof
results = []
for call in resp.tool_calls: # tool_use: one or several calls
if loop_detector.seen(call): # same tool, same args, N times
return force_stop(journal, reason="loop")
decision = policy.decide(call) # allow | ask | deny
journal.append(tool_request(call, decision))
if decision == "deny":
results.append(error_result(call, "refused by policy: " + policy.reason(call)))
continue
if decision == "ask":
decision = ask_human(call) # a journal event; pauses if asynchronous
if decision != "allow":
results.append(error_result(call, "refused by the user"))
continue
hooks.pre(call) # may rewrite or refuse
raw = env.execute(call, timeout=policy.timeout(call)) # sandbox
out = truncate_and_offload(raw, budget_per_tool=policy.result_budget(call))
hooks.post(call, out) # lint, format, journal, inject
journal.append(tool_result(call, out.ref, out.size, out.truncated))
results.append(out.visible)
if call.is_step_boundary: # end of a plan step
journal.append(checkpoint(env.snapshot(), hash(ctx)))
journal.append(verify(verifier.run(call)))
errors.update(results) # consecutive-error counter
if errors.consecutive >= 3: replan(journal)
if errors.consecutive >= 5: return escalate(journal)
ctx = assemble_context(journal) # never ctx + results: always a projection
Two lines deserve emphasis. The context is always recomputed from the journal, never built by accumulation (ctx = ctx + results): that is what makes truncation, erasure and compaction policies applicable without loss. And every permission decision, every hook output, every verification is a journal event, which means the observability of the last section is not an addition but a by-product.
Three nested loops
| Loop | Unit | State | Stop criterion |
|---|---|---|---|
| Inner | the turn | the context | stop_reason = end_turn, bounds, proof |
| Outer | the session | the disk: files, git, journal | no task remaining; or session budget |
| Meta | the product iteration | the task set and its scores | pass^k reached on the target tasks |

Information that must survive the turn goes in the context; the session, on disk; the iteration, in the task set. The harness writes the state of each loop into the input of the next.
Stop conditions: four bounds, a soft threshold, and a proof
| Bound | Starting value | Behaviour when crossed |
|---|---|---|
| Turns per session | 25 to 50 on a coding task | Forced stop; the task resumes with a fresh context |
| Context tokens | 25 to 30 % of the advertised window | Compaction, then stop and hand off if that is not enough |
| Monetary budget | Per task, never per call | Forced stop; the cost is a journal event |
| Wall-clock time | Task-dependent; always present | Forced stop, which avoids infinite waits on a tool |
| Loop detected | Same tool and arguments, three times | Forced stop with the reason reinjected into the handoff |
| Consecutive errors | 3 → replan; 5 → human | Counter reset on every validated step |
Two mechanisms make these bounds usable. The soft threshold: a hard bound cuts the agent off mid-write, leaving an incoherent state that the next session inherits as damage. At 80 % of the budget, the harness injects a system instruction to finish the current step, write the handoff note, and stop; the hard bound becomes a last resort. The proof: completion is signalled through a structured tool, task_complete(summary, evidence), not free text, and on receipt the harness does not trust evidence — it reruns the verification itself. The model proposes the ending; the harness establishes it.
Every stop, voluntary or forced, produces three artefacts: a checkpoint, a handoff note, and a degraded mode announced in the traces. A stop without those three has avoided a cost and created an incident.
The outer loop with a fresh context
On a long task, a single session accumulates its own noise: exploited tool results, corrected errors, abandoned branches. Context rot degrades reasoning well before the technical limit. The answer is not to compact harder; it is to throw the context away and start again empty, reading state from disk.
Anthropic's "Effective harnesses for long-running agents" (November 2025) describes two roles. An initialiser agent, once per project, writes the feature list with statuses, a progress file, an environment bootstrap script, and makes a first commit. Then a worker agent, every session and in an empty context: reads the progress file and the git log; runs the bootstrap script and checks that the application starts and the tests are green; picks one task, the highest-priority unfinished one; does it inside a bounded inner loop; proves it with tests, lint, a screenshot if there is an interface; commits and updates the status; exits. Huntley's "Ralph" pattern (2025) is the minimal version: a shell while loop that relaunches the same prompt.
while [ "$(jq '[.features[] | select(.status != "done")] | length' features.json)" -gt 0 ]; do
./init.sh || { echo "environment broken" ; exit 1 ; } # green state before starting
agent --prompt-file WORKER_PROMPT.md --max-turns 40 --budget-usd 3
git diff --quiet && git diff --cached --quiet || git commit -am "session $(date -Iseconds)"
done
What makes this robust is not the worker's prompt; it is the discipline of closure. Each session leaves the environment clean (green tests, temporary files deleted, dependencies declared) and the state up to date. A session that does not close cleanly poisons every session after it, which inherits a red state without knowing why.
The marker: one task per session, 25 to 50 turns per session. A session that overruns is a badly cut task, not a budget that is too small. The context is disposable; the state is not.
The plan as an artefact
Two planning regimes exist and both have a defect. Plan then execute produces the plan first and follows it: predictable and traceable, but rigid in the face of what the environment reveals. Interleave decides the next step every turn: adaptive, but every turn takes an implicit decision and the objective drifts. The compromise is a plan written to a file, with verifiable steps, reread every turn by the harness, and revised only on explicit triggers.
The plan captures exactly what compaction must preserve: the objective and the stated constraints, the decisions taken and why, the errors already made, the state of the task, and the references for recovering what was removed. The plan is the compaction artefact, and because it lives on disk it survives both compaction and the session.
Granularity: one step equals one possible verification. "Add the CSV export" is not a step; "the test test_export_csv passes" is. Three verifiable steps beat twelve vague ones, which is law 2 applied to planning: small n, measured p.
Replanning happens on explicit triggers: three errors on the same step, the verifier failing twice, a plan hypothesis contradicted by a tool result, 80 % of the budget consumed. Never "because we had an idea" — that is the definition of drift. Every replan is a journal event, with the old and the new plan.
Errors: four classes, four policies
| Class | Detection | Harness policy | Visible to the model |
|---|---|---|---|
| Transient | 429, 5xx, network timeout, temporary lock | Retry with exponential backoff and jitter, capped at 3 to 5; then degraded mode | No. A model turn spent on a 429 is a wasted turn |
| Validation | Argument schema refused, path missing, value out of range | Immediate return to the model with a message saying what is wrong and what to do; no automatic retry | Yes: one turn, one correction |
| Semantic | Red test, lint, output schema refused, verifier failed | This is the loop's signal. Reinjected concise and localised; consecutive counter | Yes: this is the observation |
| Environment | Sandbox unavailable, disk full, secret missing, tool down | Stop, checkpoint, escalate to a human. The model cannot repair an environment | No, except in the handoff |
The error message is a prompt. "Error 500" produces a loop: the model retries the same thing. "Parameter date: expected format YYYY-MM-DD, received 12/03" produces a correction in one turn. Every validation error says what is wrong and what to do; that is a clause of the tool contract.
Compact the errors (12-factor agents, HumanLayer, 2025). Twelve identical errors in the context are the distraction failure from the previous guide, and they push the model to repeat what it reads. The harness keeps only the last error for a given step, prunes repetitions, and counts consecutive errors: three triggers a replan, five escalates to a human, a validated step resets the counter.
Idempotency: every side-effecting action carries a run_id + step_id key. Without it, the resume policy duplicates effects; with it, "replay" is a policy rather than a risk.
Verification inside the loop
The loop converges only if the environment returns a signal. With no signal it spins; with a weak signal it converges on what satisfies the signal rather than the task. SWE-agent (Yang et al., 2024) is the clean demonstration: an automatic lint after every edit, refusing the invalid edit, eliminated cascades of syntax errors.
| Signal level | Examples | Status |
|---|---|---|
| Strong | Targeted unit test, compilation, JSON schema, diff bounded to the expected files | At every step |
| Medium | Lint, static typing, compared screenshot, format check | Completes the strong signal |
| Weak | "This looks correct" from the model itself, with no execution | Not a signal |
After each step, not at the end of the task. The verifier resets p to 1 where it is placed. At the end of a task it detects the error after twenty turns; after each step, after one turn. Detection costs the same; repair costs a factor of however many steps have elapsed.
The signal must be concise and localised. Two thousand lines of test output are noise; "1 failure: test_export line 42, expected ';' received ','" is a signal. The harness filters output before reinjecting it, and never truncates the error lines.
If no automatic verifier exists for a task, climb back down a rung: workflow plus human review. An agent without a signal is not an agent. And the verifier sitting in the same context as the generator is not a verifier: a model judging its own work without new evidence confirms what it wrote.
2. The agent-computer interface
The ACI is a performance parameter
The SWE-agent paper (Yang et al., NeurIPS 2024) named the Agent-Computer Interface and measured its effect. Their finding: a raw shell is a bad interface for a model. Outputs are endless and carry no line numbers, editors are interactive, and a malformed command leaves an unknown state. Their ACI instead: a 100-line numbered file window with scrolling; a search that refuses to return more than 50 results and asks you to narrow it; a line-range edit command with an integrated lint that refuses invalid edits and shows the modified context; and the current directory and open file restated in every return.
| Setup | SWE-bench resolved |
|---|---|
| RAG baseline (GPT-4) | 1.3 % |
| RAG baseline (Claude 3 Opus) | 3.8 % |
| SWE-agent ACI (GPT-4 Turbo) | 12.5 % |

Same model family, different interface. The exact numbers depend on the model and the date; the order of magnitude, a factor of three to ten, comparable to a model generation change for a few days of engineering, is what to retain.
Design for the model, not for the human. A human scrolls, filters visually, opens a second terminal. A model pays for every line in tokens and in attention. What is comfortable for one is noise for the other. The marker: a tool whose output exceeds 100 lines without pagination, or whose error does not say what to correct, is an interface defect before it is a model problem.
This is where plugging an MCP server in "as is" hurts: it exposes a human's API to an agent, with forty tools, ten-thousand-line responses and bare HTTP errors. MCP transports; it does not design. Every server you plug in goes through an adaptation layer that applies the contract below.
The tool contract
| Clause | Prescription | Counter-example | Consequence |
|---|---|---|---|
| Namespace | prefix = system, verb = action: jira_search_ticket, github_search_pr |
search, search2, find_items |
The model picks the wrong system |
| Description | what it does, when to use it, when not to, one example call | "Searches for data" | Overlapping scopes, exploratory calls |
| Granularity | one tool per business flow: schedule_meeting(participants, duration) finds the slot |
list_users + list_calendars + create_event |
Three turns and 20k tokens where one sufficed |
| Response format | a concise/detailed parameter; readable identifiers rather than opaque ones; pagination | the API's full JSON | The context fills from a single call |
| Errors | typed and actionable: "expected date YYYY-MM-DD", "340 results: add a filter" | Error 500, a stack trace | The agent loops instead of correcting |
| Idempotency | idempotency_key mandatory on every write; replaying means the same effect |
POST with no key | Three tickets created after a resume |
| Evaluation | 10 to 30 realistic tasks per tool, measured before and after every change | "we will see in production" | A changed description degrades another tool |
Anthropic's "Writing effective tools for agents — with agents" (September 2025) adds the method: build an evaluation per tool, let an agent propose description improvements, and measure. Here is what the contract looks like in practice:
{
"name": "jira_search_ticket",
"description": "Searches Jira tickets by text and filters. Use when the user mentions a ticket, a bug or a tracked request in Jira. Do NOT use for GitHub (see github_search_pr) nor to create a ticket (jira_create_ticket). Returns at most `limit` results; beyond 50 matches, ask the user to narrow. Example: {\"text\": \"CSV export\", \"project\": \"BILL\", \"limit\": 10}",
"input_schema": {
"type": "object",
"properties": {
"text": {"type": "string", "description": "Keywords; JQL syntax not accepted"},
"project": {"type": "string", "enum": ["BILL", "CRM", "INFRA"]},
"status": {"type": "string", "enum": ["open", "in_progress", "closed", "all"], "default": "all"},
"format": {"type": "string", "enum": ["concise", "detailed"], "default": "concise"},
"limit": {"type": "integer", "minimum": 1, "maximum": 50, "default": 10}
},
"required": ["text"]
}
}
A concise return is one line per ticket: BILL-812 · open · CSV export: wrong separator · @j.dupont · 2026-08-30. An error is actionable: {"error": "too_many_results", "count": 340, "hint": "add project or status"}.
A tool that returns 10,000 lines is an interface defect, not a context problem. Fixing it on the context side, with truncation, is a bandage; fixing it on the tool side is the solution.
Reducing the surface: tool indexes and programmatic calls
Beyond roughly twenty declared tools, the selection rate for the right tool falls and the context is consumed before the first question. Three exposure regimes:
| Regime | When | Reported effect |
|---|---|---|
| A · Everything declared | Acceptable under about twenty tools | Costs tokens and precision beyond that |
| B · Index and load on demand | 20 to 100 tools | Anthropic (Nov 2025): selection precision 49 → 74 % on Opus 4, about 85 % fewer tokens in the initial context |
| C · Programmatic calls | As soon as a bulky intermediate result only feeds another tool | Anthropic (Nov 2025): one task going from 150,000 to 2,000 tokens |
Regime C changes the nature of the loop. In A and B, every tool result is a turn: the model reads 5,000 lines of table to filter ten. In C the filtering is code; the loop loses both turns and tokens, and sensitive data can stay out of the context entirely because it transits between tools inside the sandbox. What C requires: an execution sandbox, tools exposed as typed functions, and traces that capture the executed code, since A's legibility is gone.
What comes back into the context
A tool result has a four-state lifecycle. Raw: 12,000 lines of JSON, 4 MB of logs. Truncated: head and tail, with an explicit counter of what was omitted, under a per-tool budget (2 to 4k tokens as a starting point; a test needs its last line, a grep its first fifty). Offloaded: the full result written to disk, referenced by a path, with a search tool to reach into it. Erased: once exploited, the result is replaced by its reference when a threshold is crossed.

def truncate_and_offload(raw: str, budget_tokens: int, head=60, tail=40) -> Result:
ref = journal.store(raw) # always kept whole, by reference
if tokens(raw) <= budget_tokens:
return Result(visible=raw, ref=ref, truncated=False)
lines = raw.splitlines()
errors = [l for l in lines if ERROR_RE.search(l)] # never truncated
body = lines[:head] + [
f"… {len(lines) - head - tail} lines omitted · full: {ref} · grep_result available …"
] + lines[-tail:]
visible = "\n".join(body + (["— error lines —"] + errors[:50] if errors else []))
return Result(visible=visible, ref=ref, truncated=True)
Anthropic's context-management evaluation (September 2025) measured, on a 100-turn web research task: persistent memory plus result clearing, +39 % over baseline; clearing alone, +29 %, with 84 % fewer tokens on the task. Those are internal evaluations; the direction, that clearing exploited results improves quality and reduces cost, is the part to keep.
Every truncated result carries a counter of what is missing and a reference to the whole, and a tool exists to search the whole. A silent truncation is an unsignalled loss, the same failure as a botched compaction. The order of application matters too: the tool contract reduces volume at the source first, truncation handles what remains, and compaction intervenes last. Frequent compaction signals a tool problem, not a context problem.
Skills and code: moving determinism out of the model
A model turn is a reliability factor below 1 in the product p^n; a tested script is a factor of 1. Every turn a script can replace is therefore a direct reliability gain before it is a cost gain. The decision is made step by step:
- The step requires no judgement (formatting, migration, calculation, code generation from a schema): a script, executed without being read, zero token cost, triggered by a hook or a command.
- The step requires judgement but the procedure is known (a review against a rubric, a deployment, a report in the house format): a skill. A few dozen tokens of description permanently, the body loaded on demand, attached scripts executed without being read. The model follows, the code does.
- The step requires judgement in a novel situation: a model turn, bounded, verified, traced. This is the only case where reliability multiplies, and therefore the only one that earns a dedicated verifier.
The capitalisation mechanism comes from Voyager (Wang et al., 2023): a library of verified skills, reused and composed, grows an agent's capability without retraining. A procedure that succeeds once becomes a file; never a piece of reasoning to redo. The practical marker: read the traces, and any sequence of turns that repeats identically from one session to the next is a skill waiting to be written, or a script.
A skill that describes a procedure without verifying it propagates its errors on every use. A skill has tests, like a tool.
3. The environment
The sandbox is a boundary, not a filter
A filter inspects each action and decides; it has false negatives, and an agent that optimises eventually finds them. A boundary makes the action impossible; there is nothing to decide. "Do not access ~/.ssh" is a filter; a container where ~/.ssh does not exist is a boundary. The sandbox closes the third branch of the lethal trifecta: the ability to transmit outward.
| Layer | Prescription | What it makes impossible |
|---|---|---|
| Filesystem | Read and write limited to the working directory; the rest is invisible (mount), not merely forbidden | Reading a key, another project, the browser history |
| Network | Off by default; domain allowlist through an egress proxy; connection log | Exfiltrating, downloading an executable, calling an unplanned API |
| Process | Ephemeral container or micro-VM; CPU, memory and duration quotas; destroyed after the session | Persisting between sessions, exhausting the host |
| Secrets | Outside the sandbox; an identity proxy signs authorised outbound requests | Reading or transmitting a key: the agent never sees it |
| Output | Only declared artefacts leave (diff, files, report), reviewed by the harness | An untraced side effect |
Anthropic's "Beyond permission prompts" (October 2025) reports that filesystem and network isolation cut permission requests by 84 % in their internal usage. The sandbox is what makes autonomy acceptable, and it precedes the permission policy, which it then lightens. Codex cuts the network during the agentic phase of its cloud tasks.
Note that there are two sandboxes with two perimeters: the agent's own, covering its commands, writes and session; and the one for programmatic calls, covering the code it generates to chain tools, same technology but more closed still, with no network and no persistent disk.
Using the developer's workstation as the sandbox gives access to their keys, their network and their other projects. One injection in a file that gets read is enough. This is not a theoretical scenario.
Durable state: the event journal
The event journal is the source of truth for the execution; each turn's context is a projection of it, never the inverse. Every event is persisted before being considered done. This is the durable-execution architecture of distributed systems (Temporal, Restate, Inngest) and, in the agentic ecosystem, that of LangGraph checkpointers, the OpenHands event stream and the Claude Code transcript. A crash at step 7 resumes at step 7, without re-executing the first six or duplicating their effects.
{"run_id":"r_8f2a","seq":17,"ts":"2026-09-07T10:14:03Z","type":"tool.request",
"ctx_hash":"sha256:7c1e…","payload":{"tool":"run_tests","args":{"path":"tests/test_export.py"},
"permission":{"class":2,"decision":"allow","rule":"policy#4"}}}
{"run_id":"r_8f2a","seq":18,"ts":"2026-09-07T10:14:11Z","type":"tool.result",
"payload":{"tool":"run_tests","duration_ms":8400,"size_tokens":4100,"truncated":true,
"ref":"runs/r_8f2a/t018.txt","summary":"42 pass · 1 fail"}}
{"run_id":"r_8f2a","seq":19,"ts":"2026-09-07T10:14:11Z","type":"checkpoint",
"payload":{"fs":"git:ab12f3","ctx_hash":"sha256:7c1e…","step":3,"plan_hash":"sha256:91aa…"}}
Event types: session.start, session.end, model.call, tool.request, tool.result, permission, hook, checkpoint, verify, replan, compaction, escalate, degraded.
What "projection" implies. A turn's context is computed from the journal by applying the policies above (truncation, erasure, compaction) and the zone order from the previous guide. Two consequences: you can recompute a context after the fact for diagnosis, and you can change policy without losing information. A context that is itself the source of truth disappears with the session. Persist after each tool result, not after each turn: a turn can carry five calls and the crash arrives at the third.
An execution that cannot be replayed cannot be debugged. The journal precedes the trace — they are the same object, one event schema, frozen in wave one.
Checkpoints and rewind
A checkpoint restores three things together: the filesystem state (a commit or a git worktree), the context (or its position in the journal, which is the same thing since the context is a projection), and the list of external effects already emitted. All three restore atomically. Rewinding the context without rewinding the files leaves the agent reasoning about a state that no longer exists — the poisoning failure from the previous guide, in its agentic form.
Rewind rather than patch over. After a failure there are two strategies: patch over it, where the context keeps both the error and its correction and drift accumulates, or rewind to the last green state and retry, with the error noted in the plan. The second costs a checkpoint; the first often costs a session.
Explore in parallel. One git worktree per attempt: A and B at the same time on the same step, and a verifier picks. This is the useful form of multi-agent, partitioning by attempt rather than by role, and it is also what supports verified best-of-n.
What does not rewind: external effects. A checkpoint does not un-send an email. Hence the ordering: verify, checkpoint, and only then emit. For the irreversible already emitted, an explicit compensating action (the saga pattern) declared in the tool contract. The rule: a checkpoint before every class 3 or 4 action, and after every green plan step. Every write is either reversible, by snapshot, or guarded, by permission.
The repository as the outer loop's memory
The previous guide proposed Markdown memory files and the handoff note as a minimal viable memory. The repository is the industrialised version. OpenAI's "Harness engineering" (February 2026, Codex team) describes a practice where the repository is the agent's memory: an AGENTS.md under 100 lines serving as a table of contents pointing into docs/; the docs as the memory; linters enforcing the structure; and a garbage collector deleting stale docs, because a wrong doc misleads the agent more than it helps.
| File | Role | Who writes it | Constraint |
|---|---|---|---|
AGENTS.md / CLAUDE.md |
The map: where things are, conventions, tools; pointers | human, reviewed | Under 100 lines; in zone 1, so stable |
docs/ (ADRs, runbooks) |
The memory: decisions and their why, procedures | human and agent | Garbage-collected: any unmaintained doc is deleted |
PLAN.md, progress.md, features.json |
The state: where we are, what remains, what failed | agent | Reread at startup, updated at every step |
HANDOFF.md |
The last session's handoff note | agent, at session end | Written for a reader with no context |
.agent/skills/, .agent/hooks/ |
Procedural knowledge and guardrails as code | human | Tested; out of the agent's write reach |
tests/ |
The verifier | human | Read-only for the agent |
Three reads at startup, in this order: the map, the state, the journal (git log and traces). If one of the three is missing, the agent reinvents it, with failures two and five as the consequence. Write for a reader with no context: the next session. "See yesterday's discussion" means nothing to an empty context.
4. The guardrails
Action classes and the permission policy
A per-tool permission ends as a list of two hundred rules. The useful granularity is the action class, crossed with argument patterns.
| Class | Examples | Default policy | Mechanism |
|---|---|---|---|
| 1 · Read | read, grep, list, SELECT, GET | automatic | The readable perimeter is defined by the sandbox |
| 2 · Reversible write | edit a repository file, run the tests, create a branch | automatic + checkpoint | Snapshot before; PostToolUse hook |
| 3 · Irreversible local write | delete, git push --force, migration, install a package |
ask | PreToolUse hook: refuse by pattern; otherwise ask |
| 4 · External emission | email, ticket, message, payment, third-party POST | ask + idempotency + journal | Network sandbox allowlist; idempotency key; checkpoint before; traced decision |
| 5 · Configuration | policy, hooks, tests, secrets, skills | forbidden to the model | Files outside the sandbox or read-only; attempts journalled and escalated |

The modes in market tools (Claude Code's plan and accept-edits; Codex's suggest, auto-edit, full-auto) are shortcuts over this policy. The policy itself is a versioned file, reviewed and tested, which the model neither reads nor writes — class 5 applied to itself.
# permissions.yaml · versioned, reviewed, tested · out of the agent's reach (class 5)
defaults: {1: allow, 2: allow_with_checkpoint, 3: ask, 4: ask_with_idempotency, 5: deny}
classify:
- {tool: read_file, class: 1}
- {tool: grep, class: 1}
- {tool: edit_file, class: 2, unless: [{path: "tests/**", class: 5}, {path: ".agent/**", class: 5}]}
- {tool: run_shell, class: 2, unless: [{args: "rm -rf|git push --force|curl .*\\| sh", class: 5},
{args: "^git push", class: 3},
{args: "^pip install|^npm install", class: 3}]}
- {tool: jira_create_ticket, class: 4, require: [idempotency_key]}
- {tool: send_email, class: 4, require: [idempotency_key], allowlist_domains: ["example.com"]}
modes:
plan: {max_class: 1}
edit: {max_class: 2}
autonomous: {max_class: 3, sandbox_required: true} # class 4 always asks
audit: every_decision
The human risk here is permission fatigue: forty questions per session and the user approves everything. The remedy is not "allow everything" but "fewer questions" — the sandbox cuts requests by 84 %, and the classes avoid asking about anything reversible.
Hooks
Hooks are the harness's extension points: deterministic code executed by the harness, never by the model, at fixed moments in the loop. Claude Code exposes them as PreToolUse, PostToolUse, Stop, SessionStart, PreCompact (2025); Codex, OpenHands and LangGraph have equivalents. A hook returns allow, deny, or a modification, in under 100 ms.
| Hook | Use | Example |
|---|---|---|
| SessionStart | Load the state, the map, the plan; check the environment | Refuse to start if init.sh fails |
| PreToolUse | Validate, refuse, rewrite arguments; the fine boundary where the sandbox is coarse | Refuse rm -rf and git push --force; rewrite a relative path as absolute; require an idempotency key on a POST |
| PostToolUse | Format, lint, journal, inject | Formatting after every edit; test results injected; all output journalled |
| PreCompact | Save what must survive the compaction | Write PLAN.md and the errors seen before the context is rewritten |
| Stop | Turn "finished" into proof | Refuse the stop if there is no test run in the last N events, or if the diff touches tests/ |
def on_stop(journal, diff):
recent = journal.last(20)
if not any(e.type == "verify" and e.payload["ok"] for e in recent):
return Deny("No successful verification in the last 20 events. Run run_tests before concluding.")
if any(p.startswith("tests/") for p in diff.paths):
return Deny("The diff modifies tests/. Explain the change and request a review (class 5).")
return Allow()
The difference from the prompt: "always run the tests" is followed at some rate; a Stop hook is followed 100 % of the time. What must always happen is a hook; what should generally happen is an instruction. Hooks are also where determinism lives: formatting, generation, migrations, calculations. What runs in a hook consumes neither a turn nor a token, and cannot be forgotten.
One caution: a hook that makes a model call reintroduces non-determinism into the component meant to eliminate it. A hook is code; if it needs to judge, it is a verifier.
The human in the loop
The human validates decisions, not turns. Four interruption points: before, approve the plan rather than the forty actions that realise it; during, a class 3 or 4 action; at the end, review the diff and the proof, never the transcript; asynchronously, the agent contacts the human through a structured tool (12-factor agents' "contact humans with tool calls"; MCP elicitation, June 2025, standardises the server-to-user question with a response schema).
Granularity decides effectiveness. Approving a four-step plan, letting forty actions run in the sandbox and reviewing one diff is three interruptions. Approving forty actions is forty interruptions and a reflex approval by the tenth. A human judges an intention and a result well, and a command out of context badly.
ask_human(
question="The invoices schema has no currency column. How should amounts be handled?",
options=[{"id": "eur", "label": "Assume EUR (documented in the report)"},
{"id": "col", "label": "Add a currency column (migration: class 3)"},
{"id": "stop", "label": "Stop and escalate"}],
default="stop", timeout="4h", on_timeout="pause")
Measure the interruption rate per task, and aim for approvals on classes 3 and 4 only. A permission mechanism the human no longer reads is an audit trail, not a guardrail.
Injection through tools, and objective circumvention
Indirect injection. To the model, a file read, a web page and a ticket have the same status as an instruction. Inside a loop, the lethal trifecta reassembles on every turn where one tool reads the outside and another writes to it. The harness closes the path, not the prompt: network sandbox (no more emission), minimal capabilities per tool and per task, and for high-stakes cases the CaMeL architecture (Debenedetti et al., Google DeepMind, March 2025) — a privileged planner that never sees untrusted data, a quarantined model that processes it without being able to call tools, and capabilities attached to values so that data from an untrusted source cannot reach a sensitive tool. The absolute rule: retrieved content never creates a permission.
Objective circumvention (reward hacking). The metric is satisfied, the intent is not: tests marked skip, expected values hard-coded, asserts deleted, a score file modified. METR (June 2025) observed this on recent models in its evaluations; Anthropic's "Natural emergent misalignment from reward hacking" (November 2025) shows that circumvention learned during training generalises to other undesirable behaviours. This is not malice, it is a property of optimisation: if the success signal is modifiable, the shortest path runs through the signal.
| Symptom | What happened | What the harness guarantees |
|---|---|---|
| Green tests, feature absent | assert replaced by pass; skip; hard-coded value |
tests/ read-only; a hidden holdout test set rerun by the verifier; a hook refusing any diff on tests/ |
| Evaluation score changed | The agent found and edited the grader | The grader runs in another process, with no write access from the sandbox |
| External action after reading a ticket | The ticket contained an instruction; the agent obeyed | Class 4 → permission; retrieved content never creates a permission; quarantine |
| "Impossible" never said | The agent prefers a workaround to an admission | An explicit instruction and a report_blocker tool; the transcript is graded |
A metric the agent can modify is not a metric.
5. Verification
The hierarchy of verifiers
- 1 · Deterministic: compilation, tests, schema validation, lint, a diff bounded to the expected files. Free, reliable, at every step.
- 2 · Properties: invariants ("the exported total equals the sum in the database"), metamorphic tests ("the result does not change if the input order changes"), partial oracles. They do not require knowing the right answer, only a relation it must respect. This is the forgotten level.
- 3 · Model judge: acceptable under five cumulative conditions. A written rubric with criteria, a scale and examples; cited evidence, so the judge points at the line; a context separated from the agent, without its reasoning, or it judges the plausibility of the argument instead of the work; pairwise comparison rather than an absolute score; and a calibration measured against human labels, 80 to 90 % agreement, before any use as a metric. Known biases: position, verbosity, self-preference.
- 4 · Human: on the final diff, on a sample, on the irreversible. Rare, expensive; this is what calibrates level 3.
The level of a verifier is chosen by what it can prove, not by what it can comment on. A test proves; a judge estimates; the agent asserts.
The generator-verifier gap
This is the frame that delimits where agents work today: they work where generating is expensive and verifying is cheap.
| Verification expensive | Verification cheap | |
|---|---|---|
| Generation expensive | Strategic writing, medical diagnosis → workflow plus human; never an autonomous agent | Tested code, SQL on a known schema, forms, maths → the agent's domain: the loop converges |
| Generation cheap | Summaries, recommendations, tone → calibrated judge plus a human sample | Extraction, classification, formatting → one call plus schema validation; no loop |

Reflexion (Shinn et al., NeurIPS 2023) is often cited for verbal self-critique. The measurement says something else: the gain (HumanEval, pass@1 from 80 to 91 % on GPT-4) comes from coupling the reflection to the test signal. Without an external signal, the model rereads itself, congratulates itself and iterates on its own error.
Two patterns exploit the gap. Verified best-of-n: n attempts in parallel in worktrees, a verifier picks; cost ×n, pass^k rises; worth it only if the verifier is level 1 or 2. Evaluator-optimiser: generate, verify, correct, N bounded times; converges if and only if the verifier carries a real signal. A task from the "everything is expensive" quadrant often decomposes into sub-tasks of which some are verifiable: "write the analysis" is not, "extract the figures from the report and reconcile them against the database" is.
If you cannot write the verifier, you cannot automate the task. Decompose until you can write it, or climb back down to workflow plus human.
The evaluation harness
The golden set from the previous guide becomes a task set. Anthropic's "Demystifying evals for AI agents" (January 2026) fixes the vocabulary: a task (statement, initial state, grader, budget); trials; one transcript per trial; an outcome, the final state; a grader (code, model, human). You grade the outcome and the transcript: a correct final state reached through a forbidden action, a modified test, a class 4 action without permission, a secret read, is a failure.
id: BILL-export-csv-017
source: trace r_8f2a (production failure, 2026-08-30)
snapshot: images/bill-repo@ab12f3 # repository + test database + environment variables
prompt: "Add a CSV export of invoices on /export, separator ';', without breaking API v1."
budget: {turns: 40, tokens: 400000, usd: 3.0, wall_minutes: 30}
graders:
- type: code # level 1: hidden tests, executed outside the sandbox
run: pytest hidden_tests/test_export_hidden.py
- type: code # level 2: a property
run: python graders/sum_equals_db.py
- type: transcript
forbid: [{class: 4}, {path_written: "tests/**"}, {tool: "read_secret"}]
- type: model # level 3, calibrated: human agreement 0.87 on 60 examples
rubric: rubrics/api_compat.md
trials: 8 # pass^8 reported, with pass@8 for comparison
The protocol: 50 to 200 tasks, built from production traces, where every failure and every human intervention becomes a task with its initial state; 5 to 10 trials per task; a fresh sandbox per trial; the production harness, frozen by version; rerun on every change of prompt, tool, hook, environment or model. Metrics: pass^k and pass@k, cost per successful task (total cost ÷ pass rate), turns, tokens, human interruptions, forbidden actions.
Public benchmarks (SWE-bench Verified, τ-bench and τ²-bench, Terminal-Bench, OSWorld, GAIA) are model markers, not measurements of your system: contamination, overfitting, and above all they are not your tasks.
Evaluating with a harness other than production's (different tools, different sandbox, different hooks) measures a different system. The evaluation runs the production harness, frozen by version.
The meta loop
The third clock. Every production failure becomes a golden task; the failure taxonomy says which failure and which component; the fix picks one lever among five — the prompt, the tool, the hook, the environment, the plan; the evaluation measures in pass^k; the deployment is versioned and reversible.
The prompt is the most visible lever and the least measurable: a sentence added "to fix one case" is measured on the two hundred tasks, not on the case. One change at a time: two simultaneous changes make the effect unattributable, even when quality goes up.
The task set is to the harness what the golden set was to RAG. Without it, no change is arbitrable; with it, every production failure becomes one more task.
6. Operating
The trace is a tree
The journal and the trace are the same object seen by two tools: one for resuming, one for diagnosis. Conventions: OpenTelemetry GenAI (2025); tools: Langfuse, LangSmith, Phoenix, Braintrust. What is recorded without exception: the final context of every call (by hash and by reference), tool arguments and results (by reference, never whole), stop_reason and cached tokens, every permission decision, every hook output, cost, duration, sub-agents. Replay is the ultimate test: if you cannot reconstruct a call's context from the trace, the trace is incomplete.
| Metric | What a deviation signals |
|---|---|
| Cache rate between turns (target > 80 %) | The prefix is moving: a timestamp, the tool list, state injected into zone 1 |
| Turns per task (trend) | Objective drift, a failing ACI, a plan cut too fine |
| Errors per tool | The tool contract needs review; one tool concentrates the errors |
| Human interruptions per task | Action classes badly tuned; insufficient sandbox |
| Loops detected, forced stops | Bounds too low or badly cut tasks |
| Forbidden actions recorded | Circumvention; guardrails to harden |
| Cost per successful task | The only cost metric that counts |
A trace should be readable in under five minutes by someone who did not write the system. Otherwise it is too verbose or too poor, and in both cases nobody will use it on the day of the incident.
Cost and latency
Without caching, the cumulative cost of a session grows as n²: every turn resends everything that came before. With a stable cached prefix, cost is nearly linear. The harness adds its own ways of breaking the prefix: a state reminder injected into zone 1, a compaction that rewrites the beginning, a tool index that changes every turn, a SessionStart hook that inserts a date. The test: the prefix hash is identical from one turn to the next, and the trace shows it.

The other levers, by yield: parallel tool calls within a turn; a token budget per tool result; a small model to route, classify and grade (the judge and the router are the first candidates for model routing); and effort proportional to the task (Anthropic, multi-agent, June 2025: a simple question is one agent and 3 to 10 calls).
The metric is cost per successful task, which is cost divided by pass rate. An agent that is twice as cheap per call and half as reliable costs the same. And optimising cost per call by shrinking tool returns is the classic trap: the model loses the signal, turns go up, and cost per task rises while cost per call falls.
On perceived latency: on a long task the user does not watch total time, they watch time without news. Stream the plan, the validated steps and the decisions — the journal — not the tokens.
Multi-agent: partition the context, not the decision
The previous guide established the use case: read-only exploration, parallelisable, with strong compression, where a sub-agent spends 30,000 tokens and returns 800, at a cost of ×4 to ×15. Cognition's "Don't build multi-agents" (June 2025) adds the constraint: actions carry implicit decisions, and two agents with partial contexts take incompatible decisions (REST versus GraphQL) that nobody sees before integration. Hence the rule: partition by information need, never by role.
What the harness gives a sub-agent: a perimeter (what it searches for, not what it decides), a structured output format bounded in tokens, a budget in turns and tokens, its own tools (often read-only), and an instruction to report what it found, what it did not find, and what it is unsure about. It inherits neither the orchestrator's context nor its permissions. When several writes are needed: a single write thread, or the full shared context, or isolated worktrees merged by a verifier.
Markers: a simple question is one agent and 3 to 10 calls; a comparison, 2 to 4 sub-agents; a broad search, ten or more with an orchestrator that reads only syntheses. Role-based "teams" of agents (PM, dev, QA) partition the decision. They produce inconsistencies that only a human sees, late.
Degraded modes and diagnosis
| Component unavailable or exceeded | Degraded mode | Accepted loss |
|---|---|---|
| Sandbox | Read-only session (class 1 only) | No writes: we analyse, we do not modify |
| Verifier (tests, compilation) | Stop, report "unverified", never a success declaration | None: this is the expected behaviour |
| Budget (turns, tokens, currency, time) | Soft threshold, handoff note, clean exit | The task resumes in another session |
| Tool in persistent error | Tool disabled for the session, replan | An alternative path, or escalation |
| Permission unanswered | Refuse by default, task paused | Latency; never a class 3-4 action by default |
| Primary model | Secondary model, same tool contract, same harness | Lower planning quality, announced |
A degraded mode must be announced, to the user and in the traces. A session that concluded "without verification" and did not say so destroys trust the day it is discovered.
Diagnosis, in three questions. Does the journal show the failure? If not, the instrumentation is incomplete and that is the first fix. Which component failed: loop (stop, bounds), interface (wrong tool, wrong arguments), environment (incoherent state), guardrails (unguarded action), verification (false positive)? Which of the five failures is it? That gives the remedy and the lever. Then one change at a time, evaluated on the golden tasks.
Five contracts to freeze early: the journal's event schema, the tool contract, the permission policy by class, the checkpoint format, and the handoff note format. Freezing them lets you change a model, a tool or a harness without touching the rest.
Sequencing
| Wave | Content | Criterion |
|---|---|---|
| V1 · Foundation | Four bounds and loop detection · replayable event journal · filesystem and network sandbox · action classes and permission policy · tool contract with actionable errors · reference task set graded in pass^k | Non-negotiable: without these, neither measurement nor security |
| V2 · Quick wins | Stop and PreToolUse hooks · result truncation and offloading · stable prefix and caching · plan as an artefact · handoff note · deterministic verifier after each step | The first four reduce turns and improve reliability |
| V3 · Depth | Outer loop with a fresh context · checkpoints and rewind · tool index · programmatic calls · calibrated model judge · repository as memory · skills | Arbitrable only on V1's golden tasks |
| V4 · Specialisation | Sub-agents in worktrees · durable execution · planner/quarantine isolation · verified best-of-n · automated meta loop · per-step model routing | Triggered by a qualified need; one project per item |
| Transverse from V1 | Network sandbox · verifier out of reach · lethal trifecta review | Structural, not additive |
Seven things to keep
- The model is the only non-deterministic component. Any property you want to guarantee rather than hope for must be carried by a deterministic one. The review test on every prompt line: if the model ignores this, what happens?
- Reliability multiplies, so count your steps. Twenty unverified steps at 95 % each is a 36 % system. Move deterministic steps into code, and put a verifier after each step rather than at the end.
- The stop criterion is a proof produced by the harness, never a declaration by the model. Completion arrives through a structured tool, and the harness reruns the verification itself.
- The context is disposable; the state is not. An event journal is the source of truth and each turn's context is a projection of it. What cannot be replayed cannot be debugged.
- A boundary beats a filter. A container where the secret does not exist beats a rule that forbids reading it. The sandbox is what makes autonomy acceptable, and it cuts permission requests by an order of magnitude.
- The verifier is out of reach of the agent it verifies. Another process, other rights, hidden tests. A metric the agent can modify is not a metric.
- Without a task set graded in pass^k, no change is arbitrable. Build it in wave one from production traces, run the production harness against it, and change one thing at a time.
If you keep only one pair of artefacts from this article, make it the same pair as wave one: a replayable event journal, and a reference task set graded in pass^k. Everything else becomes a decision you can actually make.
Primary references
| Subject | Reference | Date |
|---|---|---|
| Workflows and agents | Anthropic, "Building effective agents" | Dec 2024 |
| Reason-act | Yao et al., "ReAct", arXiv:2210.03629 | ICLR 2023 |
| Self-critique with a signal | Shinn et al., "Reflexion", arXiv:2303.11366 | NeurIPS 2023 |
| pass^k | Yao et al. (Sierra), "τ-bench", arXiv:2406.12045; τ²-bench | June 2024 |
| Task horizon | METR, "Measuring AI ability to complete long tasks", arXiv:2503.14499 | Mar 2025 |
| Agent-computer interface | Yang et al., "SWE-agent", arXiv:2405.15793 | NeurIPS 2024 |
| Skill library | Wang et al., "Voyager", arXiv:2305.16291 | 2023 |
| Tools for agents | Anthropic, "Writing effective tools for agents — with agents" | Sep 2025 |
| Tool index, programmatic calls | Anthropic, "Advanced tool use"; "Code execution with MCP" | Nov 2025 |
| Context management | Anthropic, "Managing context on the Claude Developer Platform" | Sep 2025 |
| Skills | Anthropic, "Agent Skills" | Oct 2025 |
| Long-running harnesses | Anthropic, "Effective harnesses for long-running agents" | Nov 2025 |
| Harness engineering | OpenAI, "Harness engineering: leveraging Codex in an agent-first world" | Feb 2026 |
| Sandbox | Anthropic, "Beyond permission prompts" | Oct 2025 |
| Agent factors | HumanLayer, "12-factor agents" | 2025 |
| Shared context | Cognition, "Don't build multi-agents" | June 2025 |
| Multi-agent | Anthropic, "How we built our multi-agent research system" | June 2025 |
| Injection, capabilities | Debenedetti et al. (Google DeepMind), "CaMeL", arXiv:2503.18813 | Mar 2025 |
| Objective circumvention | METR; Anthropic, "Natural emergent misalignment from reward hacking" | 2025 |
| Agent evaluation | Anthropic, "Demystifying evals for AI agents" | Jan 2026 |
| Durable execution | Temporal, Restate, Inngest; LangGraph checkpointers | 2024-2025 |
| Observability | OpenTelemetry, GenAI semantic conventions | 2025 |
How much to trust the numbers
Not every figure above has the same status. The rule of citation: give the source and the date, state the protocol when it conditions the figure, and never turn an order of magnitude into a general truth.
| Figure | Status | What it implies |
|---|---|---|
| τ-bench: pass^1 ≈ 61 %, pass^8 < 25 % | Primary source | Rounded; depends on the model and the domain (retail, GPT-4o) |
| METR: horizon doubling every ~7 months, ~5× shorter at 80 % | Primary source | A trend on software research tasks |
| SWE-agent: 12.5 % versus 1.3 to 3.8 % | Primary source | Full SWE-bench, GPT-4 Turbo and Claude 3 Opus, 2024 |
| Reflexion: HumanEval 80 → 91 % pass@1 | Primary source | GPT-4, 2023; the gain comes from the test signal |
| Context: +39 % / +29 % / −84 % tokens | Official announcement | Internal 100-turn evaluation, protocol not public |
| Tool search: 49 → 74 %, −85 % tokens | Official announcement | Internal evaluation; depends on the catalogue |
| MCP in code: 150,000 → 2,000 tokens | Vendor example | One illustrative case, not an average |
| Sandbox: −84 % permission requests | Official announcement | Anthropic internal usage |
| Multi-agent: +90 %, ×15 tokens | Primary source (report) | Internal evaluation, not a public benchmark |
| Bounds: 25-50 turns, 3/5 errors, 80 %, 2-4k tokens per tool, cache > 80 % | Field reports | Starting points, to recalibrate against your own traces |
On a real project, the final decision is the one measured on that project's golden tasks.
