# Spec Review

> Use this LAST in the Spec stage, before the human signs off — a fresh code-cold agent that FIXES the spec instead of listing complaints. You MUST run it after spec-grilling/to-prd/acceptance-criteria/environment-manifest (plus frontend-design for UI and architecture-design for a structure pass) land and before the Spec gate, while the whole bundle is still draft. It auto-fixes decidable facts (stray file paths/signatures in prd.md, dangling `see ADR-NNN`, non-verbatim CONTEXT terms, placeholders, embedded secrets/commands in environment.md) and applies-then-inline-flags contestable judgment fixes (coverage gaps, ADR-worthiness, one-feature-or-two), handing back a cleaned spec, not a punch-list.

- Skill: `celestialdust/spec-review` (Agent Skill)
- Install (CLI): `npx skillmds@latest add celestialdust/spec-review`
- Raw SKILL.md: https://api.skillmd.com/api/skills/celestialdust/spec-review/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: celestialdust (https://skillmd.com/u/celestialdust)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/celestialdust/spec-review

---


## Purpose

**Stage: Spec — the last skill before the human Spec sign-off.** The run downstream is autonomous, and
the **one oracle** it cannot out-vote is a person's act: `acceptance.md`, signed for this feature's
behavior. A human reviewing a spec littered with stray file paths, dangling ADR refs, placeholder TODOs, and
coverage gaps burns scarce attention on mechanical defects instead of the judgment calls only a human can
make. `spec-review` is a **fresh, code-cold agent
(maker≠checker)** — never the agent that authored the spec — that **fixes the spec before the human sees
it**: decidable facts are silently corrected; contestable judgment is corrected **and flagged inline**.
The human then reviews a clean spec and spends attention where it counts. The silent-re-authoring risk is
resolved by making judgment changes **visible**, not by withholding the fix.

## When to use / when to skip

**Use:** runs **last in Spec**, after `spec-grilling` (ADRs/CONTEXT.md), `to-prd` (prd.md), `acceptance-criteria`
(acceptance.md), `environment-manifest` (environment.md), `architecture-design` (architecture.md + its page,
on a structure pass), and — when the feature has UI — `frontend-design` (the design contract) have all
landed; immediately before the human Spec sign-off. `architecture-design` runs against a draft
`acceptance.md`; the two are signed together at the Spec gate — so nothing in the bundle is signed when
you run, every artifact is still editable, and a fix you apply costs nobody a re-signature.

**Skip / boundaries (depth: lite escape hatch):**
- It is **not a third gate.** It signs nothing and blocks nothing; the human still reviews and signs.
- For a one-artifact trivial fix with no cross-artifact relation, a single inline pass without the full
  bundle is acceptable — but the **default is the full relational grade** anchored on `intent.md`.

## Inputs

Read the full Spec bundle **in this order — `intent.md` first** (everything is graded *relationally*
against what the user actually asked for):

1. `intent.md` (interview-me / idea-refine) — Outcome · User · Why · Success · Constraints · Out-of-scope.
   **Read first; it is the oracle of intent.**
2. ADRs (`docs/adr/ADR-<NNN>-*.md`) + `CONTEXT.md` (spec-grilling) — referenced design substrate.
3. `prd.md` (to-prd) — Problem · Solution · User Stories · Implementation Decisions · Testing Decisions ·
   Out of Scope.
4. `acceptance.md` (acceptance-criteria) — Given/When/Then scenarios, **behavioral-only**, feature-namespaced ids
   (e.g. `PWR-A1`) back-referencing a story id.
5. `environment.md` (environment-manifest) — typed rows, closed kind enum {env-var|mcp|service|runtime-dep|fixture|
   account}; no value column, no command column.
6. design contract (frontend-design) — **only if the feature has UI**. Read `docs/design.md` alongside it
   when the repo has one: the contract records only what differs from that file, so an axis marked
   `inherits:` or `departs:` is complete as written. The three axes that file decides nothing on are the
   exception, and the split table below says what to do about them.
7. `architecture.md` + `architecture.html` (architecture-design) — **only if the feature ran a structure
   pass**. The page carries the markdown's
   source block verbatim, so the two are one artifact in two forms. `status:` reads `unsigned` here and
   stays that way — a person flips it at the gate after reading the page, and nothing an agent does flips it.

