Mingyu's LibraryHome

Orchestration research, with real measurements · 2026-07-27

Multiple Sessions or Multi-Agent?
I ran 27 agents to answer that

This isn't a comparison survey. I ran two sets of experiments in a real environment: 6 probes to pin down exactly where a subagent's isolation boundary sits, and 13 agents that each built the same game using three different orchestration modes, after which I checked every build in a headless browser to see whether it actually ran. The result doesn't match intuition — what decides success or failure is neither the session nor the agent.

27agents actually burned
1.18Msubagent tokens
3arms / one and the same spec
2/5blank-screen rate, parallel with no contract
2real concurrency ceiling, measured

TL;DRThe conclusions, up front

In one sentence

Multi-agent solves for "width inside a single task"; multiple sessions solve for "length across time + human parallelism + total context" — they're two orthogonal axes, not substitutes for each other. And what actually decides whether parallel development succeeds is whether there's an interface contract everyone follows, not which orchestration mode you picked.

  1. Parallelism doesn't produce quality; contracts do. Same setup, 5 agents writing 5 modules in parallel: the group with a contract produced 6 files with zero interface errors and assembled on the first try; in the group without one, 2 of the 5 assembly attempts went straight to a blank screen, throwing a module-load-time SyntaxError — not the kind of runtime bug you can debug your way out of.
  2. Without a contract, agents burn enormous compute guessing at their teammates' interfaces. The slowest agent in the no-contract group ran for 831 seconds and 104K tokens — it built itself a set of fake teammate modules and 18 assertions just to verify its own guesses. The fastest agent in the same group took only 169 seconds.
  3. Naming disagreements aren't evenly distributed; they cluster on the one interface with the most complex semantics. Across 5 developers who never talked to each other, named imports hit 24/26 (92%) — but the core physics stepping function alone got three different names from three people (stepPlayer / updatePhysics / stepPhysics), and that happens to be the single most critical call in the whole thing.
  4. A subagent doesn't get your conversation history, user memory, or preference settings. Confirmed by probe: all it gets is the system prompt, the task message, CLAUDE.md, git status, environment info, and the tool/skill list. All that background you assume it "knows" — it doesn't.
  5. A subagent can't ask you a question. The probe showed no AskUserQuestion and no SendUserMessage in its tool list. It can push files and notifications to you one-way, but it can't ask "does this feel right?" and wait for your answer. For game development that's a dealbreaker.
  6. worktree isolation doesn't eliminate conflicts, it just defers them. Two worktrees each cleanly edited the same line with zero interference at runtime — and the instant you merge, CONFLICT (content).
  7. The payoff from "parallelism" is hard-capped by the host's concurrency. This cloud sandbox has only 2 cores, and 8 "parallel" agents actually ran as batches of 2, four batches deep. The fan-out you pictured when writing the workflow is a queue here.

Part 1The mechanics: there are four, not two

Framing it as "multiple sessions vs multi-agent" flattens the options. Dig through the official docs and you'll find four ways to run things in parallel today, and they differ along three dimensions: who coordinates, whether the workers can talk to each other, and how files are isolated.

SUBAGENTS fan-out inside one session you main session sub 1 sub 2 sub 3 ↑ results only, no peer talk one shared filesystem AGENT TEAMS many sessions + peer messaging (experimental) you lead mate 1 mate 2 mate 3 ↑ peer messages + shared task list no auto isolation, split files by hand BACKGROUND / AGENT VIEW independent background sessions you agent view panel session session session ↑ reports to you only, no peer talk each gets its own worktree MANUAL MULTI-SESSION you open a few windows yourself you (the only link) session session session ↑ completely unaware of each other share the disk, or roll your own worktree
How the four parallel modes differ structurally. The real dividing line is the last row: how files are isolated.
DimensionSubagentsAgent teamsBackground / agent viewManual multi-session
Who coordinatesthe main agentthe lead agentyou (status in a panel)you (fully manual)
Can workers talk to each otherNo — results onlyYes, direct peer messagesNoNo
Can you talk to them directlyNo (only via the main agent)Yes, you can open any teammateYes, you can attachYes
ContextSeparate; results summarized back to the main sessionSeparate; inherits none of the lead's historyFully separateFully separate
File isolationworktree, optionalNone by default — you must split files by handworktree assigned automaticallyShare the disk, or roll your own
Token costLower (only summaries come back)High (a full instance each)High (each bills its own quota)Highest (you re-feed the background every time)
LifetimeWithin the task, resumableTied to the lead sessionPersistent; process stops after 1h idleSpans days, --resume works

