Auto Plan
Overview
Decompose a spec into the smallest set of build tasks that can each be implemented, tested, and committed
on their own, wired into a dependency DAG and layered so the build walks it safely and parallelizes where
the graph allows. The plan is self-reviewed to catch the defects (cycles, mis-ordering, fat tasks,
phantom paths) that would otherwise surface as build failures.
Phase 0: Require and read the spec; ground in the repo
- Resolve the spec (
$spec path, or the newest .ulpi/spec/*). If none, or if it's vague/untestable,
STOP and route back to auto-spec — a plan inherits the spec's groundedness.
- Require the independent, write-once intake snapshot and read its Binding selected scope checklist.
The plan's
selectedScope[] is an exact copy, not its own authority. If the spec or plan omitted,
changed, or added an id/title/source, keep the mismatch visible and route it back for correction.
- Read the repo to ground the plan: existing structure, modules, build/test commands, the workspace
validate, and the load-bearing invariants.
- Honor the prior-run lessons already in your loaded context —
auto-learn writes plan-shape
lessons into CLAUDE.md and .claude/rules (which Claude Code loads automatically), so a task pattern
that blocked last run is a BINDING input here: decompose it differently, don't re-attempt it verbatim.
- Open a
checkpoint-resume run.
Success criteria: a testable spec is loaded; the repo's structure + validate commands are known.
Phase 1: Decompose into atomic tasks
Break the spec's acceptance criteria into tasks. Each task carries:
id + a one-line title;
- acceptance criteria — from the spec, the subset this task satisfies (testable);
- scope mapping —
scopeItems[], the selected-scope ids this task covers (technical support tasks may
map none, but every selected id must map to at least one task);
- write scope — the files/dirs it may modify (disjoint from sibling tasks in the same layer);
- validate / validateCommand — a human-readable slice-scoped command that explains the proof
(greenable once this slice + deps integrate; never a whole-suite e2e that only passes at end-state);
- validateExec (executable plans only) — the actual inert argv authority:
{command,args,cwd,envAllowlist,timeoutMs}. It is parsed identically by planning, approval, and runtime;
no shell command string is executed;
- specialist routing —
agent / skill / reviewer chosen for this task (see below; null when
nothing fits and the build should run generic);
- notes / patterns to follow.
Assign a specialist per task (match the INSTALLED set by description, not by name)
For each task, pick the best-fit specialist FROM WHAT IS ACTUALLY INSTALLED — the subagent types in
your options and the domain skills in your available-skills list. Match on the agent's/skill's own
description of what it does and when to use it, never on its name: a user's Next.js specialist might
be called frontend-wizard and their Rust one crab, so read what each is FOR and route the task to
the one whose description fits the work (a Next.js page task → the agent/skill for React/SSR; a
migration → the DB/framework specialist; a UI/design task → the design skill; and so on). Record on the
task: agent (the installed subagent to build it), skill (the installed domain/design skill the
engineer should invoke for correct patterns), and reviewer (the installed specialist to review it) —
each the real installed name, or null when nothing genuinely fits (the build then runs a general
engineer). Never invent an agent/skill that isn't installed, and never route on a guessed name.
Keep tasks thin: a task should be a single vertical slice with at most 3 write-scope entries (files or
dirs) — split anything bigger; MERGE two that can't validate independently. Four planning failure modes to design out per task:
- Capability providers — a task claiming a side effect (persistence, network I/O, registration,
queueing) must STATE where that capability comes from (an existing module, or the task that provides
it as a dependency). A capability from nowhere is a phantom.
- Export/registration ownership — every NEW file needs a named owner for its export/barrel/registry/
router wiring: this task or a specific dependent. Unowned wiring is how "done" tasks ship dead code.
- Semantic-hardening splits — if a task could be "completed" with placeholders or dead wiring, split
the semantic hardening into an explicit follow-up task; never let structure-only pass as behavior.
- No vague contract language — "graceful degradation", "eventually consistent", "internal update"
are banned unless the task defines owner, concrete behavior, and recovery path.
Make the display validate genuinely slice-scoped in COMMAND FORM, not just intent: scope it to the task's
own test files (e.g. pnpm --filter <pkg> exec vitest run <file> — NOT pnpm --filter <pkg> test -- <file>, where the -- makes vitest ignore the positional and run the whole package, leaking unrelated
failures into this task's gate). For an executable plan, encode that same command without shell parsing in
validateExec (for example {"command":"pnpm","args":["--filter","<pkg>","exec","vitest","run","<file>"],"cwd":"worktree","envAllowlist":[],"timeoutMs":120000}). Every test file it runs must be in this
task's writeScope or guaranteed green by an integrated dependency. Also add top-level
finalValidateExec for the independent whole-workspace end-state gate.
Success criteria: a set of atomic tasks, each with criteria, disjoint write scope, and a slice-scoped
validate.
Phase 2: Wire the DAG and layer it
- For each task, set
dependsOn = the tasks whose OUTPUT it needs (a migration it reads, a route it
extends, a symbol it imports, a test another task grows).
- Compute
layers: a topological order where every task appears strictly after all it depends on. Tasks
within a layer must be independent (disjoint write scope) so the build can run them in parallel.
- Verify the graph is acyclic.
Write ONE canonical artifact: .ulpi/plans/<name>.json, including top-level selectedScope[], task-level
scopeItems[], top-level scopeDrops[], and — for executable plans — top-level finalValidateExec. A
drop record is valid only after the user separately
acknowledges that exact id and the record contains acknowledgedByUser: true plus the acknowledgement
evidence; general plan approval is not evidence. There is deliberately NO stored markdown
twin — a second artifact is a drift class (the copies diverge and the validator only gates one). The
human view is DERIVED on demand:
node <skill-dir>/scripts/validate-plan.mjs <plan.json> --intake <absolute-snapshot.json> --render
prints the layered, checklisted markdown — use its OUTPUT in-conversation when presenting the plan
(e.g. the approval gate); it can never disagree with what the build will execute. NEVER write the
rendering to a file unless the user explicitly asks for one — an unrequested .md on disk is exactly
the drift-prone twin this design deletes.
Success criteria: a complete {tasks[], layers[][]} graph — acyclic, topologically ordered,
intra-layer independent.
Phase 2.5: Run the structural gate — it is CODE
node <skill-dir>/scripts/validate-plan.mjs .ulpi/plans/<name>.json --intake <absolute-snapshot.json>
The DAG's safety properties are deterministic, so they are enforced by script, not prose: acyclicity,
topological layer order (nothing builds on a missing base), intra-layer write-scope disjointness
(prefix-aware), the ≤3-entry atomicity cap, ≥2 acceptance criteria per task, and BLOCKING on whole-suite
e2e validates (a bare playwright test/cypress run). The ambiguous <runner> test -- <file> form is a
non-blocking WARNING — it is the vitest footgun but ALSO canonical for Jest, and the gate can't know the
runner, so it advises an explicit runner rather than blocking a correct plan. Exit 1 = fix the graph and
re-run until 0. The critics below argue SEMANTICS; this script owns STRUCTURE.
It also owns intake fidelity plus binding-scope coverage for executable plans: --intake is mandatory;
plan selectedScope[] must exactly match the snapshot's ids/titles/sources; task.scopeItems[] and
scopeDrops[] may reference only snapshot ids; duplicates fail; and every intake id must be task-mapped
or separately user-acknowledged in scopeDrops[]. Missing/changed intake ids and unacknowledged drops exit 1.
Executable plans (coordinator-run) get HARDENED checks
A plan is executable when a coordinator will construct a git worktree/branch from each task's id
and execute validation — i.e. auto-build's per-task contract or the deterministic pipeline coordinator.
The gate treats a plan as executable when it opts in explicitly ("executable": true, or "mode" one of
executable/expansion/codex) or implicitly carries any validateExec, finalValidateExec, or legacy
coordinator validateCommand. A purely descriptive plan (tasks carry only display validate, never reach
git/exec) is NOT executable and these extra checks skip it.
For an executable plan, the gate additionally enforces (each failure carries task-specific evidence):
- Safe task ids (path/shell-inert). An executable
id is constructed into
git worktree add -b task/<id> and a worktree filesystem path, so it must match the safe charset
^[A-Za-z0-9][A-Za-z0-9_-]*$ — only [A-Za-z0-9_-], and it MUST start with an alphanumeric.
Canonical form: TASK-<n> (e.g. TASK-001). This bars path traversal (../), shell
metacharacters (spaces, ;, $, backticks), and a leading - (which git would read as a flag) from
ever reaching worktree/branch construction. An unsafe id is a BLOCKING violation — rename the task.
- Display validate (normalized).
validateCommand and legacy validate normalize to one trimmed
human-readable slice command (validateCommand preferred). It is checked for slice/end-state mistakes
but is never passed to a shell.
- Execution descriptors (required). Every executable task carries
validateExec, and the plan carries
finalValidateExec. Each is exactly {command,args,cwd,envAllowlist,timeoutMs} and must pass the shared
provider-neutral parser. Empty/flag-like/compound commands, non-string args, cwd traversal, unknown or
unapproved environment names, unsafe timeouts, missing fields, and unknown fields are blocking. The
runtime executes command + args with shell:false inside the assigned worktree.
- Required execution fields present. Every executable task must carry the fields the coordinator
needs to run it: a nonempty
writeScope[], ≥2 acceptance criteria
(acceptance/acceptanceCriteria), and one nonempty slice validate command. A missing field is a
BLOCKING violation.
- End-state-only validate is refused. Beyond the bare-e2e block above, an executable task's validate
that is a bare whole-suite runner with NO task slice (e.g.
pnpm -w test, npm test, jest,
vitest run, go test ./..., pytest) only greens at end-state, so the slice always looks red
mid-build — BLOCKING. A compound command that references a slice file/path (e.g.
node scripts/validate-skills.mjs && bash scripts/test-plan-validate.sh) is correctly NOT flagged.
Phase 3: Adversarial self-review (converge until clean)
Run converge-loop with adversarial-verify critics attacking the plan each round:
- acyclicity + topological order — any task ordered before a dependency? any cycle?
- phantom paths — any write scope / validate referencing something that doesn't (and won't) exist?
- task independence — do two same-layer tasks share write scope (a race)? does a task secretly need
another's output without a
dependsOn?
- atomicity — is a task actually two changes? Can each task's validate really go green at its slice?
If two can't validate independently, MERGE them.
- coverage — does the union of
task.scopeItems[] cover the entire intake selectedScope[], even if
the spec under-covered it? Flag every id with no task as UNCOVERED. Also check spec criteria, but never
use spec coverage as a substitute for intake coverage.
Fix findings in the JSON (the only artifact); re-review; exit clean or report the remaining defects.
Success criteria: the graph passes every check, or the loop reports the specific unresolved defects.
Phase 4: Finalize
Close the checkpoint; report the plan location, task count, layer count, and the parallelism the graph
allows (widest layer). This plan is the input to auto-build.
Success criteria: a clean, self-reviewed plan is written and ready to build.
Common Rationalizations
| Rationalization |
Reality |
| "This task is a bit big but splitting is annoying." |
A fat task can't be verified or rolled back cleanly. Split it, or the build inherits the mess. |
| "These two touch the same file but should be fine in parallel." |
Same write scope in one layer is a race. Separate layers or merge them — don't hope. |
| "I'll let the build figure out the order at runtime." |
Order is a plan property. A runtime retry building on a missing base is not ordering. |
| "The validate can be the full e2e suite." |
A whole-suite validate only greens at end-state, so every slice looks broken. Make validate slice-scoped. |
| "The self-review found nothing, one pass is enough." |
Cycles and phantom paths hide. Loop until a review pass is genuinely clean. |
| "Two tasks that can't validate alone is fine, they're logically separate." |
If neither is independently greenable, they are one unit of work. Merge them. |
| "The plan covers the whole spec, so scope is covered." |
The spec or plan can be the thing that shrank. Compare both to the independent intake snapshot; every missing/changed id is blocking. |
Red Flags
- A task's write scope or validate points at a path nothing creates.
- Two tasks in the same layer writing the same file.
- A task ordered before something it depends on (or a cycle).
- A validate command that's the full test suite rather than a slice.
- An executable task without a valid
validateExec, or an executable plan without finalValidateExec.
- Spec acceptance criteria with no task covering them.
- A selected-scope id with no
task.scopeItems[] mapping, or a drop inferred from general plan approval.
- The self-review loop ran once.
Guardrails
- Never emit phantom paths or ungrounded validate commands.
- Never make a command string execution authority; executable validation is an exact inert argv
validateExec/finalValidateExec descriptor parsed by the shared gate.
- Never create a task touching more than 3 files, claiming an unsourced capability, leaving a new file's
export/registration unowned, or hiding placeholder-completable work without a hardening follow-up.
- Never put dependent or write-scope-overlapping tasks in the same layer.
- Never order a task before its dependencies; never ship a cyclic graph.
- Never leave a spec criterion uncovered by some task.
- Never leave a selected-scope id uncovered. A drop requires a separate per-id user acknowledgement;
approving the plan never implies one.
- Never sign off a plan the self-review couldn't make clean — report the defects.
When To Load References
scripts/validate-plan.mjs — the deterministic structural gate (Phase 2.5), CI-tested by
scripts/test-plan-validate.sh. auto-build's preflight and the pipeline preflight run it too — a plan
that fails it never builds.
adversarial-verify (skill) — the plan critics in Phase 3.
converge-loop (skill) — the until-clean review loop.
checkpoint-resume (skill) — durable plan-run state.
Output Contract
Report:
- plan path (
.ulpi/plans/<name>.json — single canonical artifact), independent intake snapshot
path/hash, task count, layer count
- the DAG shape — dependency edges and the widest parallel layer
- SCOPE COVERAGE: N of M selected-scope items covered, with covered ids, explicit per-id drops, and
every
UNCOVERED id; then spec-coverage confirmation
- self-review outcome (rounds to clean, or the remaining defects)
- specialist routing — which installed agents/skills tasks were matched to (and the tasks left generic)
1---2name: auto-plan3description: Turn a spec into a self-reviewed DAG of atomic build tasks: each task gets acceptance criteria, a disjoint write scope (≤3 files), and a slice-scoped validate; dependencies are wired and layered topologically so nothing builds on a missing base. Adversarial critics then attack the graph (cycles, phantom paths, coverage vs spec, task independence) until it is clean. Writes .ulpi/plans/<name>.json. Use when a spec needs an implementable, ordered breakdown before building.4---56<EXTREMELY-IMPORTANT>7A plan's job is to make the build safe and parallelizable; a bad graph makes the build fail on a broken8base. Non-negotiable:91. GROUND EVERY TASK IN THE REAL REPO. File paths, modules, and validate commands must reference things10 that exist (or that an earlier task creates). No phantom paths — the build will try to touch them.112. TASKS ARE ATOMIC AND INDEPENDENTLY VERIFIABLE. Each task has ONE coherent change, a DISJOINT write12 scope, and a validate command that can go green once that slice + its dependencies integrate. If two13 pieces cannot each validate on their own, they are ONE task — not two.143. THE GRAPH IS ACYCLIC AND TOPOLOGICALLY LAYERED. Every task lists its `dependsOn`; `layers` must be a15 topological order (each task strictly after everything it depends on). A cycle or a mis-order is a16 PLAN defect to fix, never something the build should paper over with a retry.174. DEPENDENCY MEANS "NEEDS ITS OUTPUT". If task B needs A's migration/route/exported symbol/registry row,18 B `dependsOn` A. Getting this wrong makes the build construct on a missing base.195. FAIL CLOSED. The self-review loop exits only when the graph is clean OR it stalls — a stalled review20 reports the remaining defects; it never signs off a graph it couldn't validate.21</EXTREMELY-IMPORTANT>2223# Auto Plan2425## Overview2627Decompose a spec into the smallest set of build tasks that can each be implemented, tested, and committed28on their own, wired into a dependency DAG and layered so the build walks it safely and parallelizes where29the graph allows. The plan is self-reviewed to catch the defects (cycles, mis-ordering, fat tasks,30phantom paths) that would otherwise surface as build failures.3132## Phase 0: Require and read the spec; ground in the repo3334- Resolve the spec (`$spec` path, or the newest `.ulpi/spec/*`). If none, or if it's vague/untestable,35 STOP and route back to `auto-spec` — a plan inherits the spec's groundedness.36- Require the independent, write-once intake snapshot and read its **Binding selected scope** checklist.37 The plan's `selectedScope[]` is an exact copy, not its own authority. If the spec or plan omitted,38 changed, or added an id/title/source, keep the mismatch visible and route it back for correction.39- Read the repo to ground the plan: existing structure, modules, build/test commands, the workspace40 validate, and the load-bearing invariants.41- **Honor the prior-run lessons already in your loaded context** — `auto-learn` writes plan-shape42 lessons into CLAUDE.md and `.claude/rules` (which Claude Code loads automatically), so a task pattern43 that blocked last run is a BINDING input here: decompose it differently, don't re-attempt it verbatim.44- Open a `checkpoint-resume` run.4546**Success criteria:** a testable spec is loaded; the repo's structure + validate commands are known.4748## Phase 1: Decompose into atomic tasks4950Break the spec's acceptance criteria into tasks. Each task carries:5152- `id` + a one-line title;53- **acceptance criteria** — from the spec, the subset this task satisfies (testable);54- **scope mapping** — `scopeItems[]`, the selected-scope ids this task covers (technical support tasks may55 map none, but every selected id must map to at least one task);56- **write scope** — the files/dirs it may modify (disjoint from sibling tasks in the same layer);57- **validate** / **validateCommand** — a human-readable slice-scoped command that explains the proof58 (greenable once this slice + deps integrate; never a whole-suite e2e that only passes at end-state);59- **validateExec** (executable plans only) — the actual inert argv authority:60 `{command,args,cwd,envAllowlist,timeoutMs}`. It is parsed identically by planning, approval, and runtime;61 no shell command string is executed;62- **specialist routing** — `agent` / `skill` / `reviewer` chosen for this task (see below; null when63 nothing fits and the build should run generic);64- notes / patterns to follow.6566### Assign a specialist per task (match the INSTALLED set by description, not by name)6768For each task, pick the best-fit specialist FROM WHAT IS ACTUALLY INSTALLED — the subagent types in69your options and the domain skills in your available-skills list. **Match on the agent's/skill's own70description of what it does and when to use it, never on its name**: a user's Next.js specialist might71be called `frontend-wizard` and their Rust one `crab`, so read what each is FOR and route the task to72the one whose description fits the work (a Next.js page task → the agent/skill for React/SSR; a73migration → the DB/framework specialist; a UI/design task → the design skill; and so on). Record on the74task: `agent` (the installed subagent to build it), `skill` (the installed domain/design skill the75engineer should invoke for correct patterns), and `reviewer` (the installed specialist to review it) —76each the real installed name, or **null** when nothing genuinely fits (the build then runs a general77engineer). Never invent an agent/skill that isn't installed, and never route on a guessed name.7879Keep tasks thin: a task should be a single vertical slice with **at most 3 write-scope entries** (files or80dirs) — split anything bigger; MERGE two that can't validate independently. Four planning failure modes to design out per task:8182- **Capability providers** — a task claiming a side effect (persistence, network I/O, registration,83 queueing) must STATE where that capability comes from (an existing module, or the task that provides84 it as a dependency). A capability from nowhere is a phantom.85- **Export/registration ownership** — every NEW file needs a named owner for its export/barrel/registry/86 router wiring: this task or a specific dependent. Unowned wiring is how "done" tasks ship dead code.87- **Semantic-hardening splits** — if a task could be "completed" with placeholders or dead wiring, split88 the semantic hardening into an explicit follow-up task; never let structure-only pass as behavior.89- **No vague contract language** — "graceful degradation", "eventually consistent", "internal update"90 are banned unless the task defines owner, concrete behavior, and recovery path.9192Make the display `validate` genuinely slice-scoped in COMMAND FORM, not just intent: scope it to the task's93own test files (e.g. `pnpm --filter <pkg> exec vitest run <file>` — NOT `pnpm --filter <pkg> test --94<file>`, where the `--` makes vitest ignore the positional and run the whole package, leaking unrelated95failures into this task's gate). For an executable plan, encode that same command without shell parsing in96`validateExec` (for example `{"command":"pnpm","args":["--filter","<pkg>","exec","vitest","run","<file>"],"cwd":"worktree","envAllowlist":[],"timeoutMs":120000}`). Every test file it runs must be in this97task's writeScope or guaranteed green by an integrated dependency. Also add top-level98`finalValidateExec` for the independent whole-workspace end-state gate.99100**Success criteria:** a set of atomic tasks, each with criteria, disjoint write scope, and a slice-scoped101validate.102103## Phase 2: Wire the DAG and layer it104105- For each task, set `dependsOn` = the tasks whose OUTPUT it needs (a migration it reads, a route it106 extends, a symbol it imports, a test another task grows).107- Compute `layers`: a topological order where every task appears strictly after all it depends on. Tasks108 within a layer must be independent (disjoint write scope) so the build can run them in parallel.109- Verify the graph is acyclic.110111Write ONE canonical artifact: `.ulpi/plans/<name>.json`, including top-level `selectedScope[]`, task-level112`scopeItems[]`, top-level `scopeDrops[]`, and — for executable plans — top-level `finalValidateExec`. A113drop record is valid only after the user separately114acknowledges that exact id and the record contains `acknowledgedByUser: true` plus the acknowledgement115evidence; general plan approval is not evidence. There is deliberately NO stored markdown116twin — a second artifact is a drift class (the copies diverge and the validator only gates one). The117human view is DERIVED on demand:118`node <skill-dir>/scripts/validate-plan.mjs <plan.json> --intake <absolute-snapshot.json> --render`119prints the layered, checklisted markdown — use its OUTPUT in-conversation when presenting the plan120(e.g. the approval gate); it can never disagree with what the build will execute. NEVER write the121rendering to a file unless the user explicitly asks for one — an unrequested .md on disk is exactly122the drift-prone twin this design deletes.123124**Success criteria:** a complete `{tasks[], layers[][]}` graph — acyclic, topologically ordered,125intra-layer independent.126127## Phase 2.5: Run the structural gate — it is CODE128129```bash130node <skill-dir>/scripts/validate-plan.mjs .ulpi/plans/<name>.json --intake <absolute-snapshot.json>131```132133The DAG's safety properties are deterministic, so they are enforced by script, not prose: acyclicity,134topological layer order (nothing builds on a missing base), intra-layer write-scope disjointness135(prefix-aware), the ≤3-entry atomicity cap, ≥2 acceptance criteria per task, and BLOCKING on whole-suite136e2e validates (a bare `playwright test`/`cypress run`). The ambiguous `<runner> test -- <file>` form is a137non-blocking WARNING — it is the vitest footgun but ALSO canonical for Jest, and the gate can't know the138runner, so it advises an explicit runner rather than blocking a correct plan. Exit 1 = fix the graph and139re-run until 0. The critics below argue SEMANTICS; this script owns STRUCTURE.140141It also owns intake fidelity plus binding-scope coverage for executable plans: `--intake` is mandatory;142plan `selectedScope[]` must exactly match the snapshot's ids/titles/sources; `task.scopeItems[]` and143`scopeDrops[]` may reference only snapshot ids; duplicates fail; and every intake id must be task-mapped144or separately user-acknowledged in `scopeDrops[]`. Missing/changed intake ids and unacknowledged drops exit 1.145146### Executable plans (coordinator-run) get HARDENED checks147148A plan is **executable** when a coordinator will construct a git worktree/branch from each task's `id`149and execute validation — i.e. `auto-build`'s per-task contract or the deterministic pipeline coordinator.150The gate treats a plan as executable when it opts in explicitly (`"executable": true`, or `"mode"` one of151`executable`/`expansion`/`codex`) or implicitly carries any `validateExec`, `finalValidateExec`, or legacy152coordinator `validateCommand`. A purely descriptive plan (tasks carry only display `validate`, never reach153git/exec) is NOT executable and these extra checks skip it.154155For an executable plan, the gate additionally enforces (each failure carries task-specific evidence):156157- **Safe task ids (path/shell-inert).** An executable `id` is constructed into158 `git worktree add -b task/<id>` and a worktree filesystem path, so it must match the safe charset159 **`^[A-Za-z0-9][A-Za-z0-9_-]*$`** — only `[A-Za-z0-9_-]`, and it MUST start with an alphanumeric.160 Canonical form: **`TASK-<n>`** (e.g. `TASK-001`). This bars path traversal (`../`), shell161 metacharacters (spaces, `;`, `$`, backticks), and a leading `-` (which git would read as a flag) from162 ever reaching worktree/branch construction. An unsafe id is a BLOCKING violation — rename the task.163- **Display validate (normalized).** `validateCommand` and legacy `validate` normalize to one trimmed164 human-readable slice command (`validateCommand` preferred). It is checked for slice/end-state mistakes165 but is never passed to a shell.166- **Execution descriptors (required).** Every executable task carries `validateExec`, and the plan carries167 `finalValidateExec`. Each is exactly `{command,args,cwd,envAllowlist,timeoutMs}` and must pass the shared168 provider-neutral parser. Empty/flag-like/compound commands, non-string args, cwd traversal, unknown or169 unapproved environment names, unsafe timeouts, missing fields, and unknown fields are blocking. The170 runtime executes command + args with `shell:false` inside the assigned worktree.171- **Required execution fields present.** Every executable task must carry the fields the coordinator172 needs to run it: a nonempty **`writeScope[]`**, **≥2 acceptance criteria**173 (`acceptance`/`acceptanceCriteria`), and one nonempty **slice validate command**. A missing field is a174 BLOCKING violation.175- **End-state-only validate is refused.** Beyond the bare-e2e block above, an executable task's validate176 that is a bare whole-suite runner with NO task slice (e.g. `pnpm -w test`, `npm test`, `jest`,177 `vitest run`, `go test ./...`, `pytest`) only greens at end-state, so the slice always looks red178 mid-build — BLOCKING. A compound command that references a slice file/path (e.g.179 `node scripts/validate-skills.mjs && bash scripts/test-plan-validate.sh`) is correctly NOT flagged.180181## Phase 3: Adversarial self-review (converge until clean)182183Run `converge-loop` with `adversarial-verify` critics attacking the plan each round:184185- **acyclicity + topological order** — any task ordered before a dependency? any cycle?186- **phantom paths** — any write scope / validate referencing something that doesn't (and won't) exist?187- **task independence** — do two same-layer tasks share write scope (a race)? does a task secretly need188 another's output without a `dependsOn`?189- **atomicity** — is a task actually two changes? Can each task's validate really go green at its slice?190 If two can't validate independently, MERGE them.191- **coverage** — does the union of `task.scopeItems[]` cover the entire intake `selectedScope[]`, even if192 the spec under-covered it? Flag every id with no task as `UNCOVERED`. Also check spec criteria, but never193 use spec coverage as a substitute for intake coverage.194195Fix findings in the JSON (the only artifact); re-review; exit clean or report the remaining defects.196197**Success criteria:** the graph passes every check, or the loop reports the specific unresolved defects.198199## Phase 4: Finalize200201Close the checkpoint; report the plan location, task count, layer count, and the parallelism the graph202allows (widest layer). This plan is the input to `auto-build`.203204**Success criteria:** a clean, self-reviewed plan is written and ready to build.205206## Common Rationalizations207208| Rationalization | Reality |209|---|---|210| "This task is a bit big but splitting is annoying." | A fat task can't be verified or rolled back cleanly. Split it, or the build inherits the mess. |211| "These two touch the same file but should be fine in parallel." | Same write scope in one layer is a race. Separate layers or merge them — don't hope. |212| "I'll let the build figure out the order at runtime." | Order is a plan property. A runtime retry building on a missing base is not ordering. |213| "The validate can be the full e2e suite." | A whole-suite validate only greens at end-state, so every slice looks broken. Make validate slice-scoped. |214| "The self-review found nothing, one pass is enough." | Cycles and phantom paths hide. Loop until a review pass is genuinely clean. |215| "Two tasks that can't validate alone is fine, they're logically separate." | If neither is independently greenable, they are one unit of work. Merge them. |216| "The plan covers the whole spec, so scope is covered." | The spec or plan can be the thing that shrank. Compare both to the independent intake snapshot; every missing/changed id is blocking. |217218## Red Flags219220- A task's write scope or validate points at a path nothing creates.221- Two tasks in the same layer writing the same file.222- A task ordered before something it depends on (or a cycle).223- A validate command that's the full test suite rather than a slice.224- An executable task without a valid `validateExec`, or an executable plan without `finalValidateExec`.225- Spec acceptance criteria with no task covering them.226- A selected-scope id with no `task.scopeItems[]` mapping, or a drop inferred from general plan approval.227- The self-review loop ran once.228229## Guardrails230231- Never emit phantom paths or ungrounded validate commands.232- Never make a command string execution authority; executable validation is an exact inert argv233 `validateExec`/`finalValidateExec` descriptor parsed by the shared gate.234- Never create a task touching more than 3 files, claiming an unsourced capability, leaving a new file's235 export/registration unowned, or hiding placeholder-completable work without a hardening follow-up.236- Never put dependent or write-scope-overlapping tasks in the same layer.237- Never order a task before its dependencies; never ship a cyclic graph.238- Never leave a spec criterion uncovered by some task.239- Never leave a selected-scope id uncovered. A drop requires a separate per-id user acknowledgement;240 approving the plan never implies one.241- Never sign off a plan the self-review couldn't make clean — report the defects.242243## When To Load References244245- `scripts/validate-plan.mjs` — the deterministic structural gate (Phase 2.5), CI-tested by246 `scripts/test-plan-validate.sh`. auto-build's preflight and the pipeline preflight run it too — a plan247 that fails it never builds.248- `adversarial-verify` (skill) — the plan critics in Phase 3.249- `converge-loop` (skill) — the until-clean review loop.250- `checkpoint-resume` (skill) — durable plan-run state.251252## Output Contract253254Report:2552561. plan path (`.ulpi/plans/<name>.json` — single canonical artifact), independent intake snapshot257 path/hash, task count, layer count2582. the DAG shape — dependency edges and the widest parallel layer2593. **SCOPE COVERAGE: N of M selected-scope items covered**, with covered ids, explicit per-id drops, and260 every `UNCOVERED` id; then spec-coverage confirmation2614. self-review outcome (rounds to clean, or the remaining defects)2625. specialist routing — which installed agents/skills tasks were matched to (and the tasks left generic)