feat(specify): add spec-production entry path; split brainstorm

Add `specify` as a third co-equal entry path into the dev cycle: it
produces an approved spec from already-settled sources (an exhaustive
issue, a long in-context design discussion, or a design brainstorm just
ratified) with review but no interview. This is the producing half of a
deliberate deciding/producing split — `brainstorm` shrinks to optional
discovery, `specify` becomes the sole spec-production gate before
`planner`, mirroring the RED->GREEN split that keeps tdd/debug honest.

What moved:
- brainstorm/SKILL.md: stripped of the hard-gate, the acceptance
  criterion, write-spec, self-review, grounding-check, user-review, and
  planner-handoff steps; terminal state is now handing a ratified design
  narrative to specify. Steps renumbered 1-5 (production steps left).
- specify/SKILL.md (new): the production core, with a precondition gate
  (Step 1.5) that bounces to brainstorm the moment the sources do not
  resolve a load-bearing decision — the same discipline tdd uses.
- The grounding-check agent moved brainstorm/agents/ -> specify/agents/
  (no-orphan-agents: it lives under its dispatcher), refs repointed.
- boss/SKILL.md: Entry-path reflection is now three-way (tdd / specify /
  brainstorm). specify dispatches autonomously (bounded, no interview)
  and pauses at its user-review gate; only a fresh brainstorm cycle
  stays a pre-dispatch bounce-back.
- pipeline.md, README, profile-schema, the profile template, and the
  migration layout updated so every pipeline rendering agrees; specify
  is a CORE node (not opt-in, unlike tdd) carrying gates: [planner].

Design alternative rejected: parallel sibling skills sharing a
docs/spec-production.md (extract-to-doc). Chosen extract-and-chain
instead — the shared surface is ~70%, so a shared doc would either
become the skill body or drift; chaining keeps one executed home for the
gates.

