I Made Claude Build a Rust Coding Agent With a Team of AI Agents

July 17, 2026

There is a coding agent called Pi — a deliberately minimal, readable harness. Someone ported it to Python and called it tau, because tau = 2π ("twotimespi"). I wanted a Rust port, so I called it rho — ρ, the Greek r, for Rust. In physics ρ is density: the same agent, compiled.

Ascii
π → τ → ρ

The joke is the lineage. The interesting part is that I didn't write rho. A team of AI agents did — ~31k lines of reference Python turned into a byte-for-byte Rust port, milestone by milestone, with me acting mostly as a manager who says "no" a lot. This post is the system that made that work, because the naive version ("point an agent at the repo and wait") produces confident garbage.

Everything here is public: the rho repo, its dev-notes journal, and the pull requests where you can watch the gate catch bugs in real time.

The org chart

The mistake people make with multi-agent setups is treating agents as interchangeable workers on a shared task. They aren't. Context is the scarce resource, and a fresh agent with a tight brief beats a tired agent with a huge one. So rho has an actual hierarchy:

Ascii
COORDINATOR (persistent, expensive frontier model) • runs the design interview, writes the plan + milestone ladder • dispatches ONE builder agent per milestone • runs the merge gate on every PR • owns the memory files, the task board, and an hourly babysit cron • never writes bulk implementation code │ ├── MILESTONE AGENT M1 (cheaper Opus-class model, own branch, own PR) ├── MILESTONE AGENT M2 └── MILESTONE AGENT Mn └── CLUSTER SUBAGENTS (only for ~6k+ LOC milestones) each in an isolated git worktree, merged back

The coordinator is an expensive frontier model whose entire job is judgment: design, dispatch, review, remember. It writes essentially no implementation code, because the moment it starts typing out a thousand lines of serde, it's spending its most valuable capacity on the cheapest kind of work.

Milestone agents are strong but cheaper models. Each gets one self-contained brief — scope, the exact reference files, a mechanical definition of done, a git workflow — works on its own branch, opens a PR, and resolves the review-bot comments itself.

