Convergence
Spawning agents is easy. Finishing is not. I spent two months building the missing piece and then found git had most of it.
Motivation
Running one coding agent is a conversation. Running twenty is a management problem, and it is your management problem, because nothing in the tooling is holding the thing all twenty are supposed to be building toward. What you get instead is a grid of terminal panes, each one sensible on its own, and a person moving between them carrying the only copy of the plan.
What I wrote down in June was that a terminal multiplexer with no visualisation really bothers me, and then a list of what was missing: convergence logic, stopping criteria, branch visibility, merge review, summarising where a goal has got to, controlled resource allocation, better branch management, better convergence surfaces, explicit stopping conditions. Every one of those is something a person currently does by remembering.
The coordination cost
The reason twenty agents is harder than five is not that twenty is more work. It is that the work you are doing is not the agents' work, it is the coordination between them, and coordination between individuals grows with the square of how many there are. Five things that must agree with each other have ten pairs. Twenty have a hundred and ninety. Your working memory does not grow to match, and in practice mine capped out at about three agents, because working memory was the thing doing the coordinating.
That framing is what suggests the fix, because the square only appears if you insist on coordinating at the level of what each agent is individually doing. If the thing being coordinated is not the agents but the state they are collectively trying to reach, then adding another agent adds nothing at all to what you have to hold. You still hold one destination. The agents are below the level you are managing at.
This is the same move statistical mechanics makes on a gas, which is why the vocabulary stuck: you do not track the particles, you track temperature and pressure, and the particles are free to be as numerous as they like.
The object model
The harness I built in July has five objects, and the interesting property is which of them get names and which do not. This is the table out of the spec:
| Object | What it is | Lifetime |
|---|---|---|
| Basin | A declared macrostate: target state, acceptance criteria, termination condition, budget | Persistent, in the ledger |
| Branch | One mind's attempt: a git branch, a worktree, one agent run | Ephemeral, worktree pruned after merge; events persist |
| Lock | The merge queue: candidate branches awaiting sequential review | The only sequential point |
| Frontier | Harvested residue: open questions, unmerged good pieces | Persistent, feeds the next declaration |
| Body | The ledger, the git history, the frontier | Never erased |
Read the lifetime column on its own. The agent doing the work sits in the row marked ephemeral, while the destination and the record are the two that never get erased. The minds themselves are anonymous and memoryless, with no mechanism for one to know about another. In the spec I described them as water, which sounds decorative and is doing real work: the point of water is that you never care which particular water.
Read that list again and notice the inversion. The spec states it against a specific comparison, which is Gas Town: Gas Town manages labor, convergence manages a watershed. Gas Town is an agent orchestrator that gives its workers names, identities, memories and roles, and lets tasks flow past them, which is a coherent design and the one most of these systems land on, because it maps onto how a team of people works and is therefore easy to reason about.
This does the opposite. The destinations are what persist and accumulate; the workers are disposable and interchangeable. Neither arrangement is more correct in general. The choice depends on whether the expensive thing to preserve is the worker's accumulated understanding or the target's accumulated progress, and having watched twenty agents produce nineteen branches I did not want, I wanted the target preserved. That is also what makes it affordable to run twenty and throw away nineteen.
Mergeable results
Parallel work does not require parallelisable problems. It requires mergeable results.
This is worth sitting with, because the usual reason people give for not running many agents is that their problem does not decompose. Most interesting problems do not decompose. But you do not need the problem to split into independent halves. You need to be able to run several whole attempts and then combine what each got right, which is a different and much weaker requirement.
Everything in the system can run in parallel except the merge. The merge is sequential, and it is gated by a human, and the entire ergonomics of the thing point at that one valve.
A real example from the first week is better than the principle. I gave three sessions the same task with deliberately different approaches: one minimal, one library-first, one built around a state machine. Each ran in its own checkout of the repository, a separate folder on disk holding the same project at a different version, so all three could edit the same files at the same time without ever seeing each other. Each had its own tests. The library-first branch won and was merged. Then the malformed-input tests from one of the losing branches, which were better than the winner's, were merged on top of it.
That detail is the one I would keep if I could only keep one. Selection did not happen at the level of whole candidates. The loser contributed. Once you can merge below the level of the whole attempt, running many attempts stops being wasteful, because a branch you throw away can still leave its best part behind.
Design principles
Six rules held the implementation down, and five of them are about refusing to build things.
The ledger is the only truth. It is one file per project that is only ever added to, never edited and never deleted from, so every line in it is a thing that happened, in the order it happened. There is no stored answer to "what is the state right now". You get the state by starting at the top and replaying every line in order, which means the state cannot disagree with the history, because it is made out of the history each time you ask.
That buys the property the whole system rests on: no second store, no cache, no database that might drift out of step. It also makes the failure case boring. A file that is only ever appended to can be caught mid-write when a process dies, so the last line is half there, and the reader skips it rather than falling over. Nothing earlier is affected, because nothing earlier was ever going to be rewritten.
The whole event vocabulary fits on a screen, which is the test that the design stayed small:
basin.declared {basin, title, macrostate, criteria[], termination, budget}
basin.opened {basin, wave}
branch.spawned {basin, branch, approach, gitBranch, worktree, model}
branch.searching {basin, branch}
branch.candidate {basin, branch, summary, evidence[], commits, usage}
branch.failed {basin, branch, reason, usage?}
branch.grafted {basin, branch, mergeCommit}
branch.pruned {basin, branch, reason}
basin.converged {basin, note}
basin.killed {basin, reason}
basin.harvested {basin, frontier[], residue[]}
frontier.added {question, from}
The commits field on branch.candidate is doing more work than it looks like. A mind claiming
it is finished with nothing committed gets rejected by the runner rather than believed, and that
is the general shape of every guarantee in the system. The claim is checked against the git
history rather than taken from the agent's own account of itself.
The interface is a pure projection. It renders derived state, never writes, and can print one frame and exit, which makes it scriptable and makes it safe to run beside a live engine.
Git is the artifact store. A branch's work product is its git branch, and merging is
git merge --squash. Diffing, history, and conflict detection already exist and are better than
anything I would write.
Minds are subprocesses rather than a framework. One non-interactive agent run per worktree, with the command injectable, so the engine underneath is swappable and the tests are hermetic.
Some mistakes were made impossible to write down, wherever doing so was cheap. You cannot declare a destination without also saying what would make it stop, because the function that creates one refuses to return without that field. You cannot merge a branch that has not yet produced a candidate, because the code that advances the state refuses that transition. Neither rule is written in a document anyone has to remember. This is the same instinct as designing a language that forbids things: a rule you can push down into the structure is a rule nobody can forget.
And no daemons. Nothing runs when nothing is running.
The harness built a working demo command-line tool with six minds, and then it was used to build itself.
Build or adopt
By August the interesting question had stopped being how to build this and become which parts of it were mine to build at all.
The answer to convergence, in the end, is the branch-and-pull-request flow. It already gives you branch visibility, merge review, a sequential valve, and a permanent record of what changed and why. Holding a destination and its history is an issue tracker plus a prompt. Spinning up an isolated working environment for an agent is a pattern I already run.
Subtract all three and what is left is short: a live view of what is happening, a day-by-day timeline across parallel efforts, and memory that belongs to a pursuit rather than to a session.
The general form of that move is the transferable part of this whole thing. When you are building a system, the first useful question is which of its components are commodity infrastructure wearing an unfamiliar name. The branch isolation, the merging, the history, and the conflict detection I had built in July were all a worse version of git, and those were most of the code. The theory was not wrong and the objects were not wrong. Most of the implementation was a rediscovery.
The unit of work
The last piece arrived by watching rather than designing. Looking at how I actually work, the pattern was already there and had been for months: one long-lived session per goal, alive across weeks, carrying its own context, ending when the goal is met or abandoned.
That is the unit. Not a task, not an agent, not a run. A persistent session that is the goal, with a body and a stopping condition, and the graph of those sessions is what all of this was reaching for.
I have a sentence I keep trying to make precise, that intelligence is divergence multiplied by convergence, and I am honest that at the moment it is philosophy rather than mechanism, which annoys me. The half I can defend mechanically is the second one. Divergence is easy and getting cheaper every month. Convergence is where the work is, and the systems that will matter are the ones that make many attempts cheap to combine rather than the ones that make many attempts cheap to launch.