# Blueprint

> Use when you have a clean spec/notes file and want a readable, implementation-ready PLAN: atomic vertical-slice tasks each with a `Done when:` shell proof, plain-language requirements ([must]/[nice]/[later]), edge cases, ranked assumptions + open questions, and risks. Reads like a plan, not an RFC. Suitable for human implementation or the Claude Code goal feature. Tradeoff: slow and thorough — overkill for tasks under 1 hour. Triggers: "blueprint", "/blueprint", "plan this spec", "составь план", "clarify", "/clarify", "уточни спеку", "enrich spec", "decompose spec".

- Skill: `defaultperson/blueprint` (Agent Skill, multi-file: 10 files)
- Install (CLI): `npx skillmds@latest add defaultperson/blueprint`
- Raw SKILL.md: https://api.skillmd.com/api/skills/defaultperson/blueprint/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: DefaultPerson (https://skillmd.com/u/defaultperson)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/defaultperson/blueprint

---


# Blueprint

Turn a clean spec into a readable, implementation-ready **plan**: atomic vertical-slice tasks each with a `Done when:` shell proof, plain-language requirements, edge cases, ranked assumptions, and risks. The rigor lives in the proofs — not in ceremony.

> **Letter = spirit.** If a rule blocks you from reaching the goal it was
> written for, the rule is wrong, not the goal. Don't look for a wording
> loophole — ask what the rule is protecting, and protect that.

