All reference pages
Rule

Dispatching subagents (all projects)

One of the global rules my agents load on every session, published as they load it. Links to reference files point at documents I do not publish.

Source
rob/agents/policy/claude/subagent-dispatch.md
Revision
4303422

operating-model owns which model to route a lane to and how to scope it. This file owns the dispatch mechanics. Pass model: on every dispatch; it defaults to the parent’s.

Never pass name: to an agent whose output you need back

Re-verified 2026-09-06 on 2.1.261: a named background agent goes idle and remains resident as a teammate. Its final text arrives late in the result field of an idle_notification, via the teammate channel rather than a completion notification. Dispatch unnamed for one-shot work.

Invocation What the dispatcher receives
Agent({name:"x", run_in_background:true}) delayed idle_notification.result via the teammate channel
Agent({run_in_background:true}) (no name) <task-notification>…<result> — payload intact
// ✅ Need the output back → NO name.
Agent({ description: "...", subagent_type: "general-purpose", run_in_background: true, prompt: "..." })

// ✅ Need it before continuing → synchronous, returns inline as the tool result.
Agent({ ..., run_in_background: false })

// Output is delayed through the teammate channel.
Agent({ name: "researcher", run_in_background: true, prompt: "...write me a brief..." })

Only use name: for a long-lived peer and account for its delayed teammate-channel result. A file handoff also works: have the agent write its brief to an absolute path and read it yourself.

Write every prompt so the agent’s last message IS the complete self-contained brief — TL;DR, findings, citations, signatures. The dispatcher never sees its transcript or intermediate work.

Do not tell a top-level subagent to SendMessage(to: "main"). It is rejected — from a subagent’s seat there is no “main” target; its final message is surfaced automatically. SendMessage-by-name only works between named peers.

Cross-session SendMessage between separate Claude Code sessions is a different mechanism and does not relax any of the above. Both configs set "crossSessionInbound": "accept" so peer messages deliver without a review dialog.

Verify the artifacts yourself

Every agent that failed to return a brief had still done correct work — the reporting failed, not the work. A missing brief is not evidence of a failed task: go look at what it produced.

It cuts both ways. A returned brief is not evidence of correct work either. Check load-bearing claims against the source, especially any claim contradicting something already verified.

A subagent cannot clear an auto-mode soft block

Subagents inherit auto mode and cannot opt out — a permissionMode in agent frontmatter is ignored when the parent is in auto mode. A soft block clears only on the user’s own message naming the specific action, and a subagent’s transcript contains no user message. A teammate message or a dispatch brief is explicitly refused as consent — the classifier calls that permission laundering.

So a lane can be blocked on work the main thread would have been allowed to do, with nothing able to clear it. Do not re-dispatch a blocked lane — that trips [Auto Mode Bypass], which reads a re-dispatch with pre-justifying wording as bad faith.

How to apply: dispatched lanes do read-only investigation, code edits, tests and inventory sweeps. SSH to a host, deploys, tenant or cloud mutations, secret-store writes and destructive git stay on the main thread. If a lane comes back blocked, run that step yourself or ask Rob to name it.

~/.claude/logs/permission-denied.jsonl records every denial with a subagent flag.

A lane loads the same shared rules as its dispatcher: do not include verbatim restatements in its brief; include only task-specific narrowing, such as what counts as evidence here and which paths or actions are fenced.