parallel-agent-dispatch

v2026.09.24

Dispatch contract for spawning parallel agents covering worktree collisions, scope overflow, and silent exits. Use when fanning out concurrent agents or authoring a lead prompt.

GitHub
Install command
npx skhub add laurigates/parallel-agent-dispatch
Markdown
SKILL.md

Parallel Agent Dispatch

Conventions that apply every time more than one agent runs in parallel. Prevents the top failure modes observed across real multi-agent sessions: dirty-worktree cross-contamination, context overflow mid-task, and silent exits that require manual salvage from orphan branches.

Supporting material is split across references/ by the path that needs it; REFERENCE.md is the index. Sections below link the specific file.

When to Use This Skill

Use this skill when...Use agent-teams instead when...
Spawning >1 agent via plain Agent tool fan-out (N concurrent invocations)Single-agent delegation or one-off subagent spawn
Using the implicit team + teammate spawn for coordinated parallel workA simple background task with no parallel siblings
Running worktree-isolated parallel implementation across repos/featuresA read-only inline subagent that does not write to disk
Coordinating parallel investigation or audit swarmsThe work fits in the current session without forking

Dispatch from the Main Thread When Possible

Agent and other parallel-spawn tools may be absent from a sub-agent's sandbox even when available in the main conversation, so designing a fan-out from inside a coordinating sub-agent risks silent degradation to sequential execution.

  • Default: dispatch from the main conversation — the full tool surface is guaranteed.
  • Sub-agent orchestrator: only when the team's outputs need not feed back into the main thread. Brief it to verify tool availability up front and report sequential fallback as a first-class outcome (agent-teams → "Sub-Agent Caveat").

The Three Pillars

1. Worktree Preflight

Before spawning, the orchestrator must verify:

CheckRationale
Main working tree is clean (git status --porcelain empty)Agents inherit cwd; uncommitted changes cross-contaminate worktrees
No existing worktree at each planned path (git worktree list)Nested or duplicate worktrees are the #1 source of salvage work
Each agent gets a unique branch namePrevents commits landing on the wrong branch when cwd resolution drifts
Fixed target branch name not already taken (see Target-branch preflight below)A conventional per-issue/milestone name is one two sessions pick identically; the collision otherwise surfaces only at end-of-task rename
Shared counters snapshot (next ADR/PRP number, feature-tracker IDs)Prevents numbering collisions in parallel doc writes

If any check fails, refuse to dispatch and report the blocker. Do not "clean up" uncommitted user work — surface it and ask.

