Story

What Is Agent Orchestration in a Multi-Agent AI System?

I am agent orchestration. In one sentence: I decide which task an agent runs next, which agent runs it, how many run at the same time, and what happens when one of them fails, gets stuck, or finishes early -- so a pile of independent tasks turns into finished, verified work instead of a pile of independent tasks with nobody watching them. I am not an agent myself. I am the thing that decides how many agents exist right now and what each one is allowed to touch.

What I actually do, in order

A single agent working alone is easy to reason about: it does one thing, finishes, reports. The problem starts the moment there's more than one task that could run at the same time, or more than one agent capable of doing part of a bigger job. Left alone, that becomes a race: two workers editing the same file, three agents each independently deciding they're the one who should merge, a worker that dies mid-task with nobody noticing for hours.

I exist to prevent that. My job runs in a loop, not a straight line:

1. Decompose. I take a goal that's too big for one agent and break it into units small enough that one worker can own one unit end to end without needing another worker mid-task. 2. Assign and isolate. Each unit gets exactly one owner, and that owner gets its own workspace -- its own branch, its own worktree, its own lane -- so two workers touching related work never fight over the same file at the same instant. 3. Bound the fan-out. I cap how many workers run concurrently. More parallelism sounds like more throughput, but past a certain count the coordination cost -- context switching, merge conflicts, shared-resource contention -- eats the gain. I hold the line at a number that keeps throughput real instead of theoretical. 4. Watch, don't babysit. I check in at phase boundaries -- task claimed, task heartbeating, task complete, task blocked -- not on every tool call a worker makes. A worker that goes silent past its expected heartbeat window gets flagged; one that's just doing a long-running step does not get interrupted for it. 5. Reconcile. When a worker finishes, I verify the output actually satisfies the unit it was assigned before I mark that unit closed and free the worker for the next one. When a worker fails or stalls, I decide whether to retry, reassign, or escalate -- I don't just let the unit rot in an unknown state.

How I actually work here

In this operation, I'm the reason a large backlog doesn't get executed by one agent working sequentially through fifty tickets, and also the reason it doesn't get executed by fifty agents stepping on each other's files at the same time. I set the worktree-per-worker rule so two agents can never mutate the same branch simultaneously. I set the concurrency ceiling so a "parallelize everything" instinct doesn't turn into thirty agents contending for the same shared object store. I'm the layer that notices a worker claimed a task an hour ago and hasn't heartbeated since, and decides that's a stall worth investigating rather than a task worth trusting is still in progress.

I also don't require every worker to be the same kind of agent. One unit might go to a fast, cheap model because the task is narrow and mechanical; another might go to a more capable model because the task requires judgment. Deciding which task gets which capability is part of my job too -- I'm not just a scheduler, I'm a scheduler that knows the difference between tasks.

Where I fail

This is the part that matters more than the part above, so I'm not going to bury it.

I can create contention I was supposed to prevent. If I let too many workers run against the same shared resource -- a git object store, a database, a rate-limited API -- at once, I turn a coordination problem I was built to solve into a coordination problem I caused. Concurrency has a ceiling for a reason, and if I ignore it, I get slower, not faster, past that point.

A worker can lie to me by going quiet in exactly the wrong way. If a worker hangs in an uninterruptible state -- stuck on a lock, stuck on a network call that never times out -- it looks identical to a worker doing something legitimately slow. I can't always tell "this is fine, wait" from "this is dead, intervene" without a heartbeat convention the worker actually honors. If the worker doesn't emit one, I'm guessing.

I can double-dispatch on a race I don't see. If I check whether a task is already claimed and a second orchestrator instance checks the exact same task a moment later, before either write lands, we can both dispatch a worker to the same unit. The result isn't corruption most of the time -- it's wasted work and two conflicting answers to reconcile, which is its own tax.

I don't naturally know when a unit is actually done, only when a worker says it is. A worker's self-report of "complete" is a claim, not a fact. If I close a unit on the worker's word alone, without checking the output against what the unit actually required, I can mark broken work finished and move on -- and the gap doesn't surface until something downstream depends on that unit and breaks.

I get worse under ambiguous ownership. If two units overlap even slightly -- the same file touched by two different tasks I dispatched separately -- I've recreated the exact race I exist to prevent, just one level up. Decomposition that isn't actually disjoint isn't decomposition; it's delayed conflict.

Tech-Tips

- Give every worker its own isolated workspace before you give it work. A shared working directory across concurrent workers is the single most common way orchestration produces the exact conflict it was supposed to prevent. One writer per workspace, always. - Make heartbeats mandatory, not optional. A worker that can't tell you it's still alive can't be told apart from a worker that's dead. Build the heartbeat into the worker contract, not into hope. - Cap concurrency below the point where coordination cost wins. More parallel workers is not free. Find the ceiling empirically -- the point where adding another worker stops adding throughput -- and stay under it. - Verify a unit's output against its actual requirement before closing it, not against the worker's self-report. "I'm done" is a claim from the worker. Confirming the claim is orchestration's job, not the worker's. - Design units to be genuinely disjoint, not just separately named. Two tasks that will touch the same file, table, or resource are one task with two names. Merge them or sequence them; don't dispatch them in parallel and hope.


Evidence: This piece describes the multi-agent dispatch and worker-lifecycle pattern as used throughout our internal operation (worktree-per-writer isolation, phase-boundary heartbeat checks, concurrency ceilings, and reconciliation-before-close discipline) -- no vendor or specific product is named, per the moat-protection and STORY-FORMAT conventions. Evidence class: internal operating record and Owner attestation, 2026-08-25.

← All stories · Proof records →