Skip to main content
Run two coding agents in the same checkout and they will corrupt each other. One runs git checkout -b feature-a while the other is mid-edit; one runs pnpm install while the other reads node_modules; both write to the same file and the last save wins. The failure isn’t hypothetical — it’s the default outcome of naive fan-out, and it’s why most agent tools serialize everything and call it a day. Serializing is the wrong fix. Agent work is embarrassingly parallel in shape — N tickets, N reviewers, N research angles — and agents are slow enough (minutes per task) that parallelism is the difference between an hour and a night. What you actually need is three separate mechanisms: concurrency control (how many lanes run at once), filesystem isolation (each lane gets its own working copy), and fan-in (a fold step that can see every lane’s result). Smithers ships each as a first-class primitive: <Parallel>, <Worktree>/<Sandbox>, and deps-driven fold tasks. Be honest about the costs before reaching for this. Anthropic’s own multi-agent research system write-up measured that multi-agent runs burn roughly 15x the tokens of a single chat — parallelism multiplies spend linearly with lane count, plus the orchestrator’s overhead. And the hard part is rarely the fan-out; it’s the merge. Ten agents that each finish a feature in their own lane produce ten branches that touch overlapping files, and reconciling them is where runs actually die.

How the industry solves it

Fan-out/fan-in is the oldest pattern in distributed computing — MapReduce is literally named after it — and every orchestration system has a version. Two industry conclusions worth stealing. First, git worktrees have become the standard local isolation primitive for coding agents — Conductor and Crystal are built entirely around them, because a worktree shares the object store (cheap, instant) while giving each agent a private index, HEAD, and working copy. Second, for untrusted or destructive work, filesystem isolation isn’t enough and the industry reaches for microVMs (Firecracker underpins both AWS Lambda and E2B). Smithers does both: worktrees for parallel lanes on your machine, provider-backed sandboxes when you need a real boundary.

How Smithers does it

<Parallel>: concurrency without chaos

<Parallel> runs children concurrently with two independent caps (full reference):
  • maxConcurrency caps leaf tasks in the group’s innermost scope.
  • subtreeConcurrency caps direct children as whole subtrees — the right cap when each child is a sequence (implement → review → land), because a nested <Parallel> escapes the outer leaf cap.
On top of every group cap sits a run-wide cap: unpinned it starts at 4 and grows with demand up to SMITHERS_AUTO_MAX_CONCURRENCY_CEILING (default 16); --max-concurrency N pins it. A task runs only if the leaf cap, subtree cap, and run cap all pass. Admission is deterministic in document order, so a resumed run re-admits the same lanes.

<Worktree>: one working copy per lane

<Worktree path="..."> roots its subtree in an isolated git or jj worktree; descendants inherit the absolute worktreePath as their cwd (full reference). Worktree creation is serialized per VCS root because concurrent git worktree add / jj workspace add calls mutate the shared .git state (see packages/engine/src/engine.js).
Three verified traps, in order of how often they bite:
needs gates; it injects nothing. A fold task with only needs waits for its lanes but its prompt receives none of their output — the agent then confidently synthesizes from nothing. Pair needs (id mapping) with deps (typed row resolution into the children callback), as above. This is the single most common fan-in bug in real Smithers workflows.
Files a lane produced live in the lane’s worktree, not your checkout. Typed output rows flow through deps/ctx.outputs regardless of where the task ran, but a file written by a lane exists only under that lane’s worktree until the merge step lands it. A fold task that reads artifacts off disk must resolve the real path — ctx.worktreePath("wt-auth") or ctx.resolveWorktreePath("/tmp/smithers/auth") — never reconstruct it from process.cwd(). Better: keep structured results in typed output rows and let files travel through the merge.
Never pin cwd on an agent used inside a <Worktree>. Resolution is agent.cwd ?? worktreePath ?? repoRoot, so an explicit cwd silently breaks isolation: the agent edits the repo root, the lane branch stays empty, and the land step merges nothing.

Merging: the real bottleneck

Smithers does not auto-resolve conflicts — nobody credibly does. What it gives you is structure: <MergeQueue> serializes landing (default maxConcurrency: 1, priority 1000 so ready landing work jumps the queue), which converts an N-way conflict free-for-all into N sequential rebase-and-resolve steps, each performed by an agent that can see what landed before it. Cheap discipline that removes most conflicts: partition lanes by file ownership up front, and land early and often rather than letting ten lanes drift for hours. Note that jj-backed worktrees continuously snapshot the working copy onto the lane’s bookmark, so merge steps should diff against the base branch (git diff main...HEAD), not trust git status dirty-state.

<Sandbox>: when a worktree isn’t enough

Worktrees isolate the working copy, not the machine — a lane can still rm -rf ~, read your env, or hit the network. For untrusted or destructive work, <Sandbox> runs a child workflow through a provider (bubblewrap, docker, or any object registered via registerSandboxProvider, including microVM backends like the @smithers-orchestrator/daytona and microsandbox packages), with allowNetwork, egress proxy/CA controls, resource limits, and diff review (reviewDiffs defaults to true) before changes touch your tree. See <Sandbox> and sandbox providers.

Guarantees and limits

  • Guaranteed: deterministic lane admission on resume; serialized worktree creation; worktree cwd inheritance; caps enforced at leaf, subtree, and run level; DEPENDENCY_DEADLOCK (not a hang) when a deps key names a task that never produces.
  • Not guaranteed: conflict resolution (an agent or a human does it); dependency installation in fresh worktrees (lanes must pnpm install themselves — the isolation preamble tells agents this); cost control (N lanes ≈ N× tokens; cap concurrency and lane count deliberately).
  • Cleanup is yours to run: finished runs leave worktrees behind until pruned.

Operating it

See also