Routing & delegation policy (Wave B — "The Switchboard")
How Neo decides whether to answer directly, track steps inline, background the work, hand it to a specialist, or spin up a team — for a given incoming request. This is the policy encoded in the dispatch system-prompt section (src/runtime/agent-sections.ts, buildDispatchSection()); this document is the canonical, human-readable version of the same decision tree.
Why this exists
Reaching for delegation/background/specialist tools when a direct answer would do wastes tokens, adds latency, and makes the conversation harder to follow. Reaching for a direct synchronous answer when the work is long-running blocks a chat channel and provides a bad experience. The tree below is the single source of truth for that trade-off — see CC's own framing for why the distinction matters: reach for delegation/background "when research or multi-step implementation work would otherwise fill your context with raw output you won't need again" (claude-code-src/src/constants/prompts.ts:318-319).
The decision tree
Incoming task/request
│
├─ Quick bounded work (question, lookup, read-only inspection, small edit,
│ < ~30s of work — including routine repo-local CI/status/log checks)
│ → SELF (direct tools, no delegation)
│
├─ Session-scoped multi-step (3+ steps, bounded, same conversation, no long wait)
│ → SELF + todo-write (track steps inline, still synchronous)
│
├─ Long-running or would block the chat > ~30s
│ → delegate tool, run_in_background: true
│ → ANNOUNCE first ("I'll work on this in the background..."), then background
│ (chat channels never block on a long call — see "Auto-spawn policy" below)
│
├─ Substantial engineering where another coding harness adds real leverage
│ (multi-file implementation, broad debugging, independent review, or an
│ autonomous edit-test-verify loop)
│ → delegate tool with supervised: true — dispatches the code-specialist
│ DRIVER PERSONA (the retired "Smith" controller's data-defined successor),
│ which drives a code-session harness with a plan-review/verify loop. See
│ docs/code-session.md.
│
├─ Recurring or needs staged human approval (multi-run, scheduled, or a
│ human-gate before/between steps)
│ → workflow (src/workflows) — approval-queue + trigger substrate already
│ built for this; do not reinvent via delegate+ask-user loops. Run an
│ existing definition with the `workflow-run` tool; track and resolve via
│ `workflow-status`, `list-pending-approvals`, and `workflow-approve`
│ (all registered in src/runtime/embedded/tools-workflow.ts). Authoring
│ a NEW definition is owner-side (CLI/daemon YAML) — if no definition
│ fits, say so honestly and point the user at workflow setup.
│
└─ Multi-role work that benefits from several specialized agents collaborating
in parallel on facets of one goal
→ team (delegate tool with the "team" param — requires
run_in_background: true, not a separate tool call)Wire version (generated)
The exact dispatch system-prompt section fed to the model, injected from buildDispatchSection() (src/runtime/agent-sections.ts). The human tree above is the readable form of the same policy.
<dispatch>
How to handle an incoming request — pick ONE branch:
- Quick, bounded work (question, lookup, read-only inspection, small edit, < ~30s of work) — handle it yourself with direct tools. This includes routine repo-local checks such as reading a few files, git/CI/status/log lookups, or one obvious command. No delegation.
- Session-scoped multi-step work (3+ steps, bounded, same conversation) — handle it yourself, but call todo-write to track steps inline.
- Long-running or would block the chat > ~30s — use delegate with "run_in_background": true. Use foreground (default) when you need the results before you can proceed; use background when you have genuinely independent work to do in parallel. In a chat channel, send a short announcement first ("I'll work on this and let you know") before or alongside the call — never leave a chat channel silently waiting.
- Substantial engineering where a separate coding harness adds real leverage (multi-file implementation, broad debugging/exploration, independent code review, or an autonomous edit-test-verify loop) — use delegate with "supervised": true (and "run_in_background": true). The code-specialist persona plans, reviews its own plan, implements, and verifies, steering the coding agent turn by turn — dispatched in the background, so announce briefly and carry on. Leave "backend" unset so the specialist can choose from live capabilities unless the user pins a harness. For a delegated read-only review, set "maxPosture": "plan". Prefer plain delegation for routine one-shot tasks.
- Recurring work, or anything needing a staged human approval between steps — this is a workflow, not a delegate/ask-user loop. Run an existing definition with workflow-run; track/resolve via workflow-status, list-pending-approvals, and workflow-approve. Authoring a NEW definition is owner-side (CLI/daemon YAML) — if none fits, say so and point the user at workflow setup.
- Multi-role work needing several specialized agents in parallel on facets of one goal — use delegate with the "team" param (requires "run_in_background": true). This is a delegate option, not a separate tool.
Choose delegation for the leverage it adds, never merely because a repository is mentioned or one direct tool attempt failed. Fix ordinary tool friction directly when cheap; escalate when the direct path is genuinely blocked or the accumulated complexity warrants a separate agent context. Honor an explicit user request for a particular harness. Ambiguous expensive choices may warrant one brief clarifying question; cheap, reversible choices do not.
</dispatch>The model chooses delegation for the leverage it adds, not merely because a repository is mentioned or one direct tool attempt failed. Routine tool friction should be fixed directly when cheap; escalation makes sense when the direct path is genuinely blocked or the accumulated complexity warrants a separate agent context. An explicit user request for a particular harness is honored; otherwise the supervised persona chooses from live backend evidence. When a delegated review is read-only, the caller sets maxPosture: "plan" so the boundary is structural. Ambiguous expensive choices may route through the clarify gate (Wave B chip 2); cheap/reversible choices (self vs. self+todo) do not.
The self vs. self+todo threshold
3+ steps, or non-trivial multi-step work, is the concrete threshold for switching from a silent direct answer to tracking steps with todo-write — ported verbatim from CC's own TaskCreate guidance: create tasks proactively for 3+-step / non-trivial work, and skip it for single trivial tasks or fewer than 3 trivial steps (claude-code-src/src/tools/TaskCreateTool/prompt.ts:19-30,34-40).
Foreground vs. background
The cleanest one-line rule for when to background a call, ported verbatim from CC: "Use foreground (default) when you need the agent's results before you can proceed — e.g., research agents whose findings inform your next steps. Use background when you have genuinely independent work to do in parallel." (claude-code-src/src/tools/AgentTool/prompt.ts:263-264)
Auto-spawn policy — backgrounding work the user didn't explicitly ask to background
This is about when Neo decides on its own, without the user saying "do this in the background," to background a call:
- Duration threshold: ~30s. If a task looks likely to run longer than that, don't make a chat channel wait — background it.
- Channel-context awareness: a chat channel (Telegram/WhatsApp/etc.) must never block longer than ~30s — announce first ("I'll work on this and let you know"), then call
delegate/code-specialistwith backgrounding on. A CLI/direct session can wait synchronously longer, since a human is watching a spinner and can interrupt if they don't want to wait. - Daemon posture: the daemon is always backgroundable — there is no synchronous constraint there; nothing is waiting on a spinner.
- Neo already has the announce-then-background shape built in:
code-specialist's tool result returns immediately with a descriptive message ("Code specialist (...) dispatched and running in the background...") — that IS the announcement. Use it as-is; the model's job is to choose the right tool and let its existing return message serve as the announcement (fordelegatein background mode, send a short announcement sentence in your own reply before or alongside the tool call).
Tool reference (Neo's actual names)
| Branch | Tool | Notes |
|---|---|---|
| Self | (direct tools) | No delegation |
| Self + todo | todo-write | Tracks steps inline, still synchronous |
| Background | delegate | run_in_background: true |
| Substantial engineering | delegate | supervised: true — dispatches the code-specialist driver persona over a code-session harness; background by default (see docs/code-session.md) |
| Recurring/approval | workflow-run | Plus workflow-status / list-pending-approvals / workflow-approve; definitions authored owner-side (CLI/daemon YAML) |
| Team | delegate | team: "<name>" param, requires run_in_background: true |