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 (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: ' :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.
# 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.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.[ -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)
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.
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.
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.
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.
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.
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.
Mechanical validation. python3 scripts/verify-spec.py <spec>. FAIL → fix and re-run. (Style warnings about old ceremony are non-blocking but worth clearing.)
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.
Approval gate. Summary report + AskUserQuestion (Approve / Modify / Questions). After approval, proceed to step 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):
{
"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) 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.
1---2name: blueprint3description: 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".4---56# Blueprint78Turn 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.910> **Letter = spirit.** If a rule blocks you from reaching the goal it was11> written for, the rule is wrong, not the goal. Don't look for a wording12> loophole — ask what the rule is protecting, and protect that.1314> **Was `/clarify`.** Renamed to `/blueprint` (it's a planning tool). `clarify` / `уточни спеку` still route here as aliases.1516## Usage1718```19/blueprint <spec.md> [--consensus-rounds N]20```2122- `--consensus-rounds` defaults to 3. Set it to 0 to skip the cross-model consensus loop (Phase 7.6) — only internal validation runs.23- **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.2425## Weaknesses and when NOT to use2627- **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.28- **Does not work on raw chat exports or unstructured notes.** The input spec must already be sectioned with `## ` (after `/cleanup`). Otherwise — abort.29- **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.30- **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).31- **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.3233## How to do it wrong vs right3435### Task proof (`Done when:`)3637❌ **Wrong:** `Done when: the endpoint works.`38- "Works" — who decides?39- No command. Boolean (works / doesn't) — no UNKNOWN.4041✅ **Right:** `Done when: \`curl -sw '%{http_code}' :8080/api/users -o /dev/null\` prints 200 in <200ms.`42- Concrete numbers, a runnable command.43- Tristate: PASS / FAIL / UNKNOWN (when the server isn't running).4445### Task scope4647❌ **Wrong:** `### TASK-1: Implement authentication system`48- Touches many files, multiple purposes, not verifiable with one command.4950✅ **Right:** `### TASK-1: Create User model in src/models/user.py with email/password`51- 1 file, clear boundary, one deliverable.52- `Done when: \`python -c "from src.models.user import User; User(email='a@b',password='x')"\` runs clean.`5354### Cross-model consensus disagreement5556❌ **Wrong:** A reviewer returns "requirement X looks unusual, suggest removing it". I apply it — I remove it.57- The user added the requirement on purpose. Removing it "helped me faster" but stomped the user's intent.5859✅ **Right:** Issue type = NEEDS_USER (the reviewer flagged it that way, or Claude self-assessor reclassified). AskUserQuestion with both views. The user decides.6061### Implicit scope reduction6263❌ **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.64- 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.6566✅ **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.6768## Writing style for the plan6970> 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).7172### Challenge the task before decomposing (multi-angle)7374Before turning notes into tasks, spend one pass questioning the task itself — the first framing isn't always the right one:7576- What if we **don't build this** at all (is the underlying need met another way)?77- What if we build a **much simpler** version (80% of value, 20% of the work)?78- Does the right shape **depend on a future plan** the input hints at?79- Which **scenarios** change the answer (scale, single-user vs multi-tenant, offline)?8081If a cheaper or simpler framing is plausible, surface it in step 2 as a question — don't silently commit to the literal reading.8283### Sharpen fuzzy language8485When 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.8687❌ **Fuzzy:** "Users can manage their subscriptions"88✅ **Sharp:** "Subscription owners (Customer accounts) can cancel/resume their own subscriptions; admin operators can cancel/resume on behalf of any Customer."8990### Be opinionated about terminology9192Pick 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.9394### Keep requirements tight and plain9596Each 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`.9798❌ **Ceremonial:** `FR-001: The auth middleware MUST verify the signature, check expiration, and return 401...`99✅ **Plain:** `- [must] Bad tokens (missing / expired / wrong signature) → 401.`100101### Stress-test edge cases with concrete scenarios102103Each edge case is a concrete input + expected output, written inline (`Edge: ...`). The next reader writes a test from it without asking what "invalid" means.104105❌ **Abstract:** "Edge case: empty request body."106✅ **Concrete:** `Edge: POST /users with body {} (no email) → 400 {error:"email_required"}.`107108### Cross-reference with code109110If 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.111112❌ **Stale:** Spec describes `POST /users` accepting `{email, name}`; code already accepts `{email, name, phone}`. Plan written as-is.113✅ **Reconciled:** "Spec says POST {email, name}; code already accepts phone. Regression, or forgot phone?" — ask before proceeding.114115### Vertical slices, not horizontal layers116117Each 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.118119❌ **Horizontal:** TASK-1 "Add all DB columns"; TASK-2 "Add all API"; … Nothing demoable until task 3.120✅ **Vertical:** TASK-1 "Add email to User: column + API field + form + test"; TASK-2 "phone: same set."121122### Foundations first — don't start in mid-air, don't bury the scaffold123124A 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:125126- **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).127- **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.128129**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.)130131**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.132133### Behavioural `Done when:`, not procedural134135The proof describes what the system DOES (observable through its interface), not HOW. The reader writes the test from it without reading implementation prose.136137❌ **Procedural:** `Done when: middleware extracts the token, calls validateJWT(), returns 401.`138✅ **Behavioural:** `Done when: \`curl -H 'Auth: <expired>' :8080/me\` → 401 {error:"token_expired"}.`139140### One place for what needs a human — `## Needs your attention`141142Everything 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.143144`tasks.md` → **`## Needs your attention`** (only if it has content — omit the heading entirely on a clean plan). Two kinds of line:145146- **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.147- **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.148149`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.150151```markdown152# tasks.md153## Needs your attention154- ❓ NEEDS YOU [decision]: is DELETE admin-only? (input was ambiguous) → blocks: TASK-11155- ❓ NEEDS YOU [unknown]: spike — does the bus handle 10k msg/s at our payload? → blocks: all156- HITL: TASK-3 — auth design call (pick session vs bearer before building)157158# reference.md159## Assumptions160- Assume Postgres (high — matches src/db).161- Assume JWT, not sessions (medium — input didn't say).162```163164### tasks.md: checklist on top, full task blocks below165166The 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.167168- `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.169- `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.170- `reference.md` → **context only** (`## Overview` / `## Requirements` / `## Assumptions` / `## Risks` / `## Non-goals`) — NO task blocks. The "why", read once.171- One-to-one: every `## Checklist` line has exactly one `### TASK-n` block below, and vice-versa.172173### Keep the plan concise and DRY — lossless174175Keep 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).176177- **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.178- **Structured facts → a table/matrix**, one row per fact, instead of repeating prose.179- **Merge near-duplicates:** two paragraphs saying the same thing → one; two risks that are the same risk → one line.180- **Lossless check:** after tightening, every distinct fact from the original notes is still findable. Compression removes words and repetition — never information.181182## Roles183184Step 2 (questioner pattern) and the Phase 7.6 consensus loop (with fallback validator) — templates live in `roles/`:185186- `roles/questioner.md` — format contract for AskUserQuestion in step 2 (not a subagent — a format spec). Includes the multi-angle challenge.187- `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.188- `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.189- `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.190- `roles/spec-validator.md` — fallback used inside Phase 7.6 when NO external reviewer is available.191192Substitutions:193194| Variable | Source |195|---|---|196| `{spec_path}` | the tasks file after step 6 (write) — `<spec-stem>/tasks.md` or `<spec>.md` |197| `{round}` | round counter in Phase 7.6 (1, 2, 3) |198| `{original_baseline}` | pre-enrichment content for the coverage check: the untouched `<spec>.md` (directory mode) or `git show pre-blueprint:<spec>` |199| `{codex_prompt}` | full text content of `roles/codex-reviewer.md` (entire file, passed as the review prompt) |200201Invocations:202- **Codex adversarial review:** call `codex exec` directly via Bash. Stable public dependency (`npm install -g @openai/codex`); no `codex-plugin-cc` runtime.203 ```bash204 command -v codex >/dev/null 2>&1 || { echo "codex CLI not installed"; exit 1; }205 # Use `codex exec -` (NOT `codex review`). Two traps proven empirically on codex 0.137:206 # 1. `codex review --uncommitted` CONFLICTS with any [PROMPT] (clap rejects the combo) — so207 # our prompt got dropped and codex ran a DEFAULT review.208 # 2. `codex review` reformats output into its own review summary ("No actionable defects…"),209 # so our `\`\`\`json\`\`\`` block never appears and extraction always falls back to empty approve.210 # `codex exec -` runs our prompt verbatim and returns the model's RAW output (prompt-controlled211 # JSON), and the model reads the spec via its own shell/file tools. Feed the prompt on stdin212 # (`-`) with <spec_path> substituted; the prompt tells codex to read that file from the tree.213 PROMPT="$(sed "s|<spec_path>|$spec_path|g" skills/blueprint/roles/codex-reviewer.md)"214 OUTPUT="$(printf '%s' "$PROMPT" | codex exec -)"215 # Extract the last fenced JSON code block — the prompt instructs the model to emit findings there.216 FINDINGS="$(printf '%s' "$OUTPUT" | python3 -c '217import sys, re, json218text = sys.stdin.read()219matches = re.findall(r"```json\s*\n(.*?)\n```", text, re.DOTALL)220print(matches[-1] if matches else json.dumps({"summary":"approve","findings":[]}))221')"222 ```223 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).224- **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.225 ```bash226 [ -n "${OPENROUTER_API_KEY:-}" ] || { echo "no OPENROUTER_API_KEY; skipping third reviewer" >&2; return 0; }227 OR_MODEL="${OPENROUTER_MODEL:-z-ai/glm-5.2}" # primary228 OR_FALLBACK="${OPENROUTER_FALLBACK:-moonshotai/kimi-k2.6}" # OpenRouter auto-routes to this if primary fails229 PROMPT="$(cat skills/blueprint/roles/openrouter-reviewer.md)230SPEC FILE ($spec_path):231$(cat "$spec_path")"232 # Reasoning reviewers (GLM/Kimi) on a long spec can take minutes — allow 300s233 # (120s/180s timed out: "response never arrived"). Timeout → non-200 → graceful degrade below.234 resp="$(curl -sS --connect-timeout 20 --max-time 300 -w $'\n%{http_code}' \235 https://openrouter.ai/api/v1/chat/completions \236 -H "Authorization: Bearer $OPENROUTER_API_KEY" -H "Content-Type: application/json" \237 -H "X-Title: blueprint consensus" \238 -d "$(jq -nc --arg m "$OR_MODEL" --arg fb "$OR_FALLBACK" --arg c "$PROMPT" \239 '{model:$m, models:[$m,$fb], messages:[{role:"user",content:$c}], temperature:0.2}')")"240 http="${resp##*$'\n'}"; body="${resp%$'\n'*}"241 if [ "$http" = "200" ]; then242 TEXT="$(printf '%s' "$body" | jq -r '.choices[0].message.content')"243 # same python extractor as Codex pulls the last ```json``` block from $TEXT244 else245 echo "OpenRouter HTTP $http — dropping third reviewer this round" >&2 # graceful degrade246 fi247 ```248- **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.249- **Fallback validator (no external reviewer):** `Agent(subagent_type="Explore", prompt=substitute("roles/spec-validator.md", vars))`.250251## What the skill does (step by step)2522531. **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.2542. **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.2553. **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.2564. **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`.2575. **Self-review checklist.** Placeholder scan, internal consistency, ambiguity check, and a **hard-gate Scope-cut audit (user-facing)**.258259 **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.**260261 The **Scope-cut audit (user-facing)** scans the in-memory plan for deferral signals:262 - Requirements tagged `[later]`, or `[must]`/`[nice]` carrying phrases `(v2)`, `(future)`, `(deferred)`, `(later)`, `(stretch goal)`, `(MVP only)`, `(out of scope for now)`, `(not for now)`.263 - Items in a `Non-goals` section that map back to anything mentioned in the input.264 - Features / endpoints / edge cases present in the input with no backing task or silently dropped from a task's coverage.265266 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.2676. **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.268 - **`<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).269 - **`<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).270 - **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).271 - **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.272 - **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`.273274 Template structures: see `references/task-format.md`.2757. **Mechanical validation.** `python3 scripts/verify-spec.py <spec>`. FAIL → fix and re-run. (Style warnings about old ceremony are non-blocking but worth clearing.)2768. **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`.2779. **Approval gate.** Summary report + AskUserQuestion (Approve / Modify / Questions). After approval, proceed to step 10.27810. **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`.279280The 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.281282## Phase 7.6 — Cross-model consensus loop283284After steps 6-7 (write plan + verify-spec.py mechanical check), the convergence loop runs. Step 8 in the walkthrough is Phase 7.6.285286Each round, every **available** external reviewer produces findings independently; Claude (in a fresh `claude -p` subprocess) triages the **union**. Independent finders, one triager. Reviewers:287288- **Codex** — `codex exec -` (prompt on stdin; if `codex` on `$PATH`). Prompt: `roles/codex-reviewer.md`.289- **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`.290- If **neither** is available → single-model fallback (`roles/spec-validator.md`).291292```293MAX_ROUNDS = consensus_rounds_flag (default 3, 0 disables)294round = 0295296reviewers = []297if bash `command -v codex` nonempty: reviewers += ["codex"]298if env OPENROUTER_API_KEY set: reviewers += ["openrouter"]299300if reviewers is empty:301 log WARNING "no external reviewer (codex / OPENROUTER_API_KEY); single-model fallback"302 result = Agent(subagent_type="Explore",303 prompt=substitute("roles/spec-validator.md", {spec_path, spec_path_bak}))304 → CONSENSUS or NEEDS_FIX (single round only); goto Step 9305306# Spec is in the working tree. `codex exec` reads <spec_path> via its own file tools307# (the prompt tells it to); the OpenRouter call reads the file content inline.308309rounds = []310while round < MAX_ROUNDS:311 round += 1312313 # 1. Each reviewer finds independently → list of findings JSON (same schema),314 # tagged with its source. A reviewer erroring (codex missing JSON, OpenRouter315 # non-200) degrades to an empty approve for THIS round, logged — never aborts.316 all_findings = []317 for r in reviewers:318 all_findings += run_reviewer(r) # codex exec - | OpenRouter curl319320 # 2. Claude self-assessment over the UNION (fresh subprocess)321 assessment = bash: claude -p < (322 read("roles/claude-self-assessor.md")323 + "\n\nSpec file: " + spec_path324 + "\n\nReviewer findings (union):\n" + json(all_findings))325326 # 3. Consensus = every reviewer "approve" AND assessment.verdict == AGREE_PASS327 if all(f.summary == "approve" for f in all_findings) and assessment.verdict == AGREE_PASS:328 → CONSENSUS, exit loop329330 # 4. Process findings via assessment categorization (dedupe identical findings331 # raised by >1 reviewer — that's a STRONGER signal, note it, apply once)332 applied, rejected, needs_user = [], [], []333 for each finding in dedupe(all_findings):334 cat = assessment.categorization[finding.id]335 if cat == ACCEPT: apply finding.recommendation to spec; applied.append(finding)336 elif cat == REJECT_PETTY: rejected.append((finding, reason))337 elif cat == NEEDS_USER: needs_user.append(finding)338339 print round summary: N applied, M rejected (reasons), K queued for user, per-reviewer counts340 rounds.append({all_findings, assessment, applied, rejected, needs_user})341342 if needs_user not empty:343 AskUserQuestion with the issues + reviewer views; apply user decisions344345 # 5. Oscillation detection346 if round >= 3 and hash(dedupe(all_findings)) == rounds[-3].findings_hash:347 → ESCALATE: "the models are stuck — your call"; print full rounds[]; break348349if round == MAX_ROUNDS and not CONSENSUS:350 ESCALATE: "(A) approve as-is, (B) abort, (C) one more round"; print full rounds[]351```352353Failure modes:354- **No external reviewer** (`codex` not on `$PATH` AND no `OPENROUTER_API_KEY`) → single-model `roles/spec-validator.md`, continues with a warning.355- **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.356- **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.357- **A reviewer's response has no JSON block** → extractor returns `{"summary":"approve","findings":[]}` (logged, no-op). Persistent across rounds → escalate.358- **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.359- **Petty disagreements** → reviewer prompts exclude style/formatting/word-choice; leaks → REJECT_PETTY, logged, not applied.360- **Oscillation** → hash comparison between rounds N and N-2, escalation.361362Output schema (defined by `roles/codex-reviewer.md` and `roles/openrouter-reviewer.md` identically):363```json364{365 "summary": "needs-attention | approve",366 "findings": [367 { "file": "<spec.md path>", "line_start": <int>, "line_end": <int>,368 "confidence": <0..1>, "recommendation": "<concrete change>" }369 ]370}371```372373## Outputs374375- `<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`.)376- `<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`.377- No `.bak` — the original `<spec>.md` is left untouched (directory mode) and the git `pre-blueprint` snapshot is the rollback.378379Git: `pre-blueprint: <name>` (snapshot before) and `blueprint: enrich <name>` (after step 6).380381Phase 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.382383## Connections to other skills384385- **Input:** typically after `/cleanup` (sectioned markdown without `[MISSING]` markers). A manually written spec is also fine if structurally valid. Optionally preceded by `/extract-links`.386- **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.387- **Downstream builders:** the Claude Code goal feature (`/goal-prep` + `/goal`, autonomous) or manual implementation; finish with `/verify-done`.388- **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`.389390## Rules391392### Commonality393The 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.394395### Prior commitment396In 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.397398### Authority (scope decisions belong to the user)399Tagging 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.400401### Social proof (cross-model rationale)402Phase 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".403404## Self-check before delivering the result405406Would this plan pass review by a senior engineer who has to build the system from it? Concretely:407408- 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?409- No placeholders (`TBD`, `...`, `[NEEDS CLARIFICATION]`, `<insert here>`) in either file?410- **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.411- Were hard-to-reverse decisions noted inline in `## Risks` (one line each) — not turned into `docs/adr/` files?412- Is every task atomic — 1-3 files, single purpose, closeable by an independent worker without questions to the author?413- **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?414- **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`?415- **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)?416- **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?417- Plan written as a flat `<spec-stem>/` directory (`tasks.md` checklist + `reference.md` body) — or a single `<spec>.md` if trivial; no nested subdirs?418- Did Phase 7.6 pass (or was it explicitly skipped with reasoning)?419- Coverage: does every Overview item have at least one task? Does every task track back to Overview / a requirement?420If "no" on any item — redo, don't ship.