The harness
An org of agents that builds the product, and the rules that stop it becoming a black box.
- 178merged PRs
- 9months
- 1human, with a day job
- 7roles + a dispatcher
Who checks code an agent wrote?
- idea studio
- ideation analyst
- designer→ mockups
- implementer spawns ↳ verifier ∥ breaker, on a thin brief
- acceptance testerblind to the repo
- releasestaging → prod
-
01 of 05
An idea, typed at a bus stop.
Let people undo a misfile. One line, from my phone. That is the first of exactly three decisions that are mine: which idea gets built, design sign-off on anything a user will see, and the production deploy. Everything else reports. Nothing else blocks.
-
02 of 05
Two loops, two clocks.
The product loop turns the line into a signed-off design over days: an idea studio, an analyst, a designer. The delivery loop turns that brief into merged code over hours. Neither loop can call the other, and no role inside one can call a role inside the other.
-
03 of 05
The verifier is handed the implementer's reasoning. Does it catch more bugs, or fewer?
- More: it knows where to look
- Fewer: it inherits the blind spots
- About the same
Fewer. Hand a reviewer the builder's reasoning and you get the builder's conclusion back, with extra steps. So the implementer spawns a verifier and a breaker on a thin brief: a path and a diff range, nothing else. The acceptance tester is blind to the repo altogether. It can only use the thing.
-
04 of 05
A boring hub, not a clever watchdog.
No agent calls another. A dispatcher wakes on a timer, reads state, applies a fixed table, spawns at most one thing, and exits. Intelligence lives in the nodes; the router stays predictable, which is what keeps the whole thing explainable.
-
05 of 05
Everything they know is a file in git.
Findings travel through files and the queue, never from one agent to another. One writer per file is what lets them run in parallel without colliding, which was the first thing that broke.
The hard part wasn't getting agents to write code. It was the rules that stop them colliding, marking their own homework, or talking their way past a limit.
Three things that broke. What would you have done?
What brokeSeveral agents working in parallel livelocked. Nothing was broken, and nothing could finish.
What would you have done?Why, the fix, and what it changed
Every agent hand-edited the same three tracking documents on every shipped item. A documentation conflict forced a rebase of a branch carrying code, which re-ran the whole CI matrix, during which the next agent's pull request conflicted again.
One writer per file. New facts are new files: one per run record, bug, changelog entry, verdict. Indexes compiled by a post-merge action, never hand-edited.
Parallel agents touch disjoint files by construction, and status commits skip CI entirely.
The full storyWhat brokeThe implementer reviewing its own work: a blind spot with a name.
What would you have done?Why, the fix, and what it changed
Hand a verifier the builder's reasoning and you get the builder's conclusion back, with extra steps.
A thin spawn: the work-item path and a diff range, nothing else. A verifier re-derives what "done" means; a breaker has to produce runnable evidence, not opinions.
Nothing marks its own homework. The acceptance tester can't even read the code, so it can't be reassured by it.
The full storyWhat broke"Be careful with disk space" in a prompt did nothing.
What would you have done?Why, the fix, and what it changed
It's a suggestion, and agents are extremely good at reasoning their way past suggestions.
Heavy operations exist only behind wrapper scripts that hold real file locks and refuse to run below a disk-headroom threshold. A pre-execution hook denies the raw commands outright.
One verification at a time per machine, one deploy at a time. The wrapper isn't the polite path. It's the only path.
The design is written up in full here and in the build log; the repo it runs on is closed.
Alter is eleven services, ~289K lines of TypeScript, 178 merged pull requests across fourteen phases, shipped in nine months by one person with a full-time job.
I did not type most of it. This is the system that did, and, more to the point, the constraints that make its output trustworthy. The agents are the easy part. Everything interesting here is the environment around them.
Seven roles and a dispatcher, and three things that never became agents
Strip the names off and it’s a value stream (idea → decision → design → build → verify → ship → learn) plus a reporting plane and a scheduler. Agents are just staffing for stages. The diagram above is the whole org.
Seven roles and a dispatcher. Three things got dissolved during the design rather than built: the idea-capture “agent” (it’s a skill), the reporting “agent” (it’s a job that renders from the ledger), and the watchdog (it’s two rows in the dispatcher’s decision table). Not everything that feels like an agent should be one.
Four rules make it work; the state model mattered most
1. Route with a boring hub, not a clever watchdog. One dispatcher wakes on a timer, reads state, applies a fixed decision table, spawns at most one thing, logs, exits. Intelligence lives inside the nodes; the router stays predictable. Stall detection is a timestamp comparison, not a judgment call.
2. No lateral calls. The dispatcher is the only top-level spawner. Departments do their job, write their output, leave a ledger entry, and exit. The single exception is the implementer spawning its own verifier and breaker: a work graph inside a node. Findings travel through files and the queue, never through one agent calling another. Lateral calls are precisely how multi-agent systems become unexplainable.
3. Three human gates, and only three. Which idea gets built · design sign-off on anything user-facing · the production deploy. Everything else reports but never blocks. Speed is protected; irreversible things are gated.
4. State is the hard problem, not agents. See below. This is the one I got wrong first and it cost the most.
One writer per file ended the livelock
The first version of the loop had every agent hand-edit the same three tracking documents on every shipped item. With one agent, fine. With several in parallel it livelocked: a documentation conflict forces a rebase of a branch carrying code, which re-runs the whole CI matrix, during which the next agent’s pull request conflicts again. Nothing was broken. Nothing could finish either.
The fix was three rules, applied to every file an agent writes:
- Single writer. Every file has exactly one writer at a time. A work-item brief is written only by the agent running that item; the queue is written only by the dispatcher. A violation is a design bug, not an accident.
- New facts are new files. Anything with multiple producers (run records, bug reports, changelog entries, verification verdicts) is one file per fact in a directory. Distinct files never conflict in git. Readers glob.
- Indexes are compiled, not edited. Per-item status lives in the owning brief’s frontmatter. A dependency-free compiler patches the aggregate views, and runs in exactly one place: a post-merge action. It’s idempotent, minimal-diff, and will never un-ship a line.
Parallel agents now touch disjoint files by construction. And because status updates became documentation-only commits, they’re filtered out of CI entirely: the same problem solved twice over.
The general lesson: git is already a blackboard with a free audit log. Most multi-agent state problems are better solved by choosing a file layout that can’t conflict than by adding coordination.
A thin brief stops the builder marking its own homework
An implementer reviewing its own work is a blind spot with a name. So step 7.5 of every iteration fans out to two independent agents:
- a verifier, which re-derives what “done” means from the work-item and checks the diff against it;
- a breaker, which attacks the changed code and has to produce runnable evidence, not opinions.
The critical detail is how they’re launched: a thin spawn, the work-item path and a diff range, and nothing else. No summary of what was built, no explanation of why a decision was made, no context from the implementer at all. Hand a verifier the builder’s reasoning and you get the builder’s conclusion back with extra steps.
The same principle scales up. The acceptance tester runs against staging, from written use cases only, structurally blind to the repository. It cannot read the code, so it cannot be reassured by it.
Locks hold where instructions do not
A prompt saying “be careful with disk space” is a suggestion. Agents are extremely good at reasoning their way past suggestions.
So the heavy operations (full verification builds, deploys, migrations) exist only behind wrapper scripts that hold real file locks and refuse to run below a disk-headroom threshold. One verification at a time per machine, one deploy at a time, migration-carrying deploys exclusive. And a pre-execution hook denies the raw commands outright, so the wrapper isn’t the polite path, it’s the only path.
Same instinct as the reversibility rule in the product: don’t ask a language model to respect a limit you could enforce mechanically.
Releases are typed, and a hotfix image takes five minutes, not ninety
Release cuts are typed. A patch is a bug-fix batch, a minor is a feature batch that passed acceptance testing, and anything touching billing, auth or a non-additive migration is owner-gated. Every release writes one changelog file with a developer-facing section and a plain-English one; the plain-English half is compiled into the product’s own “what’s new” page. A release should be a sentence you can say out loud.
The build pipeline was itself a project. A one-service hotfix used to take 60–90 minutes to reach a pullable image; it now takes 2–5, with a full ten-service rebuild around seven. Two things did it: building each architecture natively on its own machine rather than emulating one, and (the part I’d recommend to anyone) copying unchanged services forward as provably identical images instead of rebuilding them and hoping. “Rebuilt and assumed identical” is a category of bug you can simply delete.
The interesting engineering is never the prompts
The interesting engineering in agent systems is almost never the prompts.
It’s the state model, so parallel work can’t collide. It’s independent verification with a deliberately narrow input, so nothing marks its own homework. It’s mechanical limits, because instructions are negotiable and locks aren’t. It’s typed releases, so shipping is boring. And it’s knowing which three decisions actually need a human, so the other few hundred don’t wait on one.
Each of these has an essay of its own, in more depth than this page allows:
- My agents livelocked on a paragraph of prose: the state model, and why the fix was a file layout rather than a protocol.
- Nothing marks its own homework: independent verification, and the trick of what you refuse to tell the reviewer.
- Building a feature bank: how an idea becomes a decision, a design, and a shipped pull request.
- Running Claude Code with worktrees: the field guide, and the five traps that cost me real time.