Claude Code Multi-Agent Runtime: Coordinator, Three Backends, Disjoint Ownership

2 min readAI Agents

How the coordinator's synthesize-first strategy prevents file conflicts, how three swarm backends (TmuxBackend, ITermBackend, InProcessBackend) share one interface, and what team context tracks.

ai-agentsclaude-codemulti-agentswarmcoordinatorparallel-execution

Running multiple agents concurrently is a coordination problem, not just a spawning problem.

Claude Code has three swarm backends, a coordinator mode, and a team context system. Each part solves a distinct piece of the multi-agent problem.

File structure

Files covered in this post5 files
src/
├── coordinator/
│   └── coordinatorMode.ts
└── utils/
    ├── swarm/
    │   ├── backends/
    │   │   ├── TmuxBackend.ts
    │   │   ├── ITermBackend.ts
    │   │   └── InProcessBackend.ts
    │   └── teamContext.ts

src/coordinator/coordinatorMode.ts

Orchestration system prompt for coordinator agents

Critical

Defines the 'synthesize first' orchestration strategy used when Claude Code runs as a coordinator. The system prompt instructs the coordinator to plan the full work breakdown before spawning subagents, assign disjoint tasks to prevent conflicts, and synthesize results after all subagents complete.

Module
Multi-Agent

Key Exports

  • coordinatorModeSystemPrompt
  • CoordinatorContext

Why It Matters

  • 'Synthesize first' means the coordinator does not spawn subagents speculatively — it completes planning before any spawning.
  • Disjoint task assignment is a deliberate instruction, not an automatic guarantee — the coordinator must reason about which files each subagent will touch.
  • The coordinator is itself an agent turn in the query loop — it uses the same execution path as a regular turn, with a different system prompt.

src/utils/swarm/backends/TmuxBackend.ts

tmux-based subagent spawning with binary tree layout

High

Spawns subagents as tmux panes arranged in a binary tree layout. Each subagent runs as a separate Claude Code process in its own pane. The binary tree ensures the layout scales to many subagents without producing unusable tiny panes.

Module
Multi-Agent

Key Exports

  • TmuxBackend
  • spawnInPane
  • buildBinaryTreeLayout

Why It Matters

  • Binary tree layout is a deliberate UX choice: it keeps each subagent's output visible as the count grows.
  • Each pane is a full Claude Code process — subagents have their own tool access, permissions context, and query loop.
  • The coordinator communicates with subagents through the task channel, not through tmux directly.

src/utils/swarm/backends/ITermBackend.ts

iTerm2 split-pane subagent spawning

Medium

Spawns subagents as iTerm2 split panes on macOS. Functionally equivalent to TmuxBackend but uses AppleScript/iTerm2 API instead of tmux socket commands. Detected automatically based on the TERM_PROGRAM environment variable.

Module
Multi-Agent

Key Exports

  • ITermBackend
  • spawnInSplit

Why It Matters

  • ITermBackend is selected automatically — the user does not configure which backend is used.
  • The observable behavior (visible subagent panes) is the same as TmuxBackend; only the pane management API differs.
  • This is the right abstraction: the coordinator uses SwarmBackend interface methods, not backend-specific calls.

src/utils/swarm/backends/InProcessBackend.ts

In-process subagent spawning without external terminals

Medium

Spawns subagents as in-process async tasks instead of external processes. Used when no tmux or iTerm2 is available, or when the session is non-interactive. Subagents share the parent process's memory but run in separate async execution contexts.

Module
Multi-Agent

Key Exports

  • InProcessBackend
  • spawnInProcess

Why It Matters

  • In-process subagents do not get their own visible terminal panes — output is routed back to the coordinator's task log.
  • This backend enables multi-agent work in CI, pipe mode, and other non-interactive contexts.
  • Shared process memory means in-process subagents must be careful about global state mutations.

How the coordinator pattern works

Multi-agent coordination flow

From coordinator turn to parallel subagent execution to synthesis.

  1. 1

    Coordinator receives task

    coordinator/coordinatorMode.ts

    The coordinator agent runs a regular turn with the coordinatorModeSystemPrompt. It plans the full work breakdown — which subtasks, which files, which subagent should own each.

  2. 2

    Select swarm backend

    utils/swarm/backends/

    Based on environment detection (tmux, iTerm2, or fallback to in-process), the appropriate SwarmBackend is selected. The coordinator uses the same interface regardless of backend.

  3. 3

    Spawn subagents with disjoint tasks

    TmuxBackend / ITermBackend / InProcessBackend

    Each subagent is spawned with a specific task assignment. The coordinator's planning step assigned disjoint file sets to prevent merge conflicts between concurrent subagents.

  4. 4

    Subagents run independently

    tasks/Task.ts / utils/swarm/

    Each subagent has its own tool access, permissions context, and query loop. They report results back to the coordinator via the task channel.

  5. 5

    Coordinator synthesizes

    coordinator/coordinatorMode.ts

    After all subagents complete, the coordinator reads their results and synthesizes a final output. The 'synthesize first' instruction means the coordinator planned for this step from the start.

Three backends, one interface

The SwarmBackend abstraction is one of the cleaner designs in the codebase.

