← Harness Engineering

Multi-agent

Multi-agent

Multi-agent is the coordination of multiple agents working on the same problem. Each agent has its own context window, its own tool set, its own lifetime. The question is how to compose them into a system that solves problems better than a single agent could.

This is different from workflow (factor: workflow.md). Workflow is about phases inside one agent; multi-agent is about how multiple agents relate to each other.


The core problem

Long sessions accumulate context pollution. Exploratory reading drags the window down. Earlier phases leave residue. The agent gets confused by its own history. A single-context-window agent degrades as the task grows.

Multi-agent fixes this by isolating contexts — a research agent never sees the editing agent’s context, and vice versa. Each agent operates at peak quality because its window is dedicated to one job.

The catch is coordination overhead. Splitting work across agents means passing information between them, which means losing nuance at every handoff. Multi-agent is not free — it trades context pollution for handoff loss.


Three composable patterns

Claude Code supports three multi-agent patterns, composable:

PatternStructureUse case
A: SubagentParent spawns child with own query loop. Blocking.Small specialized tasks (single research question, one-shot verification)
B: CoordinatorCoordinator restricted to 4 tools. Workers get full access.Large parallel projects with many independent sub-tasks
C: ForkClone context + Git worktree. Merge after.Parallel code editing on isolated branches

All three run the same query() function as the main agent. Same while(true) loop, same 4 phases, same concurrency, same permissions. Composability at the architecture level — subagents can spawn subagents, coordinators can use forks, forks can have workers.


Pattern A: Subagent (context isolation)

Parent spawns a child agent with:

  • Its own context window (clean slate)
  • Its own system prompt (specialized)
  • Restricted tool access (just what it needs)
  • Parent blocks until child returns

Example: main agent calls Agent(subagent_type="Explore", prompt="Find all places X is used"). The Explore agent runs with read-only tools, produces a report, returns. Main agent continues with just the report in its context — not the 50 file reads the Explore agent did.

Why it matters: the main context stays clean. Only the result of the exploration enters the main window, not the noise of the exploration itself.

Built-in specialized subagents in Claude Code:

  • Explore — read-only codebase search
  • Verification — adversarial “try to break it” (prompt anticipates agent cheating)
  • General-purpose — full tool access
  • Plan — read-only architect, discusses approach
  • Claude Code Guide — answers questions about the tool itself
  • Statusline Setup — configures the statusline

Each has a distinct system prompt and tool subset. The main agent doesn’t need to know how to verify — it just delegates to the Verification subagent.

Trade-off: context pollution goes down, but handoff loss goes up. The nuance the Explore agent gathered doesn’t all fit in its final report. Tuning the subagent’s output format matters.


Pattern B: Coordinator (capability restriction)

The coordinator is a subagent with only 4 tools:

  • TeamCreate — spawn workers
  • TeamDelete — tear them down
  • SendMessage — communicate with workers
  • SyntheticOutput — synthesize final results

No Bash, no Read, no Edit. The coordinator physically cannot do work — it can only coordinate.

Why this matters: LLMs take the shortest path. If the coordinator has Edit, it will skip delegation and edit directly, turning a multi-agent architecture into a single-agent architecture. Removing the tool forces delegation.

Defense in depth: both the prompt tells the coordinator to delegate AND the tool list prevents direct action. If the prompt fails under context pressure, the tool list catches it.

When to use:

  • Task is large enough to parallelize
  • Sub-tasks are independent (low merge conflict risk)
  • Workers need full capabilities but context isolation
  • You want the coordinator to remain clean across all worker handoffs

When NOT to use:

  • Task is small enough for a single agent
  • Sub-tasks share state heavily (merge hell)
  • You don’t need parallelism

Pattern C: Fork (parallel mutation)

Multiple agents editing code in parallel on isolated Git worktrees.

Mechanism:

  1. Parent creates N worktrees (Git branches in isolated directories)
  2. Parent spawns N child agents, one per worktree, with bubble permission mode (requests float to parent terminal)
  3. Each child works in its own directory, making independent edits
  4. Children report back; parent merges the branches

Prompt cache optimization: all fork children share byte-identical API request prefixes. Tool results in shared context are replaced with identical placeholder text so the API server caches the prefix. All children share the same cache entry — fork parallelism is essentially free in API cost.

Anti-recursive guard: fork children keep the Agent tool (for cache sharing) but isInForkChild() detects a boilerplate tag in conversation history and blocks further forking. Prevents exponential fork explosion (fork → each child forks → N² agents → N³ agents → death).

When to use:

  • Parallel code editing on independent components
  • A/B experimentation (try two approaches, merge the winner)
  • Independent feature implementations that can be merged later

Trade-off: merge conflicts are harder than sequential work. Overlapping edits across branches require manual resolution.