What this actually means if you're on Cowork

Of the four above, agent teams and background agents are Claude Code CLI features (the former is an experimental one, off by default — you need CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1). Inside Cowork in the desktop app, what you actually have is: subagent fan-out, dynamic workflows (scripted, large-scale subagent orchestration), and opening multiple Cowork tasks — that last one is what I mean by "multiple sessions" in this piece. One more official detail worth remembering: in the desktop app, every new session is automatically given its own worktree, so multiple desktop sessions don't share a disk the way terminal sessions naturally do.

Part 2Probing it for real: where exactly is the isolation boundary

Plenty has been written about subagents having "their own context," but nobody spells out how independent "independent" really is. I spun up 6 sets of probe agents to just ask and just try. Here's what came back. Everything tagged as measured came out of this run

Probe 1 · Parallel subagents share the filesystem, and see each other live measured

Two agents started at the same time, each wrote a file, each slept 30 seconds and then listed the directory. Both saw the other's file, and the time windows confirm the overlap (A: 1785147960382→1785148004867, B: 1785147960821→1785148006985).

What this means

Subagents running in parallel inside the same session have no file-level isolation at all. Two agents writing the same file at the same time simply clobber each other. If you want isolation, you have to explicitly open a worktree.

Probe 2 · What a subagent inherits, and what it doesn't measured

I asked an agent to report, without using a single tool, exactly what context it received at startup.

