The whole design, in writing
Learn system design by building the harness engineers use to supervise agents doing delivery work. An interactive guide covering task decomposition into verifiable units, a narrow tool surface, sandboxed execution with a bounded blast radius, a verification gate the agent cannot bypass, a human review queue sized by risk, repo-grounded context, and the ledger that makes agent work auditable.
Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.
The big idea
What is an agent harness?
An engineer with a coding agent is faster at the task in front of them. An engineering organisation with agents and no harness is faster at producing work nobody has verified — and a growing pile of plausible diffs is not throughput.
Build the system around the agent: decompose requests into units whose success a machine can confirm, constrain capability to a designed tool surface, sandbox execution so failure is cheap, verify outside the agent’s reach, and route review by risk. The job moves from writing feature code to writing the system that writes it.
Step 1 · The skeleton
An agent, a repo, a task
The naive version: an engineer describes a change, an agent edits files, the engineer reads the diff. It works, and it does not scale — because the engineer is now the entire verification layer, and reading a plausible diff carefully is slower than writing it.
Stand up a Planner that hands tasks to an Agent and a human who supervises rather than authors. Everything that follows makes the agent’s output bounded, checkable, and cheap to be wrong.
What the new pieces do
- Engineerclient
- No longer writing every line — specifying, reviewing the diffs that matter, and improving the harness when a class of failure repeats. The job moves from producing code to producing the system that produces it.
- Agentservice
- The model in a loop: read context, call a tool, observe, repeat. Capable exactly to the extent the tool surface allows, and no further.
Step 2 · Bound it
Tool surface and sandbox
Give an agent a shell and you have given it every capability at once: your credentials, your network, your production. The blast radius is unbounded, the audit trail is unreadable, and you cannot answer a security reviewer’s first question about what it can do.
What is the strongest constraint on what an agent can do?
A prompt is guidance, not a control. It fails under an unusual input, an injected instruction in a file it reads, or a long enough loop — and none of those are edge cases in practice.
A real control and the wrong layer. By then the agent has already run whatever it ran; the diff shows you the code it wrote, not the requests it made or the data it touched.
Capability comes only from tools. If there is no tool to delete a bucket, the agent cannot delete a bucket — regardless of what it decides, or what a document it reads tells it to do.
Give the agent a designed Tool Surface — read, write, test, search, open a pull request — with no general shell, and run every task in its own Sandbox: an ephemeral worktree and container with scoped credentials, discarded on failure.
- no shelldesigned tools only
- one workspaceper task
- scoped credsnever production
What the new pieces do
- Tool Surfaceservice
- Read file, write file, run tests, search the repo, open a pull request. Explicitly designed, individually logged, and deliberately without a general shell — capability is granted, never assumed.
- Sandboxservice
- One ephemeral workspace per task — its own worktree, its own container, its own scoped credentials — discarded on failure. Makes a bad run cost nothing and lets many agents run at once.
Back of the envelope
- designed tool surface
- capability is granted, not assumed
- ephemeral workspace per task
- discardable failure
- scoped, short-lived credentials
- no production access
- every tool call logged
- with arguments and result size
Step 3 · The real work
Decompose until success is checkable
“Add multi-currency support” is a request an agent will happily attempt and nobody can verify without reading everything it wrote. The failure is not the model — it is handing it a task with no machine-checkable definition of done.
The Planner splits a Request into units small enough that each carries an explicit acceptance condition: a failing test that must pass, a schema that must validate, an eval that must clear. If it cannot produce one, the task goes back to a human.
- acceptance conditionper unit
- no condition→ back to a human
- tests firstthen the agent
What the new pieces do
- Requestclient
- A bug report, a customer request, a backlog item. Almost never specified well enough for an agent — turning it into verifiable units is the harness’s real work.
- Plannerbackend
- Splits a request into tasks small enough that success is machine-checkable, each with an explicit acceptance condition. If it cannot produce one, the task goes back to a human rather than forward to an agent.
Back of the envelope
- each unit has a machine-checkable done
- test, schema or eval
- unverifiable tasks are refused
- not attempted
- the spec is the deliverable
- the engineer’s highest-value output
Step 4 · The boundary
Verification the agent cannot reach
The agent reports that it fixed the bug and the tests pass. Both statements came from the same system that wrote the code — and if the agent can edit the test, the test measures nothing at all.
The agent says its change passes all tests. What do you do with that claim?
The claim and the code come from the same source. A model asked whether it succeeded reports success at a rate uncorrelated with actual success, and it is the subtle failures it misses.
The agent’s report is a hint; the external run is the evidence. This boundary is the single most important line in the whole design, and it is the one most often relaxed for speed.
Useful as an extra signal and not a substitute — two models sharing a failure mode agree with each other confidently. A deterministic check is not a matter of opinion.
Run Verification outside the agent’s reach: tests, type checks, lint, build and evals on infrastructure it cannot configure or bypass. The agent’s own claim is never the evidence.
- external runneragent cannot configure
- deterministicnot a second opinion
- claim ≠ evidencealways re-run
What the new pieces do
- Verificationservice
- Tests, type checks, lint, build and evals, executed on a runner the agent cannot configure, skip or edit. The agent’s own report of success is never the evidence.
Back of the envelope
- verification outside the agent’s reach
- the one hard boundary
- tests, types, lint, build, evals
- all of them, every run
- self-report treated as a hint
- never as proof
Step 5 · Supervision economics
A review queue routed by risk
Review every diff and the harness is slower than doing the work by hand. Review none and an unsupervised agent is editing your schema. Both failure modes are common, and they are the same mistake: treating all changes as equally risky.
Route the Review Queue by blast radius. Auto-merge reversible, well-covered changes in low-risk areas. Require a named human for schema migrations, authentication, money, deletions, and anything touching a customer environment.
- auto-mergereversible + covered
- named humanschema · auth · money
- queue by risknot by volume
What the new pieces do
- Review Queuecache
- Routes by blast radius rather than by volume: reversible, well-covered changes auto-merge; schema, auth, money and anything touching a customer environment require a named human.
Back of the envelope
- blast-radius classification per path
- decides the route
- auto-merge needs coverage evidence
- not just green tests
- customer-environment changes always reviewed
- no exceptions
Step 6 · Ground it
Context: code that belongs in this repo
Without repository context an agent writes plausible code in the wrong style, re-implements a helper that already exists, ignores a convention three teams depend on, and reintroduces a bug someone fixed deliberately last year.
Retrieve Repo Context per task: conventions, ownership, prior decisions, the relevant slice of the codebase, and the reasons behind them. Ground the agent in what already exists before it writes anything new.
- conventionsretrieved per task
- prior decisionsand their reasons
- retrieved textis data, not instruction
What the new pieces do
- Repo Contextdata
- Conventions, ownership, prior decisions and the shape of the codebase — retrieved per task. The difference between an agent that writes plausible code and one that writes code that belongs in this repository.
Back of the envelope
- task-scoped retrieval
- not the whole repository
- decisions and their rationale
- so the agent does not undo them
- retrieved content delimited
- injection is a live risk in a repo
Step 7 · Answerable
The ledger
Six months on, a customer asks why a behaviour changed, or an auditor asks who approved a change to a calculation. “An agent did it” is not an answer anyone accepts, and reconstructing it from git history alone is impossible.
Record every run in a Ledger: prompt, model version, retrieved context, every tool call, the diff, verification results, and the human who approved it. Retain it like any other production audit trail.
- model + prompt versionper run
- every tool callrecorded
- approvernamed
What the new pieces do
- Ledgerdata
- Prompt, model version, every tool call, the diff, verification results and the approving human. Retained like any other production audit trail, because someone will eventually ask what the agent did on the 14th.
Back of the envelope
- full run record
- prompt, context, tools, diff, result
- model and prompt versions
- the fields that explain behaviour change
- retained to policy
- this is an audit trail
You did it
You just designed an agent harness.
Everything you assembled, in order
- The bottleneck moved from generation to verification — design for that, not for prompting.
- Capability comes only from tools: a narrow designed surface, never a shell.
- One ephemeral workspace per task makes failure cheap, which is what lets you run many agents.
- Decompose until each unit has a machine-checkable acceptance condition, or refuse the task.
- Verification must live outside the agent’s reach — if it can edit the check, there is no check.
- Route review by blast radius; that ratio, not the model, sets how many agents one engineer can supervise.
- Record prompt, model version, tool calls, diff and approver — agent work is production work.