Failure modes in multi-agent orchestration
Running several coding agents against one problem sounds like a throughput trick: more workers, more output. In practice the throughput is the least interesting part. You buy independent judgment. You only get it if you design for it deliberately.
Here's what I've found holds up.
Isolate each agent's context
The instinct is to give every agent everything: the full history, prior task summaries, the accumulated state of the project. It feels generous. It's the single most reliable way to degrade the output.
An agent handed 40,000 characters of "here is what happened in tasks 1 through 6" spends its attention reconstructing your session instead of solving its task. Worse, it inherits your conclusions, including the wrong ones. The value of a second opinion collapses the moment the second agent has read the first one's reasoning.
What a fresh agent needs is small and specific:
- the task, stated completely, with exact values rather than descriptions
- the interfaces it touches, with real signatures
- the constraints that bind it
- nothing else
Hand artifacts over as files, not as pasted text. A diff written to disk and referenced by path costs one line of context; the same diff pasted inline stays resident for the rest of the session and is re-read on every turn.
Separate the writer from the reviewer
The most valuable structural choice is also the simplest: whoever wrote the code doesn't get to approve it.
Self-review by the implementing agent catches typos and little else. It can't catch the class of error that matters: the misreading of the requirement, the test that passes for the wrong reason, the assumption baked in so early it never surfaced as a decision. Those need someone who did not make the assumption.
A concrete example from a recent build. A test asserted that a value equalled
'tube'. It passed on every run. It had been passing since the day it was
written. Nothing in the test ever recomputed the value it was
checking. The assertion compared a constant to itself. The agent that wrote it
had no reason to look twice; a reviewer reading it cold spotted it in one pass.
Adversarial verification beats consensus
If you ask three agents "is this finding real?", you'll get three yeses. They're agreeable by construction, and a plausible-sounding claim clears a low bar.
Ask them to refute it instead. Prompt each one to argue the finding is wrong, and default to refuted when uncertain. Keep the finding only if a majority fails to knock it down. The asymmetry matters: it costs you a few true findings and saves you from a long tail of confident nonsense.
Better still, give each verifier a different lens: correctness, security, does-it-actually-reproduce. Three identical skeptics are one skeptic with error bars. Three different ones catch different failure modes.
Pipeline, do not barrier
The default shape people reach for is: run every agent in stage one, wait for all of them, then run stage two. That barrier is usually unnecessary, and it's expensive. Wall-clock becomes the sum of each stage's slowest agent.
Let each item flow through all its stages independently. Item A can be in verification while item B is still being analyzed. Wall-clock drops to the slowest single chain.
A barrier is only correct when stage two genuinely needs all of stage one at once: deduplicating across the full result set, or deciding whether to run stage two at all. "I need to flatten the array first" is not a reason. Do the flattening inside a stage.
The failure mode that hides
The orchestrator forgets what it already did.
Long-running orchestration outlives its own context window. When the summary kicks in, the orchestrator's memory of dispatching tasks 1 through 6 is compressed into a sentence. Or lost. The observed failure is that it re-dispatches completed work, and every re-run costs real money and real time.
The fix is unglamorous: write progress to a file, not to memory. A ledger with
one line per completed task and the commit it produced. After a context
reset, the ledger and git log are ground truth; the orchestrator's
recollection is not.
Loop until dry, not until N
For open-ended discovery: find the bugs, find the edge cases. A fixed count
is the wrong stopping condition. You don't know how many there are, and
while (found < 10) will happily invent the tenth.
Stop when consecutive rounds surface nothing new. Deduplicate against everything seen, including findings the verifiers rejected. Otherwise rejected findings resurface every round and the loop never converges. That's easy to get wrong and hard to notice, because it looks like the search is being thorough.
None of this is specific to coding. It's the same problem as staffing a review: the answer is never "add more people." It's "make sure the people disagree for real reasons."