In the main session Conversation history (every turn) User memory / memory file contents User preferences Contents of files already read State of skills already invoked output style Results of every earlier tool call Attachments you uploaded auto memory a wall none of it crosses what the subagent actually gets Its own system prompt (not the main session's) The one task message the main agent wrote CLAUDE.md hierarchy (all levels) git status snapshot Environment: cwd / platform / model Tool list (names only, no contents) Names and descriptions of installed skills Body text of preloaded skills (if specified) Peer roster (if SendMessage is on)
The measured context inheritance boundary. That entire left column — the subagent sees not one word of it.

The probe agent's own words are worth copying down:

"I can see absolutely none of the main session's conversation history — not one user message from it, no assistant replies, no summary, no 'previously on.' The only task message I received is these 4 questions you're asking me right now."

"The memory tool existing ≠ me being able to see the memory."

The only thing it can do is reason backwards from the environment: it saw the working directory was called probe-repo, saw the installed skills leaned toward iOS game development and Chinese HTML documents, and guessed "this account usually cares about those areas." That's a guess, not knowledge.

The direct consequence

Every critical constraint you stated in the main session — "this project uses SpriteKit, not Unity," "keep the colors consistent with last time," "don't touch that file" — has to be manually restated by the main agent inside the subagent's prompt. Miss one and you're redoing the work. This is the most common source of rework in any fan-out orchestration.

Whatever can be made permanent belongs in CLAUDE.md: it's the only thing that automatically lands in every subagent's context.

Probe 3 · A subagent cannot ask you anything measured

I had an agent go through its own tool list line by line:

CapabilityResultWhat it means
AskUserQuestionnoCan't pose a multiple-choice question and wait for your answer
SendUserMessagenoCan't send text straight to you
SendUserFile / PushNotificationyesCan push files and notifications one-way
SendMessage (to another agent)yesCan message peer agents
Spawning another subagentnot inside a workflowThe docs allow 3 levels of nesting by default, but agents spawned from a workflow never get the Agent tool

Why this is especially lethal for game development

Between a subagent and you, the link is one-way reachable, never two-way. It can push a finished result for you to look at, but it can't stop halfway and ask "does this jump feel too heavy or too light?" and then wait for your call. A huge share of decisions in a game — feel, palette, difficulty curve — are precisely the ones only you can judge. Decisions like that have to stay at the session layer; they can't be thrown into a subagent.

Probe 4 · worktree isolation: flawless at runtime, explosive at merge time measured

Two agents each created a worktree, each changed the same line of config.jsSPEED = 100 — to a different value and committed, with a 20-second sleep in between to force overlap.

Runtime behavior was flawless: the main repo's working tree stayed clean throughout (SPEED still 100), the two branches advanced independently from the same base, and there was no index.lock contention and no git error at all. A even watched B's worktree appear out of nowhere during its own sleep, with zero effect.

Then I merged the two branches:

$ git merge feat-a
Fast-forward

$ git merge feat-b
Auto-merging config.js
CONFLICT (content): Merge conflict in config.js
Automatic merge failed; fix conflicts and then commit the result.
runtime: zero interference merge time: head-on collision base worktree A → SPEED = 250 worktree B → SPEED = 700 CONFLICT (content) two values on one line, git won't pick mutually invisible · no index.lock contention · main repo clean
What a worktree solves is "writing at the same time," not "writing different things." The latter can only be settled by a contract or by human judgment.

Probe 5 · Real concurrency: the parallelism you were promised is actually a queue measured

I launched 8 agents, each doing exactly one thing: stamp the time, sleep 15 seconds, stamp the time again. The timestamps came back in a clean pattern of four batches, two at a time:

Batchagentsstart → end (relative seconds)
1#0, #10.0 → 19.8
2#2, #329.4 → 50.6
3#4, #558.6 → 79.6
4#6, #788.2 → 109.1

The host has nproc = 2, and the measured maximum concurrency is exactly 2. The 8 agents took 109 seconds to finish; with real parallelism it should have been about 20 seconds. On top of the 15-second sleep, each agent also carries roughly 5 seconds of startup and model overhead.

Don't treat this as a general conclusion

These are this machine's numbers. The official docs put the default subagent concurrency ceiling at 20 (CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS), a cumulative cap of 200 per session, and a default nesting depth of 3. But workflow scripts have ceilings of their own, and how many actually run at once ultimately depends on what machine you're running on. Writing parallel([...16 of them]) does not mean 16 are really running.

Part 3Three-Arm Controlled Experiment

The probes answered "what the mechanism is." This section answers "how much difference it makes."

The design

One SPEC.md (a 640×360 canvas platformer, hard-split into seven files: physics / input / level / render / hud / main / index.html), built once under each of three orchestration modes:

Arm AArm BArm C
What it simulatesOne agent writing everything end to endMulti-agent inside a single sessionMultiple sessions each working on their own
Orchestration1 agent writes all 7 files1 agent writes the interface contract first → 5 agents each write one module in parallel → 1 agent reads the contract and assembles main5 agents in separate directories, spec only, no contract, each writing its own module plus its own version of main.js
Can it see teammates' code?N/ANo — alignment comes only from the contractNo, and cross-directory reads are explicitly forbidden
IntegrationBuilt inA dedicated integration agentManual merge: the 5 modules + whichever main.js wins under "first PR wins"

A note on the variable (important)

Strictly speaking, the variable isolated between B and C is not "session vs agent" — it is "shared contract or not". That is deliberate: a multiple-sessions workflow that does have a contract behaves exactly like Arm B.
In other words, this experiment measures the real root cause of failure in parallel development, not a difference in product form. Which is also the more useful conclusion.

The numbers

MetricArm A serialArm B contract-first parallelArm C no-contract parallel
Total agent working time885.6 s849.5 s1721.7 s (2.0×)
Ideal critical path
(assuming true parallelism)
885.6 s512.5 s (0.58×)831.4 s
Measured wall clock on my machine
(concurrency cap = 2)
885.6 s1012.7 s1174.0 s
subagent tokens109,822350,465 (3.2×)295,101 (2.7×)
Lines of code produced1,0576571,628
Cross-module interface errors00see below
Duplicated function implementations00drawGameOver and isOnGround each rewritten by two modules
Headless browser verificationPASSPASS2 of 5 assemblies FAIL
Files nobody wrotenonenoneindex.html — not one of the five wrote it
0s 300s 600s 900s ARM A · SERIAL single agent 885.6s ARM B · CONTRACT contract 256.6s 5 modules (parallel) slowest 143.0s integration 112.9s critical path 512.5s — 42% faster than serial ARM C · NO CONTRACT 5 independent devs parallel, no contract 207s 308s 205s 831s ⚑ 169s
That 831-second bar in Arm C is the most expensive run of the whole experiment, and everything it did was guessing teammates' interfaces and then verifying the guesses.

How Arm C actually broke

The 5 developers each wrote their own main.js. Once I had all 5 modules, I assembled the project once with each of them and ran it in headless Chromium:

Whose main.jsImport styleResultWhy
dev0 (author of physics)9 named importsPASSIt wrote physics itself, so there was nothing to guess wrong
dev1 (author of input)4 namespace + 2 namedPASSSaved by candidate-name resolution
dev2 (author of level)10 named importsFAILdoes not provide an export named 'updatePhysics'
dev3 (author of render)5 namespace importsPASSWrote argument-order sniffing plus property aliases, at a cost of 831s
dev4 (author of hud)5 named importsFAILdoes not provide an export named 'stepPhysics'

The nature of the failure is the point

Neither failure is "it runs but has a bug." Both are a SyntaxError at ES module link time — the whole module graph fails to load, the screen goes black, not a single line of code executes. In a real project this is exactly the case where a few sessions each finish their part, you merge them, and it won't compile. And it never surfaces inside any one session, because within each session its own part is perfectly fine.

Naming convergence: higher than I expected, but wrong in exactly the worst place

Cross-checking the five developers' named imports against what the real authors exported:

Named import hit rate: 24 / 26 = 92%

Hits (everyone converged on their own):
  input.js  : createInput      ✓ all three guessed right
  level.js  : getPlatforms     ✓
  render.js : render           ✓
  hud.js    : drawHUD          ✓

Misses (all on one and the same interface):
  physics.js actually exports : stepPlayer / stepBody
  dev2 guessed                : updatePhysics   ✗
  dev4 guessed                : stepPhysics     ✗

This one I did not see coming

Same model, same spec, and naming convergence hits 92% — Claudes have remarkably consistent taste in names. But the disagreement is not scattered at random. It lands precisely on the interface with the most complex semantics and the most naming freedom: the physics step function. Which happens to be the single most central call in the whole thing.

The implication: skipping the contract because "we're all the same model, we should line up" bets the project on exactly the wrong thing. The higher the convergence, the more dangerous it is, because it convinces you that you don't need a contract.

What the three builds look like

I screenshotted all three PASS builds (headless Chromium, 2.5 seconds in, holding Right + Space):

Arm A screenshot
Arm A · 1,057 linesstarfield, distant hills, rounded platforms, a character with eyes, life pips, control hints
Arm B screenshot
Arm B · 657 linesstrictly to the contract, not one pixel of improvisation
Arm C screenshot
Arm C · 1,628 linesthe richest visuals and the most bloated; 2 of the 5 assemblies give a blank screen

A cost I didn't expect

Arm B's output is only 657 lines, 38% fewer than serial A, and it's also the plainest looking. The reason is that the contract nails everyone's boundary down, so nobody throws in a starfield on the side.
A contract buys consistency and sells off room to improvise. For utility modules that's a feature; for "game feel" and art it may be a bug — which also explains why the creative-heavy parts shouldn't be dumped into brainless parallelism.

Why this happens

Read through what the 5 agents in Arm C say about their own work and the mechanism gets very clear. With no contract, a rational developer does three things, every one of which burns money:

  1. Abandon named imports for namespace imports plus a candidate-name table. dev3's own words: "With named imports, if a teammate's name is off by a single letter, the browser gives you a link-time SyntaxError and a blank screen." So it prepared a list of candidate names for every symbol and tried them one by one.
  2. Hedge shared state with property aliases. Several agents used Object.defineProperty to point state.vy and state.player.vy at the same storage, and threw in w↔width and onGround↔grounded while they were at it — purely because they had no idea which name a teammate would use.
  3. Build a set of fake teammates to verify the guesses. dev3 created a .selftest inside its own directory, wrote three sets of fake modules in completely different styles (one deliberately contradicting its own guesses), and ran 18 assertions. That step alone is why it was 4× slower than the rest of its arm.

It even left this line in its report, which says it all:

"Hedging covers 'different name.' It can't cover 'different argument order' (say, step(state, platforms, dt)) — that one only gets solved by talking to each other."

— Defensive programming can block naming divergence; it can't block semantic divergence. And a contract that takes 250 seconds to write makes all three of these unnecessary.

Part 4Seven Conclusions

1 · A contract is a precondition for parallelism, not an optional extra

Arm B spent 256.6 seconds writing the contract and got back zero interface errors, an assembly that worked on the first try, and the smallest codebase. Arm C skipped those 256.6 seconds and paid for it with double the total work time, 40% of its assembly attempts ending in a blank screen, and two modules implementing the same function twice. A contract isn't overhead, it's leverage.

2 · Parallelism doesn't reduce total work, it only compresses the critical path

Arm A serial: 885.6s. Arm B contract-first parallel: 849.5s of total work — almost identical. Parallelism didn't make the work smaller, it just squeezed out a 512.5-second critical path (42% faster than serial). So: if you don't care about wall-clock time, parallelism buys you nothing and burns 3x the tokens.

3 · The ceiling on parallel gains is set by your machine, not by your script

This machine has 2 cores, and the measured concurrency ceiling was exactly 2. Arm B's ideal critical path was 512.5s, but the measured wall clock was 1012.7s — the effective parallelism was cut by more than half. Check nproc before you write the workflow.

4 · subagents are "dumb": no background, and they can't ask

They don't get the conversation history, the memory, or your preferences, and they can't ask you a question. Put those two together and it means: any decision that comes down to "you decide" cannot be handed off to a subagent. They're only good for work with a clear goal, objective criteria, and a conclusion at the end.

5 · worktrees govern "writing at the same time", not "writing different things"

Two worktrees wrote concurrently with zero conflicts, and the merge CONFLICTed immediately. An isolation boundary solves physical conflicts; semantic conflicts can only be settled by a contract or by a human.

6 · "It's the same model, it should line up" is a dangerous intuition

A 92% naming convergence rate is exactly what creates that illusion. But the remaining 8% lands precisely on the most critical interfaces, and the failure mode is a hard failure (blank screen), not a soft degradation.

7 · Contracts suppress flair — don't use them to manage creative work

Arm B's output was the most orderly and also the plainest. For infrastructure that's a good thing; for the parts that need flair — game feel, level pacing, art style — you shouldn't force them through the same parallel pipeline.

Back to the original question

Multiple sessions and multi-agent aren't an either/or — they solve different problems.

  • Multi-agent: compresses the critical path of a single task. That only works if the task can be cut into mutually independent chunks and you can write the contract up front.
  • Multiple sessions: break past total context, span time, parallelize your own waiting, isolate failure, and clear out preconceptions. None of these is something a subagent can structurally do.

So the mature approach is to use both: cut sessions along "deliverable + time rhythm", then fan out with multi-agent inside each session.

Part 5The decision SOP

Decision tree

Can this be split into independent chunks? No Yes Keep it serial in the main sessionParallel just burns 3× the tokens Need your judgment midway (feel / taste / trade-offs)? Yes No Split into multiple sessions, not subagents subagents can't ask questions, only report back Will these chunks touch the same files? Yes No Set file ownership: one owner per file Unclear? Go serial, or a worktree per agent Worktrees only defer conflicts, never erase Spans days / needs to persist? Yes No Multiple sessions Split by deliverable + cadence, not module One CLAUDE.md per session for shared context Multi-agent, one session 3–5 is the sweet spot Past 8, coordination eats the gains ⚑ Whichever leaf you land on: write the contract before going parallel Measured: 250s of contract killed a 40% blank-screen rate and 2× the work
Four questions settle the orchestration mode. That last bar is unconditional.

Pre-parallel checklist

Follow this one as written. The first four are hard gates — miss any one of them and don't go parallel.

Hard gates

  • The contract is written and on disk. Precise down to: every field name + type + default value + who is allowed to write it for shared state; every module's exported function names + parameter order + parameter types + return values; and call order. A vague contract is no contract.
  • File ownership is settled. One file, one owner. Whatever you can't divide cleanly stays in the serial path.
  • The unclaimed work has been named. In Arm C, none of the five wrote index.html — anything that sits in nobody's remit is something nobody does. List it out and assign it to a person.
  • Verification already runs automatically. Parallel output has to have an objective test (compiles / tests pass / starts with no errors). Without an automatic check you will drown in manual review.

When writing prompts

  • Manually restate the key constraints from the main session into every agent's prompt — they can't see your conversation.
  • Any rule you can pin down goes into CLAUDE.md: it's the only thing that reaches every subagent's context automatically.
  • Spell out what an agent is not responsible for, not just what it is. drawGameOver got written twice, once in each module, precisely because nobody was told it wasn't theirs.
  • Forbid agents from reading into anyone else's directory — otherwise what you're measuring isn't parallelism, it's serial work in a parallel costume.

Scale and cost

  • Check nproc first. Your concurrency ceiling is roughly the cores the machine can give you, not the number written in your script.
  • 3–5 workers is the sweet spot (the official recommendation matches what I saw here); past 8, coordination overhead starts eating the gains from parallelism.
  • Budget: parallel costs about 3× the tokens (A→B was 3.2× here). The published official numbers are ~15× for multi-agent versus chat, ~4× for a single agent.
  • If what you're trying to save is your own time rather than wall-clock time, don't go parallel.

When merging

  • Before merging a worktree branch, run the automated verification once; don't wait until the conflicts are resolved to discover the interfaces don't line up.
  • When interfaces disagree, let the contract decide whether the caller or the callee changes — not "whoever merges first wins".
  • Re-run the full verification after merging — two modules that are each correct on their own aren't necessarily correct together.

How to split sessions

Splitting principle: split by deliverable + cadence, not by code module

Code modules have interface dependencies between them, so splitting them means paying the contract tax. Deliverables don't.

sessionDeliverableCadenceCode collisions?
Main dev lineCodeDailyThe only one allowed to write code
Asset lineImages / audioBatched, intermittentDoesn't touch code
Research lineDocs / competitors / ASOPeriodicDoesn't touch code
Monitoring lineReports / alertsScheduled jobsRead-only

Part 6iOS / SpriteKit deep dive

On Xcode projects, everything above gets one extra amplifier: project.pbxproj.

Why iOS projects are especially hostile to parallelism

.pbxproj is a single monolithic plist, and every time you add, remove, or move a file, both the file references and the group references get updated. Two sessions each adding one Swift file means two writes to adjacent regions of the same file — git's auto-merge does a poor job on this kind of structure. And that is exactly the operation parallel development performs most often.

Add two more iOS-specific serial bottlenecks on top

  • Device / simulator verification can't be parallelized. Feel, animation smoothness, touch response — you can only run one at a time, and only you can judge it. This is a hard serial segment.
  • The build is global. Multiple sessions running xcodebuild on the same project fight over DerivedData.

Three tiers of mitigation, ordered by effort

Tier 1 · Don't touch the project structure (doable today)

  • Keep exactly one coding session. Lowest cost, highest payoff of anything here. Every other session only does work that never touches .pbxproj: assets, research, copy, balance tables.
  • If you really must write code in parallel, agree that nobody adds a new file this round — only edit the contents of existing files. Editing contents doesn't touch .pbxproj, and conflict risk drops by an order of magnitude.
  • Funnel new files through one person, add them in a batch, then hand them out.

Tier 2 · Use Xcode 16+ filesystem-synchronized groups

  • Right-click a group → Convert to Folder, swapping hand-maintained groups for folders that stay in sync with the filesystem. From then on, adding a file no longer writes to .pbxproj.
  • Align your local directory structure with your Xcode group structure before converting, or you'll get one enormous diff in a single shot.

Tier 3 · Generate the project file / modularize

  • XcodeGen or Tuist: describe the project in a YAML/Swift DSL, match source files with a Sources/**/*.swift glob, and .pbxproj becomes a build artifact that never enters version control — the conflicts disappear at the root.
  • Split by domain into local Swift Packages: Gameplay / Rendering / Audio / UI, one package each, one owner each. The cost is slower compilation and indexing.

Suggested session layout for a SpriteKit game

sessionwhat it doeswhy it's separatemulti-agent inside?
Main development linethe only session that writes to the Xcode projectavoids .pbxproj conflicts; device verification needs you presentYes — but only for read-only fan-out: reading through the code in parallel to locate a problem, reviewing from multiple angles. Writing code stays serial.
Asset lineAI image generation, atlas slicing, sound effectsthe output is files, not code; the rhythm is batch-orientedYes — assets have no natural dependencies on each other, the ideal case for brainless parallelism
Research linecompetitor teardowns, mechanic research, ASO copycompletely different context; mixing it into the dev session pollutes your thinkingYes — gather from multiple angles in parallel, then synthesize
Game-feel linediagnosing and tuning animation, easing, transitionsneeds you watching the device over and over and making the call over and over — the textbook case of work that has to stay at the session layerDiagnosis can read in parallel; tuning values must be serial with you in the loop

How this plugs into your existing workflow

If you're already running the ios-app-blueprintgame-dev-kickoff chain: make the interface contract part of what kickoff produces. The contract written in 256 seconds in this experiment (shared state field table + each module's export signatures + call order) has a direct iOS equivalent: an ownership table for the fields of the shared GameState + the public API signatures of each SKNode subsystem + the call order of the per-frame update. Write it into CLAUDE.md and it lands automatically in every subagent's context — the only channel that doesn't require you to restate it by hand.

