Running multiple Claude agents in VS Code
A working tutorial for going from one Claude Code chat to a coordinated stack of leads, project leads, and autonomous workers that talk to each other with SendMessage. Built around the daily-driver setup described by Daisy, an engineer on Claude Code, and checked against the current Claude Code docs.
What changed in this revision
- Windows: cross-session messaging now works on native Windows from v2.1.234. WSL 2 is only needed on older versions. Note that a WSL 2 session and a native Windows session on the same PC cannot see each other.
- Model defaults: the
teammateDefaultModelsetting was removed in v2.1.234 and is ignored. Name the model in the spawn prompt or setCLAUDE_CODE_SUBAGENT_MODEL. - @-mentions: from v2.1.232 you can type
@pm-galleryin a prompt to address another session by name without listing first. - Idle notices: from v2.1.236 a session can ask another local session for a one-shot “tell me when you're next idle” notice, which is the right replacement for polling or heartbeats.
- Inbound control UI:
crossSessionInboundcan now be set from the/configrow “Messages from your other sessions” (v2.1.232+). - Listing:
/list-agents(alias/peers) now shows your own session's name on the first line and includes teammates (v2.1.239+). - Permission syntax: Bash rules now use the documented prefix form
Bash(npm test:*); earlier revisions used a plain*. - Role tools: implementer and reviewer now include
SendMessageso teammates spawned from them can message directly. - /agents removed: current builds drop the
/agentswizard — verify roles by asking Claude which subagent types are available. - Panel: from v2.1.199, an idle teammate's row stays visible while any agent is still working; rows hide only after the whole panel has been idle 30 s. More than three idle rows collapse into one “N idle agents” row.
SendMessage call. The dashed lines are where the human actually spends the day.- The mental model: three kinds of “agent”
- Level 0 — Install and enable everything
- Level 1 — One session, subagents
- Level 2 — Your first agent team (review only)
- Level 3 — A team that builds (file ownership, plan approval, hooks)
- Level 4 — The Daisy stack: leads, PMs, cross-session messaging
- Practice project: Gallery Proof
- Operating the stack day to day
- Field notes from the first real run
- Troubleshooting
- Sources
Before you start · The mental model: three kinds of “agent”
Claude Code has three different mechanisms that people all call “agents.” Daisy's setup uses all three, stacked. Getting the vocabulary right up front saves a lot of confusion later.
| Mechanism | What it is | Talks to | Use it for |
|---|---|---|---|
| Subagent | A helper Claude spawned inside your session with its own context window. Defined as a Markdown file in .claude/agents/. Reports a summary back when done. | Only the parent | Focused, bounded jobs: “go read 40 files and tell me X.” Cheapest option. |
| Agent team | Experimental. Your session becomes the lead; it spawns teammates, each a full Claude Code instance. They share a task list and message each other directly. | Lead and each other | Parallel work with real coordination: a feature split across backend/frontend/tests, or a debate between competing hypotheses. |
| Cross-session messaging | Separate Claude Code sessions you started yourself (different windows, worktrees, projects) can discover each other with ListAgents and message with SendMessage. | Any session you're running | Gluing independent sessions together: the “two leads” and “lead → PM” layers of Daisy's stack. |
Key fact that shapes the whole architecture: teammates can't spawn their own teammates, and a session has exactly one team. So “lead → project lead → IC agents” can't be one giant nested team. Each PM is its own session with its own team, and the leads reach PMs through cross-session messaging. That's the whole trick, and it's why Level 4 is a multi-window setup rather than one big prompt.
How this maps onto Daisy's quote
- “Two lead agents that keep each other accountable” → two long-running sessions (two VS Code windows or terminals) using cross-session messaging to check in on each other.
- “Delegate to tech lead or PM agents for 8–10 projects” → one session per project, usually in its own git worktree. Leads message PMs; PMs never message the leads' teammates.
- “Each project has 5–10 IC agents” → each PM session has agent teams enabled and spawns a team of teammates. This is where the shared task list lives.
- “IC agents work autonomously for 2–3 days” → teammates with clear file ownership, 5–6 sized tasks each, plan approval for risky changes, and quality-gate hooks so they don't go idle on bad work.
- “All communicate with SendMessage” → the same tool serves all three layers: subagent resume, teammate messaging, and cross-session messaging.
0Install and enable everything
Goal: CLI on PATH, VS Code extension signed in, agent teams turned on, and one config file you'll reuse for every project.
Platform check first. Cross-session messaging (the Level 4 glue) requires v2.1.224+ on macOS, Linux and WSL 2, and v2.1.234+ on native Windows. On an older Windows build, do Levels 0–3 natively and run Level 4 inside WSL 2. Don't mix: a WSL 2 session and a native Windows session on the same PC register under different home directories and can't see each other. Agent teams' split-pane display mode doesn't work inside VS Code's integrated terminal on any OS, so you'll use in-process mode throughout, which is fine.
- Install the standalone CLI.
The VS Code extension bundles its own private copy of the CLI for the chat panel, but it does not put
claudeon your PATH. You need the standalone install to runclaudein the integrated terminal, which is where most of the multi-agent work happens.# Native installer (recommended in the docs; avoids npm permission issues) curl -fsSL https://claude.ai/install.sh | bash # macOS / Linux / WSL # or npm install -g @anthropic-ai/claude-code claude --version # want 2.1.224+ (2.1.234+ on native Windows); 2.1.236+ for idle notices
- Install the VS Code extension. Cmd/Ctrl+Shift+X → search “Claude Code” → install the one published by Anthropic (there are look-alikes). Needs VS Code 1.94+. Click the ✱ Claude Code item in the status bar, sign in with your Claude subscription. No API key needed.
- Open the integrated terminal and run
claudeonce. Ctrl+` (or Cmd+`), typeclaude, and inside the session run/ideif it didn't auto-connect. You'll know it's connected when Claude's edits open in VS Code's diff viewer. Run/terminal-setuponce so Shift+Enter inserts a newline instead of sending. - Enable agent teams in your user settings.
Open
~/.claude/settings.json(create it if missing) and add the env var. Adding the$schemaline gives you autocomplete in VS Code.{ "$schema": "https://json.schemastore.org/claude-code-settings.json", "env": { "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1", "CLAUDE_CODE_SUBAGENT_MODEL": "sonnet" }, "teammateMode": "in-process" }CLAUDE_CODE_SUBAGENT_MODELis the default for any subagent or teammate that doesn't name a model. (teammateDefaultModelwas removed in v2.1.234 and is ignored if you still have it; delete it.) One side effect to know about: with the teams flag on, any subagent Claude decides to name launches as a teammate instead. If you ever see a delegation stall waiting for a result, that's why; flip the value to"0"and it takes effect without restarting. - Pre-approve the boring stuff.
Teammate permission prompts bubble up to the lead, and with 5–10 teammates that gets noisy fast. Add a project-level
.claude/settings.jsonthat allows the commands your project uses constantly. Keep the deny list for anything destructive.{ "permissions": { "allow": [ "Read", "Edit", "Write", "Glob", "Grep", "Bash(npm test:*)", "Bash(npm run:*)", "Bash(node:*)", "Bash(git status:*)", "Bash(git diff:*)", "Bash(git add:*)", "Bash(git commit:*)", "Bash(git log:*)" ], "deny": [ "Bash(git push --force:*)", "Bash(rm -rf:*)", "Read(.env)", "Read(.env.*)", "Read(**/.env)", "Read(**/.env.*)" ] } } - Verify.
In a terminal session run
/status(should show a “Peer address” row if cross-session messaging is available) and/list-agents(alias/peers; should run, and its first line is this session's own name). In the chat panel, type/and confirm you can see the permission mode switcher at the bottom of the prompt box.
Where things live. ~/.claude/settings.json is shared by the extension and the CLI. .claude/settings.json in a repo is per-project and safe to commit. .claude/agents/*.md are your reusable roles. CLAUDE.md at the repo root is read by every session and every teammate, so it's your single best lever for consistency across a team.
1One session, subagents
Goal: define three reusable roles as subagent files and watch Claude delegate to them. These same files become your teammate roles in Level 3, so this isn't throwaway work.
A subagent is a Markdown file with YAML frontmatter. The frontmatter is the config (name, when to use it, tool allowlist, model); the body is its system prompt. Project-level files go in .claude/agents/; personal ones in ~/.claude/agents/. The description field is what Claude reads to decide whether to delegate, so make it specific.
Three roles to create
Create these in the practice project from the Gallery Proof section, or any repo you have handy.
# .claude/agents/explorer.md — read-only, cheap, fast
---
name: explorer
description: Read-only codebase investigation. Use when you need to find where something lives, how it's used, or what a module exports, without changing anything.
tools: Read, Grep, Glob
model: haiku
---
You are a codebase investigator. Answer with file paths, line numbers, and a short summary.
Do not propose changes. Do not read .env files.
# .claude/agents/implementer.md — writes code, runs tests
---
name: implementer
description: Implements a clearly scoped feature or fix in a specific set of files, with tests. Use when the files to change are already known.
tools: Read, Edit, Write, Grep, Glob, Bash, SendMessage
model: sonnet
---
You implement exactly the scope you were given, in exactly the files you were given.
Before finishing: run `npm test`. If tests fail, fix them or report precisely why you couldn't.
Never edit files outside your assigned list. If you need a change elsewhere, say so instead of making it.
# .claude/agents/reviewer.md — read-only critic
---
name: reviewer
description: Reviews a diff or a set of files for correctness, security, and test coverage. Use after code is written and before it is merged.
tools: Read, Grep, Glob, Bash(git diff:*), Bash(git log:*), SendMessage
model: sonnet
---
You are a senior reviewer. Run `git diff` to see what changed. Report findings as
Critical / Warning / Suggestion, each with file:line and a one-sentence fix. Be specific and brief.
Restart before delegating. Role files are read when a session starts, so a session that predates .claude/agents/ won't see them — the delegation falls back to built-in agents (“Agent type 'explorer' not found”). Restart and verify by asking Claude which subagent types are available; the /agents wizard is removed in current builds.
Try it
In the VS Code chat panel or the terminal session:
What to notice: each subagent runs in its own context window and hands back a summary, so your main conversation stays clean. That isolation is the entire reason subagents exist; the main session's context is the scarce resource.
Model choice as a cost lever. Run the lead on the strongest model you have; set workers' model: explicitly (Haiku for search, Sonnet for implementation). If you leave it off, workers inherit the lead's model unless CLAUDE_CODE_SUBAGENT_MODEL is set. For teammates, you can also name the model in the spawn prompt (“use Sonnet for each teammate”). A teammate's model is fixed at spawn; /model later only changes the lead. With 5–10 workers per project, this is where the bill is decided.
2Your first agent team (review only)
Goal: spawn a team of three teammates who investigate in parallel and talk to each other, without writing any code. This is what the docs recommend as the first team, and it shows you the panel, the task list, and direct messaging with zero risk of file conflicts.
- Start a terminal session in the repo.
claudein the integrated terminal. (The graphical chat panel also supports teams, but the terminal's agent panel gives you the clearest view of who's doing what while you're learning.) - Ask for a team explicitly.
Claude sometimes spawns subagents when you meant teammates; say “agent team” and “teammates.”
PromptCreate an agent team to review the current state of this repo. Spawn three teammates named security, performance, and tests, using Sonnet for each: - security: look for auth, input-validation, and secrets-handling problems - performance: look for N+1 queries, unbounded reads, and anything that won't scale past a few hundred galleries - tests: assess what's covered, what isn't, and which untested paths are riskiest Have them message each other when one finds something relevant to another's area, then each report findings to you with severity. Wait for all three before you synthesize.
- Watch the agent panel. Teammates appear below the prompt box. ↑/↓ selects one, Enter opens its transcript so you can read it and message it directly, Esc interrupts its current turn, x stops it, Ctrl+T toggles the shared task list. Since v2.1.199 an idle row stays put while any agent is still working; rows hide 30 s after the whole panel goes idle, and more than three idle rows collapse into one “N idle agents” row (Enter expands).
- Message a teammate directly.
Select
tests, press Enter, and type: “Also check whether the upload route has any test at all.” Plain text and skills go to that teammate; built-in slash commands still run in the lead's session. - Read the synthesis, then shut down. “Ask all teammates to shut down.” Team directories clean up automatically when your session ends.
What just happened under the hood
- Each teammate is a full Claude Code session: it loaded
CLAUDE.md, your MCP servers, and skills, but not the lead's conversation history. Everything it knows about the job came from the spawn prompt. - Messages were written to per-agent inbox files under
~/.claude/teams/<session-xxxxxxxx>/inboxes/and delivered automatically; nobody polls. - When a teammate finished, the lead got an idle notification without the output. Results only travel by message or by task-list updates. That's why the prompt says “report findings to you.” (From v2.1.198, a teammate that dies on an API error reports the failure and error text instead of looking finished.)
- Teammates inherited the lead's permission mode. Prompts they trigger show up in your lead session for you to approve.
Name your teammates in the spawn prompt. Names are how you (and other agents) address them later, and names keep working even after an agent completes, because a send resumes it from its transcript.
3A team that builds
Goal: ship a real feature with a 4-teammate team, using the three habits that make autonomous multi-day work possible: strict file ownership, plan approval for risky work, and hooks as quality gates.
Habit 1 — file ownership, written down
Two teammates editing one file produces overwrites. The fix is not cleverness; it's an explicit ownership map in the spawn prompt and in the task list. The practice project below is laid out so that each module is a separate directory precisely to make this easy.
Habit 2 — plan approval for anything that touches shared contracts
Tell the lead to require plan approval for the teammate whose work others depend on (usually the data layer or the API contract). The teammate stays in read-only plan mode until the lead approves. The lead approves autonomously, so give it criteria: “only approve plans that list the exact files to be changed and include test coverage.”
Habit 3 — hooks that refuse bad completions
Three hook events exist specifically for teams. Exit code 2 sends feedback and keeps the agent working.
| Hook | Fires when | Use it to |
|---|---|---|
| TaskCompleted | a teammate marks a task complete | run the test suite; refuse completion if it fails |
| TeammateIdle | a teammate is about to go idle | check for uncommitted work or an unreported result and send it back |
| TaskCreated | the lead creates a task | enforce that every task names its owner and its file list |
# .claude/settings.json (project) — gate task completion on green tests
{
"hooks": {
"TaskCompleted": [
{ "hooks": [ { "type": "command",
"command": "npm test --silent >/tmp/cc-test.log 2>&1 || { echo 'Tests failed. Fix them before completing this task:'; tail -30 /tmp/cc-test.log; exit 2; }" } ] }
],
"TeammateIdle": [
{ "hooks": [ { "type": "command",
"command": "if [ -n \"$(git status --porcelain)\" ]; then echo 'You have uncommitted changes. Commit to your branch and message the lead with a summary before going idle.'; exit 2; fi" } ] }
]
}
}
The build prompt
Run this in the practice project after Level 2. It's long on purpose; teammates only know what the spawn prompt tells them.
While it runs
- Open Ctrl+T and watch tasks move pending → in progress → completed. Dependent tasks unblock automatically.
- If the lead starts coding, tell it: “Wait for your teammates to complete their tasks before proceeding.”
- If a task looks stuck, check whether the work is actually done (task status can lag) and nudge the owner by name.
- When done, you merge the four branches yourself, or ask the lead to. Keep the human on merges and releases; that's the 5% of your day that matters most.
Sizing rule from the docs: start with 3–5 teammates; 3 focused teammates usually beat 5 scattered ones; 5–6 tasks per teammate keeps everyone busy and lets the lead reassign if someone stalls. Scale to Daisy's 5–10 only when the work really splits that wide.
4The Daisy stack: leads, PMs, cross-session messaging
Goal: run two or more projects at once, each with its own PM session and IC team, with one or two lead sessions coordinating them through cross-session messaging. This is where “30–50 prompts a day” becomes realistic.
Everything in Levels 1–3 happened inside one session. Daisy's setup is many sessions, arranged in layers, each started by you. Here's the layout for two projects; add more PM windows for more projects.
VS Code window 1 ─ lead-a claude --name lead-a (repo root, no team; it coordinates) VS Code window 2 ─ lead-b claude --name lead-b (repo root, no team; mirrors lead-a) VS Code window 3 ─ pm-gallery claude --worktree gallery-proof --name pm-gallery (own checkout; runs a team) VS Code window 4 ─ pm-site claude --worktree site-refresh --name pm-site (own checkout; runs a team) …one window per project
Step-by-step
- One worktree per project.
Each PM session gets an isolated checkout and branch so teams can't collide across projects.
claude --worktree <name>creates it under your repo and starts the session there. Name every session so messages have stable addresses;/renameworks too. If two live sessions would share a name, Claude Code renames the newer one to a variant, and/list-agentsshows each session's working directory so you can tell them apart. - Open one VS Code window per session.
File → New Window, open the worktree folder, integrated terminal, start
claudewith the name. (Tabs in the chat panel also work for the leads, but separate windows make it obvious which agent you're typing at, and you'll be switching windows all day.) Use the Activity Bar session list's groups to keep “leads,” “PMs,” and “scratch” separate. - Confirm they can see each other.
In any session:
/list-agents. The first line is this session's own name; below it are the other local sessions with state, age and directory. If the command isn't recognized, you're below v2.1.224 (or v2.1.234 on native Windows). - Give each PM a standing brief.
This is the project's charter. It goes in the PM session's first prompt and, for anything durable, into that worktree's
CLAUDE.md.Prompt — to pm-galleryYou are the project lead for Gallery Proof. Your job is to run an agent team that delivers the milestones in docs/ROADMAP.md, in order, one milestone per team run. You never implement; you plan, spawn teammates with explicit file ownership, require plan approval for db changes, keep 5–6 tasks per teammate, and merge completed branches into this worktree's branch after qa signs off. Reporting: when a milestone completes or you are blocked for more than one task cycle, message lead-a with a 5-line status (milestone, done, blocked, next, risk). If lead-a does not reply within one cycle, message lead-b. Do not message the other project leads. Start milestone 1 now. - Give the leads their accountability loop.
Leads hold no team; they coordinate.
Prompt — to lead-a (mirror it for lead-b, swapping names)You are lead-a. Your peers are lead-b and the project leads pm-gallery and pm-site. Your responsibilities: 1. Keep a running status board in docs/STATUS.md (one line per project) from the status messages PMs send you. 2. Every time you receive a PM status, decide: approve next milestone, ask a question, or escalate to me. Escalate only for scope changes, spend concerns, or anything touching billing or customer data. 3. Accountability with lead-b: after each status update, message lead-b a one-line ping. If you receive no ping from lead-b across two PM updates, or lead-b reports an error, tell me immediately and include the last thing you heard from lead-b, so I can restart it. 4. When you approve a milestone for a PM, ask to be notified when that PM session next goes idle, so you hear about completion without polling. 5. Never approve your own permission prompts via another agent; anything that needs consent comes to me.
- Run it for a day.
Your interaction pattern should start to look like Daisy's: most prompts go to a lead, a third go to a PM when you care about the details of one project, and occasionally you're in an IC's transcript because something went sideways. From v2.1.232 you can address a session directly in any prompt with an @-mention: “Let
@pm-siteknow the shared auth helper changed.” Type@plus a letter or two and pick the session from the typeahead.
About “restart the other if either fails”
This is the part of Daisy's setup that the public docs don't spell out, so here's a clear-eyed version of what you can and can't do today.
What works out of the box
- Leads detect each other's silence or error via the ping protocol above and tell you.
- Idle notices (v2.1.236+): a session can ask any other local session for a one-shot notice when it next goes idle or exits. It costs no tokens in the watched session, fires immediately if that session is already idle, and expires after 12 hours. This replaces heartbeats.
- A message wakes an in-process teammate that's waiting on an API retry, so it retries immediately.
- If a teammate dies, the lead can spawn a replacement with the same name and brief.
- You restart a session with
claude --resume(the extension and CLI share history), and/list-agentsshows it's back.
What you'd have to build
- A lead can't “click Resume” on another window. To automate a restart, give the lead a Bash-allowed script that launches
claude --resume <session-id> --name lead-bin a new terminal (how that terminal opens is OS-specific). Treat it as a power tool and allowlist it narrowly. - Message loops are throttled by design: repeated messages from one sender are rate-limited, identical repeats in a short window are dropped, unread queues cap at 50, and a rapid burst is refused at the sender. A “ping every 30 seconds” heartbeat won't work. Tie pings to events (PM updates) or use idle notices.
- Cross-session messages carry text only, never history, and are capped at about a million characters. Put anything a restarted lead needs to recover from in
docs/STATUS.md, not in messages.
Safety boundaries you should want. A message from another agent is flagged to the receiver as coming from another Claude session, not from you. It can't approve a permission prompt, can't change settings or CLAUDE.md, a slash command inside it is just text, and a teammate that was denied something can't ask another teammate to do it. In auto mode the classifier reviews inter-agent messages before delivery. A session that bypasses permission prompts holds incoming messages for your approval by default (five-minute expiry); tune this per session with crossSessionInbound (accept / hold / refuse), now also available from the /config row “Messages from your other sessions.” Don't try to route around any of this with --dangerously-skip-permissions; with ten sessions running, the permission layer is what keeps one confused agent from becoming ten.
Practice project · Gallery Proof — a small app built to be split
A throwaway client-proofing app for photographers: upload a gallery, share a link, client marks favorites, photographer sees the picks. Close enough to your real world that the decisions feel real, small enough to build in an afternoon, and deliberately laid out so four to six agents can work without touching each other's files.
Stack
Node 20 + Express, SQLite via better-sqlite3, vanilla HTML/JS frontend served statically, Node's built-in test runner (node --test). No build step, no framework decisions to argue about, tests run in under a second, which matters when a hook runs them on every task completion.
Layout (ownership boundaries are directories)
gallery-proof/ ├── CLAUDE.md ← conventions every agent reads ├── .claude/ │ ├── settings.json ← permissions + hooks from Level 0 / Level 3 │ └── agents/ ← explorer.md, implementer.md, reviewer.md ├── docs/ │ ├── ROADMAP.md ← milestones (below) │ ├── STATUS.md ← leads write here │ └── specs/ ← one spec per feature ├── src/ │ ├── db/ ← schema.sql, migrate.js, repositories/*.js │ ├── api/ ← routes.js + one folder per resource │ ├── web/ ← static pages + client JS │ └── server.js ← wires api + static ├── tests/ │ ├── unit/ ← per-module │ └── integration/ ← end-to-end over HTTP └── package.json
Bootstrap it
Let a single session scaffold it. Spend the prompt on conventions, not code.
Roadmap (one milestone per team run)
| Milestone | Scope | Natural teammate split | Practices |
|---|---|---|---|
| M1 Galleries | Create gallery, upload images to local disk, list images, share link with token | db · api · web · qa | Level 3: ownership + plan approval on db |
| M2 Favorites | Client toggles favorites via share link; photographer sees picks | db · api · web · qa | Level 3: hooks gating completion |
| M3 Hardening | Rate limiting on share links, image size limits, expiring tokens | security · performance · tests (review team) then 2 implementers | Level 2 → Level 3 handoff |
| M4 Second project | A sibling project (e.g. a tiny “print order” form) in its own worktree | Its own PM + team | Level 4: two PMs, two leads |
The spec that teammates will read
Teammates only know what's in the spawn prompt plus the files they read, so specs need to be concrete. Example for M2:
# docs/specs/favorites.md
## Goal
A client with a valid share token can mark/unmark images as favorites.
The photographer sees a "Favorites (n)" view per gallery.
## Data
favorites(gallery_id, image_id, created_at) — unique on (gallery_id, image_id)
## API
POST /api/galleries/:token/favorites/:imageId → 201 {imageId}
DELETE /api/galleries/:token/favorites/:imageId → 204
GET /api/galleries/:token/favorites → 200 {imageIds:[...]}
All three 404 on unknown token. Token check reuses src/api/share/auth.js.
## Web
Heart toggle on each image in gallery.html; favorites.html lists picks.
## Tests
Unit: repository functions. Integration: all three endpoints incl. 404s.
CLAUDE.md — the part that does the most work
# Gallery Proof — conventions for every agent
## Boundaries (hard rules)
- src/db: all SQL lives here. Exposes repository functions only.
- src/api: HTTP only. Calls repositories. No SQL.
- src/web: static files. Talks to /api only.
- tests: node --test. Unit tests mirror src/ paths.
## Working agreement
- One branch per teammate: feat/<milestone>-<name>. Conventional commits.
- Run `npm test` before marking any task complete. Failing tests = not complete.
- Never edit files outside your assigned ownership. Message the owner instead.
- Report results by messaging the lead; going idle is not a report.
- Never read .env files. Never run git push --force.
Day to day · Operating the stack
A realistic daily rhythm
Morning (10 prompts): read docs/STATUS.md, ask lead-a for overnight exceptions, approve next milestones.
Midday (15–20 prompts): drop into one PM for the project you care about today; review its merges; adjust specs.
Afternoon (10 prompts): handle escalations; start new milestones; shut down finished teams.
Anytime (a few prompts): something derailed; open that IC's transcript, redirect or replace it.
Cost controls that matter
- Each teammate is a full Claude instance; token cost scales roughly linearly with active teammates.
- Run
/usagein the chat panel: it attributes usage to subagents and flags “subagent-heavy” behaviour. - Haiku for exploration, Sonnet for implementation, your strongest model for leads and PMs only. Set
CLAUDE_CODE_SUBAGENT_MODELas the floor. - Shut teams down when a milestone ends. Idle teammates are cheap; forgotten ones aren't.
Checklist before letting a team run unattended
- Ownership map is in the task list and in the spawn prompt.
- TaskCompleted hook runs tests; TeammateIdle hook demands a commit and a report.
- Plan approval is on for the data layer.
- Permissions allowlist covers routine commands; deny list covers the destructive ones.
- Every agent has a name you chose.
- The PM's reporting protocol names a lead and a fallback lead, and the lead subscribes to idle notices instead of polling.
- Merges and releases stay with you.
Field notes from the first real run
This guide was walked end-to-end on a MacBook Pro against Claude Code v2.1.240 before being published. Everything worked; these are the trip-wires that actually fired, so you can step over them.
Subagent role files load at session start, not on save. The first delegation after creating .claude/agents/ failed with “Agent type 'explorer' not found” and silently fell back to the built-in Explore agent, because the session predated the files. Restart the session (/exit, then claude again) after creating or editing roles. The /agents wizard has been removed in current builds — verify your roles loaded by asking Claude “which subagent types are available?” or by typing @exp and checking the typeahead.
Give implementer and reviewer the SendMessage tool. The tools: line is an allowlist, and the original versions of these role files left SendMessage off. The build team routed around it with shared scratchpad files — it worked, but direct messaging is better and Stage 5 depends on it. The role files in this revision include it.
Watch for a stale editor tab while Claude edits settings. If a file is open in VS Code with unsaved changes (white dot on the tab instead of an ×), VS Code will not refresh it when Claude writes the file on disk — you'll be looking at your stale copy while the real file is different. This bit us twice on .claude/settings.json: once when a pasted hooks block was never actually saved, once when Claude's edit didn't appear until the file was reopened. Rule: before asking Claude to edit a file, save or close your tab for it, and after, trust cat over the editor.
Things that worked exactly as designed, worth watching for: the plan-approval gate on the shared-contract teammate ran as described (the lead reviewed and approved the physics plan before engine and ui unblocked); the QA teammate rejected an untestable design and the owning teammate added a pure primitive in response — cross-owner correction with no human involvement; and a spec gap surfaced as a decision rather than a bug (“bricks killed by laser don't drop capsules — deliberate or oversight?”). Specs don't need to be complete; they need to be concrete enough that gaps become questions.
Small mechanics: when Claude offers numbered options, arrow keys + Enter, the number key, or Tab all select — you don't retype the option. And git diff --stat (the file-by-file +/- summary Claude shows before committing) is your fastest ownership check: did it change the files you'd expect, and only those?
When it goes sideways · Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| “Agent type not found” for a role you created | The session started before the role files existed; roles load at session start | Restart the session (/exit, claude); verify by asking which subagent types are available |
| Asked for a team, got subagents | Claude judged the task didn't need a team | Ask again with “agent team” and “teammates” explicitly; say how many |
| Teammate rows vanished from the panel | Idle rows hide 30 s after the whole panel goes idle; >3 idle rows collapse into one | They're still running. Select the “N idle agents” row and press Enter, or message the teammate by name |
| Lead started implementing itself | Default lead behaviour | “Wait for your teammates to complete their tasks before proceeding” |
| Dependent task never unblocks | Teammate forgot to mark its task complete | Check the work; update the task or tell the lead to nudge the owner |
| Too many permission prompts in the lead | Teammates inherit the lead's mode; each prompt bubbles up | Pre-approve routine commands in project settings |
| Delegation stalls waiting for a subagent result | With teams enabled, a named subagent launched as a teammate and only sent an idle notice | Set CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS to "0" for that project; takes effect on save |
| Teammate runs on the wrong model | teammateDefaultModel is ignored since v2.1.234 | Name the model in the spawn prompt or set CLAUDE_CODE_SUBAGENT_MODEL |
After /resume, lead messages teammates that don't exist | In-process teammates aren't restored on resume | Tell the lead to spawn new teammates with the same names and briefs |
/list-agents not recognized | Below v2.1.224 (v2.1.234 on native Windows), or a telemetry-disabling env var (DISABLE_TELEMETRY, DO_NOT_TRACK, etc.) turned off the feature flag | Update the CLI; unset the variable; on old Windows builds, use WSL 2 for Level 4 |
/list-agents works but a message never arrives | A deny rule on SendMessage/ListAgents, or the receiver's crossSessionInbound is holding/refusing; or WSL vs native Windows sessions | Check both sessions' settings or the /config row; held messages expire after about five minutes by default; keep all sessions on one side of WSL |
| “Too many messages to this session just now” | Burst refused at the sender (v2.1.236+) | Batch into one message or wait; never tie pings to timers |
| “Failed to write to teammate inbox” | Disk full or inbox directory not writable | Free space / fix permissions under ~/.claude/teams/; the send was not delivered |
Verified against · Sources
- Orchestrate teams of Claude Code sessions — code.claude.com/docs/en/agent-teams (enable flag, display modes, plan approval, hooks, architecture, limitations, troubleshooting)
- Use Claude Code in VS Code — code.claude.com/docs/en/vs-code (extension install, bundled vs standalone CLI, tabs/windows, session groups, settings, worktrees)
- Message your other Claude Code sessions — code.claude.com/docs/en/cross-session-messaging (ListAgents/SendMessage, platform support, @-mentions, idle notices, inbound controls, throttling)
- Create custom subagents — code.claude.com/docs/en/sub-agents (frontmatter fields, scopes, model selection)
- Claude Code docs index — code.claude.com/docs/llms.txt