@ericrisco/rsc 4.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.0.0",
2
+ "version": "4.0.1",
3
3
  "counts": {
4
4
  "skills": 274,
5
5
  "agents": 34,
@@ -4376,8 +4376,9 @@
4376
4376
  "registry"
4377
4377
  ],
4378
4378
  "recommends": [
4379
+ "asdd",
4380
+ "constitution",
4379
4381
  "specify",
4380
- "implement",
4381
4382
  "verify"
4382
4383
  ],
4383
4384
  "profiles": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ericrisco/rsc",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "A guided harness builder that turns your goal into a minimal, verified AI-agent setup using strict best practices.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,7 +14,8 @@ import { join } from 'node:path';
14
14
  import { spawnSync } from 'node:child_process';
15
15
 
16
16
  export const LEDGER_PATH = '.rsc/review-ledger.jsonl';
17
- export const OUTCOMES = ['fixed', 'false', 'intentional', 'irrelevant'];
17
+ // `parked`: the finding's task went to the decision queue; neither fixed nor dismissed, a human decides.
18
+ export const OUTCOMES = ['fixed', 'false', 'intentional', 'irrelevant', 'parked'];
18
19
  export const MAX_ROUNDS = 3;
19
20
  export const EVALS_DIR = '02-DOCS/wiki/sdd/review-evals';
20
21
 
