← All writing

Externalized coordination: the missing primitive for agent swarms

· 8 min read

Running a single agent is a chat problem. Running many is a coordination problem.

Continuing from the software factories post, I want to sit with coordination, because it is the layer I am least familiar with, and also the one I am most sure our current tools cannot deliver. Of the five, this is the one where I keep landing on “I don’t have this yet” instead of “here’s how I do it.”

So this one is more instinct than blueprint. Fair warning.

Single thread feels solved

A single agent in a single chat coordinates with you. You are the driver and you hold the state in your head: where it is, what it just did, whether you believe it, what’s next. Add a second agent and you can still hold it, barely. Add ten and you’re usually the thing that breaks first, long before the agents do; we just cannot hold ten dependency graphs at once, or remember which of fourteen branches is waiting on a human decision and which is just stuck, or skim that many PRs and still honestly claim you reviewed them. When the swarm falls over it’s tempting to blame the model, but for the most part the model is fine. The only coordination primitive in the room was a human, and we don’t parallelize.

By now, runtime and orchestration are mostly handled; a lot of teams know and use worktrees and sandboxes, triggers from tickets and webhooks and CI. So starting work and providing reliable context is no longer the bottleneck, and neither is fanning out across a hundred branches. The pain is now in the middle, the state that lives between starting and finishing, across more transitions than any one person can watch. Who owns this work right now? What evidence is required to leave the stage it’s in? Can another agent safely pick it up? That’s the part nothing I use today does well.

The problem with issue trackers

GitHub and Linear are good tools, and I’m not dunking on them. They were built with a different coordinator in mind: a person, reading, deciding, at human speed and human volume. A PR assumes a reviewer who reads the diff and forms a judgment; a comment thread assumes a human mentally diffing the latest message against the last one they read. These are notification systems wrapped around prose, and prose is sadly the medium that lets an agent narrate progress instead of proving it, the failure mode I keep hitting at every layer and trying to avoid.

When ten agents work in parallel that prose multiplies but the signal doesn’t: a hundred confident comments, assignees that mean nothing because an agent “assigned” itself by writing a sentence, a label that says in review with no machine-checkable claim about what “review” even required. State implied by activity and never asserted as fact. We can squint through that. A coordinator making hundreds of small accept/reject decisions per hour should not squint; it needs something it can ask, mechanically, and get a yes or no for. That question is the missing primitive.

The leasing problem

Let’s start with the most concrete failure: two agents reach for the same file.

In human teams this is rare and “easy” to solve: someone says “I’m in the billing module today,” during standup and the other person waits or branches. Agent swarms have none of that. Two agents from two tickets both decide the cleanest fix lives in the same module, but neither knows the other exists; each produces a locally correct change that passes its own tests, and each opens a PR. Now you have a merge conflict no agent caused on purpose and none is responsible for resolving, plus a human reconstructing two intents from two transcripts to decide which shape wins, with little to no input into either agent’s definition of the problem.

We have a boring fix for this, a lease. Before an agent works on something — a module, a set of files, a stage of a ticket — it acquires a lease with a holder, a scope, and an expiry, so a crashed agent doesn’t deadlock the swarm forever. Another agent that wants the same region asks, gets told it’s held, and waits, picks different work, or escalates. It’s a mutex with a name and a timeout, nothing clever. But it converts “two agents silently collided” into “one agent was told no,” and that’s the whole point.

Externalized claims.

”Done” is the wrong question

The lease handles who touches what and that is fine. Stage state handles whether work may advance, and I think this is where the deeper rot is.

As we’ve discussed before, ask an agent if it’s done and it will always say yes. It has a confident answer to all of your questions and no way to know if it’s right. So the question that actually coordinates a swarm cannot be “am I done.” It has to be: has the external system accepted the evidence required to leave this stage?

That reframes the whole thing as transitions instead of status. Each stage of the lifecycle — shaping, design, implementation, verification, review — should get an exit condition that’s evidence the system can check, not simply a sentence the agent can write. Implementation reaches review not because an agent declared it ready but because a verification receipt exists: these tests ran, here, against this, with this result. The process layer defines the path; coordination is the part of the system deciding whether a piece of work may take the next step on it, and a clean handoff note — the kind the memory layer preserves — lets the next agent pick up where this one stopped. Same move IaC made: we stopped trusting “I deployed it” once we could check the actual state.

Local-correct, global-wrong

Conflict and false completion both have the decency to look like problems. The case that actually gives me pause is the agent that is genuinely right about its own task and quietly wrong about the system.

Imagine this: an agent fixes a bug perfectly. Clean diff, real tests, honest evidence. It also quietly loosens a shared validation rule because that was the cleanest path to its fix — a constraint three other parts of the system depend on, that nobody asked it to touch. No test it ran would catch it, because the blast radius lives outside its task. Lou Bichard’s talk has a name for this whole shape: coordination as the missing primitive for agent swarms, the part left over once runtime, orchestration, and triggers are handled. The information needed to catch that change doesn’t live in the diff. It lives in the relationships between work items: what else is in flight, what depends on this rule, which human decision is the current constraint. A coordination ledger is the only place that cross-cutting state could plausibly live, because it’s the only thing watching all the work at once.

But this is a hard problem and I don’t have a clean answer here. My instinct is that some transitions can never be machine-accepted; they route to a human-judgment gate by design, because “is this change globally safe” is exactly the kind of question we should be reserving judgment for, so the work stops there on purpose instead of slipping past because everyone, human and machine, was looking at a green diff.

What I’m actually building toward

Rondo is where I’ve started poking at this, and I’ll be honest about its altitude: it’s still too early, and it points more at execution observability — what’s running, what’s stuck, what’s burning tokens — than at a full coordination ledger. But you have to see the line before you can install gates on it. So I treat Rondo as the instrument, and the ledger as the thing I can’t quite see the shape of yet.

I am less attached to the shape it will take than to the responsibility. Maybe it’s a state machine, maybe a CLI gateway agents call to claim work and request transitions, maybe it rides on top of GitHub rather than replacing it. What it has to do is fixed even if the form isn’t: hold leases, define stages, accept or reject evidence, record handoff notes, and stop at the gates where a human has to decide. It’s a core part of harness engineering, just more environment around the model, the part that decides what the model is allowed to do, not how well it does it.

It should be boring, the way a deployment pipeline is boring: predictable enough that I’d let it run unattended on a Friday afternoon and not spend the weekend wondering what it touched.

The model writes the code. It always could. The coordinator decides whether that code is allowed to become part of the system, and at a big enough scale, that decision can’t live in a person’s head or a notification feed. It has to live somewhere the agents can read, write, and be told “no” by. That somewhere is the layer I think is still missing.