**Refuse to run** if the minimum bundle (`intent.md` + `prd.md` + `acceptance.md`) cannot be resolved —
without `intent.md` there is no oracle to grade against. Missing optional items (ADRs, or the design
contract on a non-UI feature) are soft warnings; proceed. **Run as a fresh, code-cold subagent: the agent
that authored these artifacts must NOT be the one reviewing them** (maker≠checker; cr-evaluator §"Why
this stage exists").

## Process

1. **Dispatch a fresh, code-cold subagent.** No author memory, no conversation history from spec-grilling/
   to-prd. Read the bundle in the order above — `intent.md` first.
2. **Grade relationally.** For each downstream artifact ask: does it deliver what `intent.md` asked for?
   Walk the four classic axes (placeholders · internal consistency · scope · ambiguity) PLUS the
   artifact-boundary rules and the coverage ledger.
3. **Classify every issue** as **Decidable (fact)** or **Contestable (judgment)** — see the split table. A
   few rows there are marked **outside your grant**, and those you leave exactly as written; the row says
   why. Two buckets would otherwise force a third kind of item into one of them, and both of those end in an
   edit.
4. **Decidable facts → auto-fix in place**, then re-run the **deterministic re-check** (grep) until it
   converges. Greppable, so it converges cheaply. No inline flag — it was simply wrong.
5. **Contestable judgment → apply your best correction in place too**, but **mark each change inline**:
   `<!-- spec-review: changed X → Y because Z — revert if you disagree -->`. **No loop** — one pass; the
   human is the convergence point.
6. **Hand back the cleaned spec** + write `spec-review.md` (auto-fixes, flagged judgment changes, coverage
   ledger, "open the referenced ADRs" list). That handback and `architecture.html` are what the person
   opens at the gate, so anything they have to see before signing has to be in one of the two. Tell the
   human: review the fixed spec, judgment changes are flagged inline, you keep final authority — revert any.

## The fact / opinion split

| Issue | Class | Action |
|---|---|---|
| File path / signature / driver-or-library internal appears in `prd.md` (prd MUST NOT contain these) | **Decidable** | Strip it; re-home to an ADR reference. Re-check: grep `prd.md` for paths/extensions/signatures. |
| Dangling `see ADR-NNN` — referenced ADR file does not exist | **Decidable** | Fix the ref or create the missing ADR pointer. Re-check: cross every `ADR-\d+` in prd.md against `docs/adr/`. |
| `CONTEXT.md` `## Glossary` term used non-verbatim in `prd.md` | **Decidable** | Normalize to the exact `## Glossary` term (prd uses CONTEXT terms verbatim). |
| Placeholder / `TODO` / `TBD` / incomplete section | **Decidable** | Fill from context or remove. A design-contract axis reading `inherits: docs/design.md` or `departs: docs/design.md` is **not** incomplete — never fill either in. The first points at the decided look, the second at that axis's own `## Departure` block; restating what either already says is the second copy the format exists to prevent. Nor is `architecture.md`'s §6 (Open questions for the human) — see its own row below. |
| Value or command embedded in `environment.md` (no value column, no command column — structurally illegal) | **Decidable** | Remove. If it is a real secret → also a security STOP (see Red flags). |
| `acceptance.md` scenario contains a file path / signature / table (behavioral-only) | **Decidable** | Rewrite as an observable outcome. |
| `architecture.html`'s embedded source block differs from `architecture.md` | **Decidable** | Re-splice the block from the markdown — never retype it. Compare the two byte for byte; a copy that was typed rather than spliced is already drifting while looking exactly like one that is not. This is the last stop before a person signs the page, and they read the page rather than the markdown. |
| An `acceptance.md` scenario traces through no module or seam in `architecture.md` | **Contestable** (spotting it is mechanical; which structure absorbs it is not) | A backstop: `architecture-design` traces the draft first, so a scenario reaching here got past that. Say which of the two you believe is wrong. Where the **structure** is missing a part, draft the trace into `architecture.md` and **flag it inline** with the scenario id. Where you believe the **scenario** is the thing that does not belong, **flag it for the person and change nothing** — that judgement is contestable, and your grant on `acceptance.md` covers a decidable fact, not a call about which side is right. Deleting a scenario is the one correction that cannot be flagged inline, because a removed scenario leaves no line to carry the flag; it would leave no RED test, nothing for `quality-verification` to grade, and the story-coverage checks pass either way. Name it by id in `spec-review.md` instead. Calling it a structural gap when it is a scenario gap is how a structure gets bent around a scenario nobody wanted — and quietly cutting the scenario is how a behaviour ships unguarded. |
| `architecture.md`'s §6 (Open questions for the human) reads like unfinished work — every row states a question **and a recommended answer** | **Neither — outside your grant** | Leave every row exactly as written, recommended answer included. That section opens with a line saying its rows are unanswered by design and that the person answers them at the gate; the recommended answer is what its format requires of each row, not an invitation to apply it. This overrides both standing rules: §6 is not a placeholder to fill, and not a contestable item to correct-and-flag. Resolve one and you hand the person a section that looks settled, which removes the only question they were being asked — and the rows raised by the code-cold sweeps are precisely the ones nobody has put to them yet. |
| Acceptance **coverage gap**: a `prd.md` user story or `intent.md` success-criterion with no scenario (every story must map to ≥1 reachable scenario) | **Contestable** | Draft the missing scenario; **flag it inline**. |
| **ADR-worthiness**: a hard-to-reverse ∧ surprising decision buried in prd prose instead of an ADR | **Contestable** | Propose extracting an ADR; **flag**. |
| **One feature or two**: `intent.md` describes two independent subsystems crammed into one spec | **Contestable** | Propose the split; **flag**. |
| Internal contradiction between `prd.md` and an ADR or `acceptance.md` | **Contestable** | Reconcile to one side; **flag the chosen side**. |
| A design-contract `Quality floor`, `Restraint`, or `Copy-as-design-material` axis reads `inherits: docs/design.md` or `departs: docs/design.md` | **Contestable** (spotting it is mechanical; what the `delta:` says is not) | `docs/design.md` decides the other four axes and holds nothing on these three, so the line points at nothing and Verify would grade the axis against a file with no decision in it. Draft the `delta:` from what this surface decided; **flag it inline**. |

## ADR-open check (risk mitigation / handoff)

Design now lives in **referenced** ADRs that the single Spec gate does not name — so the gate could
rubber-stamp design it never opened. `spec-review` closes that gap: verify every `see ADR-NNN` in `prd.md`
(a) resolves to a real ADR file (decidable) and (b) was surfaced inline by `to-prd`; then put the full list
of referenced ADR ids into `spec-review.md` under `## Open the referenced ADRs` so the human opens each at
the gate. ADR cross-refs are immutable once written — rename/supersede → update referrers in the same commit.

## Rationalizations

- *"The spec looks clean, I'll skip the relational pass."* → The whole value is a fresh code-cold read;
  the author can't see their own gaps. Do the full grade against `intent.md`.
- *"I'll just list the issues for the human."* → No. Hand back a **cleaned spec, not a punch-list**.
  Apply the fix.
- *"This judgment call is too risky to change."* → Apply your best correction **and** flag it inline; the
  human reverts if they disagree. Withholding the fix IS the failure mode.
- *"I'll loop on the judgment fixes until they're perfect."* → No loop on contestable items — the **human**
  is the convergence point. Only decidable facts get the deterministic re-check loop.
- *"I wrote this spec, I can review it."* → maker≠checker. A fresh code-cold agent reviews, never the author.

## Red flags

- You are the agent that authored the spec → **STOP**; dispatch a fresh code-cold subagent.
- You are producing a list of complaints instead of edits → **STOP**; fix in place.
- You are looping on a judgment call → **STOP**; one pass, flag, move on.
- You silently re-authored a contestable section with no inline flag → **STOP**; every judgment change
  must be visible.
- `intent.md` is absent → **refuse to run**; there is no oracle to grade against.
- An embedded value in `environment.md` looks like a real, committed secret → remove it from the manifest
  AND treat the exposure as a security STOP per safety rail 2 (`references/safety-rails.md`) — hard halt, surface to the human.

## Verification (ending criteria)

Done when ALL hold:
- Every **decidable** check greps clean: no file paths/signatures in `prd.md`; no dangling `ADR-NNN`; no
  non-verbatim `## Glossary` terms; no placeholders/TODO/TBD; no value/command in `environment.md`; no
  behavioral-only violations in `acceptance.md`.
- Every **contestable** change is applied **and** carries an inline `<!-- spec-review: … -->` flag.
- Every `acceptance.md` scenario id back-references a story id, and every story / `intent.md`
  success-criterion maps to ≥1 scenario (coverage ledger has no orphan story).
- `spec-review.md` written with the four stable sections, including `## Open the referenced ADRs`.
- The bundle is handed back **cleaned** (not a punch-list); the human is told judgment changes are flagged
  and they keep final authority.

## Outputs & handoff contract

- **Emits — fixed spec:** the bundle artifacts (`prd.md`, `acceptance.md`, `environment.md`, `CONTEXT.md`,
  ADRs) edited in place; decidable facts silently corrected, contestable judgments corrected + inline-flagged.
- **Emits — `spec-review.md`:** ephemeral, **OUT of the resume-spine** (not a chain link). Stable
  sections: `## Auto-fixed (facts)` · `## Flagged (judgment — revert if you disagree)` · `## Coverage
  ledger` (scenario↔story map + any not-reachable classification) · `## Open the referenced ADRs`.
- **Not a gate:** `spec-review` does **not** flip STATE.md feature state. The human Spec sign-off does that
  (feature `spec → plan`); `spec-review` runs immediately before it.
- **Stable-section discipline:** it reads consumer artifacts' stable sections (per the registry) and edits
  those sections in place — it never invents a new artifact or a new stable section.