@@ -129,7 +130,8 @@ export function stats(root) {
129
130
  for (const e of ledger) if (e.type === 'outcome') last.set(e.id, e.outcome);
130
131
  const outcomes = Object.fromEntries([...OUTCOMES, 'open'].map((k) => [k, 0]));
131
132
  for (const f of originals) outcomes[last.get(f.id) || 'open'] += 1;
132
- const closed = originals.length - outcomes.open;
133
+ // Parked is undecided: a human holds it. Counting it as closed would understate the false rate.
134
+ const closed = originals.length - outcomes.open - outcomes.parked;
133
135
  const rounds = new Map();
134
136
  for (const e of ledger) if (e.type === 'round') rounds.set(e.task, Math.max(rounds.get(e.task) || 0, e.n));
135
137
  const totalRounds = [...rounds.values()].reduce((a, b) => a + b, 0);
@@ -6,7 +6,7 @@
6
6
  // Spec: 02-DOCS/wiki/sdd/specs/asdd-v4.md.
7
7
 
8
8
  import { spawnSync } from 'node:child_process';
9
- import { SELLO_STATE_PATHS, classifyRisk, lensesRequired, readEffectiveConfig, trunkRef } from '../../targets/sello.mjs';
9
+ import { NESTED_WORKTREES, SELLO_STATE_PATHS, classifyRisk, lensesRequired, readEffectiveConfig, trunkRef } from '../../targets/sello.mjs';
10
10
 
11
11
  function git(root, args) {
12
12
  const r = spawnSync('git', ['-C', root, '-c', 'core.quotePath=false', ...args], { encoding: 'utf8', windowsHide: true, maxBuffer: 64 * 1024 * 1024 });
@@ -24,7 +24,11 @@ export function changedPaths(root, base = null) {
24
24
  const mb = head ? (git(root, ['merge-base', 'HEAD', ref]) || '').trim() : '';
25
25
  if (!mb) return { ok: false, message: `rsc: cannot find a common ancestor with ${ref}. Pass --base <ref> or the paths themselves: npx @ericrisco/rsc risk <path…>` };
26
26
  // --no-renames: a rename reports both paths, so moving a file OUT of a tier-2 place still counts.
27
- const diff = (from) => new Set([...nul(git(root, ['diff', '--name-only', '--no-renames', '-z', from])), ...nul(git(root, ['ls-files', '--others', '--exclude-standard', '-z']))]);
27
+ // Nested worktrees are skipped only while UNTRACKED (another checkout sitting in the tree). Once
28
+ // committed, a path under them is part of the change and keeps its tier.
29
+ const untracked = () => nul(git(root, ['ls-files', '--others', '--exclude-standard', '-z'])).filter((n) => !NESTED_WORKTREES.test(n));
30
+ const diff = (from) => new Set([...nul(git(root, ['diff', '--name-only', '--no-renames', '-z', from])), ...untracked()]
31
+ .filter((n) => !SELLO_STATE_PATHS.includes(n)));
28
32
  let names = diff(mb);
29
33
  let shown = ref;
30
34
  // Working straight on the trunk (FTD on an open main), the merge-base IS HEAD, and a change
@@ -34,7 +38,6 @@ export function changedPaths(root, base = null) {
34
38
  names = diff('HEAD~1');
35
39
  shown = 'HEAD~1 (on the trunk: the last commit)';
36
40
  }
37
- for (const p of SELLO_STATE_PATHS) names.delete(p);
38
41
  return { ok: true, base: shown, paths: [...names].sort() };
39
42
  }
40
43
 
package/scripts/rsc.js CHANGED
@@ -46,7 +46,7 @@ if (['--version', '-v', 'version'].includes(rawArgv[0])) {
46
46
  process.exit(0);
47
47
  }
48
48
  const GENERAL_HELP = [
49
- 'Use: npx @ericrisco/rsc onboard | reassess | add <id...> | install --profile <p> | consult "<text>" | list | capabilities [--full|gap-log] | audit | registry refresh | doctor | sync | agent-model <target> <model|inherit|status> | agents [status|reset <name|--all>] | memory <on|off|status|save|resume|learn|metrics> | risk [--base <ref>] [paths…] | review consolidate|ledger|escapes | ingest <file...> | copy check <file> | copy rules | sello <on|off|status|…> | worktrees [reap [path] [--confirm]] | backups | restore <id|latest> | upgrade | repair | uninstall <id...> | purge | help',
49
+ 'Use: npx @ericrisco/rsc onboard | reassess | add <id...> | install --profile <p> | consult "<text>" | list | capabilities [--full|gap-log] | audit | registry refresh | doctor | sync | agent-model <target> <model|inherit|status> | agents [status|reset <name|--all>] | memory <on|off|status|save|resume|learn|metrics> | risk [--base <ref>] [paths…] | spec gate [specs…] | review consolidate|ledger|escapes | ingest <file...> | copy check <file> | copy rules | sello <on|off|status|…> | worktrees [reap [path] [--confirm]] | backups | restore <id|latest> | upgrade | repair | uninstall <id...> | purge | help',
50
50
  'Any command takes --target <claude|codex|cursor|copilot|gemini|…> (comma-separate for several)',
51
51
  ' → without it, rsc uses the assistant already installed here; if two are, it asks instead of guessing.',
52
52
  ].join('\n');
@@ -77,7 +77,7 @@ const PURGE_HELP = [
77
77
  const GLOBAL_VALUE_FLAGS = new Set([
78
78
  '--topic', '--target', '--technical-level', '--accompaniment', '--project-kind', '--goal', '--goal-base64', '--software-scope', '--workflow', '--accept-plan',
79
79
  ]);
80
- const COMMANDS = new Set(['ingest', 'copy', 'onboard', 'reassess', 'add', 'install', 'consult', 'catalog', 'audit', 'list', 'doctor', 'memory', 'knowledge-sync', 'git-permissions', 'agent-model', 'agents', 'main', 'isolation', 'sync', 'backups', 'restore', 'upgrade', 'registry', 'worktrees', 'capabilities', 'review', 'risk', 'sello', 'repair', 'uninstall', 'purge']);
80
+ const COMMANDS = new Set(['ingest', 'copy', 'onboard', 'reassess', 'add', 'install', 'consult', 'catalog', 'audit', 'list', 'doctor', 'memory', 'knowledge-sync', 'git-permissions', 'agent-model', 'agents', 'main', 'isolation', 'sync', 'backups', 'restore', 'upgrade', 'registry', 'worktrees', 'capabilities', 'review', 'risk', 'spec', 'sello', 'repair', 'uninstall', 'purge']);
81
81
  function positionalTokens(input) {
82
82
  const out = [];
83
83
  for (let i = 0; i < input.length; i++) {
@@ -1232,6 +1232,19 @@ async function main() {
1232
1232
  if (hits.length) process.exitCode = 1;
1233
1233
  return;
1234
1234
  }
1235
+ case 'spec': {
1236
+ // `spec gate` — the specify phase's exit gate, reachable from a user project. It was only an npm
1237
+ // script inside this package, so the one check ASDD's spec depends on could not run where specs live.
1238
+ const USE = 'Use: npx @ericrisco/rsc spec gate [spec.md ...]\n Without paths it checks 02-DOCS/wiki/sdd/specs/ of this project. Exits 1 when a spec fails.';
1239
+ if (argv[1] !== 'gate') { say(USE); if (argv[1]) process.exitCode = 1; return; }
1240
+ const { spawnSync } = await import('node:child_process');
1241
+ const { fileURLToPath: toPath } = await import('node:url');
1242
+ const rest = argv.slice(2);
1243
+ const specs = rest.filter((a, i) => !a.startsWith('--') && rest[i - 1] !== '--target');
1244
+ const r = spawnSync(process.execPath, [toPath(new URL('./spec-gate.js', import.meta.url)), ...specs], { stdio: 'inherit', windowsHide: true });
1245
+ process.exitCode = r.status ?? 1;
1246
+ return;
1247
+ }
1235
1248
  case 'risk': {
1236
1249
  // How much review and whose approval a change needs, by WHAT it touches (ASDD, P1/P7). The
1237
1250
  // agent never picks its own tier: this does, from the sello's path table, sello on or off.
@@ -1266,7 +1279,7 @@ async function main() {
1266
1279
  const USE = [
1267
1280
  'Use: npx @ericrisco/rsc review consolidate [report.md|findings.json ...] [--json] [--window <lines>]',
1268
1281
  ' Without files it reads stdin. Each lens report carries its findings in ```json findings blocks.',
1269
- ' npx @ericrisco/rsc review ledger record <findings.json> [--task <id>] | outcome <id> <fixed|false|intentional|irrelevant> | round <slug>/<task> | stats [--json]',
1282
+ ' npx @ericrisco/rsc review ledger record <findings.json> [--task <id>] | outcome <id> <fixed|false|intentional|irrelevant|parked> | round <slug>/<task> | stats [--json]',
1270
1283
  ' npx @ericrisco/rsc review escapes [--since <ref>] [--json]',
1271
1284
  ].join('\n');
1272
1285
  if (argv[1] === 'ledger' || argv[1] === 'escapes') {
@@ -6,14 +6,24 @@
6
6
  // the phase's own instrument, and the author's.
7
7
  import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
8
8
  import { join, dirname, basename, resolve, relative } from 'node:path';
9
- import { fileURLToPath } from 'node:url';
10
9
  import { execFileSync } from 'node:child_process';
11
10
  import { specCompleteness, statusClaims, checkClaims } from './lib/spec-gate.js';
12
11
 
13
- const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
14
- const DEFAULT_DIR = join(ROOT, '02-DOCS', 'wiki', 'sdd', 'specs');
12
+ // The corpus of the project the gate is run FROM — not of the package that ships the script. Run as
13
+ // `npx @ericrisco/rsc spec gate` in a user project, a package-relative default found nothing.
14
+ function projectRoot() {
15
+ try {
16
+ const top = execFileSync('git', ['rev-parse', '--show-toplevel'], { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
17
+ if (top) return top;
18
+ } catch { /* not a git project: look upward for 02-DOCS instead */ }
19
+ for (let dir = process.cwd(); ; dir = dirname(dir)) {
20
+ if (existsSync(join(dir, '02-DOCS'))) return dir;
21
+ if (dirname(dir) === dir) return process.cwd();
22
+ }
23
+ }
15
24
 
16
25
  function defaultTargets() {
26
+ const DEFAULT_DIR = join(projectRoot(), '02-DOCS', 'wiki', 'sdd', 'specs');
17
27
  try {
18
28
  return readdirSync(DEFAULT_DIR)
19
29
  .filter((f) => f.endsWith('.md') && !f.endsWith('.plan.md'))
@@ -28,7 +28,7 @@ This skill does **not** do a phase. It names the method, reads the register, pla
28
28
  d verify findings e mandatory controls
29
29
  f fix (max 3 rounds) g fix & record ─or─ park ─► DECISION QUEUE ◄── HUMAN
30
30
  5. CLOSE ────► verify against main + rollback plan · closing review (whole branch vs spec) · explainer
31
- SHIP ─────► tier 0-1: self-merge on green CI + empty queue │ tier 2: ◄── HUMAN APPROVES
31
+ SHIP ─────► remote: PR (self-merge only tier 0-1 + green CI + no blocking entry) │ no remote: park ◄── HUMAN
32
32
  LEARN ────► finding outcomes · reverts → reviewer cases · repeated finding → rule
33
33
  ```
34
34
 
@@ -36,13 +36,15 @@ This skill does **not** do a phase. It names the method, reads the register, pla
36
36
  | --- | --- | --- |
37
37
  | **1 Base** | `../constitution/SKILL.md` (also drafts `REVIEW.md`), `../sdd-init/SKILL.md` | `constitution.md`, `REVIEW.md`, `config.yaml` |
38
38
  | **2 Spec** | `../specify/SKILL.md` → `../clarify/SKILL.md` | `specs/<slug>.md` |
39
- | **3 Plan** | `../plan/SKILL.md` → `../tasks/SKILL.md` → `../analyze/SKILL.md` | `plans/<slug>.md` |
39
+ | **3 Plan** | `../plan/SKILL.md` → `../tasks/SKILL.md` → `../analyze/SKILL.md` | `plans/<slug>.md`, `analysis/<slug>.md` |
40
40
  | **4 Loop** | `../implement/SKILL.md`, calling `../verify/SKILL.md` and `../review/SKILL.md` per task | `progress/<slug>.md` (with `## Decision queue`), `decisions.md` |
41
- | **5 Close** | `../verify/SKILL.md` (closing mode), then `../review/SKILL.md` (closing review, on what will ship), `../ship/SKILL.md` | `verifications/<slug>-<date>.md`, the PR |
41
+ | **5 Close** | `../verify/SKILL.md` (closing mode), then `../review/SKILL.md` (closing review, on what will ship), `../ship/SKILL.md` | `verifications/<slug>-<date>.md`, `reviews/<slug>.md`, the PR or parked branch |
42
42
  | on demand | `../debug/SKILL.md` · `../worktrees/SKILL.md` · `../parallel/SKILL.md` | — |
43
43
 
44
44
  **Where the human enters — three places only:** approving the spec, answering the decision queue, approving a tier-2 merge. Everything else runs.
45
45
 
46
+ No ASDD phase edits the root `CLAUDE.md`: every artifact is indexed in `02-DOCS/wiki/index.md` (the Knowledge map). The root `CLAUDE.md` is owned by `harness` and is tier 2 in `rsc risk`.
47
+
46
48
  ### The spec proves itself
47
49
 
48
50
  Every spec answers four questions, and `spec:gate` refuses one dated 2026-11-03 or later without them: **what the user can do**, **what result they get** (against a reference the human approved — a screenshot, a sketch, an expected API response), **what it must respect** (prior decisions, specs and behaviour; read from the brain when the project has one, else `decisions.md`), **how it proves it** (how the check runs, what evidence remains — "yes, I tested it" is not evidence). Plus an explicit **out of scope**: what the agent might add "just in case".
@@ -57,14 +59,24 @@ Every spec answers four questions, and `spec:gate` refuses one dated 2026-11-03
57
59
 
58
60
  ### Park, don't block
59
61
 
60
- Tier 2, a contradiction with a recorded decision, a gap clarify could not close, or no convergence in 3 rounds: the task goes to the **decision queue** (`## Decision queue` in `progress/<slug>.md`) with its evidence, and the agent **continues with the independent tasks**. The run never waits at 2 a.m. for an answer.
62
+ Tier 2, a contradiction with a recorded decision, a gap that would make the build unsafe, or no convergence in 3 rounds: the task goes to the **decision queue** (`## Decision queue` in `progress/<slug>.md`) with its evidence, and the agent **continues with the independent tasks**. The run never waits at 2 a.m. for an answer.
63
+
64
+ Every entry carries a kind, one line each:
65
+
66
+ ```markdown
67
+ - [blocking] Q3 — <what must be decided>, <evidence>, <task>
68
+ - [review-after] Q4 — <assumption taken>, <where it is recorded>, <task>
69
+ ```
70
+
71
+ - **`blocking`** — tier 2, a contradiction with a recorded decision, no convergence in 3 rounds, a hard failure. It stops a self-merge.
72
+ - **`review-after`** — clarify's recorded assumptions, a spec reference the agent had to write itself, a provisional constitution ratification. It never stops a merge; it is listed in the PR body (or the parked summary) for the human.
61
73
 
62
74
  ## The invoke rule — route to the phase that fits
63
75
 
64
76
  > **The new-feature gate (hard).** The moment the user is thinking about a new feature or change — "add…", "build…", "it should also…", "quiero añadir…" — and the decisor chose this lane, it goes to `specify` first, *even if a stack skill also fired*. **No feature code is written — by any skill — until a spec AND a plan exist and are approved.** Small authorised changes go to `../ftd/SKILL.md`, which escalates here when `rsc risk` says tier 2 or the change contradicts a recorded decision.
65
77
 
66
- 1. **No `02-DOCS/wiki/sdd/config.yaml` and this is non-trivial?** → `sdd-init`.
67
- 2. **No constitution, or `status: draft`?** → `constitution` once (it also drafts `REVIEW.md`).
78
+ 1. **Empty repo (nothing to detect)?** → `constitution` first (it decides the stack), then `sdd-init`.
79
+ 2. **No `02-DOCS/wiki/sdd/config.yaml` and this is non-trivial?** → `sdd-init`. **No constitution, or `status: draft`?** → `constitution` once (it also drafts `REVIEW.md`).
68
80
  3. **Ambiguous / architectural / risky before a spec?** → optional proposal via `specify`.
69
81
  4. **Fuzzy idea, no spec?** → `specify`. **Spec with gaps?** → `clarify`.
70
82
  5. **Approved spec, no plan?** → `plan`, then `tasks`, then `analyze`.
@@ -78,9 +90,19 @@ Skip honestly: `constitution` runs once per project; `clarify` and `analyze` are
78
90
 
79
91
  ## Autopilot — one up-front yes
80
92
 
81
- The user says "take it all the way" once (or `sdd.autopilot: true` in `config.yaml`) and the chain runs end to end **without asking to continue between phases**, still writing every artifact. The up-front yes satisfies the new-feature gate.
93
+ The user says "take it all the way" once (or `execution_mode: automatic` under `sdd:` in `config.yaml`, set by `sdd-init`) and the chain runs end to end **without asking to continue between phases**, still writing every artifact. The up-front yes satisfies the new-feature gate.
94
+
95
+ Autopilot does not stop the run for questions — it **parks**: real ambiguity, tier 2, a contradiction with a recorded decision or a non-converging task go to the decision queue while independent work continues. It **stops** only for a hard failure nothing else can proceed past, and for destructive or irreversible actions.
96
+
97
+ **In autopilot, per phase:**
98
+
99
+ - `constitution` ratifies provisionally → `review-after` entry.
100
+ - `specify` uses a checkable reference it wrote itself (expected output, sample response) → `review-after` entry.
101
+ - `clarify` records its assumptions (`suposición tomada`) → `review-after` entries.
102
+ - `implement` parks `blocking` items and continues with the independent tasks.
103
+ - `ship` lands by the rule below.
82
104
 
83
- Autopilot does not stop the run for questions — it **parks**: real ambiguity, tier 2, a contradiction with a recorded decision or a non-converging task go to the decision queue while independent work continues. It **stops** only for a hard failure nothing else can proceed past, and for destructive or irreversible actions. Landing follows the risk: tier 0-1 with green CI and an empty queue may self-merge; tier 2 waits for the human.
105
+ **Landing — there is always a legal path.** A remote exists → open a PR; self-merge only when `rsc risk` says tier 0-1, CI is green and no `blocking` entry is open; `review-after` entries go into the PR body. No remote → **park**: leave the branch committed, write the explainer, the open entries and the exact merge command for the human into `progress/<slug>.md`, and stop. Never merge locally around the branch guard: when `rsc main status` says `closed: true`, the guard denies it.
84
106
 
85
107
  Narrate in the `orient` voice: name each stage as it passes and show its artifact, one line each.
86
108
 
@@ -101,8 +123,10 @@ Each phase declares a tier — `heavy` (constitution, plan, analyze, review, deb
101
123
  ├── proposals/<slug>.md ← optional pre-execution briefing
102
124
  ├── specs/<slug>.md ← one spec per feature (four questions + out of scope)
103
125
  ├── plans/<slug>.md ← plan + tasks
126
+ ├── analysis/<slug>.md ← analyze's cross-artifact report
104
127
  ├── progress/<slug>.md ← append-only loop ledger + ## Decision queue
105
128
  ├── verifications/<slug>-YYYY-MM-DD.md
129
+ ├── reviews/<slug>.md ← closing review record
106
130
  ├── review-evals/<sha>.md ← one case per revert that escaped review (`rsc review escapes`)
107
131
  ├── archive/<slug>/
108
132
  ├── sessions/<date>-<slug>.md
@@ -111,7 +135,7 @@ REVIEW.md ← project root: how this repo is judged
111
135
  .rsc/review-ledger.jsonl ← findings, outcomes, rounds (tool-written)
112
136
  ```
113
137
 
114
- The directory keeps its `sdd/` name: existing projects keep working. Index it in `02-DOCS/wiki/index.md` under the `sdd/` topic; the harness keeps it honest.
138
+ The directory keeps its `sdd/` name: existing projects keep working. Index it in `02-DOCS/wiki/index.md` under the `sdd/` topic — never in the root `CLAUDE.md`; the harness keeps it honest.
115
139
 
116
140
  ## Stack handoff and skill registry
117
141
 
@@ -126,7 +150,7 @@ Every phase ends with this block so the dispatcher can chain without interpretin
126
150
  "status": "complete|blocked|failed",
127
151
  "executive_summary": "one short paragraph",
128
152
  "artifact": "path/to/artifact-or-none",
129
- "next_recommended": "sdd-init|specify|clarify|plan|tasks|analyze|implement|verify|review|ship",
153
+ "next_recommended": "sdd-init|constitution|specify|clarify|plan|tasks|analyze|implement|verify|review|ship",
130
154
  "risk": "low|medium|high",
131
155
  "model": { "tier": "heavy|balanced|light", "resolved": "model-id", "routing": "on|off" },
132
156
  "skill_resolution": { "used": [], "missing": [], "fallback": [], "compact_rules": [] },
@@ -152,7 +176,7 @@ A run stopped mid-flight writes `02-DOCS/wiki/sdd/sessions/<date>-<slug>.md` ins
152
176
 
153
177
  ## Start here
154
178
 
155
- - **New project** → `sdd-init`, `constitution`, then `specify`.
179
+ - **New project** → `sdd-init`, `constitution`, then `specify`. **Empty repo** → `constitution` first (it decides the stack), then `sdd-init`.
156
180
  - **Existing project, new feature** → `specify` (proposal first if risky or architectural).
157
181
  - **Small authorised change** → `../ftd/SKILL.md`.
158
182
 
@@ -69,7 +69,7 @@ capability:
69
69
  - "Includes sdd-init as step zero when 02-DOCS/wiki/sdd/config.yaml is missing, and explains that it detects test commands, strict TDD and .rsc/skill-registry.json"
70
70
  - "Applies the invoke rule: a fuzzy idea with no spec on disk routes to specify (and notes constitution runs once per project if not yet present)"
71
71
  - "Does NOT itself write the spec or start coding — it hands off to the specify phase skill (../specify/SKILL.md)"
72
- - "Notes that artifacts will be written under 02-DOCS/wiki/sdd/ (specs/, plans/, decisions.md) and indexed in the root CLAUDE.md Knowledge map"
72
+ - "Notes that artifacts will be written under 02-DOCS/wiki/sdd/ (specs/, plans/, decisions.md) and indexed in 02-DOCS/wiki/index.md (never the root CLAUDE.md)"
73
73
  - "Mentions standard result envelopes with status, artifact, next_recommended, risk, skill_resolution and evidence"
74
74
  - "Defers concrete stack tooling (test runner, build, framework idioms) to the relevant stack skill rather than prescribing it here"
75
75
 
@@ -86,6 +86,17 @@ Run in order. The discipline is: find many candidate gaps, keep only the ones th
86
86
 
87
87
  Then re-read the spec once more: did resolving one gap open a new one? If so, one more short loop. Otherwise, hand it to the human for approval.
88
88
 
89
+ ## In autopilot — nobody to ask
90
+
91
+ Under an up-front autopilot yes there is no one to answer, so step 4 changes: **every question that
92
+ would have been asked is recorded instead** as a **suposición tomada** in the spec (your recommended
93
+ answer, its basis, the spec line it came from) and mirrored as a `review-after` entry in the
94
+ **decision queue** (`## Decision queue` in `02-DOCS/wiki/sdd/progress/<slug>.md`; create it if
95
+ absent). The run continues; the human reviews these afterwards and nothing waits on them. Only a gap
96
+ that would make the build **unsafe**, or whose answer would **contradict a recorded decision**, is a
97
+ `blocking` entry — and only `blocking` stops the branch from merging itself. Never ask and wait in
98
+ autopilot; never stay silent about what you assumed.
99
+
89
100
  ## Late gaps — found during implementation
90
101
 
91
102
  A gap nobody saw until `implement` is running does not stop the run. It goes to the **decision queue** of `implement`'s per-task loop (`../implement/SKILL.md`): the task that needs the answer is parked, independent tasks continue, and when the human answers, the answer is written back into this spec (and its `## Clarifications` log) so the spec stays the contract.
@@ -121,7 +132,7 @@ After baking back, the spec line becomes a bounded, testable behaviour ("rejects
121
132
 
122
133
  ## Exit gate
123
134
 
124
- The gate is passed when the spec, constitution, prior decisions and profile were all read (settled questions cited, not re-asked); **every typed point in the handoff has its declared outcome** (asked / validated / left deferred / graduated), with nothing silently dropped; every gap checklist row was walked; only questions that change what gets built were asked, one at a time with a recommendation; every other call is a **suposición tomada** in the spec; every answer is baked into the spec body with observable acceptance criteria, logged under `## Clarifications` and bounded under out of scope; and the final re-read opened no new gap.
135
+ The gate is passed when the spec, constitution, prior decisions and profile were all read (settled questions cited, not re-asked); **every typed point in the handoff has its declared outcome** (asked / validated / left deferred / graduated), with nothing silently dropped; every gap checklist row was walked; only questions that change what gets built were asked, one at a time with a recommendation (in autopilot: recorded as assumptions with their `review-after` / `blocking` queue entries); every other call is a **suposición tomada** in the spec; every answer is baked into the spec body with observable acceptance criteria, logged under `## Clarifications` and bounded under out of scope; and the final re-read opened no new gap.
125
136
 
126
137
  ## Result envelope
127
138
 
@@ -31,7 +31,7 @@ Before asking anything, read `02-DOCS/wiki/harness/user-profile.md` and use its
31
31
 
32
32
  The harness may already hold real conventions under `02-DOCS/wiki/stack/*` (e.g. `nextjs.md`, `fastapi.md`, `postgresdb.md`). The constitution does not copy them — it **ratifies the principle and links the detail**. Run this reconciliation pass first:
33
33
 
34
- 1. **Read the Knowledge map** — the full index lives in `02-DOCS/wiki/index.md` (root `CLAUDE.md` keeps only a short pointer to it). List every `02-DOCS/wiki/stack/*` article that exists.
34
+ 1. **Read the Knowledge map** — `02-DOCS/wiki/index.md`. List every `02-DOCS/wiki/stack/*` article that exists.
35
35
  2. **Read each stack article.** Pull out anything already phrased as a rule (a version pin, a lint config, a test threshold, a naming convention).
36
36
  3. **For each existing rule, decide:** is it a *project-wide non-negotiable* (→ ratify it as a principle, linking the stack article for detail) or a *local mechanic* (→ leave it in the stack wiki, do not lift it into the constitution)?
37
37
  4. **Contradictions are findings, not fixes.** If two stack articles disagree, or a stack article contradicts what the user states now, surface it and let the user resolve — never silently pick a winner.
@@ -72,7 +72,7 @@ For any significant either/or (e.g. "strict types or gradual?", "squash or merge
72
72
 
73
73
  Two principles are inherited from the rsc ecosystem and appear in every constitution unless the user explicitly overrides them:
74
74
 
75
- - **Git authorship is the human's.** Commits and PRs are authored by the human (Eric, or whoever owns the repo). No `Co-Authored-By` an AI, no "generated with" footer. Enforced at the `ship` phase.
75
+ - **Git authorship is the human's.** Commits and PRs are authored by the responsible person — the configured git user. No `Co-Authored-By` an AI, no "generated with" footer. Enforced at the `ship` phase.
76
76
  - **Decisions are logged.** Every significant decision is appended to `02-DOCS/wiki/sdd/decisions.md` (or the harness `decisions.md`) with date, options considered, and the why. The constitution itself is the highest-order decision record.
77
77
 
78
78
  ## Drafting the constitution
@@ -107,6 +107,7 @@ The constitution is versioned so `analyze` and `review` can cite "constitution v
107
107
  - **Semantic-ish versioning.** MAJOR when a principle is removed or reversed (breaks existing work); MINOR when a principle is added or materially tightened; PATCH for wording/clarity with no behavior change.
108
108
  - **Amendments are append-only in the log.** Never silently edit a ratified principle — strike it (mark superseded) and add the new one, bump the version, and record date + why in the amendment log.
109
109
  - **Ratification.** A new or amended constitution is shown to the user and ratified explicitly before it takes effect. "Ratify" is an explicit yes on the shown draft; walk each change only if the user asks.
110
+ - **In autopilot** (`execution_mode: automatic`, or an up-front "take it all the way"), there is no one to ask: ratify **provisionally** (`status: provisional` in the frontmatter), log it in `decisions.md`, and add a `review-after` entry to the decision queue (`../asdd/SKILL.md`) so the human confirms it later. It does not block the run or a self-merge.
110
111
  - **Downstream notice.** When a principle changes mid-project, flag that existing specs/plans may now be inconsistent — `analyze` will catch the drift on the next run.
111
112
 
112
113
  ## Anti-patterns
@@ -120,7 +121,7 @@ The constitution is versioned so `analyze` and `review` can cite "constitution v
120
121
  | "I'll rewrite the existing constitution to match what they said today." | Amend, don't overwrite. Strike + add + bump version + log the why. |
121
122
  | "No profile yet, I'll assume they're technical." | Use analogies; ask once "technical or with analogies?" or send them to `init`. |
122
123
  | "I'll add a Co-Authored-By so the commit credits the assist." | No. Git authorship is the human's — it's a fixed principle, enforced at `ship`. |
123
- | "I'll ratify it myself since it's obvious." | The user ratifies. Show the draft, get the explicit yes, then it takes effect. |
124
+ | "I'll ratify it myself since it's obvious." | The user ratifies. Show the draft, get the explicit yes, then it takes effect. Only autopilot ratifies provisionally, as a `review-after` queue entry. |
124
125
 
125
126
  ## Checklist before handing off
126
127
 
@@ -131,18 +132,18 @@ The constitution is versioned so `analyze` and `review` can cite "constitution v
131
132
  - [ ] Fixed principles included: human git authorship + decisions logged.
132
133
  - [ ] `02-DOCS/wiki/sdd/constitution.md` written with version + ratified date + amendment log.
133
134
  - [ ] `REVIEW.md` drafted at the project root; always-human paths mirrored into `.rsc/sello-config.json` `risk.raise` (or the user told why they are not).
134
- - [ ] Root `CLAUDE.md` `## Knowledge map` pointer has the read-first row for the constitution.
135
- - [ ] The constitution was shown to the user and explicitly ratified.
135
+ - [ ] The constitution is indexed in `02-DOCS/wiki/index.md` (the Knowledge map) under the `sdd/` topic.
136
+ - [ ] The constitution was shown to the user and explicitly ratified — or, in autopilot, ratified provisionally with a `review-after` queue entry.
136
137
 
137
- ## Project grounding (02-DOCS + CLAUDE.md)
138
+ ## Project grounding (02-DOCS)
138
139
 
139
- This skill's `02-DOCS` record is the constitution at `02-DOCS/wiki/sdd/constitution.md`. It is a **read-first** pointer entry, so its row stays in the short `## Knowledge map` pointer in the root `CLAUDE.md` (create `CLAUDE.md` if absent, additive only — never delete existing sections) — unlike other sdd artifacts, which are indexed in `02-DOCS/wiki/index.md` (the full Knowledge map that root `CLAUDE.md` points to). Add this row to the root pointer if it is not already present:
140
+ This skill's `02-DOCS` record is the constitution at `02-DOCS/wiki/sdd/constitution.md`, indexed in `02-DOCS/wiki/index.md` (the Knowledge map) like every other sdd artifact:
140
141
 
141
142
  ```markdown
142
143
  | Project constitution (SDD non-negotiables) | `02-DOCS/wiki/sdd/constitution.md` |
143
144
  ```
144
145
 
145
- Every later rsc-sdd phase reads this file before it works. The harness maintains and improves the article over the life of the project; this skill is the place that ratifies and amends it.
146
+ Never edit the root `CLAUDE.md` from here: it is owned by `harness` and is tier 2 in `rsc risk`. Every later ASDD phase reads the constitution before it works. The harness maintains and improves the article over the life of the project; this skill is the place that ratifies and amends it.
146
147
 
147
148
  ## Result envelope
148
149
 
@@ -68,5 +68,5 @@ capability:
68
68
  - "Every principle is numbered, imperative, and testable (a number, command, or named artifact), not an aspiration like 'write clean code'."
69
69
  - "Includes the two fixed principles: git authorship is the human's (no Co-Authored-By AI / generated-with footer, enforced at ship) and significant decisions are logged."
70
70
  - "Produces a Definition of Done checklist (the merge bar verify runs against) covering format/lint, types, tests/coverage, conventions, branch+PR+authorship, security, and decisions logged."
71
- - "Writes 02-DOCS/wiki/sdd/constitution.md with a version (v1.0.0), ratified date, and an append-only amendment log; adds the Knowledge-map row to root CLAUDE.md additively (never deleting sections)."
71
+ - "Writes 02-DOCS/wiki/sdd/constitution.md with a version (v1.0.0), ratified date, and an append-only amendment log; indexes it in 02-DOCS/wiki/index.md and never edits the root CLAUDE.md."
72
72
  - "Shows the draft and gets explicit ratification from the user before it takes effect, and ends by pointing to the specify phase as next in the chain."
@@ -124,8 +124,8 @@ first — isolate which side actually fails before you open either stack's debug
124
124
 
125
125
  When the cause is non-obvious — a race, an order dependency, an environment-only failure, a
126
126
  regression a reviewer would otherwise have to rediscover — append a short entry to
127
- `02-DOCS/wiki/sdd/decisions.md` (append-only; create it if absent and add a row to the root
128
- `CLAUDE.md` `## Knowledge map` under the `sdd/` topic). One entry:
127
+ `02-DOCS/wiki/sdd/decisions.md` (append-only; create it if absent and index it in
128
+ `02-DOCS/wiki/index.md` under the `sdd/` topic — never the root `CLAUDE.md`). One entry:
129
129
 
130
130
  ```text
131
131
  ## YYYY-MM-DD — bug: <symptom in five words> (feature: <slug>)
@@ -72,6 +72,10 @@ COMMIT → commit this task as one logical unit (authorship = Eric; see ship f
72
72
  The order is load-bearing. **Red before green** is not a suggestion — a test you write *after* the
73
73
  code tells you nothing about whether it can fail. **Refactor only on green** keeps every cleanup
74
74
  reversible against a passing bar. Self-check means evidence: the test output you saw, not "I tested it".
75
+ **ESM (and any language where a missing module fails at load):** a test importing a module that
76
+ does not exist dies on the import, not on the assertion — that red proves nothing. Create the module
77
+ first with a stub export (`export function total() { return 0 }`), then the red must fail on the
78
+ assertion.
75
79
 
76
80
  ### Four things you may never do to reach green
77
81
 
@@ -104,6 +108,9 @@ task's risk forecast. **The agent never picks or argues down its own tier.**
104
108
 
105
109
  ### c) Review scaled by tier — fresh context
106
110
 
111
+ Tier 1 or 2: first call `npx @ericrisco/rsc review ledger round <slug>/<task>` — before **every**
112
+ review of the task, this first one included. Round 1 is the task's first review, not its first fix.
113
+
107
114
  | Tier | Review |
108
115
  | --- | --- |
109
116
  | 0 | none — the controls (e) still run |
@@ -120,10 +127,13 @@ finding.
120
127
  ### d) Consolidate, verify, record
121
128
 
122
129
  Exactly as `../review/SKILL.md` → *Consolidate, then verify*: `npx @ericrisco/rsc review consolidate
123
- <reports…>`, then `finding-verifier` on every finding it marks `verify` (tier 1: on every blocker).
124
- Then `npx @ericrisco/rsc review ledger record <findings.json>`. A finding the ledger already holds
125
- comes back `repeat`: **never raise it again**. When a finding closes, record its outcome:
126
- `review ledger outcome <id> fixed|false|intentional|irrelevant`.
130
+ <reports…>` (tier 1 too: it applies the downgrade), then `finding-verifier` on every finding it
131
+ marks `verify` — every blocker and should-fix. Findings use the JSON shape in
132
+ `references/per-task-review.md`. Then `npx @ericrisco/rsc review ledger record <findings.json>`. A
133
+ finding the ledger already holds comes back `repeat`: **never raise it again**. When a finding
134
+ closes, record its outcome: `review ledger outcome <id> fixed|false|intentional|irrelevant|parked`
135
+ (`parked` = its task went to the decision queue). The ledger is local (`.rsc/`, not committed);
136
+ what lasts is the review-evals cases and rules `review` writes from it.
127
137
 
128
138
  ### e) Mandatory controls — every tier
129
139
 
@@ -140,8 +150,8 @@ A control that did not run is reported as **not run**, never as passed — and t
140
150
 
141
151
  ### f) Fix loop — at most 3 rounds
142
152
 
143
- Each round opens with `npx @ericrisco/rsc review ledger round <slug>/<task>`. It exits non-zero on the 4th
144
- round with the action to take; obey it (step g).
153
+ Each re-review opens with another `review ledger round <slug>/<task>` (step c). It exits non-zero
154
+ on the 4th round with the action to take; obey it (step g).
145
155
 
146
156
  - **From round 2, re-review only the new diff** (BASE = the previous round's head).
147
157
  - **Fix the bug class, not the line**: name the class and fix every instance in the task's scope.
@@ -153,12 +163,15 @@ round with the action to take; obey it (step g).
153
163
  | Situation | Action |
154
164
  | --- | --- |
155
165
  | Trivial, or no business weight | Fix it and record it in `decisions.md` with the task id |
156
- | Tier 2, contradicts a decision in `decisions.md`, or no convergence in 3 rounds | Park the task in the **decision queue** and continue with independent tasks |
166
+ | Tier 2, contradicts a decision in `decisions.md`, or no convergence in 3 rounds | Park the task in the **decision queue** (`blocking`) and continue with independent tasks |
157
167
 
158
168
  The **decision queue** is a `## Decision queue` section in `02-DOCS/wiki/sdd/progress/<slug>.md`, one
159
- entry per parked task: task id, tier, the question, the options, your recommendation, the evidence
160
- path. Tasks that depend on a parked task wait; the rest continue. Only a human empties the queue — an
161
- agent's approval never counts as human.
169
+ entry per item: its kind, task id, tier, the question, the options, your recommendation, the
170
+ evidence path. **Kind is `blocking` or `review-after`.** Tier 2, a contradiction with a recorded
171
+ decision and no convergence are `blocking`: the task is parked, its dependents wait, and the branch
172
+ cannot merge itself. An assumption you took so work could continue is `review-after`: nothing waits
173
+ and it does not stop a self-merge — the human reads it afterwards. Only a human closes an entry —
174
+ an agent's approval never counts as human.
162
175
 
163
176
  Then append the progress entry and show the checkpoint (below).
164
177
 
@@ -167,18 +180,10 @@ Then append the progress entry and show the checkpoint (below).
167
180
 
168
181
  ## Delegating the test tooling (do not reinvent it)
169
182
 
170
- Write the *intent* of the test here; get the *mechanics* from the stack skill. The discipline is
171
- yours; the fixtures, runners and idioms are theirs.
172
-
173
- | Stack | Where the test mechanics live | What you delegate |
174
- | --- | --- | --- |
175
- | FastAPI / async Python | `../fastapi/references/testing.md` | `ASGITransport` in-process client, `dependency_overrides`, transactional rollback fixture, `pytest-asyncio` |
176
- | Go services | `../go/references/testing.md` | table-driven subtests, `httptest`, interface fakes, golden files, `errors.Is` checks |
177
- | Next.js / React | `../nextjs/references/testing.md` | Vitest + Testing Library for units, Playwright for flows, server-action / RSC testing |
178
- | Flutter | `../flutter/references/testing.md` | `flutter test` widget tests, `pump`/`pumpAndSettle`, golden tests, mocked repositories |
179
-
180
- If the feature spans two stacks (a Next.js front-end calling a FastAPI back-end), each task names its
181
- stack and pulls that stack's testing reference — you do not mix idioms inside one task.
183
+ Write the *intent* of the test here; get the *mechanics* (fixtures, runners, idioms) from the stack
184
+ skill's testing reference: `../fastapi/references/testing.md`, `../go/references/testing.md`,
185
+ `../nextjs/references/testing.md`, `../flutter/references/testing.md`. A feature spanning two stacks
186
+ names the stack per task and never mixes idioms inside one task.
182
187
 
183
188
  ## Skill digestion and resolution
184
189
 
@@ -252,9 +257,13 @@ each dispatch self-sufficient and make its return machine-checkable:
252
257
 
253
258
  - **Carry the task, not your context.** Hand the worker its task brief, its **Interfaces** block
254
259
  (`Consumes`/`Produces`, exact signatures — from `tasks`), and the plan's **§0 Global Constraints**.
255
- Anything not in those three is invisible to it. Prefer **file paths over pasted text** (use
256
- `scripts/task-brief` and `scripts/review-package`, below) — pasted briefs and diffs stay resident
257
- in your context and get re-read every turn.
260
+ Anything not in those three is invisible to it. Prefer **file paths over pasted text** —
261
+ `scripts/task-brief <plan> <N>` (N as `1`, `001` or `T001`; reads the `tasks` table row plus any
262
+ `### T1` detail block) and `scripts/review-package` — pasted briefs and diffs stay resident in
263
+ your context and get re-read every turn.
264
+ - **Native isolation leaves worktrees behind.** A subagent dispatched with worktree isolation
265
+ works in `.claude/worktrees/`. `rsc risk` ignores that folder, but after you cherry-pick the
266
+ worker's commits, remove it (`git worktree remove <path>`).
258
267
  - **Model is REQUIRED on every dispatch.** Always set the subagent's model explicitly (the
259
268
  `developer` agent already pins balanced). An omitted model silently inherits the session's model —
260
269
  usually the most expensive one — which is the quiet way fan-out cost explodes.
@@ -271,10 +280,8 @@ each dispatch self-sufficient and make its return machine-checkable:
271
280
 
272
281
  ## Model tier — `balanced` (opt-in routing)
273
282
 
274
- Default tier **`balanced`** — the bulk of TDD execution. Routing is **off** unless `models.enabled:
275
- true` in `02-DOCS/wiki/sdd/config.yaml`; when on, resolve and apply it per
276
- `../asdd/references/model-routing.md` (fan-out on `balanced`, a hard sub-problem escalated to
277
- `heavy`). Routing off → honor the session model silently.
283
+ Default **`balanced`**. Routing is **off** unless `models.enabled: true` in `config.yaml`; when on,
284
+ apply `../asdd/references/model-routing.md` (hard sub-problem → `heavy`); off → session model.
278
285
 
279
286
  ## Checkpoints — what to say
280
287
 
@@ -327,13 +334,12 @@ outranks the plan, and the plan outranks your in-the-moment preference.
327
334
 
328
335
  ## Red flags — stop and re-route
329
336
 
330
- - **No plan or no task list** → you cannot implement intent you haven't planned. Route to `plan` /
331
- `tasks`.
337
+ - **No plan or no task list** → you cannot implement intent you haven't planned: `plan` / `tasks`.
332
338
  - **Unresolved `analyze` findings** (contradiction between constitution ↔ spec ↔ plan ↔ tasks) →
333
339
  resolve them before any code; they will only get more expensive after the diff exists.
334
340
  - **A task forces a constitution violation** → surface it, stop, kick it back to `analyze`/`plan`.
335
341
  - **You're on the default branch** → stop before the first commit and ask: branch or default branch (`worktrees`).
336
- - **A test won't go red** (wrong reason — import error, syntax) → fix the test before trusting it.
342
+ - **A test won't go red** for the right reason (import error, syntax) → stub the module, fix the test.
337
343
  - **The combined suite is red after a parallel merge** → do not checkpoint as done; debug with
338
344
  `debug` before continuing.
339
345
  - **You're tempted to disable/skip a failing test to "make progress"** → that's the bug talking.
@@ -344,11 +350,11 @@ outranks the plan, and the plan outranks your in-the-moment preference.
344
350
  ```text
345
351
  - [ ] a) RED for the right reason → GREEN → REFACTOR; committed as one unit (authorship = Eric)
346
352
  - [ ] b) `rsc risk` on the task diff; tier recorded
347
- - [ ] c) review by tier, fresh context, spec + diff + surrounding code, no pre-judging
348
- - [ ] d) `review consolidate` + `finding-verifier`; `review ledger record`
353
+ - [ ] c) `review ledger round <slug>/<task>` before each review (≤ 3); by tier, fresh context, no pre-judging
354
+ - [ ] d) `review consolidate` + `finding-verifier`; `review ledger record`; outcomes closed
349
355
  - [ ] e) tests, build, security, traceability — each passed or **not run**
350
- - [ ] f) `review ledger round <slug>/<task>` each round; ≤ 3; class fixed, no repeats
351
- - [ ] g) fixed + logged in decisions.md, or parked in the decision queue
356
+ - [ ] f) re-review only the new diff; class fixed, no repeats
357
+ - [ ] g) fixed + logged in decisions.md, or in the decision queue (`blocking` / `review-after`)
352
358
  - [ ] Progress entry appended; skill resolution recorded; checkpoint shown
353
359
  ```
354
360
 
@@ -375,12 +381,9 @@ End every implementation checkpoint or completed batch with:
375
381
 
376
382
  ## What this skill is NOT
377
383
 
378
- - **Not the closing gate.** The per-task controls prove each task; the whole branch against main,
379
- the rollback plan and the human explanation are stage 5 (`verify` closing mode, `review`, `ship`).
380
- - **Not spec or plan writing.** If you find the plan is wrong mid-build, stop and amend it via
381
- `plan`; don't silently re-architect inside a commit.
382
- - **Not the deep-debug protocol.** When a test fails for a reason you don't understand, switch to
383
- `debug` (reproduce → isolate → hypothesize → fix → verify) instead of guessing.
384
+ - **Not the closing gate** — whole branch, rollback plan and explanation are stage 5 (`verify`, `review`, `ship`).
385
+ - **Not plan writing** — a plan found wrong mid-build is amended via `plan`, never inside a commit.
386
+ - **Not deep debugging** — a failure you don't understand goes to `debug`, not to guessing.
384
387
 
385
388
  ## Where you are in ASDD
386
389
 
@@ -393,4 +396,3 @@ branch. Parked tasks travel in the decision queue for the human.
393
396
  ## Orientación (siempre)
394
397
 
395
398
  Habla con la voz de `orient`: frases cortas, una idea por frase, y cada respuesta se entiende sola. Registro técnico o con analogías según `technical_level` en `02-DOCS/wiki/harness/user-profile.md`. Cierra cada turno con el **bloque-brújula** (📍 dónde estás · ➡️ siguiente, terminando en pregunta; ✅ y 🧭 cuando hay algo hecho o decidido). **Nunca termines en seco.** Protocolo completo: skill `orient` → `skills/orient/references/orientation-contract.md`. (Defiere a `suggest` el "¿instalo la skill que falta?".)
396
-
@@ -26,8 +26,17 @@ words at dispatch; keep the contract identical.
26
26
  Every finding uses `review`'s format and severities (`blocker` / `should-fix` / `nit`, with
27
27
  `file:line`, why, repro, fix) and names its **bug class**, so the fix covers every instance. The
28
28
  report ends with a ` ```json findings ` block — the input to `review consolidate` and
29
- `review ledger record` — and one line: `VERDICT: pass` or `VERDICT: changes-needed (<counts>)`.
30
- Zero findings is a valid, expected outcome on a clean task — do not invent findings to look thorough.
29
+ `review ledger record` — holding an array of objects in exactly this shape:
30
+
31
+ ```json
32
+ {"severity":"blocker|should-fix|nit|question","file":"path","line":N,"claim":"…","failure_scenario":"concrete input → wrong result","evidence":"quoted code","class":"bug class"}
33
+ ```
34
+
35
+ A `blocker` or `should-fix` without `file`+`line` or without `failure_scenario` is downgraded to
36
+ `question` by `review consolidate`; then every remaining blocker and should-fix goes to
37
+ `finding-verifier`. Close the report with one line: `VERDICT: pass` or
38
+ `VERDICT: changes-needed (<counts>)`. Zero findings is a valid, expected outcome on a clean task —
39
+ do not invent findings to look thorough (an empty array is fine).
31
40
 
32
41
  ## Anti-pre-judging (the reviewer is told this too)
33
42