From planning-suite
Analyzes an approved plan and decomposes it into a dependency-ordered task graph via a two-phase TaskCreate and TaskUpdate pass. Detects linear DEPENDS ON chains (shared worktree), identifies independent work streams (parallelized), and executes the full worktree-isolated run. Also handles Branch B (learnings from session context). **AUTOMATICALLY INVOKE** when: - ExitPlanMode hook triggers (PostToolUse) - User says "schedule tasks", "create task graph", "decompose plan into tasks", "execute plan" - /schedule-plan-tasks is invoked **References:** JIT-loaded from `${CLAUDE_SKILL_DIR}/references/`
How this skill is triggered — by the user, by Claude, or both
Slash command
/planning-suite:schedule-plan-tasksThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Two entry points, one execution engine. **Branch A** reads an approved plan file and extracts its steps as proposals — the plan was reviewed before `ExitPlanMode`, so it goes straight to task-graph execution. **Branch B** assesses session learnings, drafts proposals from session state, and runs them through the senior engineer reviewer. Both branches converge into the same task-graph executor w...
VALIDATION.mdfixtures/my-node-server/package.jsonfixtures/my-node-server/src/index.jsfixtures/my-node-server/test/smoke.test.jsfixtures/plan1-max-concurrency.mdfixtures/plan2-linear-chain.mdfixtures/plan3-diamond.mdfixtures/plan4-trivial-mixed.mdfixtures/plan5-multi-chain.mdfixtures/plan6-deep-cascading.mdfixtures/plan7-assert6-violation.mdreferences/create-wt-prompt.mdreferences/delivery-agent-description.mdreferences/dry-run-analyzer.mdreferences/investigation-task-template.mdreferences/reviewer-full.mdreferences/simulate-prefix.mdTwo entry points, one execution engine. Branch A reads an approved plan file and extracts its steps as proposals — the plan was reviewed before ExitPlanMode, so it goes straight to task-graph execution. Branch B assesses session learnings, drafts proposals from session state, and runs them through the senior engineer reviewer. Both branches converge into the same task-graph executor with native worktree isolation. Every unit of work — git prep, worktree creation, agent execution, merge-back — is a Task with explicit dependencies. Each delivery-agent merges its own changes back to the target branch after completing, using optimistic concurrency with rebase + retry.
References model: Verbatim sub-agent prompts and bash templates live in ${CLAUDE_SKILL_DIR}/references/*.md and are loaded just-in-time. Citation rule: when a step says "Read references/X then dispatch", you MUST Read that file at its absolute path and paste its content verbatim into the Agent dispatch. Do not paraphrase, summarize, or "improve". Once Read in a session, the file content stays in context — re-Read only if it scrolls out.
The skill runs in one of four modes, decided at the top of Step 0 by inspecting arguments:
| Mode | Trigger flag | TaskCreate/Update | Agent dispatch | Use case |
|---|---|---|---|---|
live | (no flag) | real | real (real work) | production execution |
dry-run | --dry-run | real | real (simulate-prefix mode) | end-to-end protocol validation |
plan-only | --plan-only | skipped (ledger) | skipped | static topology report (no side effects) |
dry-run-analyze | --dry-run-analyze | skipped (ledger) | skipped + analyzer Agent | static report + LLM critique |
Mode is set in Step 0 before any side-effecting action and threaded through every TaskCreate / TaskUpdate / git / Agent verb downstream.
Mode behaviors:
live — Real Task API ceremony, real git, real Agent dispatch with real work. Production path.
dry-run — Real TaskCreate/TaskUpdate/TaskList ceremony AND real Agent dispatch, but each
agent receives the contents of references/simulate-prefix.md PREPENDED to its task description.
The prefix instructs the agent to: simulate work (no git, no file changes), run the cascade-dispatch
directive read-only (TaskList/TaskGet only, no Agent or claim TaskUpdate), emit the standard
RESULT/WORK/INCOMPLETE/FAILURE/ARTIFACT/DISPATCHED status block listing would-be-dispatched IDs.
The orchestrator collects status blocks, dispatches the next wave (also with simulate prefix),
iterates until the graph drains. Validates the agent ↔ orchestrator exchange end-to-end.
plan-only — No real Task API ceremony, no Agent dispatch. Skipped TaskCreate/TaskUpdate
print [DRY] would <verb> #DRY-N: <subject> and mutate an in-memory ledger. Step 4 execution
loop is replaced by the Dry-Run Report. Static topology analysis only.
dry-run-analyze — Same skip rules as plan-only, then dispatches one analyzer Agent on
the Dry-Run Report to flag wiring errors, missing dependencies, mis-classified isolation,
and anti-patterns.
Trivial-task override: in plan-only and dry-run-analyze modes, trivial proposals get the
full create-wt → delivery-agent → merge chain so the complete worktree isolation structure is
visible in the static report. (Live + dry-run skip the create-wt for trivial tasks.)
In-memory task ledger (used by plan-only and dry-run-analyze) — list of entries
{id: DRY-N, type, subject, description, blocked_by[], chain_id, chain_role, metadata: {}}
built during the creation and wiring phases. Source of truth for the wiring integrity check
and the Dry-Run Report. The metadata field is populated from the metadata argument of each
ledger TaskCreate verb, using the same schemas defined in the Phase 1 creation pass below.
Mode detection (runs FIRST — before any side-effecting action): inspect the arguments.
--dry-run-analyze → Mode = dry-run-analyze--plan-only → Mode = plan-only--dry-run → Mode = dry-runMode = livePlan path detection: if args contain --plan <path>, capture PlanPath = <path> (absolute or relative to cwd). Otherwise PlanPath = nil (auto-discovery runs in Step 1).
Print the active mode banner once:
## schedule-plan-tasks — Mode: <live|dry-run|plan-only|dry-run-analyze>
Per-mode banner notes:
dry-run: print [DRY-RUN] Real Task API + real Agent dispatch with simulate-prefix. No git or file side effects. End-to-end protocol validation.plan-only: print [PLAN-ONLY] No Tasks or git operations will be created. Output: static task list + dependency graph.dry-run-analyze: print [ANALYZE] Same as plan-only, plus analyzer Agent on the report.Initialize an empty in-memory task ledger when Mode == plan-only or dry-run-analyze. Assign IDs DRY-1, DRY-2, … in (would-be) TaskCreate order. (live and dry-run use real task IDs from real TaskCreate.)
TaskList. If errors:
Task API unavailable: [error]schedule-plan-tasks requires TaskCreate, TaskUpdate, TaskList. Halting before review agent dispatch.If TaskList succeeds, continue. Narrate progress in plain prose — do not create phase-tracking tasks.
Plan-mode preflight: if invoked while plan mode is active, call ExitPlanMode immediately after confirming the plan file is readable (Branch A) or as the first action of this step (Branch B). The plan is already approved — do not stall waiting for the user to exit. If ExitPlanMode is unavailable (host is not in plan mode), this is a no-op.
Mode-aware ExitPlanMode guard: when Mode != live, do NOT call ExitPlanMode — print [DRY] would ExitPlanMode and stay in plan mode (truly side-effect-free).
Git context prime (skip all git commands if no .git, note "no git repo"):
git log -1 --oneline — last commit SHA + messagegit diff HEAD --name-only — files currently modifiedgit show --stat HEADCall TaskList — note all tasks in the backlog (completed, in_progress, pending). These are off-limits for new proposals.
Identify external resources (non-git files agents may need):
cat .gitignore | grep -iE '(data|fixtures|test-data|dataset|large|samples)'pytest.ini, jest.config.*, .env.test, conftest.py) for DATA_DIR, FIXTURES_PATH, etc.find . -maxdepth 3 -name "*.env*" -not -path "*node_modules*"Local resources → note absolute path (will be symlinked into each worktree). Remote resources (S3, HTTP, DB strings) → include the full URI in the task's External resources field; do NOT ln -s.
Branch A — Plan file:
Resolve the plan file path:
PlanPath is set (from --plan <path>): use it directly. Verify the file is readable; halt with Plan file not found: <path> if not.ls -t ~/.claude/plans/*.md | head -1.For each step, extract proposal: What=step title; Why=rationale/Context; Scope=small|medium|large
Note sequencing (step N requires step M) → record as logical DEPENDS ON hints; these drive chain detection and the two-phase creation/wiring pass
Output Mapping Pass (structural analysis): For each proposal, note the files it expects to modify. Using the DEPENDS ON graph from step 3, identify parallel proposals (no dependency path between them) that touch the same file. For each overlap, reason through three cases — touching the same file is NOT automatically a conflict:
Case A — Independent contributions: Each parallel task adds distinct, non-overlapping content to a shared file (e.g., each adds its own unique route mount line, a distinct config key, a separate function). No structural problem. Git rebase handles concurrent appends to different regions. Continue as-is.
Case B — Shared structural prerequisite: Multiple parallel tasks all require a structural element in a shared file to already exist — e.g., a router object, a base schema, a config section, an import block. The element doesn't exist yet and each task would try to create it. Extract it. Add a parent prepare task (DEPENDS ON in step 3) that creates the prerequisite; the parallel tasks extend it. Key signal: "Does this shared-file overlap reveal a missing setup task that all children depend on?"
Case C — Overlapping functionality: Two or more parallel tasks modify the same region with semantically overlapping logic (same function body, same config key, same variable). These cannot be merged cleanly and represent a logical conflict. Restructure. Add DEPENDS ON to serialize them, or extract the shared region into a dedicated task.
Emit an advisory note for Case A, a structural suggestion for Case B, and a required restructuring notice for Case C. Do not abort for Cases A or B.
Note plan's Verification section → seed for validation tasks
{context} = full plan file content
Branch B — Learnings (no plan file):
Mode-aware Branch B guard: when Mode != live, skip git commands entirely. Use conversation context alone — what the user was working on, what was just built or fixed, what was deferred or left to the user. Reason directly from that to produce proposals. {context} = concise narrative of what was learned from the conversation.
In live mode, scan recent work and draft proposals:
git log -10 --oneline — what just shippedgit diff HEAD --name-only — uncommitted workFrom those signals, draft 1–5 proposals. Bias toward drafting; the senior reviewer (Step 2) prunes the weak ones. If the scan finds nothing actionable (no recent commits, clean tree, no conversation signal), say so plainly and stop — no proposals, no Step 2 dispatch.
{context} = concise findings narrative covering points 1–3.
Both branches produce a proposals table (excluding anything already in backlog):
| # | What | Why | Scope |
|---|---|---|---|
| 1 | Example improvement | Finding or plan step that motivates it | small |
Draft only — no TaskCreate yet.
Routing (decided silently — never ask the user):
ExitPlanMode. Skip Step 2. Proposals are the source of truth; sequencing comes from "step N requires step M" hints noted in Step 1; regression scope comes from the plan's Verification section. Trivial flag: any plan step the user/plan annotated as trivial (rename, comment, single-line config) — otherwise treat as full main task.The execution mode (worktree vs serial main-workspace) is decided per-proposal, not as a global path: trivial proposals run inline in the main workspace; non-trivial proposals run in worktrees and self-merge on completion. The mechanism is the Isolation: line on each delivery-agent task (native worktree vs none (trivial)) — see the task-type contract in Step 4.
Skip this step entirely on Branch A (plan was already reviewed before ExitPlanMode). Branch A proceeds directly to Step 3 with proposals, plan-derived sequencing hints, and the plan's Verification section as the regression seed.
Print:
---
**Dispatching senior engineer review** (N proposals)
---
Runs FOREGROUND — wait for output before proceeding.
Agent description: "Senior engineer review — evaluating N improvement proposals".
Substitutions:
{context} → full findings text{proposals} → markdown table from Step 1{existing_backlog_filtered} → ALL in_progress + 20 most recent pending (NOT full backlog)Dispatch protocol:
Read ${CLAUDE_SKILL_DIR}/references/reviewer-full.md — load the verbatim prompt into context.On agent return, print changelog:
**Review complete:**
✓ Kept N — unchanged
✗ Removed N — #3 (too vague), #7 (duplicate of existing task #42)
↑ Promoted N — #11 → position 1
+ Added N — "Add OpenTelemetry tracing"
~ Trivial N — #2 #5 (no prep/validation generated)
Reviewer's output is the sole source of truth. Auto-continue to Step 3 — no confirmation gate.
Git repo guard (before Pass 1): if no .git, halt with No git repo — initialize one (git init + initial commit) and re-run /schedule-plan-tasks. Plan execution does not bootstrap repos.
Chain detection (runs after proposals are finalized, before the creation pass):
succ[N] = set of proposals whose delivery-agents must be blocked by N's delivery-agent (N's direct successors)pred[N] = set of proposals N directly depends on (N's direct predecessors)|succ[N]| == 1. These are potential chain heads or interior entry points.|pred| == 1 (still a linear run)|succ| != 1 (fan-out or leaf) — this node is the path tailstandalone (chain: none). This includes nodes with succ==0 that were never seeds (e.g. a fan-in terminus: A→B, A→C, B→D, C→D — D has succ==0, never seeded, explicitly collected as standalone here).chain-1, chain-2, …head; middle nodes → link; last node → tail; all standalones → chain: noneExamples (all correctly handled):
A→B→C: seed=A (succ==1); B has pred==1,succ==1 → extend; C has pred==1,succ==0 → stop. Path=[A,B,C] → chain-1. A=head, B=link, C=tail.A→B, A→C (fan-out): A has succ==2 → not a seed. B and C → standalone.A→B, B→C, B→D, C→E, D→E, E→F (cascade — chain tail fans out, standalones converge to new chain):
Print chain assignments after detection:
[chain] chain-1: Proposal-A (head) → Proposal-B (link) → Proposal-C (tail)
[chain] standalone: Proposal-D
Mode-aware verb guards (4-tier):
| Verb / op | live | dry-run | plan-only | dry-run-analyze |
|---|---|---|---|---|
TaskCreate | real | real | ledger only | ledger only |
TaskUpdate | real | real | ledger only | ledger only |
TaskList / TaskGet | real | real | n/a (ledger) | n/a (ledger) |
| Git operations (worktree etc.) | real | skipped | skipped | skipped |
| Agent (delivery-agent, regression) | real | real, simulate-prefix prepended | skipped | skipped |
| Agent (analyzer) | n/a | n/a | n/a | real (audits report) |
Reference files are loaded in all modes — delivery-agent-description.md and create-wt-prompt.md
must be Read before the creation pass using the same JIT protocol regardless of mode. All
placeholder substitutions are performed: Target branch from git branch --show-current, Self-merge
from chain role (yes for tail/none, no for head/link), working directory from worktree path,
definition of done from proposal. In live and dry-run modes [TASK_ID] is substituted with the
real task ID returned by TaskCreate (Phase 1.5); in plan-only and dry-run-analyze it's
substituted with DRY-N. Chain/Chain ID remain in task metadata for wiring integrity checks but
are no longer substituted into task description text.
Ledger-only behavior (plan-only, dry-run-analyze):
[DRY] would TaskCreate #DRY-N: <subject>,
append {id: DRY-N, type, subject, description, blocked_by: [], chain_id, chain_role, metadata: <metadata arg>} to the task ledger.[DRY] would TaskUpdate #DRY-N addBlockedBy=[DRY-M, ...], mutate the ledger entry.[DRY] would TaskUpdate #DRY-N description,
mutate the ledger entry.Trivial override (plan-only and dry-run-analyze): when Mode in {plan-only, dry-run-analyze},
ignore any Trivial: yes classification from the reviewer. Treat every proposal as a full
non-trivial task — generate the complete create-wt → delivery-agent chain per proposal (or per
chain: one create-wt for the whole chain). This makes the static report show the full worktree
isolation structure. In live and dry-run modes, trivial proposals keep Isolation: none (trivial) and run inline without their own create-wt.
Two-phase task creation:
Phase 1 — Creation: Create ALL tasks in parallel via a single response with N TaskCreate calls. Do not embed upstream task_ids in the subject, description, or activeForm of any task. Capture the returned task_ids. Print creation ticker.
Phase 1 — Metadata: Every TaskCreate includes a metadata argument. Schemas by task type:
git-prep: { task_type: "git-prep", chain_id: null, chain_role: null, isolation: "none",
step: "preflight | checkpoint | setup-worktrees" }
create-wt: { task_type: "create-wt", chain_id: "chain-K"|null, chain_role: null,
isolation: "native worktree", worktree_path: ".worktrees/chain-K",
target_branch: "<captured branch>" }
delivery-agent: { task_type: "delivery-agent", chain_id: "chain-K"|null,
chain_role: "head|link|tail|none", // "none" = standalone (chain_id=null)
isolation: "native worktree|none (trivial)",
worktree_path: ".worktrees/chain-K", proposal_index: N,
scope: "trivial|small|medium|large", target_branch: "<captured branch>" }
regression: { task_type: "regression", chain_id: null, chain_role: null, isolation: "none" }
Phase 2 — Wiring: Wire dependencies via a single response using both addBlockedBy (on the downstream task) AND addBlocks (on the upstream task) for every dependency edge, so the graph is traversable in both directions without extra lookups. Print wiring ticker.
Pattern for each dependency pair (upstream → downstream):
TaskUpdate({ taskId: downstream_id, addBlockedBy: [upstream_id] })
TaskUpdate({ taskId: upstream_id, addBlocks: [downstream_id] })
Sequential TaskCreate is only required when the dependent task's description must literally reference the prerequisite's task_id — which should be avoided. Carry the relationship in TaskUpdate instead.
Git-prep (always exactly 3 tasks — never merge or collapse):
[git-prep 1/3] Task: Pre-flight staging check → capture ID_preflight
[git-prep 2/3] Task: Checkpoint commit → capture ID_checkpoint
[git-prep 3/3] Task: Setup .worktrees directory → capture ID_setup
Wire: ID_checkpoint is blocked by ID_preflight; ID_setup is blocked by ID_checkpoint.
For each chain or standalone (topological order — predecessors before dependents):
Chain (chain-K, N members):
[create-wt chain-K] Task: Create worktree chain-K → capture ID_cwt_K
[delivery-agent head] Task: Delivery agent: <head proposal> → capture ID_head_K
[delivery-agent link] Task: Delivery agent: <link proposal> → capture ID_link_K
[delivery-agent tail] Task: Delivery agent: <tail proposal> → capture ID_tail_K
Wire (via TaskUpdate addBlockedBy): ID_cwt_K is blocked by ID_setup AND all upstream delivery-agent IDs this chain depends on; ID_head_K is blocked by ID_cwt_K; ID_link_K is blocked by its preceding delivery-agent; ID_tail_K is blocked by its preceding delivery-agent.
Standalone (task-N):
[create-wt task-N] Task: Create worktree task-N → capture ID_cwt_N
[delivery-agent task-N] Task: Delivery agent: <proposal> → capture ID_sa_N
Wire: ID_cwt_N is blocked by ID_setup + upstream tails; ID_sa_N is blocked by ID_cwt_N.
Regression (if present):
[regression] Task: Regression: <scope> → capture ID_regression
Wire: ID_regression is blocked by ALL chain-tail and standalone delivery-agents.
Regression Blocker Reduction: When a tail/standalone node R has a downstream tail/standalone S (via DEPENDS ON), the direct R → regression edge may be redundant.
S's tests exercise R's output — e.g., S runs an integration test that calls R's API, reads R's store, or asserts on R's behavior.R has tests that S does NOT cover — e.g., R has unit-level tests for isolated logic that S's integration test bypasses.Trivial delivery-agents: create only the delivery-agent task (no create-wt). In its Execution context set Isolation: none (trivial) and Self-merge: yes. Chain tasks are never trivial. (Sub-task spawning is always available — no field controls it; the agent decides at runtime.)
Target branch capture:
At the start of the creation pass, capture once:
Target branch = output of git branch --show-current (the branch being worked on — the merge destination)feat/xyz or chore/abc, run git branch --show-current and use whatever branch is actually checked out.This command runs in both live and dry-run modes — it is read-only and not subject to the dry-run verb guard. Substitute into every worktree delivery-agent task description and every create-wt description. This value is NEVER [placeholder]. Assert 6 catches any remaining placeholders before execution.
Delivery-agent description (dispatch protocol):
Read ${CLAUDE_SKILL_DIR}/references/delivery-agent-description.md — load the verbatim envelope template. The agent's behavior contract (lifecycle, merge protocol, status protocol, specialist catalog, sub-task spawning) lives in agents/delivery-agent.md and is loaded by the harness — do NOT paste any of that into the description.TaskCreate.description, and dispatch with subagent_type: "delivery-agent". Do not paraphrase.
Working directory: to the absolute worktree path (e.g. /repo/.worktrees/chain-1) or main workspace for trivial tasks.MERGE_TARGET: to the Target branch value for all orchestrator-dispatched tasks.Isolation: to native worktree or none (trivial).Self-merge: to yes for Chain: none and Chain: tail tasks; no for Chain: head and Chain: link tasks.[one-paragraph guidance] with a single paragraph (≤ ~120 words) of plan-level guidance distilled from the proposal — see "Task definition rules" below.Task ID: as the literal placeholder [TASK_ID] — the orchestrator fills it in Phase 1.5 after TaskCreate returns real IDs.Task definition rules — the one-paragraph guidance:
The envelope guidance is a single paragraph that the delivery-agent reads to infer purpose, what-to-do, and Definition-of-done. It MUST be plan-level, not implementation-level. The agent writes the code; the paragraph tells it what to build, where, and how success will be observable.
The paragraph should weave in:
src/db/userStore.js, test/middleware/requireJwt.test.js)createUser({email, password, name}) → {id, email, name, createdAt})The paragraph MUST NOT contain:
```js ... ```) — even if the plan has them. Distill the contract; the agent writes the body.const crypto = require('crypto')")Rule of thumb: if a competent agent could not figure out what to build and how to verify it from the paragraph alone, the paragraph is missing detail. If the paragraph reads like an implementation script (numbered typing steps, code blocks, line-by-line instructions), it has too much detail.
Example transformation (from a plan that embeds code):
PLAN INPUT (verbatim):
3. Read `src/config/auth.js` — confirm `JWT_SECRET` export before implementing.
4. Create `src/db/userStore.js`:
```js
const crypto = require('crypto');
const { JWT_SECRET } = require('../config/auth');
const users = new Map();
let nextId = 1;
function hashPassword(pw) { ... }
function createUser({ email, password, name }) { ... }
...
module.exports = { createUser, getUserById, getUserByEmail, verifyPassword };
CORRECT one-paragraph guidance:
Build the in-memory user store at src/db/userStore.js consumed by the auth router. After confirming JWT_SECRET is exported from src/config/auth.js, create src/db/userStore.js exporting createUser({email, password, name}) → {id, email, name, createdAt} (rejects duplicate emails with a 409 error), getUserById(id) → user|null, getUserByEmail(email) → user|null, and verifyPassword(user, password) → boolean. Passwords must be HMAC-hashed (sha256) using JWT_SECRET as the key; IDs are assigned monotonically as strings. Done when the four named exports exist, duplicate-email registration throws, the hash uses crypto.createHmac('sha256', JWT_SECRET) (verifiable by reading the file), and a single commit lands on chain-1-branch.
**Phase 1.5 — Task ID embedding** (runs after Phase 1 returns all IDs, before Phase 2 wiring):
For each delivery-agent task created in Phase 1, replace `[TASK_ID]` in its description with the real task ID:
For each (task_id, description) from Phase 1 output: enriched = description with "[TASK_ID]" replaced by str(task_id) Mode == live → TaskUpdate({ taskId: task_id, description: enriched }) Mode != live → print [DRY] would TaskUpdate #DRY-N description (embed Task ID: DRY-N) mutate ledger entry: description = enriched
**Create worktree task:** **FIRST: Read `${CLAUDE_SKILL_DIR}/references/create-wt-prompt.md`** — load the verbatim bash template. Before pasting, perform these substitutions in the bash code lines only (skip `#`-prefixed comment lines — those are instructional and use `task-N` as a label, not a substitution target):
- `[TARGET_BRANCH]` → the captured target branch value (appears twice: once as the fork point for `git worktree add`, once in the failure message)
- `task-N` → `chain-K` (e.g. `chain-1`) for chain create-wt tasks, or `task-N` (e.g. `task-3`) for standalone tasks
- `task-N-branch` → `chain-K-branch` (e.g. `chain-1-branch`) for chain tasks, or `task-N-branch` for standalones
Then paste verbatim as the TaskCreate description. Do not paraphrase any other content.
**Regression task description** (source: Branch A → plan's Verification section; Branch B → reviewer output):
Final regression suite — confirms no regressions across all merged changes.
main workspace
Isolation: none (serial)
Run: [runner from plan Verification or reviewer output] Confirm: [regression areas from plan Verification or reviewer output] All tests must pass. If any fail: investigate, attempt fix, rerun. Do NOT return until suite passes or failure is confirmed unresolvable.
End with: STATUS: success | failure NOTES: [list any failing tests and attempted fixes, or "all tests passed"]
On Branch B, match reviewer-output titles to creation-pass IDs when wiring blockers.
**Print final task graph:**
```markdown
## Task Graph — N Tasks
| ID | Type | Subject | Blocked by |
|-----|-----------|----------------------------------|-----------------|
| #80 | git-prep | Pre-flight staging check | — |
| #81 | git-prep | Checkpoint commit | #80 |
| ... | ... | ... | ... |
| #87 | create-wt | Create worktree: Refactor auth | #83 #85 |
| #88 | delivery-agent | Refactor auth | #87 |
| #89 | regression| Regression: suite | #88 |
Mode-aware execution guards (4-tier):
Mode == live → run the loop exactly as specified below.Mode == dry-run → run the loop, but every dispatched Agent gets references/simulate-prefix.md
PREPENDED to its task description. Git operations are skipped (git-prep tasks are TaskCreate'd
and immediately marked completed so create-wt tasks unblock; create-wt itself is dispatched as
an Agent that simulates the worktree creation per the simulate prefix). The orchestrator
collects each agent's status block, parses DISPATCHED, dispatches the next wave (also with
simulate prefix) until the graph drains. Produces a Simulated Execution Trace at the end.Mode in {plan-only, dry-run-analyze} → skip the execution loop entirely. Run the wiring
integrity check against the in-memory task ledger, then print the Dry-Run Report.Task-type contract:
| Task type | Identity | Has create-wt? | Has Merge? | Asserts | Resume method | Dispatch lane |
|---|---|---|---|---|---|---|
| delivery-agent worktree | Isolation: native worktree | yes | self (built-in) | 6 | branch in git log --merges → completed | parallel worktree; may spawn sub-task agents internally |
| delivery-agent trivial | Isolation: none (trivial) | no | no | — | git log title vs checkpoint | serial main-workspace |
| regression | type=regression | no | no | 5 | re-run agent | serial main-workspace |
| create-wt | type=create-wt | self | n/a | 3 | git worktree list | parallel background |
| git-prep | type=git-prep | n/a | n/a | — | re-run command (idempotent) | inline (orchestrator) |
Wiring integrity check (inline, runs once before resume check):
| Check | Applies to | Condition | Violation message |
|---|---|---|---|
| Assert 3 | every create-wt | blockers include Setup .worktrees AND (if DEPENDS ON exists) ≥1 upstream tail/standalone | "Create worktree #N missing expected upstream delivery-agent blocker" |
| Assert 5 | every regression (0 or 1) | blocked by ALL chain-tail and standalone delivery-agents; chain-head/link NOT direct blockers | "Regression task #N missing blockers [...]" OR "directly blocked by head/link" |
| Assert 6 | every native-worktree delivery-agent | metadata.target_branch is non-empty and not a placeholder (dry-run: ledger; live: TaskGet) | "Delivery-agent #N metadata.target_branch is missing or placeholder" |
| Assert 7 | every chain (≥2 members) | exactly one create-wt exists and belongs to the chain-head | "chain-link/tail task #N has its own create-wt — should share chain-K's" |
| Assert 8 | every delivery-agent description | is ≤ ~1 KB and is a runtime header followed by ONE paragraph of guidance — no ## What to do, ## Definition of done, ## Execution lifecycle, MAX_RETRIES, or ## Status protocol headings (those live in agents/delivery-agent.md) | "Delivery-agent #N description leaks invariant content — should be slim envelope only" |
Assert 3 exception: if the proposal has no DEPENDS ON, only Setup .worktrees is required — skip the upstream-blocker check.
If all pass: print Wiring integrity: OK — N tasks verified and continue.
Resume check (runs once before Phase A dispatch):
Query TaskList for in_progress tasks. None → proceed to Phase A. Else execution was interrupted; recover per task type before re-entering the wave-1 dispatch:
| Task type | Recovery |
|---|---|
| git-prep | Re-run the git command (all idempotent). Mark completed. |
| create-wt | git worktree list. If present → mark completed. If missing → re-run git worktree add → mark completed. |
| delivery-agent (trivial) | git log <checkpoint-sha>..HEAD --oneline. Commit matching task title present → mark completed; run cascade (TaskList scan → dispatch unblocked tasks). None → mark failed; do not cascade. |
| delivery-agent (worktree, Self-merge: yes) | git log --merges --oneline | grep <branch>. Found → mark completed; run cascade. Not found but commits present → re-dispatch agent from self-merge step. No commits → mark failed; do not cascade. |
| delivery-agent (worktree, Self-merge: no) | git -C .worktrees/<chain-K> log HEAD --oneline | grep <task subject>. Commit present → mark completed; run cascade. None → mark failed; do not cascade. |
| regression | Re-dispatch agent with existing description. |
Execution — wave-1 dispatch + self-orchestrating cascade:
Execution Guard: Do not begin dispatch until ALL Phase 2 TaskUpdate wiring calls have returned successfully. Tasks are inert metadata until claimed; wiring must be complete before any agent starts so dependency constraints are fully established.
Phase A — git-prep (orchestrator-inline, sequential):
Run each git-prep task directly:
Pre-flight staging check → Checkpoint commit → Setup .worktrees
Mark each completed after running. Capture checkpoint SHA into orchestrator context.
Phase B — create-wt (background Tasks, parallel batch):
Dispatch all create-wt tasks in ONE parallel batch (run_in_background=True).
Poll TaskList until every create-wt reaches completed or failed.
Any create-wt failure → halt; report; do not dispatch its delivery-agent.
Phase C — wave-1 delivery-agents (parallel dispatch, self-orchestrating cascade):
wave1 = [t for t in TaskList({}) where t.status == "pending" and t.blockedBy all completed]
This is every chain-head and standalone delivery-agent (their only blocker was a create-wt, now done).
Dispatch ALL wave-1 delivery-agents in a SINGLE response as parallel Agent() calls,
each with `subagent_type: "delivery-agent"`. Each agent receives its task envelope
(TaskGet(id).description = runtime header + one-paragraph guidance). The behavior contract
(lifecycle, self-merge, cascade) lives in `agents/delivery-agent.md` and is loaded by the
harness — no additional wrapping needed in the prompt.
**Mode == dry-run:** before each Agent() call, JIT-load `references/simulate-prefix.md`
and PREPEND its content to the task envelope. The agent simulates the work, runs the
cascade-dispatch directive read-only (TaskList/TaskGet only), and emits the standard status
block listing would-be-dispatched IDs. The orchestrator iterates: parse each returned status
block, dispatch the next wave (also with simulate prefix), repeat until DISPATCHED is "none"
for all agents.
Cascade self-manages from here:
Each agent → does work → commits → self-merges if Self-merge: yes →
calls TaskUpdate(completed) → scans TaskList for newly unblocked pending tasks →
claims them via TaskUpdate(in-progress) → dispatches Agent() for each in parallel
(downstream tasks are also dispatched with `subagent_type: "delivery-agent"`).
The graph drains itself. The orchestrator does not loop. (In dry-run, the agent's
cascade-dispatch is read-only per the simulate prefix; the orchestrator iterates the
waves explicitly using the DISPATCHED field of each agent's status block.)
Failure: agents follow `## On RESULT: failed` in `agents/delivery-agent.md`, which JIT-loads
`references/investigation-task-template.md` to TaskCreate a sibling investigation task.
Status protocol uses RESULT/WORK/INCOMPLETE/FAILURE/ARTIFACT/DISPATCHED fields; FAILURE ∈
{no_change, partial_change, test_failures, conflict_needs_user, needs_split}. Failed tasks
do not cascade — downstream dependents stay pending, visible via `TaskList({})`.
Trivial delivery-agents and regression tasks are claimed and dispatched by whichever upstream
agent's TaskList scan finds them newly unblocked.
Git prep task execution (inline):
Pre-flight staging check: git status, stage relevant files (git add <files>, never -A)Checkpoint commit: if staged changes → git commit -m "checkpoint: pre-execution state" and capture SHA into orchestrator context; if clean → capture HEAD SHA. This SHA is held by the orchestrator for trivial task resume checks — it is never written into task descriptions.Setup .worktrees: use existing .worktrees/ or create it, add to .gitignore, commitCreate worktree task execution (async background Task):
Each create-wt is dispatched as a background Task (run_in_background=True). Description = exact bash from ${CLAUDE_SKILL_DIR}/references/create-wt-prompt.md (loaded JIT). No prose form. Agent runs and reports a STATUS line. All ready create-wt dispatch in one parallel batch.
Orchestrator reads only the STATUS: line. success → mark create-wt completed (unblocks its delivery-agent). failure → mark failed, halt dependent delivery-agent, report.
Dry-Run Report (printed at end of Step 4, plan-only / dry-run-analyze only):
(In dry-run mode, the orchestrator prints a Simulated Execution Trace instead — see below.)
## Dry-Run Report
Mode: <plan-only|dry-run-analyze>
Branch: <A|B>
Plan file: <path|none>
Senior reviewer: <dispatched (Branch B)|skipped (Branch A)>
### Proposals (N)
<proposals table from Step 1 / reviewer output>
### Chains
Build from ledger: for each unique `chain_id`, emit one row — list members in creation order (arrow-separated delivery-agent DRY IDs from `chain_role: head` → links → tail); worktree = `.worktrees/<chain_id>` for chains, `.worktrees/task-<C>` for standalones where `<C>` is the **create-wt task's DRY-N** (always one before its delivery-agent in the creation pass); merge point = the `chain_role: tail` member's delivery-agent DRY-N (or the standalone's delivery-agent DRY-N).
| Chain ID | Members (in order) | Worktree | Merge point |
|----------|-------------------------------|-----------------------|-------------|
| chain-1 | DRY-6 → DRY-9 → DRY-12 | .worktrees/chain-1 | DRY-12 |
| none | DRY-15 (standalone) | .worktrees/task-14 | DRY-15 |
### Task List — N tasks
| # | ID | Type | Subject | Blocked by | Isolation |
|---|-------|-----------|---------------------------------------|---------------------|-------------------|
| 1 | DRY-1 | git-prep | Pre-flight staging check | — | n/a |
...
### Dependency Graph
<ASCII tree rooted at first task with no blockers>
### Task Details
**Required — do not omit.** This section is the audit trail for the wiring integrity check and the dry-run-analyze agent. Every ledger entry must appear with its fully-substituted description.
For each ledger entry:
--- DRY-N (<type>): <subject> ---
Blocked by: <comma-separated DRY IDs, or "—">
Unblocks: <comma-separated DRY IDs, or "—">
Description: <full description text>
### Wiring Integrity
<PASS — N tasks verified | violation list>
In plan-only mode, stop after printing the Dry-Run Report. In dry-run-analyze mode, continue:
Analyzer dispatch (dry-run-analyze only):
FIRST: Read ${CLAUDE_SKILL_DIR}/references/dry-run-analyzer.md — load the verbatim prompt into context.
THEN: Dispatch the Agent with that prompt verbatim, substituting {report} → the full Dry-Run Report text. Do not paraphrase.
On agent return, print under:
## Dry-Run Analyzer Findings
<table from analyzer output>
VERDICT: <READY-TO-EXECUTE | NEEDS-FIXES> — <summary>
Stop after printing findings. Do not auto-promote dry-run results to live mode — the user re-invokes /schedule-plan-tasks (no flag) when ready.
Simulated Execution Trace (printed at end of Step 4, dry-run mode only):
After all dispatched simulate-prefix Agents return their status blocks and the cascade has drained (no DISPATCHED IDs remain), emit:
## Simulated Execution Trace
Mode: dry-run (real Task API ceremony, real Agent dispatch, simulate-prefix on each agent)
Branch: <A|B>
Plan file: <path|none>
Total tasks: <N> Waves: <M> Total agents dispatched: <N>
### Wave 1
| Agent (task ID) | RESULT | WORK summary | DISPATCHED → |
|-----------------|--------|--------------|---------------|
| #87 head A | complete | "would create src/config/auth.js" | #88 |
| ... | ... | ... | ... |
### Wave 2
| ... |
(repeat per wave until DISPATCHED is "none" for all)
### Aggregate cascade
- Total dispatch edges traced: <K>
- Cascade depth: <D> waves
- Failures (RESULT: failed): <list, or "none">
- Partial (RESULT: partial): <list, or "none">
### Validation
- Every chain head dispatched in wave 1 ✓ / ✗
- Every chain tail dispatched downstream of its head ✓ / ✗
- Regression dispatched after all chain-tail and standalone delivery-agents ✓ / ✗
- Failed tasks did not cascade ✓ / ✗
- DISPATCHED graph matches the static topology from --plan-only ✓ / ✗
Stop after printing the trace. The user re-invokes /schedule-plan-tasks (no flag) for live execution.
Rules not stated inline elsewhere. For mode/reference/wiring discipline, see Modes, Steps 0–3, and the dispatch protocols.
MERGE_TARGET = the parent delivery-agent's working branch. Never TARGET_BRANCH.run_in_background=True only on create-wt. Never on delivery-agent.conflict_needs_user does not halt the loop — only the affected merge chain stalls. Independent delivery-agents continue.git add -A is forbidden in checkpoints. Stage by name to avoid pulling unrelated state.git diff alone is not terminal.npx claudepluginhub whichguy/claude-craft --plugin planning-suiteProvides a checklist for code reviews covering functionality, security, performance, maintainability, tests, and quality. Use for pull requests, audits, team standards, and developer training.