When a milestone is too big for one context (rho's session-and-CLI milestone was ~7k LOC), the milestone agent becomes a temporary coordinator: it splits the work into independent modules, gives each its own git worktree on its own branch, and merges them back. That third tier is born and dies inside a single milestone.

Grill me before you write anything

The first thing I did was refuse to let it code. I ran a design interview — the coordinator's job was to grill me until every branch of the decision tree was resolved and written into a table of locked decisions: full byte-level read+write compatibility with tau's session format (not "similar" — a session started in tau has to resume in rho and vice versa), all six providers in one milestone, ratatui for the TUI, WASM extensions last, golden fixtures as the source of truth.

This is the highest-leverage hour in the whole project. A builder agent works from a brief, and a brief built on an unresolved decision produces confidently-wrong code that the gate then has to catch. Grilling deletes whole categories of downstream rework. It's the one place the expensive model earns its cost purely by arguing with me.

Fixtures are the truth, and the code is always wrong

Before porting a single line of behavior, the first milestone did something counterintuitive: it extracted a corpus of golden fixtures from tau's own serialization code, at a pinned git revision. Not hand-written expected JSON — the extraction scripts import tau and call the exact functions tau uses in production, then commit whatever bytes come out.

The policy attached to those fixtures is one sentence, and it decides every argument:

If a golden test diffs, the code is wrong — never the fixture.

A fixture a human typed encodes a human's belief about the reference. A fixture the reference printed is ground truth. This flips the usual debate: nobody argues about what "correct" means, because correct is a file on disk. On top of that sits a bidirectional crosscheck that runs the same session through both implementations with the clocks and id generators frozen identically: the session files come out raw byte-identical — the same bytes on disk, committed as a literal interchange artifact — and rho's file then resumes inside tau and replays to the same state.

That matters for one specific reason: it frees the review to hunt for what fixtures can't catch.

The merge gate, and the five bugs green CI never saw

Every PR passes the same gate before a rebase-merge: the coordinator re-runs the tests, lint, and crosscheck itself (not "CI is green on the PR" — actually re-execute); a fresh adversarial reviewer goes line-by-line against the reference; two public review bots — OpenAI's Codex reviewer and CodeRabbit — get every comment resolved; then a fix round; then merge.

The adversarial reviewer gets a very specific brief, and it's the part I'd copy into any serious agent project:

The test suite is already green. Do not re-verify the happy path. Find what the passing tests cannot show — error paths, resource lifecycles (drops, cancellations, file descriptors, child processes), timing and concurrency semantics, fidelity to the reference on inputs the fixtures don't cover. When you suspect a divergence, prove it: run both implementations and diff.

Here's the empirical case for why this exists. In every milestone, the full suite was green — fixtures matched, ported tests passed — and the review layer still found a real, shipping-blocking bug:

MilestoneThe bug a green suite missed
M1A persisted usage: null made the entire session file refuse to load — an untagged enum tried to deserialize null into a struct and failed the whole line. A real session that ever saved a null would be unopenable.
M2The harness was permanently bricked if a consumer dropped the event stream mid-run: Rust's async streams run no finally on drop, so a "running" flag never reset and every future turn was rejected.
M3A total-request timeout would kill any LLM stream slower than the timeout. The reference applies it per-read; the naive Rust port made it a total deadline — fine in tests, fatal on a real 90-second generation.
M4aThe bash tool hung forever whenever a command spawned a backgrounded child — the child held a write pipe open, so the reader never saw EOF. Every test that did it hung.
M4bSilent data loss on a persist failure: the write path swallowed a storage error and returned a stale count, re-appending an already-durable message instead of aborting the turn.

Notice what none of these are: none is a serialization bug. Fixtures already guard those. Every single one is an error path, lifecycle, or timing bug — exactly the class a green byte-for-byte suite is blind to, and exactly what the adversarial brief points at. Each one is written up in the dev-notes, one phase-N.md per milestone.

An agent died on a rate limit. A cron brought it back.

Autonomous agents fail in boring, mechanical ways, and the ops layer is what keeps a fleet from quietly dying. The coordinator runs an hourly babysit cron: it reads the task board, pings the in-flight agents, and distinguishes an idle heartbeat (working, just quiet) from a real stall (dead or blocked).

The best moment of the whole build was when this paid off unplanned. A milestone agent hit a rate limit and died on spawn — the kind of failure that would normally strand the run until I noticed. But the babysit cron was already scheduled, and its next firing happened to land about a minute after the rate limit reset. It saw the dead agent, re-dispatched from the brief, and the work resumed. Zero loss, no human in the loop. The cron isn't belt-and-suspenders — it is the recovery mechanism.

There's a subtler ops detail too: the coordinator and the agents message asynchronously, so a "still working?" and a "done, PR is up" constantly cross on the wire. The fix is to treat messages as idempotent status and reconcile against the actual git and PR state, never against message order.

Knowledge has to outlive the agent

Every agent in this system is disposable — it does a milestone and it's gone. So the facts it learned have to be written down where the next one will read them. Two mechanisms carry that:

  • Memory files — small notes indexed by a MEMORY.md, holding the locked decisions and the hard-won facts, loaded on demand across sessions.
  • The teaching journal — every milestone writes a dev-notes/phase-N.md explaining which Rust idiom replaced which Python pattern and why, plus any reference behavior later milestones must respect. The agent that discovered that the reference's exclude_none doesn't recurse into free-form JSON is long gone; the fact survives it.

The honest costs

This is not free, and it's not always worth it.

The gate is the expensive part and the valuable part. Running a fresh adversarial reviewer on every PR, re-executing the whole test matrix as coordinator, and doing a fix round costs real tokens and real wall-clock — the review often costs more than the implementation. It's only worth it because rho has a hard correctness bar and a reference to diff against. Take away the oracle — the reference implementation, the golden fixtures — and the whole thing collapses: the adversarial review has nothing objective to anchor to and degrades into taste, and you're better off just dispatching one agent and reading its PR.

So my honest boundary: use this when you're porting, rewriting, or reimplementing something against a reference, correctness matters more than speed, and the scope genuinely exceeds one context. Skip it for prototypes, glue code, or anything where "looks right and passes tests" is actually good enough. The ceremony is a tax you only want to pay when the gate can catch something.

It's the same lesson I keep coming back to: the harness is the product. Here the harness is organizational — a coordinator, a gate, a cron, and a rule that fixtures are truth. The models are interchangeable. The system around them is what shipped a byte-compatible Rust agent while I mostly watched.


I packaged this whole method as a reusable agent skill — milestone-orchestration, with the milestone brief, the merge-gate checklist, the adversarial-review prompt, and the babysit-cron recipe. The rho repo is the worked example.