> **Was `/clarify`.** Renamed to `/blueprint` (it's a planning tool). `clarify` / `уточни спеку` still route here as aliases.

## Usage

```
/blueprint <spec.md> [--consensus-rounds N]
```

- `--consensus-rounds` defaults to 3. Set it to 0 to skip the cross-model consensus loop (Phase 7.6) — only internal validation runs.
- **Third reviewer (optional, opt-in):** if `OPENROUTER_API_KEY` is set, Phase 7.6 adds one diverse frontier model via OpenRouter (default `z-ai/glm-5.2`, override with `OPENROUTER_MODEL`) as an independent finder alongside Codex. No key → it stays Codex + Claude, no error.

## Weaknesses and when NOT to use

- **Slow and thorough — overkill for hour-long tasks.** Decomposition + Done-when proofs + edge cases + 3 consensus rounds (if codex is available) take 10-15 minutes. For smaller tasks, write the plan by hand.
- **Does not work on raw chat exports or unstructured notes.** The input spec must already be sectioned with `## ` (after `/cleanup`). Otherwise — abort.
- **Not suited for product-style PRDs.** This skill forces a `Done when:` shell proof per task; for freeform product-management PRDs (success metrics, not shell proofs) it's the wrong tool.
- **Phase 7.6 consensus loop needs at least one external reviewer.** Best signal comes from the `codex` CLI on `$PATH` (npm `@openai/codex`), invoked as `codex exec -` with the prompt on stdin (NOT `codex review`, whose `--uncommitted` conflicts with a prompt and whose default output isn't JSON). Optionally a third model via OpenRouter (`OPENROUTER_API_KEY`). With **neither** available — fallback to internal validation (a single model reviewing its own output, weaker).
- **Not for autonomous orchestration.** The output has no `[P]` markers, Stages, or dependency graphs — the execute pipeline was removed from this repo in v2.0. Output is for the goal feature or manual work.

## How to do it wrong vs right

### Task proof (`Done when:`)

❌ **Wrong:** `Done when: the endpoint works.`
- "Works" — who decides?
- No command. Boolean (works / doesn't) — no UNKNOWN.

✅ **Right:** `Done when: \`curl -sw '%{http_code}' :8080/api/users -o /dev/null\` prints 200 in <200ms.`
- Concrete numbers, a runnable command.
- Tristate: PASS / FAIL / UNKNOWN (when the server isn't running).

### Task scope

❌ **Wrong:** `### TASK-1: Implement authentication system`
- Touches many files, multiple purposes, not verifiable with one command.

✅ **Right:** `### TASK-1: Create User model in src/models/user.py with email/password`
- 1 file, clear boundary, one deliverable.
- `Done when: \`python -c "from src.models.user import User; User(email='a@b',password='x')"\` runs clean.`

### Cross-model consensus disagreement

❌ **Wrong:** A reviewer returns "requirement X looks unusual, suggest removing it". I apply it — I remove it.
- The user added the requirement on purpose. Removing it "helped me faster" but stomped the user's intent.

✅ **Right:** Issue type = NEEDS_USER (the reviewer flagged it that way, or Claude self-assessor reclassified). AskUserQuestion with both views. The user decides.

### Implicit scope reduction

❌ **Wrong:** Input mentions "batch user creation" and "admin role for DELETE". Mid-decomposition I think "those feel like v2" — I tag them `[later]` and move on. Same for "rate limiting" → a `Non-goals` section, without asking.
- The user wrote those on purpose. Tagging them `[later]` silently == deleting a user-stated requirement. The builder skips them; the user finds out only on the final read.

✅ **Right:** Step 5 has a hard-gate Scope-cut audit. Anything tagged `[later]`, moved to `Non-goals`, or dropped from a task's coverage gets surfaced via batched AskUserQuestion **before** the spec is written. Per item: `Keep deferred` / `Include in v1` / `Drop entirely`. Nothing gets quietly downgraded.

## Writing style for the plan

> The "challenge the task" and "assumptions & open questions" moves are borrowed from [malakhov-dmitrii/fusion](https://github.com/malakhov-dmitrii/fusion) (MIT). Applies in step 2 (questioner) and steps 3-5 (decomposition / requirements / edge cases).

### Challenge the task before decomposing (multi-angle)

Before turning notes into tasks, spend one pass questioning the task itself — the first framing isn't always the right one:

- What if we **don't build this** at all (is the underlying need met another way)?
- What if we build a **much simpler** version (80% of value, 20% of the work)?
- Does the right shape **depend on a future plan** the input hints at?
- Which **scenarios** change the answer (scale, single-user vs multi-tenant, offline)?

If a cheaper or simpler framing is plausible, surface it in step 2 as a question — don't silently commit to the literal reading.

### Sharpen fuzzy language

When the input uses vague or overloaded terms ("user", "account", "system", "data"), propose a precise canonical term and ask which the user means. Push for precision until the next reader can't misread it.

❌ **Fuzzy:** "Users can manage their subscriptions"
✅ **Sharp:** "Subscription owners (Customer accounts) can cancel/resume their own subscriptions; admin operators can cancel/resume on behalf of any Customer."

### Be opinionated about terminology

Pick ONE canonical term per concept and keep it through the whole plan. If the input uses synonyms (user/account/principal), pick the most precise — usually the one mapping to a code type — and note the aliases in a brief "Terminology" preamble if the ambiguity is worth flagging.

### Keep requirements tight and plain

Each requirement is one plain sentence, tagged `[must]` / `[nice]` / `[later]` — no RFC-2119 `MUST/SHOULD/MAY` shouting, no forced `FR-NNN` ids (add light `R1`/`R2` ids only for big specs that need task↔requirement links). Describe WHAT the system does, not HOW. Details: `references/contracts.md`.

❌ **Ceremonial:** `FR-001: The auth middleware MUST verify the signature, check expiration, and return 401...`
✅ **Plain:** `- [must] Bad tokens (missing / expired / wrong signature) → 401.`

### Stress-test edge cases with concrete scenarios

Each edge case is a concrete input + expected output, written inline (`Edge: ...`). The next reader writes a test from it without asking what "invalid" means.

❌ **Abstract:** "Edge case: empty request body."
✅ **Concrete:** `Edge: POST /users with body {} (no email) → 400 {error:"email_required"}.`

### Cross-reference with code

If the codebase has paths/types matching spec terms, read them. If code says X and the spec says Y, surface the conflict in step 2 (questioner). The spec must agree with shipping code or explicitly call out the divergence.

❌ **Stale:** Spec describes `POST /users` accepting `{email, name}`; code already accepts `{email, name, phone}`. Plan written as-is.
✅ **Reconciled:** "Spec says POST {email, name}; code already accepts phone. Regression, or forgot phone?" — ask before proceeding.

### Vertical slices, not horizontal layers

Each task cuts through ALL relevant layers end-to-end (schema → API → UI → tests), not one layer. A finished slice is demoable on its own. Prefer many thin slices over few thick ones.

❌ **Horizontal:** TASK-1 "Add all DB columns"; TASK-2 "Add all API"; … Nothing demoable until task 3.
✅ **Vertical:** TASK-1 "Add email to User: column + API field + form + test"; TASK-2 "phone: same set."

### Foundations first — don't start in mid-air, don't bury the scaffold

A plan must not read like it begins half-built. The **first task** leaves the project **runnable and verifiable**, so every later `Done when:` has something to run against:

- **Greenfield** (no test runner / build / entrypoint yet) → the first task creates the minimal skeleton + a smoke test; its `Done when:` proves a fresh clone goes green (e.g. `npm ci && npm test` exits 0 with ≥1 passing test; `uv run pytest -q` collects+passes one).
- **Brownfield** (code already builds) → the first task is a one-line **baseline proof** that the existing build/test is green (`make test` exits 0) — cheap, and it pins the starting state `/verify-done` re-checks first. If truly trivial, state the starting state in the `> Context` pointer instead of a task.

**No buried scaffold.** The test harness, fixtures, and CI entrypoint live in that first foundation task — later tasks reuse them via `**Leverage**: <foundation task> harness`, never re-create them as a side effect. If a task needs scaffolding that doesn't exist yet, that scaffolding belongs earlier, not smuggled into a feature task. (In the real run, the pytest scaffold was created in TASK-7 yet reused by TASK-16 — that's buried scaffold; it belongs first.)

**Order, don't graph.** Group by area; each area appears **exactly once**; order tasks (and areas) so a genuine prerequisite always sits *above* what needs it. This ordering — plus an inline `· after TASK-x` note in the checklist for a real data/file dependency — is the ONLY sequencing artifact. No Stages, no `[P]`, no dependency graph (removed in v2.0). If most tasks need `after …`, the slices are too horizontal — re-cut them.

### Behavioural `Done when:`, not procedural

The proof describes what the system DOES (observable through its interface), not HOW. The reader writes the test from it without reading implementation prose.

❌ **Procedural:** `Done when: middleware extracts the token, calls validateJWT(), returns 401.`
✅ **Behavioural:** `Done when: \`curl -H 'Auth: <expired>' :8080/me\` → 401 {error:"token_expired"}.`

### One place for what needs a human — `## Needs your attention`

Everything that needs **you** (the reader) before or during execution goes in **one** block at the top of the tasks file — never scattered across both files and a dozen per-task `Status:` lines. A planner can't verify everything; be honest in one place instead of guessing silently or hiding the doubt inside task bodies.

`tasks.md` → **`## Needs your attention`** (only if it has content — omit the heading entirely on a clean plan). Two kinds of line:

- **Blocking forks** — `❓ NEEDS YOU` decisions/unknowns the model must not pick alone. One line each, ending in **`→ blocks: TASK-n[, TASK-m]`** (or `→ blocks: all` for a global gate like a foundational spike). This is the machine-greppable wiring that ties a question to what it freezes.
- **HITL tasks** — one line per task that needs human judgment (architecture call, external access, manual review): `TASK-n — title (why)`. Aggregated here so a reader sees up front what they can't delegate.

`reference.md` → **`## Assumptions`** — the ranked, **non-blocking** assumptions only (high / medium / low + what each is based on). The blocking `❓ NEEDS YOU` items do **NOT** appear here — they live solely in `tasks.md` `## Needs your attention`. No item in both files.

```markdown
# tasks.md
## Needs your attention
- ❓ NEEDS YOU [decision]: is DELETE admin-only? (input was ambiguous) → blocks: TASK-11
- ❓ NEEDS YOU [unknown]: spike — does the bus handle 10k msg/s at our payload? → blocks: all
- HITL: TASK-3 — auth design call (pick session vs bearer before building)

# reference.md
## Assumptions
- Assume Postgres (high — matches src/db).
- Assume JWT, not sessions (medium — input didn't say).
```

### tasks.md: checklist on top, full task blocks below

The tasks file holds the whole plan — but **checklist first, detail after**, so it's scannable and trackable without scrolling. The reference is the "why" only.

- `tasks.md` → **`## Checklist`** — one line per task, **`- [ ] TASK-n — short title`** (≤ ~6 words), grouped under `**▸ AREA-n**` / `**US-n**` (each group once, foundations-first), with light inline flags `· after TASK-x` (a real prerequisite) / `· HITL` / `· ❓` (gated by a `## Needs your attention` item). Bare `TASK-n` (the `### TASK-n` headers belong to the blocks below). The at-a-glance map + GFM-checkbox progress tracker. No graph, no `[P]`, no Stages.
- `tasks.md` → **`## Tasks`** — the full `### TASK-n` blocks below the checklist (`**Files**`, `**Leverage**`, the `Done when:` shell proof, inline `Edge:`), grouped by `▸ AREA-n` in the same order. `Done when:` (the acceptance contract) lives here; `/verify-done` and goal-prep read the proofs from these blocks.
- `reference.md` → **context only** (`## Overview` / `## Requirements` / `## Assumptions` / `## Risks` / `## Non-goals`) — NO task blocks. The "why", read once.
- One-to-one: every `## Checklist` line has exactly one `### TASK-n` block below, and vice-versa.

### Keep the plan concise and DRY — lossless

Keep the plan **tight and non-redundant**: state each fact ONCE and cross-reference instead of repeating. Tighten prose to facts — cut filler, hedging, restatement — but **never drop a fact** (every requirement, decision, assumption, risk, edge case, and code-pointer the input or your analysis produced must survive somewhere).

- **No cross-section duplication.** A fact lives in exactly one place — `## Overview` / `## Requirements` / `## Assumptions` / `## Risks` / `## Non-goals` (reference) or a task's `### TASK-n` block (tasks). The `## Checklist` line is just a pointer + short title; it doesn't restate the block. If Requirements states it, Overview points at it — doesn't restate it.
- **Structured facts → a table/matrix**, one row per fact, instead of repeating prose.
- **Merge near-duplicates:** two paragraphs saying the same thing → one; two risks that are the same risk → one line.
- **Lossless check:** after tightening, every distinct fact from the original notes is still findable. Compression removes words and repetition — never information.

## Roles

Step 2 (questioner pattern) and the Phase 7.6 consensus loop (with fallback validator) — templates live in `roles/`:

- `roles/questioner.md` — format contract for AskUserQuestion in step 2 (not a subagent — a format spec). Includes the multi-angle challenge.
- `roles/codex-reviewer.md` — **full prompt** (with `<spec_path>` substituted) piped to `codex exec -` (Claude host) or `claude -p -` (Codex host). Owns the adversarial role, substance criteria, user-intent preservation rule, and JSON output schema. Standalone — no template wrapping.
- `roles/openrouter-reviewer.md` — **full prompt** for the optional third reviewer (a diverse frontier model via OpenRouter). Same adversarial role + JSON schema; reviews the spec content passed in the request body.
- `roles/claude-self-assessor.md` — Phase 7.6 Claude self-assessment in a fresh subprocess (`claude -p`), categorizes the union of reviewer findings as ACCEPT / REJECT_PETTY / NEEDS_USER.
- `roles/spec-validator.md` — fallback used inside Phase 7.6 when NO external reviewer is available.

Substitutions:

| Variable | Source |
|---|---|
| `{spec_path}` | the tasks file after step 6 (write) — `<spec-stem>/tasks.md` or `<spec>.md` |
| `{round}` | round counter in Phase 7.6 (1, 2, 3) |
| `{original_baseline}` | pre-enrichment content for the coverage check: the untouched `<spec>.md` (directory mode) or `git show pre-blueprint:<spec>` |
| `{codex_prompt}` | full text content of `roles/codex-reviewer.md` (entire file, passed as the review prompt) |

Invocations:
- **Codex adversarial review:** call `codex exec` directly via Bash. Stable public dependency (`npm install -g @openai/codex`); no `codex-plugin-cc` runtime.
  ```bash
  command -v codex >/dev/null 2>&1 || { echo "codex CLI not installed"; exit 1; }
  # Use `codex exec -` (NOT `codex review`). Two traps proven empirically on codex 0.137:
  #   1. `codex review --uncommitted` CONFLICTS with any [PROMPT] (clap rejects the combo) — so
  #      our prompt got dropped and codex ran a DEFAULT review.
  #   2. `codex review` reformats output into its own review summary ("No actionable defects…"),
  #      so our `\`\`\`json\`\`\`` block never appears and extraction always falls back to empty approve.
  # `codex exec -` runs our prompt verbatim and returns the model's RAW output (prompt-controlled
  # JSON), and the model reads the spec via its own shell/file tools. Feed the prompt on stdin
  # (`-`) with <spec_path> substituted; the prompt tells codex to read that file from the tree.
  PROMPT="$(sed "s|<spec_path>|$spec_path|g" skills/blueprint/roles/codex-reviewer.md)"
  OUTPUT="$(printf '%s' "$PROMPT" | codex exec -)"
  # Extract the last fenced JSON code block — the prompt instructs the model to emit findings there.
  FINDINGS="$(printf '%s' "$OUTPUT" | python3 -c '
import sys, re, json
text = sys.stdin.read()
matches = re.findall(r"```json\s*\n(.*?)\n```", text, re.DOTALL)
print(matches[-1] if matches else json.dumps({"summary":"approve","findings":[]}))
')"
  ```
  Output schema (controlled by `roles/codex-reviewer.md`): `{summary: "needs-attention"|"approve", findings: [{file, line_start, line_end, confidence, recommendation}]}`. No JSON block → falls back to an empty `approve` result (logged, treated as no-op).
- **OpenRouter third reviewer (optional):** only if `OPENROUTER_API_KEY` is set. The spec is a chat-API review, so pass the prompt + the spec content in the request body. Same JSON schema; same extractor.
  ```bash
  [ -n "${OPENROUTER_API_KEY:-}" ] || { echo "no OPENROUTER_API_KEY; skipping third reviewer" >&2; return 0; }
  OR_MODEL="${OPENROUTER_MODEL:-z-ai/glm-5.2}"                # primary
  OR_FALLBACK="${OPENROUTER_FALLBACK:-moonshotai/kimi-k2.6}"  # OpenRouter auto-routes to this if primary fails
  PROMPT="$(cat skills/blueprint/roles/openrouter-reviewer.md)
SPEC FILE ($spec_path):
$(cat "$spec_path")"
  # Reasoning reviewers (GLM/Kimi) on a long spec can take minutes — allow 300s
  # (120s/180s timed out: "response never arrived"). Timeout → non-200 → graceful degrade below.
  resp="$(curl -sS --connect-timeout 20 --max-time 300 -w $'\n%{http_code}' \
    https://openrouter.ai/api/v1/chat/completions \
    -H "Authorization: Bearer $OPENROUTER_API_KEY" -H "Content-Type: application/json" \
    -H "X-Title: blueprint consensus" \
    -d "$(jq -nc --arg m "$OR_MODEL" --arg fb "$OR_FALLBACK" --arg c "$PROMPT" \
          '{model:$m, models:[$m,$fb], messages:[{role:"user",content:$c}], temperature:0.2}')")"
  http="${resp##*$'\n'}"; body="${resp%$'\n'*}"
  if [ "$http" = "200" ]; then
    TEXT="$(printf '%s' "$body" | jq -r '.choices[0].message.content')"
    # same python extractor as Codex pulls the last ```json``` block from $TEXT
  else
    echo "OpenRouter HTTP $http — dropping third reviewer this round" >&2   # graceful degrade
  fi
  ```
- **Claude self-assessment:** Bash subprocess `claude -p` with the prompt from `roles/claude-self-assessor.md` plus the union of reviewer findings JSON pasted in.
- **Fallback validator (no external reviewer):** `Agent(subagent_type="Explore", prompt=substitute("roles/spec-validator.md", vars))`.

## What the skill does (step by step)

1. **Read and analyze the spec.** Validate (markdown, has `## ` headers, no cleanup markers `[MISSING]`/etc), classify type (product / technical / small), scan the codebase if present, flag `[NEEDS CLARIFICATION]` items.
2. **Challenge + ask** (hard gate). Run the multi-angle challenge (don't-build / simpler / future-dependent / scenarios). Then ask the user what's unclear — max 5 questions via AskUserQuestion, format in `roles/questioner.md`. If the spec is already clear and the framing is sound — skip.
3. **Decompose into atomic tasks.** Format adapts to type — details in `references/task-format.md`. Main rule: each task touches 1-3 files (a vertical slice) and has a `Done when:` line with a runnable shell proof. **Foundations first** — the first task leaves the project runnable/green (greenfield: skeleton + smoke test; brownfield: a baseline proof), the harness lives there and is reused, never buried in a later task. Order so prerequisites precede dependents; each area appears exactly once. No `[P]` markers, Stages, `AC-N.N`, or `Given/When/Then` — those are gone.
4. **Pin requirements & contracts.** Plain sentences tagged `[must]`/`[nice]`/`[later]` (light `R1`/`R2` ids only if a big spec needs links). Skip if the spec is small or single-component. Details in `references/contracts.md`.
5. **Self-review checklist.** Placeholder scan, internal consistency, ambiguity check, and a **hard-gate Scope-cut audit (user-facing)**.

   **Hard-to-reverse decisions** (no ceremony). If a task locks in a choice that's costly to undo and carries a real trade-off (DB schema, public API contract, auth/infra/messaging choice, security boundary, major dependency lock-in), record it in **one line** in the reference file's `## Risks` — what was chosen + the trade-off. A genuine fork you shouldn't pick alone already goes to the tasks file's `## Needs your attention` as `❓ NEEDS YOU` (with `→ blocks: TASK-n`). **No `docs/adr/` files, no numbering, no template.**

   The **Scope-cut audit (user-facing)** scans the in-memory plan for deferral signals:
   - Requirements tagged `[later]`, or `[must]`/`[nice]` carrying phrases `(v2)`, `(future)`, `(deferred)`, `(later)`, `(stretch goal)`, `(MVP only)`, `(out of scope for now)`, `(not for now)`.
   - Items in a `Non-goals` section that map back to anything mentioned in the input.
   - Features / endpoints / edge cases present in the input with no backing task or silently dropped from a task's coverage.

   If any signal is found, surface a batched AskUserQuestion (multiSelect=false, one question per item, up to 4 per call — batch into multiple calls if more) with options `Keep deferred (current)` / `Include in v1` / `Drop entirely` / `Drop (record in the plan)`. Apply user decisions to the in-memory plan. For `Drop (record in the plan)`, note it in one line under the reference file's `## Non-goals` (what + why) — no separate files. Loop back to step 3/4 if scope changes require re-decomposition. NEVER write to disk while scope cuts are unconfirmed. If the audit finds nothing — gate silently passes.
6. **Write the plan.** The plan is normally **two files**. **When the plan is more than one file, put them in a flat directory `<spec-stem>/`** (named after the spec, e.g. `auth-spec.md` → `auth-spec/`) — **no nested subdirectories**. A trivial spec that fits one file stays as `<spec>.md` (no directory). **No `.bak`** — in directory mode the original `<spec>.md` is left untouched (it IS the backup), and the git `pre-blueprint` snapshot covers both modes.
   - **`<spec-stem>/tasks.md` — the plan (checklist on top, blocks below).** In this order: a `> Context (the "why"): see reference.md` pointer line; **`## Needs your attention`** (only if it has content — blocking `❓ NEEDS YOU` forks each ending in `→ blocks: TASK-n`, plus one line per HITL task); **`## Checklist`** — one `- [ ] TASK-n — short title` line per task, grouped by `**▸ AREA-n**` (each group once, foundations-first), with light `· after TASK-x` / `· HITL` / `· ❓` flags (bare `TASK-n`, no `###`); then **`## Tasks`** — the full `### TASK-n` blocks (`**Files**`, `**Leverage**`, `Done when:` shell proof, inline `Edge:`), grouped by `▸ AREA-n` in the same order. This is the downstream contract — `/goal`, per-stage sub-agents, `/verify-done`, and goal-prep read THIS file (the `## Tasks` blocks' `Done when:` proofs).
   - **`<spec-stem>/reference.md` — context only (the "why", read once).** `## Overview` narrative, full `## Requirements` (`[must]/[nice]/[later]` + rationale), Terminology, **`## Assumptions`** (ranked **non-blocking** only — blocking `❓ NEEDS YOU` live in tasks.md, never here), `## Risks` (hard-to-reverse decisions, one line each), `## Non-goals`. **NO task blocks.** Keep it **concise and DRY — lossless** (each fact once, cross-ref don't repeat; cut words and dupes, never facts).
   - **Single-file fallback:** a trivial spec → just `<spec>.md` holding `## Needs your attention` (if any), `## Checklist`, `## Tasks`, and the context sections folded in (no directory, no separate reference).
   - **Self-sufficiency rule:** to execute TASK-n, read its `### TASK-n` block in `## Tasks` (the `Done when:` IS the acceptance); the `## Checklist` tells you what's left and in what order. Every checklist line ↔ exactly one `### TASK-n` block.
   - **Threshold:** split when non-trivial (the default → directory form); a trivial / single-component spec may stay one file `<spec>.md`. Downstream readers resolve the tasks file as `<spec-stem>/tasks.md` (or `<spec>.md`) and read the `### TASK-n` blocks + `Done when:` from its `## Tasks`.

   Template structures: see `references/task-format.md`.
7. **Mechanical validation.** `python3 scripts/verify-spec.py <spec>`. FAIL → fix and re-run. (Style warnings about old ceremony are non-blocking but worth clearing.)
8. **Cross-model consensus loop (Phase 7.6).** External reviewer(s) find, Claude self-assesses, iterate until CONSENSUS or max rounds. Details — next section. Can be skipped with `--consensus-rounds 0`.
9. **Approval gate.** Summary report + AskUserQuestion (Approve / Modify / Questions). After approval, proceed to step 10.
10. **Done.** Print a one-line summary + `"Plan written to <path>. /clear before continuing."`. No `.bak` to dispose of — the original `<spec>.md` stays as the backup (directory mode) and the git `pre-blueprint` snapshot is the rollback (`git checkout <spec>`). Hand off to a builder — a human, or the Claude Code goal feature (`/goal-prep` + `/goal`) — and finish with `/verify-done`.

The old "Execution Order" section (Stages, [P] markers, dependency graph for parallel spawn) is GONE in v2.0. It existed for the execute orchestration, which no longer ships.

## Phase 7.6 — Cross-model consensus loop

After steps 6-7 (write plan + verify-spec.py mechanical check), the convergence loop runs. Step 8 in the walkthrough is Phase 7.6.

Each round, every **available** external reviewer produces findings independently; Claude (in a fresh `claude -p` subprocess) triages the **union**. Independent finders, one triager. Reviewers:

- **Codex** — `codex exec -` (prompt on stdin; if `codex` on `$PATH`). Prompt: `roles/codex-reviewer.md`.
- **OpenRouter third reviewer** — a diverse frontier model (default `z-ai/glm-5.2`) via the chat API (if `OPENROUTER_API_KEY` is set). Prompt: `roles/openrouter-reviewer.md`.
- If **neither** is available → single-model fallback (`roles/spec-validator.md`).

```
MAX_ROUNDS = consensus_rounds_flag (default 3, 0 disables)
round = 0

reviewers = []
if bash `command -v codex` nonempty:          reviewers += ["codex"]
if env OPENROUTER_API_KEY set:                reviewers += ["openrouter"]

if reviewers is empty:
  log WARNING "no external reviewer (codex / OPENROUTER_API_KEY); single-model fallback"
  result = Agent(subagent_type="Explore",
                 prompt=substitute("roles/spec-validator.md", {spec_path, spec_path_bak}))
  → CONSENSUS or NEEDS_FIX (single round only); goto Step 9

# Spec is in the working tree. `codex exec` reads <spec_path> via its own file tools
# (the prompt tells it to); the OpenRouter call reads the file content inline.

rounds = []
while round < MAX_ROUNDS:
  round += 1

  # 1. Each reviewer finds independently → list of findings JSON (same schema),
  #    tagged with its source. A reviewer erroring (codex missing JSON, OpenRouter
  #    non-200) degrades to an empty approve for THIS round, logged — never aborts.
  all_findings = []
  for r in reviewers:
    all_findings += run_reviewer(r)   # codex exec -  |  OpenRouter curl

  # 2. Claude self-assessment over the UNION (fresh subprocess)
  assessment = bash: claude -p < (
    read("roles/claude-self-assessor.md")
    + "\n\nSpec file: " + spec_path
    + "\n\nReviewer findings (union):\n" + json(all_findings))

  # 3. Consensus = every reviewer "approve" AND assessment.verdict == AGREE_PASS
  if all(f.summary == "approve" for f in all_findings) and assessment.verdict == AGREE_PASS:
    → CONSENSUS, exit loop

  # 4. Process findings via assessment categorization (dedupe identical findings
  #    raised by >1 reviewer — that's a STRONGER signal, note it, apply once)
  applied, rejected, needs_user = [], [], []
  for each finding in dedupe(all_findings):
    cat = assessment.categorization[finding.id]
    if cat == ACCEPT:        apply finding.recommendation to spec; applied.append(finding)
    elif cat == REJECT_PETTY: rejected.append((finding, reason))
    elif cat == NEEDS_USER:   needs_user.append(finding)

  print round summary: N applied, M rejected (reasons), K queued for user, per-reviewer counts
  rounds.append({all_findings, assessment, applied, rejected, needs_user})

  if needs_user not empty:
    AskUserQuestion with the issues + reviewer views; apply user decisions

  # 5. Oscillation detection
  if round >= 3 and hash(dedupe(all_findings)) == rounds[-3].findings_hash:
    → ESCALATE: "the models are stuck — your call"; print full rounds[]; break

if round == MAX_ROUNDS and not CONSENSUS:
  ESCALATE: "(A) approve as-is, (B) abort, (C) one more round"; print full rounds[]
```

Failure modes:
- **No external reviewer** (`codex` not on `$PATH` AND no `OPENROUTER_API_KEY`) → single-model `roles/spec-validator.md`, continues with a warning.
- **Codex unavailable / errors** (not installed, or `codex exec` fails) → drop Codex for that round (OpenRouter still works — it reads the file inline). If that leaves no reviewer, fall back to spec-validator.
- **OpenRouter unavailable** (missing/invalid key → 401, no credits → 402, rate-limit → 429, provider down → 5xx) → drop the third reviewer for that round, log the HTTP code, continue with whoever's left. Pin the exact model slug so a future GLM/Kimi version doesn't silently change the reviewer.
- **A reviewer's response has no JSON block** → extractor returns `{"summary":"approve","findings":[]}` (logged, no-op). Persistent across rounds → escalate.
- **Models gang up on user intent** → `roles/codex-reviewer.md` / `roles/openrouter-reviewer.md` forbid proposing removal of unusual requirements; `roles/claude-self-assessor.md` mirrors the rule when categorizing.
- **Petty disagreements** → reviewer prompts exclude style/formatting/word-choice; leaks → REJECT_PETTY, logged, not applied.
- **Oscillation** → hash comparison between rounds N and N-2, escalation.

Output schema (defined by `roles/codex-reviewer.md` and `roles/openrouter-reviewer.md` identically):
```json
{
  "summary": "needs-attention | approve",
  "findings": [
    { "file": "<spec.md path>", "line_start": <int>, "line_end": <int>,
      "confidence": <0..1>, "recommendation": "<concrete change>" }
  ]
}
```

## Outputs

- `<spec-stem>/tasks.md` — the **plan** (downstream contract): `## Needs your attention` (if any) + `## Checklist` (`- [ ] TASK-n` per task) + `## Tasks` (the `### TASK-n` blocks with `Done when:` proofs — what `/verify-done` + goal-prep read). (A trivial single-file spec stays as `<spec>.md`.)
- `<spec-stem>/reference.md` — **context only** (the "why"): `## Overview` / `## Requirements` / `## Assumptions` (ranked, non-blocking) / `## Risks` / `## Non-goals`. Concise + DRY, lossless. For a trivial single-file spec these fold into `<spec>.md`.
- No `.bak` — the original `<spec>.md` is left untouched (directory mode) and the git `pre-blueprint` snapshot is the rollback.

Git: `pre-blueprint: <name>` (snapshot before) and `blueprint: enrich <name>` (after step 6).

Phase 7.6 internals (per-round findings, applied/rejected/escalated breakdown, per-reviewer counts) live in memory and are printed to stdout at round boundaries — no critique files written. On failure/oscillation, the full round-by-round summary is dumped before user escalation.

## Connections to other skills

- **Input:** typically after `/cleanup` (sectioned markdown without `[MISSING]` markers). A manually written spec is also fine if structurally valid. Optionally preceded by `/extract-links`.
- **Output (on disk):** the plan directory `<spec-stem>/` (`tasks.md` + `reference.md`), or `<spec>.md` for a trivial single-file spec; the original `<spec>.md` is left untouched (directory mode) as the backup — no `.bak`. Hard-to-reverse decisions are noted inline in the plan's `## Risks`, not in a `docs/adr/` tree.
- **Downstream builders:** the Claude Code goal feature (`/goal-prep` + `/goal`, autonomous) or manual implementation; finish with `/verify-done`.
- **Cross-model dependency:** Phase 7.6 uses `codex exec -` ([codex CLI](https://github.com/openai/codex)) and/or a diverse OpenRouter model (`OPENROUTER_API_KEY`). Without either — graceful fallback to `roles/spec-validator.md`.

## Rules

### Commonality
The plan is a shared artifact. Downstream work (the goal feature, a manual builder) makes decisions from it. If you let a placeholder through, leave a vague `Done when:`, or fail to resolve a contradictory requirement — the next step works from a holey map. Not "helping faster" — breaking the shared work.

### Prior commitment
In step 5 (self-review) you committed to placeholder scan + consistency + ambiguity check + **Scope-cut audit (user-facing gate)**. In step 7 — `verify-spec.py`. In step 8 — the consensus loop (or fallback). Skipping any step withdraws the basis for the final verdict the user acts on.

### Authority (scope decisions belong to the user)
Tagging a requirement `[later]`, moving a feature into `Non-goals`, or dropping an edge case from a task's coverage — these are scope decisions, NOT cleanup or "spec hygiene". The user wrote the input on purpose; deciding what's in v1 vs deferred is theirs. The Scope-cut audit in step 5 is a hard user-facing gate precisely so the model never makes this call alone. "It looks unconventional" / "v2 would be cleaner" / "the user probably didn't mean it" are not valid reasons to silently downgrade — surface it.

### Social proof (cross-model rationale)
Phase 7.6 exists because single-model self-review is weaker. An independent reviewer (Codex, plus an optional diverse third model from a different lab) catches issues the first biases past. A finding raised by more than one reviewer is a stronger signal. If you "skip" Phase 7.6 when a reviewer is available, you remove the only real basis to trust the plan beyond "Claude approved its own output".

## Self-check before delivering the result

Would this plan pass review by a senior engineer who has to build the system from it? Concretely:

- Does every `### TASK-n` block (in tasks.md `## Tasks`) have a concrete `Done when:` shell proof (not "it works", not "manual check") + a `**Files**` field?
- No placeholders (`TBD`, `...`, `[NEEDS CLARIFICATION]`, `<insert here>`) in either file?
- **Was the step 5 Scope-cut audit run**, with every detected deferral (`[later]`, `Non-goals` items, dropped input features/edge cases) confirmed by the user via AskUserQuestion? No silent deferral.
- Were hard-to-reverse decisions noted inline in `## Risks` (one line each) — not turned into `docs/adr/` files?
- Is every task atomic — 1-3 files, single purpose, closeable by an independent worker without questions to the author?
- **Foundations first:** does the first task leave the project runnable/green (greenfield skeleton + smoke test, or brownfield baseline proof)? Is the test harness in that task and reused — not buried in a later one? Does each area appear exactly once, prerequisites before dependents?
- **One attention surface:** are ALL blocking `❓ NEEDS YOU` items in tasks.md `## Needs your attention` (each with `→ blocks: TASK-n`) plus the HITL tasks — with NONE of them duplicated in reference.md? Does reference.md carry only the ranked **non-blocking** `## Assumptions`?
- **Checklist ↔ blocks:** is tasks.md's `## Checklist` one `- [ ] TASK-n` line per task (bare ids not `###`, each area once), and does every checklist line have exactly one `### TASK-n` block below in `## Tasks` (and vice-versa)?
- **Concise + DRY + lossless:** no fact stated in two sections; per-task specifics only in their `### TASK-n` block; near-duplicate prose/risks merged — yet every requirement, decision, assumption, risk, edge, and code-pointer survives somewhere?
- Plan written as a flat `<spec-stem>/` directory (`tasks.md` checklist + `reference.md` body) — or a single `<spec>.md` if trivial; no nested subdirs?
- Did Phase 7.6 pass (or was it explicitly skipped with reasoning)?
- Coverage: does every Overview item have at least one task? Does every task track back to Overview / a requirement?
If "no" on any item — redo, don't ship.