Verification agent (the adversarial pattern)

The Verification agent is worth its own section. Its system prompt anticipates agent cheating:

  • Verification avoidance — narrating what you would test without running it (“I would check X, Y, Z…”)
  • Being seduced by the first 80% — declaring success when the easy cases pass but edge cases are untested
  • Narrating over executing — describing the verification process without actually performing it

The prompt explicitly warns against these failure modes. The agent is designed to be paranoid about the code it’s verifying, including paranoid about itself.

Why this matters: LLMs want to please the user. They’ll claim “done” when it feels like they should be done. A dedicated adversarial agent, with a prompt designed to distrust the happy path, is the only reliable way to catch “I’m done” lies.

Takeaway: the verification step belongs in a separate agent with a different prompt, not in the same agent that produced the code. The adversarial relationship is what makes it work.


Composability through recursion

The key insight: all agents run the same engine. Main agent, subagent, coordinator, worker, fork child — they all execute the same query() function with the same while(true) loop.

This means:

  • A subagent can spawn subagents
  • A coordinator can spawn subagents or forks
  • A worker can use all the same patterns the main agent uses
  • Patterns compose arbitrarily

The only distinction is which tools they have and which system prompt they run with. The execution machinery is identical.

Implication for harness engineering: if you build the agent loop once and parameterize the tool set and system prompt, you get multi-agent for free. Composability is a consequence of architectural discipline.


Anti-patterns

  1. Coordinator with execution tools. Collapses multi-agent to single-agent. The #1 failure mode.
  2. Subagent without context isolation. Parent passes its entire history to the child. Both windows fill up in lockstep — no win.
  3. No verification subagent. “Done” claims are self-reported. The agent marks its own homework.
  4. Recursive forks without a guard. Exponential explosion. Kills the budget in minutes.
  5. Coordinator with opinions about the work. Coordinator should be a router, not a reviewer. Reviewing work means holding the work in context, which re-pollutes the coordinator.
  6. Handoff via free-form text. Agents pass messages as prose; structure is lost. Use structured outputs (JSON, typed schemas) at every handoff.
  7. Team memory leaks across teams. One team’s memory store is readable by another team. Permission boundary violated.
  8. Synchronous parallelism. “Parallel” agents run sequentially under the hood because the orchestrator blocks on each in turn.

Takeaways for harness engineering

  1. Separate agents by context need, not by task type. If the task fits in one context, use one agent. Multi-agent is an answer to pollution, not a default.
  2. Coordinator has no execution tools. Ever. This is not negotiable. Remove the tools; don’t ask the coordinator to restrain itself.
  3. Verification is a separate agent. Different prompt, adversarial framing, distrust the happy path.
  4. Forks reuse cache. Design for prompt cache sharing — identical prefixes + replaced tool results. Free parallelism.
  5. Guard against recursive spawning. Any agent that can spawn agents needs a guard to prevent exponential fanout.
  6. All agents run the same engine. Parameterize tool set + prompt; share the loop. Composability for free.
  7. Structured handoffs, not prose. Define the schema at every agent boundary.
  8. Hard-scope team memory. If agents share memory, the scope is per-team, enforced by code.
  9. Notification scoping matters. Main thread only gets notifications for its own agentId. No cross-agent pollution in the UI either.

What this repo does

  • hooks/subagent-init.cjs — injects minimal context into subagents (~200 tokens, read from env vars set at SessionStart). Keeps subagent windows clean.
  • hooks/guard-task.cjs — forces user approval before any Task tool call. Prevents the main agent from silently spawning subagents.
  • skills/decompose/ — produces feature specs that subagents can execute independently. Decomposition IS the multi-agent handoff format.
  • skills/qa/ and skills/qa-design-review/ — both use the verify-then-fix loop, though they run in the main agent context rather than as dedicated verification subagents. Upgrade opportunity: split into a dedicated Verification subagent.
  • skills/plan-eng-review/, skills/plan-ceo-review/ — review subagents with distinct system prompts. Each runs adversarially against the plan.

Open problems

  • No coordinator skill in this repo. If the user moves to multi-agent work, a coordinator skill with the 4-tool restriction would be the next addition.
  • No fork worktree skill. Parallel code editing across worktrees is manual. Worth adding a fork skill that manages worktree creation + teardown.
  • Verification is inline, not a separate agent. verify, stop-verify.cjs, and the QA skills all run in the main context. This works for now but loses the adversarial prompt advantage.
  • Team memory is not modeled. Single-user assumption throughout. If the user works on shared team harnesses, scoped team memory becomes necessary.
  • Handoff format is informal. Subagent results come back as prose. A structured handoff schema (e.g., {decision, evidence, risks}) would reduce loss.