Part 7What I Didn't Verify

To give you the full picture, here's what this round didn't test or didn't test cleanly. Don't extrapolate these conclusions:

  • Real multiple sessions were never measured directly. I couldn't open a second Cowork session in this environment. Arm C simulated it with "separate directories + no cross-reading + no shared contract," which reproduces the information isolation of multiple sessions but not the across-days, human-in-the-loop, context-repriming dimensions.
  • Arm C's isolation is self-reported. All 5 agents claimed they didn't read each other's directories, and I didn't go through the transcripts line by line to falsify that.
  • The wall-clock numbers are polluted by the concurrency cap. This machine has 2 cores, and the three arms were also competing with each other. The "ideal critical path" in the table is a modeled value recomputed from each agent's measured duration, not measured wall clock.
  • n = 1. Each arm ran once. No repeats, no variance estimate. That 831-second outlier may not be reproducible.
  • The task was on the small side. A 600–1600 line mini-game is an order of magnitude away from a real iOS game project. The value of a contract probably rises with project size, but I didn't test that here.
  • Context decay over long sessions went untested. One of the biggest payoffs of multiple sessions — breaking past the total context ceiling — got no direct measurement at all this round; I can only cite outside research (see sources).
  • Agent teams / background agents: docs only, no actual runs. They're Claude Code CLI capabilities and weren't available in this environment.

