Imported from dariovr1/sixth-sense (
skills/dispatching-parallel-agents/SKILL.md). Install upstream withnpx skills add dariovr1/sixth-sense --skill dispatching-parallel-agents. Copyright stays with the author.
Dispatching Parallel Agents
Pre-Dispatch: Read Preferences
Before any dispatch, read .sixth-sense/model-preferences.md from the project root.
If the file does not exist, use defaults: Sonnet medium, Haiku batching enabled for the haiku-batch tier only (the eligibility bar below is already the guardrail — narrow it further, don't widen it), max 4 agents, Opus routing enabled (opus-high tier, see Effort Routing below and opus-routing: false to disable). Opus routing is distinct from opus-review: opus-review is a narrow, always-separate read-only second-opinion pass in specifying-and-implementing's Spec phase/requesting-code-review, never used for dispatch; opus-high is a normal Effort Routing tier, dispatched like any other tier, reserved for genuinely architectural work and the fix loop's last-resort escalation (see Post-Dispatch). See configuring-sixth-sense. Run /setup-sixth-sense to change these defaults (e.g. to turn Haiku batching or Opus routing off entirely), not to unlock them.
When to Dispatch
Dispatch parallel subagents only when:
- Independence: Tasks have no shared state or sequential dependencies (they do not modify the same files or rely on each other's output).
- Workload: The total volume of work is significant or benefits from parallel execution.
- Complexity: The task can be cleanly decomposed into distinct, non-overlapping workstreams.
If tasks are sequential or tightly coupled, execute them iteratively.
Effort Routing
For each task, estimate difficulty and assign the appropriate agent-type:
| Criteria | Agent |
|---|---|
| Single-file change, <20 lines, no architectural decision | haiku-batch (if enabled, otherwise sonnet-low) |
| Well-defined task, 1-3 files, straightforward logic | sonnet-low |
| Business logic, targeted refactor, 2-5 files | sonnet-medium |
| Architectural decisions, multi-file refactor, non-obvious logic | sonnet-high |
Genuinely novel design/planning judgment with no existing pattern to extend, a decision that will constrain many later tasks, or a task already escalated once from sonnet-high in the fix loop |
opus-high (requires opus-routing: true, default) |
| Web research — gathering sources, fact-checking, SERP/keyword lookups, writing a dossier | smart-search (the only tier with WebSearch/WebFetch — every other tier lacks them, so don't fall back to general-purpose for research batches) |
opus-high is a ceiling, not a default guess. Reserve it for work that needs it from the start (a design decision with no existing pattern to extend, something that will constrain many later tasks) or for the fix loop's round-5 escalation below. "This looks hard" rounds up to sonnet-high, not opus-high — the same asymmetry that governs every other tier decision in this skill applies here too: round up when genuinely unsure between two tiers, but opus-high is not "two tiers up," it's the tier reserved for when sonnet-high has already been tried or is visibly insufficient before the first attempt.
smart-search vs. the alternatives — these get conflated in practice, so the boundary is worth stating explicitly:
- An external fact blocking a decision (what does this API actually return, what does the spec say) →
smart-search. - A decision reached through dialogue with the user, no external fact needed →
roundtable. - A durable architectural decision worth recording for the project's own future reference →
domain-modeling(ADR/CONTEXT.md), not a one-off research dossier.
smart-search dispatches default to writing their findings to .sixth-sense/research/<slug>.md (same convention as .sixth-sense/spec.md/.sixth-sense/progress.md) unless the dispatch prompt asks for a different format — a cited file that survives the dispatch, not an answer that only exists in the subagent's returned summary.
Don't route on the task description alone — check the actual code being touched. Free-form judgment on "how complex does this sound" is inconsistent run to run: the same task can plausibly read as sonnet-low or sonnet-medium depending on framing, and the wrong call costs a resume, not just a slower one. Before assigning a tier, spend one cheap look at what's actually being modified:
- New, empty-slate file with a fully-specified test list → the description-based tier stands.
- Existing file being modified, especially one with async/timer/concurrency/lifecycle logic (
useEffect, listeners, connection/resource handling), or with no existing test coverage to lean on → bump the tier up one notch from what the description alone would suggest, regardless of how self-contained the task sounds. This costs oneRead/Grep, not a subagent turn. - When genuinely unsure between two tiers, round up, not down — the same asymmetry as
dylan-dog-triage's calibration: a task that turns out easier than its tier wastes some budget; a task that turns out harder than its tier wastes a whole resume cycle.
Cost per turn beats price per token: the cheaper model often takes 2-3x the turns on a multi-step task, costing more overall. Don't route to haiku-batch just because it's the cheapest tier — route there only genuinely mechanical tasks (single file, complete spec, no decisions).
Batching
Tasks that fall within the haiku-batch threshold must be grouped into a single call — not dispatched one by one. The prompt to the batch agent must contain the full list of tasks to execute in sequence.
This applies beyond haiku-batch too: when two or more tasks are the same shape at any tier (e.g. the same rename applied across N files, the same doc update mirrored across N locations), group them into one dispatch to one agent instance of that tier instead of N separate Agent() calls — one dispatch with a numbered list of the N targets, one result to verify. Only split them back out if they turn out not to be truly identical in shape (different surrounding context per file, different risk profile) once looked at.
Agent Cap
Do not dispatch more than N Agent() calls in the same turn. N defaults to 4, configurable in .sixth-sense/model-preferences.md under the max-parallel-agents field.
Protocol
- Present the Dispatch Plan: Before calling any
Agent(), show the full plan as a table — every task (or batch), the agent-type assigned to it, a one-line reason tied to the Effort Routing criteria above, and a rough relative cost band ($haiku-batch,$$sonnet-low,$$$sonnet-medium/smart-search,$$$$sonnet-high,$$$$$opus-high — don't invent precise dollar figures, they go stale and imply false precision; the point is to make the relative spend visible, not to forecast a bill). E.g. "Task 3 →haiku-batch(Haiku 4.5,$): single-file rename, no logic change" / "Task 5 →sonnet-high($$$$): touches 6 files, requires an architectural call on the caching layer".- Conflict scan: before presenting the table, list every pair of tasks (or batches) in this dispatch that share a file, an interface, or the same output artifact, and note what you found. A plan with tasks written down but never checked against each other is not a scan — one line per pair is enough when there's no overlap ("Tasks 2 and 4 both touch
checkout.ts— sequencing them, not parallelizing"). If a real conflict turns up, resolve it (reorder, merge, or split the tasks) before presenting the table, not after a subagent hits it mid-run. - Interactive session: use
AskUserQuestion(or an explicit question if no other interactive tool is available) and wait for the user's actual reply before dispatching anything. Narrating the plan and continuing in the same turn does not satisfy this gate. If the user pushes back on a tier, adjust only the disputed items and re-confirm before proceeding. - Non-interactive session (headless/
-p): see references/non-interactive-session.md for the shared gate mechanics (subagent short-circuit,ToolSearchcheck, when to stop waiting). Here, what's being confirmed is the dispatch plan just shown — once the gate is confirmed skipped, proceed to dispatch in the same turn instead of stopping with nothing dispatched.
- Conflict scan: before presenting the table, list every pair of tasks (or batches) in this dispatch that share a file, an interface, or the same output artifact, and note what you found. A plan with tasks written down but never checked against each other is not a scan — one line per pair is enough when there's no overlap ("Tasks 2 and 4 both touch
- Scope Isolation: Give each subagent only the task description and the relevant files. Do not pass the entire session context.
- Instruction Precision: Explicit, granular instructions. For file edits, provide exact strings to search and replace.
- Skill Enforcement: Subagents have
Readbut notSkill— they cannot invoke a skill by trigger phrase or name. "Follow the tdd skill" means nothing to them. Instead, inline the concrete rules that matter for this task directly in the dispatch prompt, and/or give the exact file path to a reference file for them toReadthemselves (e.g.skills/test-driven-development/references/java.md,skills/dylan-dog/checklist.md) — never assume a subagent will discover or load anything you didn't name explicitly. - Result Synthesis: Each subagent must provide a concise summary of the work done, with the commit SHA or verification evidence.
- Conflict Prevention: Strictly forbid concurrent agents from modifying the same file.
- Log the Dispatch: Before the first dispatch in a campaign, pick a short kebab-case slug for it — reuse the basename of a plan/spec file already driving this work if one exists (e.g.
.sixth-sense/spec.md→spec), otherwise derive one from the task at hand (e.g.checkout-pricing-fix) — and use.sixth-sense/dispatch-log/<slug>.mdas this campaign's ledger, distinct from every other campaign's. Create the directory and file with a header if they don't exist yet (# Dispatch ledger — campaign: <slug>). If that file already exists when a campaign starts (a resumed session, a break mid-work), read it first: rows already markedcompletedare done — don't re-dispatch them, resume at the first task without one. Append one line per subagent:<ISO timestamp> | <agent-type> | <task description> | <one-line rationale> | status: dispatched. Update each row'sstatusas the task's fate becomes known (dispatched→completed/blocked/retried), and append aRuling: <what> — <why> — <cost if wrong>line beneath a task's row whenever a Ruling (below) is made about it. Never leave a row atdispatchedonce the outcome is known — a ledger with stale rows is as useless as no ledger. Scoping the ledger per campaign, instead of one flat project-wide file, keeps "where were we" answerable without first figuring out which rows belong to this campaign versus an unrelated one from weeks ago.
Skipping step 1 is not a shortcut, it's a bug: routing decisions spend the user's money and turns on their behalf — a 10-second confirmation is cheap relative to discovering a wrong tier only after 4 subagents have already run.
Rulings, Not Stalls
The confirmation gate in Protocol step 1 covers the plan as presented — it does not mean every ambiguity that surfaces once subagents are actually running must come back to the user. Four things justify stopping mid-campaign, and only these: an irreversible or destructive action; a security-sensitive action; a side effect outside this worktree/output directory that norms say to ask about first (a merge, a push, a publish); a plan so broken every path forward is a guess. Everything else that emerges during execution — a subagent finds an interface different from what the plan assumed, two tasks presented as independent turn out to conflict once real work starts, a finding that contradicts the confirmed plan's text — gets decided and logged, not escalated: Ruling: <what you decided> — <why> — <what it costs if wrong>, appended to the ledger row for the task it concerns, and the campaign continues.
This does not reach backward: a Ruling never overrides a tier or scope the user already confirmed in step 1, and it never substitutes for that initial confirmation on a plan not yet presented — it only resolves what the confirmed plan didn't anticipate. When a campaign finishes, surface every Ruling made (from the ledger) in the final summary — a decision made on the user's behalf that dies in the ledger without being reported back was made in secret.
Worktree Isolation
Passing isolation: "worktree" to Agent() has the harness create the worktree directly — it branches from the current HEAD commit, not from the working tree, and it bypasses managing-development-branches' own Setup safety checks entirely (that phase only runs when its own steps are followed, not when a caller reaches straight for this parameter). Before dispatching with isolation: "worktree", run git status --short yourself: if there are uncommitted changes the dispatched task needs to see, commit (even as a WIP commit) or stash them first. Otherwise the subagent works from a stale snapshot, and its diff may come back unmergeable with whatever is still sitting uncommitted in the original tree.
While Waiting for Background Dispatches
When subagents run with run_in_background: true, completion notifications arrive automatically — do not poll. No placeholder Bash calls (sleep, echo waiting), no scheduling a wakeup, no re-checking status in a loop. Do something else useful, or simply end the turn and let the notification bring you back. Only actively re-check a subagent yourself if you have a concrete reason to suspect it stalled (e.g. a notification is overdue relative to the task's expected size) — verify by reading the files it was supposed to produce, not by trusting a status message.
Post-Dispatch
After completion, aggregate the outputs, resolve any logical conflicts, and run a final verification (verification-before-completion skill) before merging.
Fix loop. When verification finds issues, sort them by severity first — same bands as requesting-code-review/Dylan Dog (Critical/Important/Minor):
- Minor-only findings never enter the loop. Log them immediately as
Task <N>: minor (deferred): <one-liner>in the ledger and mark the task complete. Point the final verification/aggregation step (Post-Dispatch) at the campaign's deferred-minor lines so they get triaged before merge — a list nobody reads back is a silent discard, not a deferral. - Any Critical or Important finding enters the capped loop below instead of being re-dispatched at random or dropped silently:
- Rounds 1-3: re-dispatch the same agent-type with the specific findings, unchanged tier. The agent already has context on the task.
- Round 4: escalate one tier of capability (e.g.
sonnet-medium→sonnet-high) with fresh context — a new dispatch, not a resumed conversation carrying the same blind spot forward. - Round 5: if the task was already at
sonnet-high, escalate toopus-high(requiresopus-routing: true) with fresh context and this framing in the dispatch: a prior implementer attempted this task multiple times; read the ledger entry and any report file for what was tried, then look for what they couldn't see, not for reasons to repeat their attempt. - Every round: append the outcome to the ledger (
Task <N>: fix round <R>/5 (<X> addressed, <Y> open — commits <a7>..<b7>)). - At round 5 with findings still open: stop escalating. This is a Ruling, not a stall — adjudicate each open finding yourself: park it with a ruling if it's contestable or non-blocking, or resolve it directly and record why if it's load-bearing. Log the adjudication in the ledger either way (
Task <N>: Ruling: <finding> — <what you decided and why>); a fix loop capped at 5 rounds does not mean silently shipping unresolved findings.