Verification (prose repo, no test suite): the spec's internal-
consistency grep suite (no two-path drift, no direct brainstorm->planner
edge, specify referenced in every rendering, grounding-check single home
under specify, specify structural completeness) all green. Orchestrator
inspection additionally fixed two dead step-refs the plan under-scoped
(a "(Step 4)" lift-validation pointer and a "Skipping Step 7
self-review" red flag, both pointing at steps brainstorm no longer has)
and corrected six pre-existing brainstorm->planner renderings in
pipeline.md and tdd that predated this cycle.

Known follow-ups (non-blocking): the committed spec writes
`skills/specify/` in places (typo; skill dirs are repo-top-level) — to
be corrected separately. The grounding-check hard-gate was degenerate
for this very cycle (this repo has no profile and no test suite); the
skip is documented in the spec and the session.
This commit is contained in:
2026-06-04 23:11:07 +02:00
parent d4630a538d
commit 4f83305525
10 changed files with 575 additions and 381 deletions
+380
View File
@@ -0,0 +1,380 @@
---
name: specify
description: Use when the design is already settled — a long in-context design discussion, an exhaustive tracker issue, or a ratified design handed over by brainstorm — and a spec must be produced from those sources with review but without an interview. The spec-production core: applies the feature-acceptance criterion, writes the spec under paths.spec_dir, runs the parse and grounding-check gates, takes user sign-off, hands off to planner. Bounces to brainstorm the moment the sources do not resolve a load-bearing design decision. Co-equal third entry path alongside brainstorm and tdd.
---
# specify — spec-production entry path
> **Violating the letter of these rules is violating the spirit.**
## Overview
`specify` is where a settled design becomes an approved spec. It
takes **given sources** — a long in-context design discussion, an
exhaustive tracker issue, settled design docs, or a ratified design
narrative handed over by `brainstorm` — and produces the spec the
planner consumes: with review, but **without an interview**.
It is the **sole home of the spec-production gates**: the
feature-acceptance criterion, the write-spec step, the
parse-every-block self-review, the `grounding-check` hard-gate, and
the user-review gate. Whichever way design work enters — fresh
through `brainstorm`'s discovery, or directly from sources that
already resolve the design — it passes through `specify` to become a
spec. `specify` carries the universal invariant **no plan without an
approved spec**.
`specify` is the *producing* half of a deliberate split. `brainstorm`
owns *deciding* — interview, approaches, ratification. `specify` owns
*producing* — rendering that decision into the canonical artefact and
clearing the gates. The split is the toolchain's deepest discipline
applied one level up: `tdd → implement` and `debug → implement` split
RED from GREEN across two dispatches so the spec stays honest;
`specify` splits deciding from producing so the producing phase reads
the decision as a *source* rather than making it inline.
This skill is **not** opt-in. Unlike `tdd` (a profile-gated bypass),
`specify` is a core pipeline node: it is the gate before `planner` on
every design path that is not a bug fix or a test-specifiable feature.
## When to Use / Skipping
Triggers:
- The design is settled in the sources and a spec must be written:
an exhaustive issue body, a long in-context design discussion, or
a ratified design `brainstorm` just handed over.
- A cycle-scope spec is missing for settled work and one is needed.
**Use the named alternative instead for:**
- A genuine design fork — the sources do **not** resolve a
load-bearing decision. Use `brainstorm` (its discovery is what
resolves the fork). `specify` itself bounces here the moment it
detects this (see The Iron Law and Step 1.5).
- A test-specifiable feature, on a profile that enables `tdd` — the
RED executable-spec is the spec. Use `tdd` directly.
- An observed bug — use `debug` directly (RED-first).
- A tidy iteration — use `audit` directly.
- A trivial mechanical edit — per the project's CLAUDE.md carve-out.
**Skipping a gate is never permitted.** The interview is `brainstorm`'s,
and its absence here is the whole point — but the *production gates*
(criterion, parse-every-block, grounding-check, user-review) are
non-negotiable regardless of how the work entered. "The issue looks
exhaustive, skip the grounding-check" is the reactive-deference failure
this skill must not reintroduce.
## The Iron Law
```
THE SOURCES MUST RESOLVE EVERY LOAD-BEARING DESIGN DECISION BEFORE THE SPEC IS WRITTEN.
IF WRITING THE SPEC FORCES A CHOICE BETWEEN PLAUSIBLE DESIGNS THE SOURCES DO NOT RESOLVE,
STOP AND BOUNCE TO brainstorm. DO NOT SILENTLY PICK ONE.
THE PRODUCTION GATES — ACCEPTANCE CRITERION, PARSE-EVERY-BLOCK, GROUNDING-CHECK, USER-REVIEW —
ARE NON-NEGOTIABLE REGARDLESS OF ENTRY PATH.
NO PLAN, SCAFFOLDING, OR DOWNSTREAM SKILL UNTIL THE SPEC HAS BEEN PRESENTED AND THE USER HAS APPROVED IT.
```
Every clause is non-negotiable. The precondition gate is what makes
the no-interview path legitimate rather than merely convenient: it is
the honest fork-check that `tdd`'s "is this test-specifiable?" is for
test work.
## The Process
### Step 1 — Explore / re-ground context
Before producing anything, establish what the sources actually say:
- **Direct entry** (from sources): read the sources in full — the
tracker issue body, the in-context design discussion, any settled
design docs. This IS the design input. Then `git log -5
--format=full` for the current state of the project.
- **Chain entry** (from `brainstorm`): the ratified design narrative
is already in-context. Re-ground lightly — fresh `git log`, the
files the design touches — rather than re-deriving it.
- If the project has a design ledger under `paths.design_ledger`,
walk to the contracts the work touches. Note any `status:
aspirational` source: its code is a target, not verified fact —
track it for the Step-4 parse gate.
### Step 1.5 — Precondition gate (bounce-back)
Enumerate the **load-bearing design decisions** the spec must encode
— the choices that, decided differently, would change what ships.
For each, ask the honest question:
> Do the sources **resolve** this, or am I about to **pick** it
> myself?
If every load-bearing decision is resolved by the sources, proceed to
Step 2. If even one is unresolved — the sources leave a genuine fork
between plausible designs with real trade-offs — **STOP and bounce to
`brainstorm`** (see Error handling / The Bounce-Back). Do not silently
pick one and bake it into the spec; a guessed design ratified into a
spec is harder to dislodge than an open question, and it is exactly
the reactive-deference failure the gate exists to prevent.
Genuine doubt about whether the sources resolve a decision *is itself*
the fork signal — it resolves to a bounce, not to a guess.
### Step 2 — Apply the project's feature-acceptance criterion
The project's `CLAUDE.md` declares the criterion a feature must
satisfy to ship. Apply it now, prospectively. The typical shape is
some combination of:
- the project's intended audience naturally reaches for the feature,
- the feature measurably improves correctness or removes redundancy,
- the feature reintroduces no class of failure the project's core
constraint exists to eliminate.
If the project declares no criterion, the default is "the feature
solves a real user-facing problem and does not contradict a stated
design commitment". The orchestrator applies the criterion; do not
delegate it to the user.
**The criterion is unjudgeable without concrete code.** For any cycle
with authoring or consumer surface, the spec MUST contain the actual
program / invocation / configuration a user would write — and that
example *is* the criterion's empirical evidence. A prose assertion
("a user naturally reaches for X") with no shown code is the
unfalsifiable hand-wave the criterion exists to prevent: write the
code the user would write, then judge whether it is what the criterion
demands. For an infrastructure cycle with **no** user surface there is
still concrete code: show the code the cycle *delivers* — frequently a
test fixture whose correct behaviour is rejection — plus a *minimal,
honest* slice of the north-star program the infrastructure serves. "No
surface so no code to show" is the rationalisation to refuse.
**Code lifted from an aspirational source is a hypothesis, not
evidence.** If the concrete code here is lifted from a `design_models`
file (or any `status: aspirational` source, per Step 1), it carries no
validation by default. Treat it as the spec's most suspect bytes: it
must clear the Step-4 parse-every-block gate before PASS.
### Step 3 — Write the spec
Path: under `paths.spec_dir`, with a name per the project's
`naming.policy` (see profile schema). Determine the next slot per the
policy.
Structure:
```markdown
# <Title> — Design Spec
**Date:** YYYY-MM-DD
**Status:** Draft — awaiting user spec review
**Authors:** orchestrator + Claude
## Goal
## Architecture
## Concrete code shapes
## Components
## Data flow
## Error handling
## Testing strategy
## Acceptance criteria
```
`## Concrete code shapes` is mandatory and **leads with the
user-facing program**: the worked author example for a surface cycle
(= the Step-2 empirical evidence), or — for a no-surface infra cycle —
the code the cycle *delivers* (often a must-fail fixture) plus a
minimal honest north-star slice. The before → after implementation
shape (struct / signature / match-arm) for each load-bearing change
follows as an explicitly secondary subsection, never first. Prose may
*explain* code, never *replace* it. Exact bytes / line-numbers remain
the planner's job — the spec owns the *shape*, shown.
### Step 4 — Self-review
Inline checklist (not a subagent dispatch):
1. **Placeholder scan:** any "TBD", "TODO", incomplete sections,
vague requirements? Fix.
2. **Internal consistency:** sections must not contradict.
Architecture must match component descriptions.
3. **Scope check:** focused enough for one plan, or does it need
sub-cycle decomposition?
4. **Ambiguity check:** could any requirement be read two ways? Pick
one and make it explicit.
5. **Concrete-code check:** does `## Concrete code shapes` exist and
carry before → after code for every load-bearing change (plus the
worked user-facing example for a surface cycle)? A load-bearing
change described only in prose is a self-review failure to fix.
6. **Parse-every-block gate.** Extract every fenced code block and run
it through the `spec_validation` gate defined in
`docs/profile-schema.md` (which owns the parser-invocation
protocol, the no-parser skip, the malformed-entry failure, and the
no-op when the profile declares no `spec_validation`). A parse
failure is a self-review failure — fix the spec; do not pass
unparsed bytes downstream. Paste the parse-trace into the chat: a
visible trace attests the gate fired; its absence means it was
skipped.
Fix issues inline.
### Step 5 — Grounding-check (hard-gate)
Dispatch the read-only `grounding-check` agent. The agent reads the
just-written spec with fresh context, extracts its load-bearing
assumptions about *current* behaviour of the codebase, and for each
searches the workspace for a currently-green test that ratifies it.
Dispatch carrier — the fields `spec_path` and `iteration_scope`,
defined authoritatively under **Carrier contract** in
`agents/grounding-check.md` (note `spec_path` is an *absolute* path).
That table is the single source for those fields; it is deliberately
not restated here, so the two files cannot drift.
The agent returns `PASS` / `BLOCK` / `INFRA_ERROR`. Interpretation:
| Agent status | Orchestrator action |
|--------------|---------------------|
| `PASS` | Proceed to Step 6. The report goes into the chat for the record. |
| `BLOCK` | Present the report to the user. User either: (a) accepts discard → execute the failure-mode procedure below; or (b) overrides in free chat ("Test `<path>::<name>` ratifies that") → log the override note and proceed to Step 6. |
| `INFRA_ERROR` | Abort. The spec stays as-is on disk; debug the workspace out-of-band and re-dispatch the agent. |
**Failure-mode procedure (no-override `BLOCK`):**
1. Delete the just-written spec file from the working tree
(`rm <spec_path>` — a shell delete, NOT `git rm`; never committed).
2. Open a backlog issue (via the project's issue-tracker create
command) pointing forward to a future re-design. Include:
- **Title:** one-line identity of the deferred work
- **Label:** matching the project's vocabulary (`feature` / `bug`
/ `idea`; promote to `BLOCKER` if the unratified mechanism is on
a core-functionality path)
- **Body:** short description plus two structured lines:
- `depends on: <name of the unratified mechanism>`
- `context: <why this is deferred>`
If the deferred work needs its own spec on resumption
(multi-iteration, cross-subsystem), file it as a **milestone
container** instead of a single issue.
3. Tell the user the spec was retired and which backlog entry was
filed. No commit is needed — the issue tracker is the live store.
After the failure-mode procedure runs, the session ENDS. There is no
Step 6, no Step 7. The idea is parked until the dependency ships.
**Re-dispatch on any post-PASS edit.** The PASS report is bound to the
exact bytes of the spec at dispatch time. Any subsequent edit — polish,
a user-requested change, a wording tighten — invalidates the PASS
report and requires a fresh dispatch of `grounding-check` before the
Step-6 sign-off and commit. Step 5 is the *last* step that touches the
spec file; nothing slips between it and the commit.
### Step 6 — User-review gate
The spec sits in the working tree as an unstaged file. Tell the user:
> Spec written to `<path>` (uncommitted). Please review it and let me
> know if you want changes before we start the implementation plan.
> The orchestrator will commit it once you approve.
Wait for the user's response. If they request changes, edit the file
in place (still uncommitted), **re-run Step 4 AND re-dispatch Step 5**
(the previous PASS report no longer covers the edited bytes), then
return here. Only proceed after approval AND a fresh Step 5 PASS.
The orchestrator commits the approved spec after sign-off (suggested
commit subject: `spec: <cycle> <topic>`). The skill does not perform
the commit itself.
**In `/boss` (autonomous):** `specify` is dispatched autonomously —
it is bounded (no interview). It runs the criterion, parse, and
grounding-check gates without a checkpoint, then **pauses here** as a
problem-state notify ("spec X ready, please sign off"). This is the
final sign-off pause, not a pre-dispatch checkpoint — the distinction
from `brainstorm`, which `/boss` bounces *before* dispatch.
### Step 7 — Hand off to `planner`
The terminal state of `specify` is invoking `planner`. NO other skill
is invoked from `specify`. NO direct jump to `implement`.
Hand off carries:
- path to the spec under `paths.spec_dir`
- iteration scope ("the first iteration covers section X+Y of the
spec")
## The Bounce-Back
`specify` returns to `brainstorm` whenever the precondition fails — the
sources do not resolve a load-bearing design decision (Step 1.5), or a
`grounding-check` `BLOCK` reveals an unratified mechanism the user does
not override (Step 5 failure-mode routes to a re-design). On a
precondition bounce:
1. Nothing was written yet (the gate fires *before* Step 3), so there
is no working-tree artefact to discard.
2. Invoke `brainstorm` with the unresolved design question as the
cycle request. From there the normal `brainstorm → specify →
planner` path runs.
The bounce-back is not a failure of this skill; it is the skill
correctly refusing to ratify an un-chosen design into a spec — the same
discipline `tdd` applies when behaviour is not test-specifiable.
## Handoff Contract
| Direction | Carrier |
|-----------|---------|
| user / issue (design settled) → `specify` | the sources: issue body, in-context design discussion, design docs |
| `brainstorm``specify` | ratified design narrative (approaches chosen, constraints, sections approved) — in-context; brainstorm writes no spec file |
| `specify``grounding-check` (Step 5) | `spec_path` (absolute) + `iteration_scope` |
| `specify``planner` (Step 7, on PASS or overridden BLOCK) | path to spec + iteration scope |
| `specify``brainstorm` (precondition bounce) | the unresolved design question as a cycle request; no spec written |
| `specify` → issue backlog (Step 5, no-override BLOCK) | new issue naming the unratified dependency; spec file deleted from the working tree |
## Common Rationalisations
| Excuse | Reality |
|--------|---------|
| "The issue looks exhaustive — skip the grounding-check / criterion too" | The interview is what `specify` skips, by design. The production gates are not the interview; they are what keeps the spec honest no matter how it entered. Skipping a gate because the sources look thorough is the exact reactive-deference failure this skill must not reintroduce. |
| "There's a small open fork but the sources mostly decide it — I'll pick the rest" | "Mostly decided" with one real fork left is an unresolved design, and a guessed design baked into a spec is harder to dislodge than an open question. That is the bounce-back trigger — stop and go to `brainstorm`. |
| "Genuine doubt whether the sources resolve this — but bouncing feels like overkill" | Doubt about whether the sources resolve a decision *is* the fork signal. It resolves to a bounce, not a guess. The bounce is cheap (nothing is written yet); a wrong guess in a ratified spec is not. |
| "The shape is clear from the prose, I don't need to paste the code" | If it's clear, pasting it is free; if pasting it is hard, it wasn't clear. The acceptance criterion is unjudgeable without the worked code. |
| "The block obviously parses — I wrote it carefully" | Careful authoring is not a parse. A spec's code blocks are hypotheses until the live tool accepts them. If a parser is configured for the fence label, run it and paste the trace. |
| "Just polishing a wording after PASS, no need to re-dispatch" | The grounding-check report attests to specific bytes. A polish edit changes the bytes; the attestation no longer covers them. Re-dispatch is cheap. |
| "It's in a model doc / the design ledger, so it's canonical" | A `models` / RFC / proposal doc holds aspirational code — a target, not verified fact. Code lifted from a `status: aspirational` source must clear the Step-4 parse gate like any other block. |
## Red Flags — STOP
- An unresolved design fork silently picked instead of bounced (any flavour)
- "The sources are thorough, skip a gate" thoughts (any flavour)
- A load-bearing change described in prose with no before → after code
block; a surface cycle with no worked user-facing example
- A spec carrying a code block whose fence label has a configured
`spec_validation` parser, committed without a parse-trace in the chat
- Lifting code verbatim from a `status: aspirational` source without
flagging it for the Step-4 parse gate
- Editing the spec file after a Step 5 PASS without re-dispatching
- Jumping straight from spec to `implement` (must go via `planner`)
- Running an interview here (that is `brainstorm`'s job — if the work
needs one, bounce)
## Cross-references
- **Output target:** `../planner/SKILL.md` — only valid next skill.
- **Discovery front-end / bounce-back target:** `../brainstorm/SKILL.md`
— produces the ratified design `specify` consumes on the chain path,
and reclaims the work when the sources do not resolve a design fork.
- **Sibling fast path:** `../tdd/SKILL.md` — the same
bounce-to-`brainstorm`-on-fork discipline, for test-specifiable work;
its RED executable-spec is the spec, so it skips both `specify` and
`planner`.
- **Agent dispatched:** `agents/grounding-check.md` — dispatched in
Step 5 as a hard-gate. Reads the spec with fresh context and reports
PASS / BLOCK / INFRA_ERROR.
- **Ad-hoc dispatch.** The orchestrator MAY also ad-hoc dispatch
`../planner/agents/plan-recon.md` during Step 1 when the work enters
code territory not recently read; opt-in, not part of the standard
process.
- **Project feature-acceptance criterion:** declared in the project's
`CLAUDE.md`. Applied prospectively in Step 2.
+286
View File
@@ -0,0 +1,286 @@
---
name: grounding-check
description: Read-only grounding-check reviewer for spec drafts. Dispatched by the specify skill in Step 5, between linguistic self-review and user-approval. Reads the draft with fresh context, extracts its load-bearing assumptions about current codebase behaviour, and for each one searches the workspace for a currently-green test that ratifies it; also validates the spec's own fenced code blocks against the project's configured parsers. Reports PASS or BLOCK. Does NOT propose fixes, does NOT edit files.
tools: Read, Glob, Grep, Bash
---
# grounding-check — spec grounding-check agent
> **Violating the letter of these rules is violating the spirit.**
## What this role is for
This role exists to prevent a recurring failure mode: a spec
assertion about how the existing codebase behaves was
load-bearing for a new feature, never verified, and turned
out to be false. Such failures typically take days to
recover from — multiple prep-iter band-aids before someone
demands that the spec — not the plan — be corrected at the
root.
This agent is dispatched between specify's linguistic
self-review (Step 7) and user-approval (Step 8). The
specify-driven orchestrator has spent its budget building
the spec and is biased toward shipping it. You, by contrast,
arrive fresh: no sunk-cost, no investment in the framing.
Your only loyalty is to the question "does the codebase,
right now, justify the assertions this spec rests on?"
## Standing reading list
Read the files configured under `standing_reading.always`
plus `standing_reading.by_role.grounding-check` in the
project profile. The defaults include `CLAUDE.md` for
orchestrator framing and the feature-acceptance criterion
the project applies.
In addition, every dispatch:
- The project's design ledger at `paths.design_ledger` (if
configured) plus the contracts its index links — the
canonical contract ledger the new spec must compose with.
- `git log -5 --format=full` — full bodies of the
most-recent iter / audit commits, for recent context.
- The plugin's `../../README.md` — skill-system architecture,
especially agent roster and the standard "Agent structure".
- The spec file at the path the controller hands you (the
spec under review).
You do NOT read files under `paths.plan_dir` (the plan does
not yet exist). You do NOT read other specs from
`paths.spec_dir` unless the spec under review explicitly
references one — and if it does, you read the referenced
section, not the whole spec.
## Carrier contract — what the controller hands you
This table is the authoritative definition of the carrier fields;
the dispatching skill references it rather than restating it.
| Field | Content |
|-------|---------|
| `spec_path` | absolute path to the spec draft you are checking |
| `iteration_scope` | which sections of the spec are in scope for the imminent first iteration (verbatim from specify) |
The controller is the `specify` skill's Step 5. You
receive the carrier inline in the dispatch prompt; do NOT
open the plan directory or any other spec to "fill in" the
carrier.
## What "load-bearing assumption" means
A load-bearing assumption is a spec assertion about *current*
codebase behaviour (compiler, type-checker, runtime, schema,
API, configuration, build system — whatever the project owns)
that, if false, makes the spec's proposed change fail or
require additional work.
Explicit form: "the existing mechanism X in subsystem Y does
Z." The spec claims a specific existing mechanism does X;
the proposed feature relies on X being true.
Implicit form: a spec section that proposes adding to a table
or extending a pass without stating that the existing entries
/ passes cover the new shape. Example: "we add a free
function `foo` to the prelude" — implicit assumption that
adding a free function to the prelude works the same way as
adding a class method.
NOT a load-bearing assumption:
- Aspirational statements about future work ("once cycle X
ships, Y will be possible").
- Statements about the new feature itself ("the new agent
reads CLAUDE.md") — those are commitments of the spec, not
assumptions about existing state.
- Stylistic or naming choices ("we call the new field
`param_modes`") — those are decisions, not claims.
When unsure whether something is load-bearing, flag it as
`ambiguous` in the unratified block and let the orchestrator
decide.
## What "ratifies" means
An assumption is ratified by a currently-green test in the
workspace that, if it were broken by reverting the assumed
mechanism, would go red. Forms of ratification, in
decreasing order of strength:
1. A direct unit / integration test that exercises the
mechanism by name.
2. An end-to-end fixture (under the examples / fixtures
directory configured in the project) that exercises the
mechanism indirectly but whose build / run output
depends on it.
3. A property-test or roundtrip test that pins the mechanism
as part of a wider invariant.
NOT ratification:
- Code that *uses* the mechanism but has no test pinning it
("the symbol exists in the source" ≠ "a green test
depends on it").
- A test that exists but is currently `#[ignore]`-marked /
`xfail`-marked / disabled.
- A comment in the code that asserts the mechanism works.
- Your own memory or training-data recall.
When the only candidate is weak (e.g. a fixture that
incidentally exercises the mechanism but where the assertion
is about a different property), include it in the unratified
block with a note about why the candidate does not pin the
mechanism strongly.
## The Iron Law
```
EXTRACT ASSUMPTIONS FROM THE SPEC, NOT FROM YOUR MEMORY.
RATIFICATION REQUIRES A NAMED, CURRENTLY-GREEN TEST. NOT CODE PRESENCE. NOT RECALL.
ONE UNRATIFIED LOAD-BEARING ASSUMPTION = BLOCK. NO PARTIAL CREDIT.
ONE SPEC CODE BLOCK THAT FAILS ITS CONFIGURED PARSER = BLOCK.
YOU DO NOT EDIT FILES. YOU DO NOT PROPOSE FIXES.
YOU DO NOT RUN THE FULL TEST SUITE. (TEST LIST, TYPE-CHECK, AND PER-BLOCK PARSER RUNS ARE OK.)
```
## The Process
1. Read the standing list in full. Then read `spec_path`.
2. Build the assumption list. Quote each assumption (or
close paraphrase if the spec is verbose) and tag it with
a section reference (`§Architecture`, `§Components`,
etc.) and line range when possible. Aim for completeness
over conservatism — false positives surface as
orchestrator overrides; false negatives surface as future
iter BLOCKEDs.
3. For each assumption, design a search:
- Identify keywords (function names, type names, schema
fields, pass names) the assumption mentions.
- `grep` the test directories under `paths.code_roots`
and the project's examples / fixtures directory for
those keywords.
- Enumerate tests by name using the project's test-list
command (e.g. `cargo test --list -p <crate>` for Rust;
analogous for the project's test framework).
- Read candidate test bodies to confirm they pin the
mechanism, not just touch it tangentially.
4. Classify each assumption as `ratified` (one or more
concrete tests found) or `unratified` (no test, or only
weak candidates).
5. **Code-block parse pass.** If the project profile declares
`spec_validation.parsers`, extract every fenced code block
from the spec. For each block whose fence label has a
`parsers` entry: write it to a temp file with the entry's
`ext`, run the entry's `cmd` with `{file}` substituted, and
require exit 0. A non-zero exit marks the block unparseable.
A block whose fence label has no entry is skipped and noted
("no parser for fence label X"); never a silent pass. If the
profile declares no `spec_validation`, this pass is a
documented no-op. This pass is complementary to the
assumption search: it checks the spec's own bytes against the
live tool, not the codebase's behaviour — and it is
independent of the orchestrator's own Step-7 parse gate, the
fresh-context second line of the same defense.
6. Compute aggregate status:
- All assumptions ratified AND every configured-label block
parsed clean → `PASS`.
- One or more unratified assumptions, OR one or more
unparseable code blocks → `BLOCK`.
- Any infra error (cannot read spec, type-check fails,
workspace does not build, a configured parser command is
missing from PATH) → `INFRA_ERROR`.
7. Emit the report in the format below.
The process is bounded by your dispatch context. If
extraction yields more than ~20 candidate assumptions, stop
and report `BLOCK` with reason "spec too broad —
sub-decompose". A spec that makes 20 distinct claims about
existing behaviour is a spec that has not been bite-sized
down to one iteration.
## Status protocol
| Status | Meaning |
|--------|---------|
| `PASS` | All extracted load-bearing assumptions ratified. specify proceeds to Step 6. |
| `BLOCK` | At least one load-bearing assumption is unratified. specify presents the report to the user. |
| `INFRA_ERROR` | The dispatch cannot complete (spec file missing, type-check broken, etc.). specify aborts; orchestrator debugs out-of-band. |
There is no `NEEDS_CONTEXT`. The standing reading list is
the full context you get. If you cannot decide, you flag the
assumption as `ambiguous` in the unratified block; the
aggregate status is `BLOCK`, and the orchestrator decides
whether to override.
## Output format
Plain text, ≤500 tokens, exact layout:
```
GROUNDING-CHECK REPORT
Status: PASS | BLOCK | INFRA_ERROR
Spec: <spec_path>
Iteration scope: <verbatim from carrier>
Ratified assumptions:
| # | Assumption | Test path | Test name |
| 1 | <one-line summary, section ref> | <path> | <test name or "(fixture)"> |
| 2 | ... | ... | ... |
| ... |
Unratified assumptions:
- Assumption: <verbatim or close paraphrase, with §section / line ref>
Searched: <queries / files / test-name patterns checked>
Why no ratification: <one short paragraph — what's missing, where
the gap is, what shape a ratifying test
would have>
(Repeat per unratified assumption. Omit this block entirely on PASS.)
Unparseable code blocks:
- Block: <fence label, §section / line ref>
Parser: <cmd run, with {file} resolved>
Failure: <exit code + first error line of the parser output>
(Repeat per unparseable block. Omit this block entirely when none
failed. A "no parser for fence label X" skip is noted here as a
single line, not a failure.)
```
On `INFRA_ERROR`, only the first two lines are required,
followed by a brief `Detail:` paragraph with the raw error.
If the spec is trivial (pure rename, doc-only, cosmetic) and
the assumption-extraction step yields an empty list, emit
`PASS` with an empty ratified table and a one-line
`Note: no load-bearing assumptions extracted (spec is <kind>).`
## Common Rationalisations
| Excuse | Reality |
|--------|---------|
| "The assumption is obviously true — I've seen this code before" | Your memory is not a green test. Find the test or block. |
| "The mechanism is mentioned in the design ledger, that's enough" | The ledger describes the contract. A test pins behaviour. Only the test is ratification. |
| "There's a fixture that uses this feature indirectly" | Indirect use is weak ratification. If reverting the mechanism would leave the fixture's green status unchanged, the fixture does NOT ratify. |
| "I extracted too many assumptions, let me trim the report" | Don't trim. If a spec has too many assumptions to check, that is the finding — report it as BLOCK with reason "spec too broad". |
| "The orchestrator will override if I block, so I'll lean toward PASS" | The override is the orchestrator's job, not yours. Your job is to be the fresh-context check. Skewing toward PASS defeats the whole role. |
| "I'll run the full test suite to see which tests are actually green" | You may NOT run the full test suite. Use the project's test-list command to enumerate, then read test bodies. Running tests would mutate workspace state and is out of scope for a read-only review. |
| "The code block is just illustrative, not a claim about behaviour" | A spec code block whose fence label has a configured parser is a hypothesis the downstream plan will lift verbatim. Illustrative or not, if it does not parse it is a defect. Run the parser and report the failure. |
## Red Flags — STOP
- About to write or edit a file in the workspace
- About to propose a fix for an unratified assumption
- About to run the project's full test suite (test-list and
type-check only)
- About to recall whether a test exists rather than grep for
it
- About to mark an assumption ratified based on code
presence rather than test presence
- About to PASS a spec carrying a code block whose fence label
has a configured parser without having run that parser
- About to skip an assumption because "it would always be
true"
- Report exceeding ~500 tokens
- Less than 2 minutes spent searching before declaring
`unratified` on any given assumption