What to test next to make this research solid

  1. Rerun the same experiment 3 times on a multi-core machine to get variance, and take concurrency out of the bottleneck.
  2. Add an Arm D: no contract, but agents can talk to each other (agent teams mode), to see whether "can communicate" substitutes for "has a contract." This is the comparison I most wanted and had no way to run.
  3. Scale the task up to 5000+ lines and see whether the contract's payoff is linear or superlinear.
  4. Repeat Arms B/C on a real Xcode project and quantify the .pbxproj conflict rate.

AppendixSources

Experiment artifacts (generated in this run)

  • Probe workflow: 14 agents, 419,907 tokens, 369.7s
  • Three-arm workflow: 13 agents, 755,388 tokens, 2266.2s
  • Verification tooling: Playwright + Chromium headless, 7 runs; static cross-analysis of ES module imports and exports

Official documentation

  • Create custom subagents — the context inheritance checklist, the three limits on concurrency / total count / nesting, the fork mechanism, the resume mechanism
  • Run agents in parallel — the official comparison table of the four parallelization modes
  • Orchestrate agent teams — teammates talk to each other, a shared task list, no automatic file isolation, the 3–5 recommendation
  • Manage agents with agent view — independent background sessions, automatic worktree isolation, linear quota consumption
  • Run parallel sessions with worktrees--worktree, .worktreeinclude, the desktop app assigning every session its own worktree automatically
  • How we built our multi-agent research system — multi-agent beats single agent by +90.2%; roughly 15× / 4× the tokens; token usage explains 80% of the performance variance; and it explicitly notes that most coding tasks are less parallelizable than research tasks

Practice and research