@pilotspace/add 2.2.0 → 2.3.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,66 @@ All notable changes to the ADD method (`@pilotspace/add` on npm,
4
4
  `pilotspace-add` on PyPI) are documented here. The format follows
5
5
  [Keep a Changelog](https://keepachangelog.com/); versions follow semver.
6
6
 
7
+ ## [2.3.0] — 2026-07-24
8
+
9
+ Minor: three waves — a **signal graph** view over the task DAG, the **§2
10
+ Scenarios fold** into §4, and a **call-lean pass** that kills the two measured
11
+ WM1 freeze/scope call sinks. Engine changes are additive; templates migrate
12
+ in place (no manual task-file edits).
13
+
14
+ ### Signal graph — one addressable node for every note/todo/delta
15
+ - **unified signal model** — note, todo, and spec-delta collapse into ONE
16
+ addressable `signal` node (status + edges), projected as a VIEW, not a new
17
+ store; `_signals` reader.
18
+ - **`graph --signals`** — opt-in overlay renders signals on the task DAG;
19
+ **exit-criteria** render as `ec_` *delivered-by* nodes (a milestone's own
20
+ criteria appear as ✓/○ nodes wired to the tasks that meet them).
21
+ - **`graph --html`** — emits a self-rendering HTML page to tmp (Mermaid inline,
22
+ no external host).
23
+ - **atomicity signal** — the freeze SEEDS a persistent atomicity signal into the
24
+ task's todos instead of an ephemeral print, so the nudge survives the session.
25
+
26
+ ### §2 Scenarios folded into §4 — one place for cases
27
+ - the standalone **§2 SCENARIOS** section is **retired in place**: pass/fail cases
28
+ now live with the tests in **§4 · TESTS & SCENARIOS** (primary-case rigor). The
29
+ §3–§7 numbers are unchanged so the freeze parser and every §-reference keep
30
+ working — the §1→§3 jump is intentional.
31
+ - **PROJECT.md** gains a managed `ADD:SPECS` pointer block (init/migrate inject it
32
+ from the 5-DD spec set); foundation references reconciled to the `.add/specs/`
33
+ model; the generated **CLAUDE.md** block finalized in the generator.
34
+ - the Step-2 Scenarios book chapter folds into Step-4; `add-flow.png` retired.
35
+
36
+ ### Call-lean pass — the WM1 freeze/scope sinks die at the source
37
+ - **freeze-flag slot** — `PLAN.md.tmpl` §3 carries a drafted-blank *Least-sure
38
+ flag* slot; the first freeze no longer fails `unflagged_freeze` (0/6 reps in the
39
+ re-measure, was 3/3). The unfilled part-menu placeholder still never satisfies
40
+ the gate — the floor holds.
41
+ - **scope-first freeze** — a §3 Scope that resolves to zero cover is now refused
42
+ `scope_unresolved` AT the freeze with a paste-ready fix, instead of surfacing
43
+ later as `scope_violation` → re-cross → re-gate. UNDECLARED stays grandfathered;
44
+ greenfield `[MISSING]` tokens still freeze; a `.add/tasks/` token gets a
45
+ task-dir teach note.
46
+ - **scope-walk prunes** — `.venv` · `venv` · `.tox` · `.mypy_cache` · `.ruff_cache`
47
+ · `.eggs` and any `*.egg-info` dir are pruned from the scope walk, so an
48
+ in-workspace virtualenv or `pip install -e .` metadata is never read as an
49
+ out-of-scope write. `dist`/`build` stay watched (can be a real write-set). The
50
+ untouched-Scope-default warning now self-explains (a note, not a blocker; clears
51
+ only by editing the Scope line — re-cross does not clear it).
52
+ - **honest fidelity meter** — the artifact-blind LLM `spec_fidelity` metric is
53
+ replaced by a deterministic `requirement_coverage` (frozen per-WM checklists +
54
+ probes, no LLM in the metric path); `oracle_pass_rate` promoted to the headline;
55
+ the judge demoted to an advisory `code_quality_annotation`.
56
+ - **§3 Build-strategy relabel** — *Scope (may touch)* is the HARD scope-lock; the
57
+ rest (Strategy · Regression floor · Persona) is SOFT/optional. `MILESTONE.md`
58
+ gains a drafted-blank `## Strategy` slot.
59
+
60
+ Retired (no CLI/install surface change): the standalone **§2 SCENARIOS** section
61
+ (folded into §4) · the **ccsk / rule-file** mode · `add-flow.png`.
62
+
63
+ Pinned by `test_release_2_3_0.py`. `ENGINE_MD5` re-aimed to `60eef504…`
64
+ (scope refusal + walk prunes); the four tooling twins stay byte-identical;
65
+ full tooling suite green (2277 tests).
66
+
7
67
  ## [2.2.0] — 2026-07-22
8
68
 
9
69
  Minor: the Direction beat gains a **fable reasoning discipline** — a prompt-only
package/bin/cli.js CHANGED
@@ -39,7 +39,7 @@ function parseArgs(argv) {
39
39
  // hint only echoes flags the user actually chose (shortest true command).
40
40
  const args = { _: [], force: false, check: false, noSkill: false, stage: null, name: null,
41
41
  yes: false, nonInteractive: false, global: false, globalData: false,
42
- fromGlobalData: false, ruleFile: false, lockTimeout: null };
42
+ fromGlobalData: false, lockTimeout: null };
43
43
  for (let i = 0; i < argv.length; i++) {
44
44
  const a = argv[i];
45
45
  if (a === "--force") args.force = true;
@@ -61,9 +61,6 @@ function parseArgs(argv) {
61
61
  // already provides the `add` skill, so a plugin bootstrap uses this to materialize
62
62
  // .add/tooling/ into the project without a duplicate .claude/skills/add.
63
63
  else if (a === "--no-skill") args.noSkill = true;
64
- // --rule-file: write the ADD block to .claude/rules/add-workflows.md + reference it from
65
- // CLAUDE.md instead of inlining (auto-on for ccsk projects with a .ccsk/ dir).
66
- else if (a === "--rule-file") args.ruleFile = true;
67
64
  else if (a === "--stage" || a === "--name") {
68
65
  const v = argv[++i];
69
66
  // fail loudly on a trailing/abutting flag — never silently drop a value
@@ -263,126 +260,6 @@ function writeAgentPointer(target, profile) {
263
260
  }
264
261
  }
265
262
 
266
- // --- rule-file mode: ccsk-style relocation of the CLAUDE.md block ---------------------
267
- // Mirror of add.py / _installer.py rule-file logic (twins by duplication). For ccsk projects
268
- // (.ccsk/ dir) or an explicit --rule-file, the ADD pointer goes to .claude/rules/add-workflows.md
269
- // and CLAUDE.md keeps only a reference bullet. CLAUDE-only — AGENTS.md/.clinerules stay inline.
270
- const RULES_FILE_REL = path.join(".claude", "rules", "add-workflows.md");
271
- const WORKFLOW_HEADINGS = ["Rules & Workflows", "Workflows", "Rules"];
272
- const RULE_REF_LINE = "- ADD (AI-Driven Development) Workflows rules: ./.claude/rules/add-workflows.md";
273
-
274
- // True when the ADD block belongs in .claude/rules/add-workflows.md instead of inline.
275
- // Re-derived from disk: explicit flag, a ccsk project (.ccsk/), or a rule file already present.
276
- function ruleFileMode(root, flag) {
277
- if (flag) return true;
278
- try {
279
- if (fs.existsSync(path.join(root, ".ccsk")) &&
280
- fs.statSync(path.join(root, ".ccsk")).isDirectory()) return true;
281
- if (fs.existsSync(path.join(root, RULES_FILE_REL))) return true;
282
- } catch (_e) { /* fail-soft */ }
283
- return false;
284
- }
285
-
286
- // Remove an inline ADD:BEGIN..END region (migration), collapsing the gap. An unterminated
287
- // BEGIN is left as-is rather than eating the rest of the file.
288
- function stripInlineBlock(text) {
289
- const begin = text.indexOf(GUIDE_BEGIN);
290
- if (begin === -1) return text;
291
- let end = text.indexOf(GUIDE_END, begin);
292
- if (end === -1) return text;
293
- end += GUIDE_END.length;
294
- const head = text.slice(0, begin).replace(/\n+$/, "");
295
- const tail = text.slice(end).replace(/^\n+/, "");
296
- if (head && tail) return head + "\n\n" + tail;
297
- return head || tail;
298
- }
299
-
300
- // Insert the ADD rule-file bullet under an existing Workflows/Rules heading, or append a fresh
301
- // '## Workflows' section. Caller guarantees the bullet is absent.
302
- function insertRuleReference(text) {
303
- const lines = text.split("\n");
304
- let headingIdx = -1;
305
- for (let i = 0; i < lines.length; i++) {
306
- const m = lines[i].match(/^(#{1,6})\s+(.*?)\s*$/);
307
- if (m && WORKFLOW_HEADINGS.some((h) => m[2].trim().toLowerCase() === h.toLowerCase())) {
308
- headingIdx = i;
309
- break;
310
- }
311
- }
312
- if (headingIdx === -1) {
313
- const body = text.replace(/\n+$/, "");
314
- const sep = body ? "\n\n" : "";
315
- return body + sep + "## Workflows\n\n" + RULE_REF_LINE + "\n";
316
- }
317
- const level = lines[headingIdx].match(/^(#{1,6})/)[1].length;
318
- let end = lines.length;
319
- for (let j = headingIdx + 1; j < lines.length; j++) {
320
- const m = lines[j].match(/^(#{1,6})\s+/);
321
- if (m && m[1].length <= level) { end = j; break; }
322
- }
323
- let insertAt = headingIdx + 1;
324
- for (let j = headingIdx + 1; j < end; j++) {
325
- if (lines[j].trim()) insertAt = j + 1;
326
- }
327
- lines.splice(insertAt, 0, RULE_REF_LINE);
328
- return lines.join("\n");
329
- }
330
-
331
- // Make CLAUDE.md reference the ADD rule file under a Workflows/Rules heading, migrating any prior
332
- // inline ADD block out. created|updated|unchanged|skipped; .bak on change; fail-soft.
333
- function ensureClaudeReference(claudeMd) {
334
- try {
335
- const existed = fs.existsSync(claudeMd);
336
- const current = existed ? fs.readFileSync(claudeMd, "utf8") : "";
337
- let next = stripInlineBlock(current);
338
- if (next.indexOf("add-workflows.md") === -1) next = insertRuleReference(next);
339
- if (!next.endsWith("\n")) next += "\n";
340
- if (next === current) return "unchanged";
341
- if (existed) fs.writeFileSync(claudeMd + ".bak", current);
342
- fs.writeFileSync(claudeMd, next);
343
- return existed ? "updated" : "created";
344
- } catch (e) {
345
- warn("could not write CLAUDE.md — " + (e && e.message ? e.message : e) + "; skipped");
346
- return "skipped";
347
- }
348
- }
349
-
350
- // Rule-file variant of writeAgentPointer for the Claude profile: write the ADD pointer block to
351
- // .claude/rules/add-workflows.md and leave a reference in CLAUDE.md. Fail-soft.
352
- function writeRuleFilePointer(target, profile) {
353
- const rulesPath = path.join(target, RULES_FILE_REL);
354
- const block = agentPointerBlock(profile);
355
- try {
356
- if (fs.existsSync(rulesPath)) {
357
- const current = fs.readFileSync(rulesPath, "utf8");
358
- const begin = current.indexOf(GUIDE_BEGIN);
359
- let next;
360
- if (begin !== -1) {
361
- const endIdx = current.indexOf(GUIDE_END, begin);
362
- if (endIdx !== -1) {
363
- next = current.slice(0, begin) + block + current.slice(endIdx + GUIDE_END.length);
364
- } else {
365
- next = current.replace(/\n+$/, "") + "\n\n" + block + "\n";
366
- }
367
- } else {
368
- next = current.replace(/\n+$/, "") + "\n\n" + block + "\n";
369
- }
370
- if (next !== current) {
371
- fs.writeFileSync(rulesPath + ".bak", current);
372
- fs.writeFileSync(rulesPath, next);
373
- }
374
- } else {
375
- fs.mkdirSync(path.dirname(rulesPath), { recursive: true });
376
- fs.writeFileSync(rulesPath, block + "\n");
377
- }
378
- } catch (e) {
379
- warn("could not write " + RULES_FILE_REL + " — " + (e && e.message ? e.message : e) + "; skipped");
380
- return "skipped";
381
- }
382
- ensureClaudeReference(path.join(target, "CLAUDE.md"));
383
- return "ok";
384
- }
385
-
386
263
  // --- interactive layer (clack on a real TTY; plain text everywhere else) -----
387
264
  // Designed-for-failure: any doubt (non-TTY, CI, --yes, a failed import, an
388
265
  // un-promptable stream) degrades to the EXACT plain-text path below. The clack
@@ -650,16 +527,7 @@ function dropFiles(args, target, profile, intent) {
650
527
  // Agent detection: write THE detected agent's integration file (a marker-delimited
651
528
  // pointer init's sync-guidelines later supersedes) + tailor the closing next-step.
652
529
  // Best-effort + fail-soft — never aborts the successful drop above.
653
- // Rule-file mode (ccsk projects / --rule-file) relocates the CLAUDE.md pointer to
654
- // .claude/rules/add-workflows.md + a reference — CLAUDE-only; other agents stay inline.
655
- if (profile.integration_file === "CLAUDE.md" && ruleFileMode(target, args.ruleFile)) {
656
- if (fs.existsSync(path.join(target, ".ccsk"))) {
657
- log(" ccsk detected — ADD rules go to .claude/rules/add-workflows.md");
658
- }
659
- writeRuleFilePointer(target, profile);
660
- } else {
661
- writeAgentPointer(target, profile);
662
- }
530
+ writeAgentPointer(target, profile);
663
531
 
664
532
  // Gemini CLI auto-loads GEMINI.md, not AGENTS.md — so for the gemini profile we ALSO merge
665
533
  // .gemini/settings.json (context.fileName) to load the AGENTS.md pointer. Fail-soft + idempotent.
@@ -1985,7 +1853,4 @@ module.exports = {
1985
1853
  scopeOptions: scopeOptions,
1986
1854
  writeIntentNote: writeIntentNote,
1987
1855
  writeGeminiSettings: writeGeminiSettings,
1988
- ruleFileMode: ruleFileMode,
1989
- ensureClaudeReference: ensureClaudeReference,
1990
- writeRuleFilePointer: writeRuleFilePointer,
1991
1856
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pilotspace/add",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "ADD (AI-Driven Development) 2.0. The agent is the hands; ADD is the memory, judgment, and conscience — a minimal, state-tracked skill: one freeze per feature, trust from passing tests, state on disk so context rot never survives a new session.",
5
5
  "bin": {
6
6
  "add": "bin/cli.js"
@@ -3,7 +3,7 @@ name: add
3
3
  description: >-
4
4
  ADD (AI-Driven Development) — a minimal, state-tracked workflow: the AI writes
5
5
  the code, the human owns direction and verification. Drives every feature through
6
- one lean PLAN.md: Specify → Scenarios → Plan → Tests → Build → Verify → Observe,
6
+ one lean PLAN.md: Specify → Plan → Tests & Scenarios → Build → Verify → Observe,
7
7
  red/green TDD built in. Use whenever a repo has `.add/`, or the user says "add",
8
8
  "start a task", "next phase", "specify this feature", "ADD method", "AI-driven
9
9
  development", or wants spec/tests-first discipline over vague-prompt coding. Also
@@ -15,7 +15,7 @@ argument-hint: "status | init | continue | --todo <text> | [describe new short g
15
15
  license: MIT
16
16
  metadata:
17
17
  author: add
18
- version: "2.0.0"
18
+ version: "2.3.0"
19
19
  ---
20
20
 
21
21
  # ADD — memory · judgment · conscience (the agent is the hands)
@@ -70,9 +70,9 @@ confirms. Unsharp intent? **Interview before you size** (`intake.md`). A milesto
70
70
  Every task is three beats (seven steps, folded), three engine calls, ONE human decision:
71
71
 
72
72
  1. **DIRECTION** — load the domain-fit persona (seed via add-worker persona-mode if none), then compose
73
- the whole bundle in ONE silent draft — §1–§3 + §5-scope, no per-section narration; §4 then runs red — in PLAN.md: §1 rules + ranked ⚠ flag (co-specify) ·
74
- §2 scenarios (optional gherkin — §4 is canonical) · §3 PLAN (grounding → frozen contract shape → build-strategy + Scope + Target) ·
75
- §4 red suite one test per Must & per Reject (a Must/Reject encoded in neither §2 nor §4 = §1 not understood — stop); run red for the RIGHT reason; fill each `covers:` key. Then the ONE approval,
73
+ the whole bundle in ONE silent draft — §1·§3 + §5-scope, no per-section narration; §4 then runs red — in PLAN.md: §1 rules + ranked ⚠ flag (co-specify) ·
74
+ §3 PLAN (grounding → frozen contract shape → build-strategy + Scope + Target) ·
75
+ §4 TESTS & SCENARIOS — the red suite (cases live here; optional inline gherkin when a human needs it): one test per Must & per Reject (a Must/Reject encoded in no §4 test = §1 not understood — stop); minor behaviors are prose build-guidance, not gated; run red for the RIGHT reason; fill each `covers:` key. Then the ONE approval,
76
76
  presented lowest-confidence-first: `add.py freeze` (a setup session's baseline `lock` IS this approval).
77
77
  2. **BUILD** — code in `src/` until every red is green; change no test, no frozen contract; stay
78
78
  inside the §3 Scope. A test OUTSIDE your suite failing? `add.py locate` names the owning node, the
@@ -43,7 +43,7 @@ If a learning touches two, ask "which competency, once updated, would have PREVE
43
43
 
44
44
  ```
45
45
  emit (OBSERVE) human review
46
- open ───────────▶ folded (merged into PROJECT.md; version bumps)
46
+ open ───────────▶ folded (merged into its `.add/specs/<dd>` spec; version bumps)
47
47
  └──────────▶ rejected (deliberately NOT consolidated — trail kept)
48
48
  ```
49
49
 
@@ -42,7 +42,7 @@ Record **before** review-domain: project **defaults** in DESIGN.md's `## Design
42
42
 
43
43
  ### 1 · review-domain
44
44
  Start from the **domain**, not a blank canvas. Read the domain model — entities, flows, the
45
- ubiquitous language in `PROJECT.md` / `GLOSSARY.md` — and derive **which screens** the feature needs
45
+ ubiquitous language in `.add/specs/domain.md` / `GLOSSARY.md` — and derive **which screens** the feature needs
46
46
  + each screen's **regions**. Map each entity to a *presentational* component (owns no domain
47
47
  decision). Output: the screen list + regions.
48
48
 
@@ -5,6 +5,28 @@ acceptance check — passes, without changing any test or the contract. This is
5
5
  AI leads; §1–§4 removed all ambiguity. Write code into `.add/tasks/<slug>/src/` (a non-coding
6
6
  task writes its artifact to the path its §5 Scope declares).
7
7
 
8
+ ## The reasoning discipline — build's slice of the arc (definitions: `phases/direction.md`)
9
+
10
+ **Fluent ≠ true** holds in code: a diff feels done in proportion to how much you wrote, not how
11
+ much you verified — a green you haven't refuted is a claim, not evidence. Run the arc per batch:
12
+
13
+ - **FRAME** — restate the batch's tests and the frozen §3 clause they cover before writing a line.
14
+ - **GROUND** — open every anchor before editing it; a recalled file/symbol/flag is `[PRIOR]`
15
+ until re-confirmed against the live tree THIS session; a live read outranks memory.
16
+ - **REASON** — name the mechanism ("because…"), simulate one concrete input through the change
17
+ before committing it; when a red survives a fix, hold a second hypothesis instead of patching harder.
18
+ - **ATTACK** — before presenting green: re-run the suite fresh, then reviewer-read your own diff
19
+ hunting the cheap falsifying input (file · line · values). An earned green is one you tried to
20
+ kill. Security findings stay HARD-STOP.
21
+ - **DELIVER** — the green report leads with the outcome and tags its claims: a pass you ran THIS
22
+ session is `[OBSERVED]`; a remembered pass is `[PRIOR]` — never present it bare.
23
+ - **Constraint loop on the §5 Scope allowlist** — don't eyeball it: list the files actually
24
+ touched (`git status` / diff) and check each against the declared Scope tokens mechanically
25
+ before the gate; a miss is `scope_violation` — caught by you, not by the engine.
26
+ - **Follow-through (the Floor's second check)** — green tests ≠ the goal reached; simulate the
27
+ human's end state once — run the artifact under the BARE declared runtime — before calling
28
+ the batch done.
29
+
8
30
  ## Work in small batches
9
31
 
10
32
  Pick ONE task-sized slice, restate its tests, implement, iterate to green —
@@ -153,7 +153,7 @@ Rejects: `not_classified` · `dangling_criterion` · `no_milestone` · `duplicat
153
153
 
154
154
  ---
155
155
 
156
- ## Rules (§1) + scenarios (§2) — co-specification
156
+ ## Rules (§1) — co-specification
157
157
 
158
158
  State what the feature MUST do and MUST REJECT — zero ambiguity left to guessing. Co-specify in three moves: **Diverge** (surface the 2–3 genuine framings +
159
159
  open questions; let the user react), **Converge** (draft §1 by PROJECTING from the milestone
@@ -171,8 +171,10 @@ the design-definition loop (`design.md`).
171
171
  `⚠ <assumption> — lowest confidence because <why>; if wrong: <cost>`.
172
172
  </output_format>
173
173
 
174
- Every Must and Reject must be checkable — canonically as a §4 test (its `covers:` tag); §2 gherkin is
175
- an OPTIONAL readable projection, added only when a human needs prose cases at the freeze, never as ceremony:
174
+ Every Must and Reject must be checkable — canonically as a §4 test (its `covers:` tag). Cases live
175
+ in §4 · TESTS & SCENARIOS (the old standalone §2 SCENARIOS section is retired). A Given/When/Then
176
+ line is an OPTIONAL readable projection, added inline in §4 only when a human needs prose cases at
177
+ the freeze, never as ceremony:
176
178
 
177
179
  ```gherkin
178
180
  Scenario: <short name>
@@ -182,16 +184,17 @@ Scenario: <short name>
182
184
  And <what must remain unchanged> # REQUIRED for every rejection
183
185
  ```
184
186
 
185
- Then sweep the edge cases — boundary · duplicate · partial failure · concurrency · malformed input
186
- one per applicable case, or rule it out on purpose. Every Then is specific and observable, never
187
+ Then sweep the edge cases — boundary · duplicate · partial failure · concurrency · malformed input.
188
+ Gate the PRIMARY cases + primary edge cases with a red test each; minor/secondary behaviors are
189
+ DESCRIBED in prose as build-guidance, not a red test. Every Then is specific and observable, never
187
190
  "then it works". Your §1 ranking feeds the bundle-level flag the human reads at the freeze.
188
191
 
189
192
  <exit_gate>
190
193
  - [ ] Framings weighed noted; every required behavior stated; every rejection has a named error code.
191
194
  - [ ] Assumptions ordered lowest-confidence first; the 1–2 `⚠` flags carry why + cost — or an honest
192
195
  "none material" that still names the single biggest risk (never a blank "none").
193
- - [ ] Every Must and Reject is encoded a §4 test (canonical) or an optional §2 gherkin scenario;
194
- every rejection asserts what stays unchanged; edge cases covered or ruled out on purpose.
196
+ - [ ] Every Must and Reject is encoded as a §4 test (`covers:` tag); every rejection asserts what
197
+ stays unchanged; primary edge cases covered or ruled out on purpose (minor ones may be prose).
195
198
  </exit_gate>
196
199
 
197
200
  ---
@@ -259,7 +262,7 @@ first, then walk the rest before saying yes:
259
262
  waiting at verify.
260
263
  - **⚠ flags first** — read the lowest-confidence flags; accept each knowing its cost if wrong. The engine refuses an unflagged freeze before build (`unflagged_freeze`).
261
264
  - **Intent** — does §1 say what you actually want built?
262
- - **Cases** — does every Must and Reject have an observable §2 scenario?
265
+ - **Cases** — does every Must and Reject have an observable §4 test/scenario (`covers:` tag)?
263
266
  - **Shape** — glossary names, error codes, additive vs breaking: is THIS the shape to freeze?
264
267
  - **Shape self-verify (the constraint loop)** — for the mechanically-checkable output-shape rules — the
265
268
  frozen §3 tag census (the closed XML vocabulary), the §5 Scope path tokens, each §4 `covers:` key, any
@@ -280,8 +283,8 @@ Reject any line → the bundle goes back to draft; the freeze stays the only gat
280
283
  Run the suite now, with no implementation — **red for the right reason** (missing implementation,
281
284
  not a broken harness). A test green before code exists is testing nothing. **A test is any
282
285
  machine-checkable assertion**, not only xUnit code — a metric threshold (ML/data), a reconciliation
283
- query, a plan-diff (infra), a rendered-screen diff (UI). Produce: one executable test per §2
284
- scenario asserting **behavior, not internals** · contract-conformance tests (shapes + error
286
+ query, a plan-diff (infra), a rendered-screen diff (UI). Produce: one executable test per PRIMARY
287
+ case/scenario asserting **behavior, not internals** · contract-conformance tests (shapes + error
285
288
  responses) · side-effect assertions on rejection paths (`assert balance unchanged`) · a recorded
286
289
  coverage target.
287
290
 
@@ -9,13 +9,34 @@ sufficient. Fill **§6** in PLAN.md including the GATE RECORD.
9
9
  > **security is always a HARD-STOP and is never auto-passed**. Under `conservative`, or whenever
10
10
  > residue is found, this phase is **human-led** (auto-PASS conditions: `run.md`).
11
11
 
12
+ ## The reasoning discipline — verify's slice of the arc (definitions: `phases/direction.md`)
13
+
14
+ **Fluent ≠ true** holds for verdicts: a verdict's confidence tracks how much evidence prose you
15
+ wrote, not what you checked — a checkbox ticked from memory is a guess wearing a PASS. The arc
16
+ at the gate:
17
+
18
+ - **GROUND** — every Part-one checkbox is a factual claim: tick it only `[OBSERVED]` — you ran
19
+ the suite / read the evidence THIS session; a remembered pass is `[PRIOR]` — re-run it, never
20
+ recall it. A safety-grep counts only once you confirmed it actually matched.
21
+ - **ATTACK** — verify IS the arc's attack beat: the earned-green refute-read (Part four) whose
22
+ primary output is a concrete falsifying input, and the three lenses of Part two. Security
23
+ stays HARD-STOP.
24
+ - **DELIVER** — the gate card leads with the outcome, exposes the residue yourself
25
+ lowest-confidence-first, and tags its claims by evidence basis — never a bare "all green".
26
+ - **Floor Goal check** — before recording PASS, restate the goal in the human's world: a suite
27
+ that satisfies the §4 words but misses what they actually wanted is the most expensive miss,
28
+ and this gate is the last place to catch it.
29
+ - **Constraint loop on the §6 record** — the record blocks (3-lens verdict · Deep checks ·
30
+ Refute-read verdict · `Reported:`) are mechanically checkable: sweep them as a census before
31
+ the gate — an unfilled block is an unrecorded verdict, caught by you, not by the spot-audit.
32
+
12
33
  ## Part one — confirm the evidence
13
34
 
14
35
  - [ ] All tests pass — or, for a non-coding task, every §4 acceptance check is green (the evidence it names is real).
15
36
  - [ ] Coverage did not decrease.
16
37
  - [ ] No test or contract was altered during build.
17
38
  - [ ] The §3 Target (measurable) is hit — including any declared outcome tests can't show, confirmed by real evidence.
18
- - [ ] §1 rules trace to §2/§4 — an untraced rule is a coverage gap (`add.py check` warns on it).
39
+ - [ ] §1 rules trace to a §4 test (`covers:` tag) — an untraced rule is a coverage gap (`add.py check` warns on it).
19
40
  - [ ] every §3-cited symbol still resolves in the CURRENT tree.
20
41
 
21
42
  If any is false, stop and return to Build.
@@ -73,7 +94,7 @@ python3 .add/tooling/add.py gate PASS # marks the task done
73
94
  Verify owns the loop's tail since the six-phase merge. After the gate, fill §7:
74
95
 
75
96
  1. **Release behind a scope-of-impact limit** — a flag and/or gradual rollout.
76
- 2. **Reuse scenarios as monitors** — the §2 scenarios that defined "correct" define
97
+ 2. **Reuse scenarios as monitors** — the §4 scenarios/tests that defined "correct" define
77
98
  what you alert on: overall error rate, each rejection's rate, latency of the risky op.
78
99
  3. **Draft the next spec delta** — every defect, surprise, or new need becomes a change
79
100
  that re-enters the flow at Specify (a new task). Emit lessons tagged by the