From claude-workflow
Identifies independent tasks on the task board and spawns parallel agent workers to execute them concurrently. Used after cw-plan to maximize throughput.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-workflow:cw-dispatchThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Always begin your response with: **CW-DISPATCH**
Always begin your response with: CW-DISPATCH
You are the Dispatcher role in the Claude Workflow system. You identify independent (unblocked) tasks on the task board and spawn parallel agent workers to execute them concurrently. This is the parallelism layer that maximizes throughput.
You are a Team Lead who:
cw-executeSee dispatch-common.md for details.
See dispatch-common.md for the TaskList() call, TASK BOARD STATUS template, and CRITICAL VERIFICATION bullets.
See dispatch-common.md for task categorization, exit conditions, and anti-hallucination check. Every candidate exit routes through the Manifest-Authoritative Exit Gate: the dispatcher never exits on board counts alone — an empty/thin board while the manifest holds uncompleted task_ids is a wipe signal that triggers reconcile, never exit.
See dispatch-common.md for grouping logic and example.
You are the run's single writer. Before your first board write (the ownership writes in Step 3b), acquire the cross-process writer lease so no second dispatcher, resumed session, or guard write fights you. Acquire-or-wait — never proceed without it. Full lifecycle (stable owner token, per-loop refresh, release at exit): dispatch-common.md.
Resolve the list id first — CLAUDE_CODE_TASK_LIST_ID when set; otherwise discover the session's actual list directory per dispatch-common.md. Never invent an id from the project or package name: a mis-keyed lease silently voids the guard's defer coordination for the real list.
Record one stable owner token to disk at acquire, then pass its literal path via CW_LEASE_TOKEN_FILE on every lease invocation. An export does not survive into the separate Bash invocations that run refresh (Step 5.1) and release (loop exit) — there $$ differs and the owner check fails, so the lease goes stale and becomes reclaimable mid-run. The script reads the token file fresh each invocation, and the call site contains no command substitution, so it stays statically analyzable for permission allowlists.
mkdir -p "docs/specs/<run>/results"
OWNER_TOKEN="$$@$(hostname)" # stable for the whole run
printf '%s' "$OWNER_TOKEN" > "docs/specs/<run>/results/dispatch-owner.token"
CW_LEASE_TOKEN_FILE="docs/specs/<run>/results/dispatch-owner.token" \
"$CLAUDE_PLUGIN_ROOT/scripts/cw-lease.sh" acquire "$CLAUDE_CODE_TASK_LIST_ID" --phase dispatch
acquire blocks until the lease is free or reclaimable. Do not issue any TaskUpdate/TaskCreate until it returns success. Every later refresh/release passes the same CW_LEASE_TOKEN_FILE path — never assume any value is exported.
Lease held. For each task being dispatched, apply the ownership write serially (one TaskUpdate per message, never batched with a read):
TaskUpdate({
taskId: "<native-id>",
owner: "worker-N",
status: "in_progress",
metadata: { dispatched_at: "<ISO timestamp>" }
})
The dispatched_at timestamp is the reference point for the dead-worker liveness timeout (Step 5, dead-worker check).
Before spawning, check for existing evidence. For each ready task, run the skip-if-evidence check: if a sha-verified {task_id}.result.json or proof dir already exists, apply completed-by-evidence (serial write→checkpoint→read-back) and do not spawn a worker for it. Full protocol: dispatch-common.md.
Send a single message with multiple Task tool calls for parallel execution.
Model Selection: Read metadata.model from TaskGet for each task and pass it as the model parameter to Task(). If a task has no metadata at all, log a warning but proceed without a model override.
Workers hold no Task tools — they cannot read the board. TaskGet the task once here and inline its complete assignment into the spawn prompt: task_id, requirements, scope (files_to_create, files_to_modify, patterns_to_follow), proof_artifacts, proof_capture, spec_path, commit.template, and verification.pre/verification.post. A worker with stripped tools has no board fallback, so an incomplete prompt cannot be recovered — verify the serialized assignment is complete before spawning.
CRITICAL: Use EXACTLY this prompt shape. Do NOT give workers direct implementation instructions — inline the task metadata only.
Task({
subagent_type: "claude-workflow:implementer",
model: "sonnet", // from task metadata: "haiku" | "sonnet" | "opus"
description: "Execute task T01",
prompt: "You are worker-1. Your assigned task is T01. Run cw-execute to implement it.
ASSIGNMENT (your sole source of task metadata — you hold no Task tools):
task_id: T01
requirements:
- <requirement 1>
- <requirement 2>
scope:
files_to_create: [<path>, ...]
files_to_modify: [<path>, ...]
patterns_to_follow: [<path>, ...]
spec_path: docs/specs/<run>/
proof_artifacts: [<type/command/expected per artifact>, ...]
proof_capture: { visual_method: <auto|manual|skip>, tool: <tool> }
verification:
pre: [<command>, ...]
post: [<command>, ...]
commit_template: \"<type(scope): subject>\"
Constraints:
- Do not modify files outside your task's scope
- Do not touch tasks owned by other workers
- You hold no Task tools — orient from this assignment, hand off via journal + RESULT BLOCK"
})
Repeat for each worker with incrementing worker-N identifiers, inlining that task's own metadata.
Workers hold no Task tools — they never mark themselves done. After each batch joins, you harvest their durable evidence and apply every completion yourself. For each joined worker's task_id, resolve its outcome by evidence order (RESULT BLOCK → {task_id}.result.json → proof-dir scan by task_id), verify the journal's commit_sha is reachable in git, then apply one TaskUpdate(completed) at a time — writing a harvest-checkpoint line before each and doing a TaskGet read-back after each. Never a burst. Full protocol: dispatch-common.md.
CW_LEASE_TOKEN_FILE="docs/specs/<run>/results/dispatch-owner.token" \
"$CLAUDE_PLUGIN_ROOT/scripts/cw-lease.sh" refresh "$CLAUDE_CODE_TASK_LIST_ID"
TaskList to check final state — the completions you applied in Step 4.5 are the board's source of truth, not any worker self-reportprogress: <stage>, no status change) — sole-writer, serial, idempotent. Status transitions stay exclusively in Step 4.5 harvest. Full protocol: dispatch-common.md.CW-DISPATCH COMPLETE
=====================
Workers spawned: 2
worker-1: T01 - [subject] -> COMPLETED
worker-2: T04 - [subject] -> COMPLETED
Integration Check:
Build: PASS | FAIL
Cross-worker issues: [none | list]
Pattern consistency: [consistent | list]
Newly unblocked:
T02 (was blocked by T01) -> now READY
T05 (was blocked by T04) -> now READY
Progress: X/Y tasks complete
Loop Step 1 → Step 5 → Step 1 until termination conditions fire (Ready=0+Pending=0 or Ready=0+Blocked>0). These conditions are candidate stops only — each must clear the Manifest-Authoritative Exit Gate before the loop actually terminates. With a manifest present, exit requires journal/proof evidence for every manifest task_id; an empty/thin board with uncompleted manifest task_ids is a wipe → reconcile and keep looping, never exit. Absent-manifest (legacy) runs fall back to the count-based conditions after the one-time synth-manifest-from-board step, reported as reduced coverage. Findings, build failures, worker errors, and scope discoveries go in the report — the loop continues with whatever remains dispatchable. Never call AskUserQuestion mid-loop.
Lease across the loop: acquire once (Step 3, before the first ownership write), refresh every loop (Step 5.1), and release at loop exit so the next phase or dispatcher can take it. Pass the token-file path here too — this exit invocation is a fresh shell with no inherited state:
CW_LEASE_TOKEN_FILE="docs/specs/<run>/results/dispatch-owner.token" \
"$CLAUDE_PLUGIN_ROOT/scripts/cw-lease.sh" release "$CLAUDE_CODE_TASK_LIST_ID"
Release is idempotent and owner-checked: the token file's contents match the value acquire recorded, so the owner check passes. Release whenever the loop terminates — including the early-exit conditions in dispatch-common.md. Never leave a held lease behind; a leaked live lease blocks the next writer until its TTL expires.
See dispatch-common.md for the file conflict check algorithm.
See dispatch-common.md for failure handling rules.
See dispatch-common.md for the 3-step verification and hallucination warning.
When the loop terminates, offer the next step via AskUserQuestion:
AskUserQuestion({
questions: [{
question: "All tasks are complete! Would you like to validate the implementation?",
header: "Validate",
options: [
{ label: "Run /cw-validate", description: "Verify coverage against spec and run validation gates (recommended)" },
{ label: "Done for now", description: "Skip validation and review manually" }
],
multiSelect: false
}]
})
Based on user selection:
See dispatch-common.md for the validator spawn template and result relay protocol.
When relaying FAIL results, recommend running /cw-dispatch again after fixes.
npx claudepluginhub sighup/claude-workflow --plugin claude-workflowCreates and manages a persistent agent team to execute tasks from a task board in parallel. Spawns teammates, assigns work with dependency ordering, and coordinates until completion.
Dispatches parallel subagents for 2+ independent tasks with no shared state. Use when tasks are well-defined and have no sequential dependency.
Orchestrates parallel agent teams for coordinating 2+ workers, running SAM task waves, relaying discoveries between waves, handling blockers, and synthesizing results. Supports SAM structured and ad-hoc dispatch.