Target-branch preflight (#1969). isolation: "worktree" auto-names the branch; renaming onto a fixed conventional name another session's worktree already holds is refused only at the end-of-task rename, deep into the run (real case: two sessions both reached PR-open → duplicate-PR reconcile). Check the name is free first (git branch -a --list "$target", git worktree list, git ls-remote --heads origin "$target"); any hit ⇒ stop and reconcile, not race to a duplicate PR (.claude/rules/concurrent-session-pr-check.md). Mitigation: push via explicit refspec (git push origin HEAD:$target) instead of renaming. See references/worktree-hazards.md → Target-branch preflight.

Transient worktree leaks (#1319). While a wave runs, a file a child wrote inside its worktree can briefly appear in the parent as an untracked entry at the same relative path, then vanish when the child commits. Do not stash, restore, or commit untracked parent files during a wave; wait for the child's completion, then let its branch reclaim the file. /git:coworker-check raises worktree_leak_suspected for this — run it before every parent-side commit.

cwd-reset leaking git writes (#1480). Distinct from the transient leak: an agent thread's bash cwd resets between calls and can land on the main repo root, so a git-write agent's bare commands mutate main instead of its worktree. Brief every git-write agent: pin the root once (git rev-parse --show-toplevel → $WORKTREE) and prefix every call with git -C "$WORKTREE" …; forbid bare git checkout -B / git rebase --autostash until inside the worktree. After the agent returns, run the post-run main-repo integrity check (see references/worktree-hazards.md → cwd-reset guardrail) — a changed branch or new dirty state is silent main-repo mutation.

GIT_DIR/GIT_WORK_TREE export leak (#1692 sibling). A worktree reporting core.bare = true is shared-checkout corruption — STOP and report it; never "work around" it by exporting GIT_DIR/GIT_WORK_TREE, which override git -C so every later git call targets the shared common config, breaking all sibling worktrees at once. A subprocess that must run git in a sandbox neutralizes inherited env first: env -u GIT_DIR -u GIT_WORK_TREE git -C "$dir" …. See references/worktree-hazards.md → GIT_DIR-export leak.

Nested-repo workspaces — isolation: "worktree" isolates the outer repo (#1838). The harness worktrees the session's repo, not the repo the agent was told to edit, so in a portfolio layout the target files are absent from the worktree and the agent's only path to them is the shared checkout — which the Edit-tool isolation guard correctly blocks. Detect it before dispatch: git -C <target-dir> rev-parse --show-toplevel ≠ git rev-parse --show-toplevel; when they differ, isolate the nested repo explicitly. See references/worktree-hazards.md → Nested-repo isolation for the detection script and rules.

Concurrent agents default to the same scratchpad path (#2370). Sibling subagents share the session scratchpad, so two agents each told to "make your own clone" pick the identical path and silently operate one working tree. Give each an explicit, distinct path (<scratchpad>/<agent-name>) whenever they work outside worktree isolation — which the nested-repo case above forces. See references/worktree-hazards.md → Shared scratchpad collisions.

A deleted worktree kills the agent's shell, not its reasoning (#2372). Every Bash call then fails, and a spawned subagent inherits the same dead cwd. See references/worktree-hazards.md → Deleted worktree.

isolation: "remote" may resolve to a LOCAL worktree (#2447). The tool result never says which mode ran; git worktree list and the notification's worktreePath are the tells. An empty remote is evidence about the push, not the work — audit local worktrees alongside gh pr list / git ls-remote before calling work lost, and brief agents to open a draft PR early, pushing after each commit. See references/worktree-hazards.md → isolation: "remote".

2. Scope Budget (per-agent prompt rules)

Every agent prompt must declare:

  • File scope: exclusive write paths (glob or explicit list). Out-of-scope discovery → stop and report (see agent-teams).
  • Read budget: soft cap on files examined (default "≤10 files per hop, ≤3 hops before returning").
  • Output budget: expected length of the return summary — discourages echoing full file contents when a diff or line reference will do.
  • The user's ask, verbatim: quote the user's instruction and any stated boundary rather than paraphrasing — drift starts there (Fable 5.1 system card: distorts user intent briefing agents).
  • No borrowed authority: a brief never speaks as the user or asserts approvals not given this session (system card: fabricated user quotes were observed).

These budgets prevent the "agent hit context limits" and "prompt too long" failure modes — without them an agent exhausts its window on exploration and truncates its deliverable.

Orchestrator-only files. Even with disjoint write scopes, shared files must be excluded from every agent's write-path under an ### Orchestrator-only files heading in the brief: the blueprint manifest (ID registry), the feature tracker, top-level plan/roadmap docs, build manifests, justfile/Makefile, and local task-queue stores. Last-writer-wins silently destroys earlier work on these. See references/brief-templates.md for the full template and evidence.

Pre-allocated IDs. The shared-counter snapshot must expand into explicit per-agent ID assignment in each brief ("Use WO-012; others claim WO-013/014"). "Pick the next free ID" is a race under parallelism. Applies to any shared monotonic identifier (ADR, migration, PRP).

Wave splits for exclusive locks. An agent needing an exclusive lock (Ghidra project lock, shared git index, migration lock, taskwarrior bulk ops, single-writer caches) cannot share a wave with another lock-contender. Dispatch it alone, or pre-compute its artefacts so downstream agents are read-only. See exclusive-lock-dispatch.

Refactor briefs. For bulk content rewrites, use the per-step / PRECIOUS / per-file-cap shape — see references/brief-templates.md → Refactor-brief template.

3. Return Contract (mandatory structured summary)

Every parallel agent must end its run with a structured ## Result summary as its final message, regardless of success or failure (status / branch / pr / commits / worktree, plus Scope delivered, Deferred, Issues encountered, and Orchestrator action needed). Include the schema verbatim in every dispatched agent's prompt under a heading like ### Return contract (mandatory) — agents follow concrete schemas more reliably than prose. Copy the full schema from references/dispatch-contract.md → Return Contract schema; for the failure-mode → schema-field rationale, see references/dispatch-contract.md → Failure modes.

Orchestrator edits needed must be verbatim patches, not prose (literal CMake blocks, full justfile recipes, literal doc paragraphs), and the agent writes the final prose for any docs update its slice requires. See references/brief-templates.md → Verbatim patches.

Loud-failure contract (never surrender silently)

A dispatched agent that hits a wall must say so loudly. The dominant failure shape (issue #1422) is an agent that runs 50–200 tool calls, thrashes against hooks, then emits a one-word final message — Terminal., Done., Stopped. — with no PR URL and no blocked list. That is indistinguishable from success to the orchestrator, so the harness reads "no changes", cleans up the worktree, and the work is lost.

Tie the escalation to the Return Contract's status field:

OutcomeThe agent must return
SuccessPR URL plus one summary metric (test/line delta) — status: success
Partial blockerPush the WIP, open a draft PR, return its URL plus an explicit "what's blocked" list — status: partial
Total blockerExplain exactly what blocked it, which tools were denied, what it tried — status: failed. Never a bare Terminal. / Done. / Stopped.

The one-sentence contract to paste into every brief: "Your final message is the only thing I can act on — a one-word summary loses all your work. On any blocker, push what you have, open a draft PR, and tell me exactly what stopped you." Optional enforcement: a SubagentStop hook that flags sub-~20-char or bare-surrender final messages (see hooks-plugin).

A workflow harness does not replace this contract; it turns what prose can only request into what a runtime enforces. The prose→primitive mapping, its two cautions, and the cost gate before reaching for one: references/dispatch-contract.md → Workflow primitives.

4. Agent self-verification in bulk-edit briefs

When fanning out agents to bulk-edit content covered by a regression script, the brief must include the script as the agent's own final verification step. Exit 0 means ship; non-zero means fix-and-re-run inside the same agent's budget — shifting validation from commit-time to edit-time.

Bulk editAgent's final verification step
SKILL.md description rewritespython3 scripts/audit-skill-descriptions.py --strict-all
Context-command edits in skill bodiesbash scripts/lint-context-commands.sh
allowed-tools / bash-permission editsbash scripts/plugin-compliance-check.sh

Treating the script as advisory defeats the purpose — the regression lands in the agent's diff and the agent already has the context to fix it. See references/brief-templates.md → Bulk-edit self-verification and .claude/rules/regression-testing.md.

Closed-list mechanical batches need a completion manifest, not just a self-report. A refactor agent assigned a fixed list (symbols to delete, files to touch) must emit a machine-checkable manifest of what it completed — and never trust that manifest alone: re-run the authoritative checker (knip / build / test) afterward and diff against the assignment. A truncated or optimistic summary reads as success even when the batch fell short (issue #1601: a ~23-symbol batch completed only ~5, invisible until knip was re-run). Cap the per-agent batch so an early stop costs little. See references/brief-templates.md → Refactor-brief template.

5. Reviewer-agent verification (verify-then-fix)

Self-attestation is unreliable. For high-stakes dispatches (PR "ready to merge", security audits, shared-state mutations), spawn a separate reviewer agent after the worker reports done and before trusting it. The reviewer runs in its own worktree, ideally a different model, receives the claim and branch (not the reasoning trace), and re-derives a verdict from the diff. On a flag, fix inline or dispatch a follow-up worker — do not close on the worker's self-claim.

Self-author guard for gh pr flows: gh pr review --reviewer <user> returns HTTP 422 when the target is the PR author; brief reviewers to post inline comments instead. See references/brief-templates.md → Reviewer-agent verification.

Who Pushes?

Agents push their own commits in the normal case — worktree isolation plus per-agent branches makes this safe and keeps the lead context lean. The lead pushes instead only for: web sandbox sessions (CLAUDE_CODE_REMOTE=true, where teammates may hit TLS errors on push — see agent-teams), cross-agent dependencies where Phase 1 commits must land as a single merge base for Phase 2, and explicit user instruction ("I'll push manually").

Handling a Missing Return

If an agent exits without emitting the Return Contract, treat it as a silent stall, not a success. Before deciding, discriminate empty vs dirty worktree:

git -C <worktree> status --porcelain
git -C <worktree> log --oneline origin/main..HEAD
  • Dirty / commits present → the agent did the work; salvage it (commit/push the WIP, open the PR) rather than re-dispatching.
  • Empty / trivial diff → nothing to salvage; resume or re-dispatch.

Do not report the parent task complete until every spawned agent has produced a Return Contract (or been explicitly accounted for). Two causes leave the work intact: a pre-commit hook blocking git commit, or a rate-limit cut-off after the implementation but before the StructuredOutput call (issue #1491).

Defensive mitigation: instruct worktree-isolated agents to commit WIP at checkpoints — after each substantive slice and before they would terminate — so partial work survives a lost structured result. See references/failure-recovery.md → Agent stalled at commit / push and references/failure-recovery.md → WIP salvage before re-dispatch.

Idle without report (#2039)

An implementer can finish its work (clean commit + tree) then go idle emitting only an idle_notification — the work isn't lost, the communication is (intermittent; siblings can deliver fine). Not a failure signal: run the empty-vs-dirty check above, then SendMessage the named agent to resend the Return Contract (read-only, so the #1546 caveat below does not apply). Never respawn — a fresh agent lacks context and can't take the branch. Prevention: implementers SendMessage the report to the lead as their final act. See references/failure-recovery.md → Idle without report.

Killing a Thrashing Agent Preserves Its Worktree

TaskStop does not discard the agent's work — its worktree stays on disk with every uncommitted change intact, making TaskStop a recovery affordance. When an agent is thrashing (high Bash:Edit ratio with a rising error rate on hook-blocked Bash calls), killing it early and salvaging beats waiting for a silent give-up. Then decide from the worktree state:

Worktree stateDecision
Substantive diff vs origin/mainSalvage — finish in the parent session, commit, push, open the PR
Empty / trivial diff, or wrong designRestart — git worktree remove <path> first, then re-dispatch

For the quantitative kill thresholds and the rate-limit vs hook-block discriminator, see references/failure-recovery.md → Killed-agent worktree recovery.

Concurrent Rate-Limit Risk

1M-context parents (every Fable 5.1 session, or Opus with the [1m] suffix) running six or more concurrent subagents can hit Server is temporarily limiting requests partway through a wave (distinct from your account usage limit; varies by time of day). "It worked with N agents yesterday" is not a guarantee. Start conservative, then scale up:

Agent profileSafe starting concurrency
Heavy (installs / builds / long tool chains)2–3
Light (read-only analysis, single-file edits)up to 5

Prefer sequential waves of small batches over one big fan-out beyond ~4 heavy agents, and treat the rate-limit signal as backoff-and-retry, not task failure — re-dispatch rejected agents with backoff and reduced concurrency. When the burst killed agents at startup, the dead worktrees leave empty branch refs behind: git worktree prune and delete them before the retry, or each agent's git switch -c <branch> collides with the leftover ref. See references/failure-recovery.md → Concurrent rate-limit recovery and .claude/rules/skill-fork-context.md.

Skill-less agentType for Read-Only Fan-Out

For read-only / structured-output fan-out — classification, verification, audit sweeps where each agent reads files and emits a result, nothing more — dispatch a Skill-less agentType rather than general-purpose. A general-purpose subagent carries the Skill tool, which injects a skill_listing attachment (~88k chars / ~22k tokens) plus a deferred_tools_delta (~3k tokens) before its first tool call; add file reads and a forced StructuredOutput schema and that ~25k fixed tax pushes the subagent over its window (Prompt is too long, 40–100% batch failures — issue #1549). Agents without the Skill tool receive no skill_listing injection at all.

Fan-out needagentTypeWhy
Read-only classify / verify / auditagents-plugin:reviewRead/Glob/Grep, no Skill tool → no skill_listing tax; its review system prompt does not interfere given an explicit rubric + schema
Read plus Write (e.g. emit a report file)agents-plugin:docsSame Skill-less lean tool set, with write capability
Genuinely needs the skill catalog or broad Bash (gh/task filing)general-purposeThe ~25k tax is only worth paying when the catalog is actually used

Preserve agents-plugin:review's lean, no-Skill tool set when reaching for it as a fan-out building block. See references/dispatch-contract.md → Skill-less agentType; sibling authoring guidance is in custom-agent-definitions.

Composition with agent-teams

agent-teams covers the implicit-team / SendMessage / TaskUpdate mechanics; this skill adds the dispatch-time contract that applies to both team and non-team fan-out. When both apply, follow both — the out-of-scope protocol from agent-teams slots into the Issues encountered / Deferred sections here.

Resuming agents: SendMessage loses worktree isolation

SendMessage-resume of a completed worktree-isolated agent does not re-enter that agent's worktree — the resumed run executes in the orchestrator's main checkout, so the resume loses worktree isolation; resuming several file-mutating agents this way runs them concurrently in the main checkout and tangles branch state (issue #1546).

ContinuationSafe to SendMessage-resume?Do instead
Read-only / single-checkout follow-upYes — no worktree to re-enterResume freely
Parallel file-mutating agent that must stay in its worktreeNo — resume runs in the main checkoutRe-dispatch a fresh Agent with isolation: "worktree"

Resuming a workflow: resumeFromRunId re-runs succeeded worktree agents

Workflow({resumeFromRunId})'s "completed agent() calls return cached results" holds for ordinary agents but not for isolation: "worktree" ones: a worktree agent that already succeeded is re-executed on resume, re-firing its outward side effects — an agent that opened a PR opens a duplicate (PR #1858 dup of #1857; issue #1868). Opposite failure to the SendMessage case above (there the resume loses its worktree; here it re-runs the whole agent).

So to retry a few failed worktree agents, do not resume the whole run — re-dispatch only the failed agents in a fresh sequential pass (which also dodges the burst rate limit), checking for an already-open PR first (gh pr list --state all --search …, reading state/mergedAt per .claude/rules/gh-json-fields.md). Non-worktree stages cache correctly. See .claude/rules/agent-coworker-detection.md.

Quick Reference

Orchestrator Checklist

  • Working tree clean; no conflicting worktrees
  • Each agent has unique branch name and exclusive file scope
  • Each prompt includes file/read/output budgets
  • Each prompt includes the Return Contract schema verbatim
  • Each prompt mandates the loud-failure contract (no one-word surrenders)
  • Agents authorized to push their own commits (unless sandbox/dependency exception)
  • Every returned summary parsed; missing returns treated as stalls

Common Mistakes

MistakeCorrect Approach
Spawning agents from a dirty main treeCommit or stash first; refuse to dispatch on dirty state
Scope described in prose, not globExplicit write-path list per agent
"Report back when done" with no schemaInclude Return Contract verbatim in every prompt
Treating agent silence as successNo Return Contract = stall; investigate before reporting done
Respawning after an idle_notification with no reportCheck the branch, then SendMessage the agent to resend the report (#2039)
Accepting a one-word final message (Terminal./Done.)Mandate the loud-failure contract: push work, open a draft PR, explain
Centralizing pushes as a defaultAgent pushes its own work; lead pushes only on sandbox/dependency exceptions

Related

  • REFERENCE.md — index over references/: dispatch contract, briefs, failure recovery, worktree hazards, verifier shared state
  • agent-teams — implicit-team / SendMessage mechanics, out-of-scope discovery protocol
  • custom-agent-definitions — agent file structure, tool restrictions, context forking
  • .claude/rules/agent-development.md — agent authoring conventions
  • .claude/rules/sandbox-guidance.md — when sandbox constraints override push defaults
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

agent-patterns-plugin/skills/parallel-agent-dispatch

Default branch

main

Latest commit

1668324

Tree SHA

b2d4cc3