The coordinator never calls tmux new-window or AppleScript directly. It calls backend.spawn(task) and backend.getResult(id).

The backend selection happens once at session start, based on environment detection:

  1. check for tmux socket → TmuxBackend
  2. check TERM_PROGRAM for iTerm2 → ITermBackend
  3. fallback → InProcessBackend

This means the same multi-agent workflow works in a developer's local terminal (visible panes), in macOS iTerm2 (native splits), and in a CI environment (in-process, no panes).

The tradeoff is that InProcessBackend loses the observability of visible panes. The coordinator can still see subagent results through the task channel, but the user cannot watch subagents working in real time.

Why "synthesize first" is the right coordinator strategy

An alternative coordinator strategy is "spawn then plan" — spawn subagents speculatively and let them explore, then synthesize based on what they found.

Claude Code explicitly rejects this in the coordinator system prompt.

The reason: uncoordinated exploration produces file conflicts.

If two subagents both decide to refactor a shared utility file without coordination, their changes will conflict. The coordinator's planning step must assign disjoint file ownership before any spawning happens.

"Synthesize first" means:

  1. understand the full task before splitting it
  2. assign disjoint ownership explicitly
  3. spawn subagents with complete instructions
  4. collect and synthesize results

This is slower to start (planning takes time before any subagent begins work) but produces fewer conflicts and more predictable output.

Team context in AppState

AppState includes a team context field that tracks which subagents are active, their assigned tasks, and their current status.

This is the coordination state for the coordinator turn — it knows who is working on what.

The UI can surface this as a "team" view showing active subagents and their status, even when using InProcessBackend where there are no visible panes.

Swarm backend abstraction and coordinator pattern

The interface that decouples coordinator from backend, and the coordinator turn structure. JS shows the actual pattern; Python and Go show the same shape.

typescriptutils/swarm/ + coordinator/ (simplified)

SwarmBackend interface and coordinator flow — actual pattern from learning-claude-code

// The interface all backends implement
interface SwarmBackend {
spawn(task: SubagentTask): Promise<SubagentHandle>
getResult(id: string): Promise<SubagentResult>
stop(id: string): Promise<void>
}

interface SubagentTask {
id: string
instructions: string
fileOwnership: string[]  // disjoint file set assigned by coordinator
context: SessionContext
}

// TmuxBackend: each subagent gets a pane in a binary tree layout
class TmuxBackend implements SwarmBackend {
async spawn(task: SubagentTask): Promise<SubagentHandle> {
  const paneId = await this.createPane(buildBinaryTreeLayout(this.activeCount))
  await this.runInPane(paneId, `claude --task "${task.id}"`)
  return { id: task.id, paneId }
}
}

// InProcessBackend: subagent runs as async task
class InProcessBackend implements SwarmBackend {
private tasks = new Map<string, Promise<SubagentResult>>()

async spawn(task: SubagentTask): Promise<SubagentHandle> {
  const result = runAgentInProcess(task)
  this.tasks.set(task.id, result)
  return { id: task.id }
}

async getResult(id: string): Promise<SubagentResult> {
  return this.tasks.get(id)!
}
}

// Coordinator turn: plan → spawn → collect → synthesize
async function runCoordinatorTurn(
task: string,
backend: SwarmBackend
): Promise<string> {
// 1. Plan with coordinator system prompt (synthesize first)
const plan = await planWithCoordinator(task)

// 2. Spawn subagents with disjoint task assignments
const handles = await Promise.all(
  plan.subtasks.map(t => backend.spawn(t))
)

// 3. Collect results
const results = await Promise.all(
  handles.map(h => backend.getResult(h.id))
)

// 4. Synthesize
return synthesizeResults(results, plan)
}

The Claude Code coordinator uses a 'synthesize first' strategy — it completes a full work breakdown plan before spawning any subagents. What failure mode does this prevent that a 'spawn then plan' strategy would not?

medium

Multiple subagents run concurrently, each with their own file system access and tool permissions. The coordinator assigns each subagent a specific set of files to work on.

  • ASubagents calling the model API simultaneously would exceed rate limits
    Incorrect.Rate limit management is a concern but it is handled at the API call level, not by the planning strategy. 'Synthesize first' does not reduce total API calls.
  • BUncoordinated subagents would modify the same files simultaneously, producing merge conflicts that the coordinator cannot resolve
    Correct!Correct. If the coordinator spawns subagents before planning, two subagents may independently decide to modify the same shared utility file. Their changes conflict, and the coordinator receives two conflicting results with no way to automatically merge them. 'Synthesize first' assigns disjoint file ownership before any subagent starts, so conflicts are prevented at planning time rather than requiring resolution after the fact.
  • CSpawning subagents speculatively wastes compute on tasks that turn out to be unnecessary
    Incorrect.Compute efficiency is a valid concern but it is not the primary reason. The deeper issue is correctness: conflicting file modifications from uncoordinated subagents can produce broken output regardless of compute cost.
  • DWithout a plan, subagents would not know when to stop running
    Incorrect.Subagents are given specific task instructions and stop when their task is complete. The absence of a coordinator plan does not leave subagents running indefinitely.