rcf-lite 0.9.0 → 0.10.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +86 -54
  2. package/README.md +8 -4
  3. package/bin/rcf.js +145 -57
  4. package/fixtures/canary-manifest.json +9 -9
  5. package/guidance/README.md +1 -1
  6. package/guidance/build-cycle-playbook.md +51 -51
  7. package/guidance/build-cycle.md +7 -7
  8. package/guidance/document-model.md +1 -1
  9. package/guidance/elicitation-playbook.md +29 -29
  10. package/guidance/harness-template.md +10 -10
  11. package/guidance/managed/README.md +1 -1
  12. package/guidance/managed/agent-instructions-block.hash +1 -1
  13. package/guidance/managed/agent-instructions-block.md +9 -9
  14. package/guidance/manifest.json +1 -1
  15. package/guidance/overview.md +4 -4
  16. package/package.json +4 -6
  17. package/rcf/adrs/adr-008.json +1 -1
  18. package/rcf/adrs/adr-009.json +4 -4
  19. package/rcf/code-nodes/cn-016.json +1 -1
  20. package/rcf/code-nodes/cn-019.json +1 -1
  21. package/rcf/code-nodes/cn-020.json +1 -1
  22. package/rcf/code-nodes/cn-021.json +1 -1
  23. package/rcf/code-nodes/cn-022.json +1 -1
  24. package/rcf/code-nodes/cn-049.json +1 -1
  25. package/rcf/code-nodes/cn-055.json +1 -1
  26. package/rcf/code-nodes/cn-057.json +5 -5
  27. package/rcf/code-nodes/cn-061.json +1 -1
  28. package/rcf/code-nodes/cn-066.json +1 -1
  29. package/rcf/code-nodes/cn-069.json +1 -1
  30. package/rcf/fbs/fbs-005.json +1 -1
  31. package/rcf/fbs/fbs-006.json +2 -2
  32. package/rcf/fbs/fbs-007.json +1 -1
  33. package/rcf/fbs/fbs-014.json +1 -1
  34. package/rcf/fbs/fbs-015.json +9 -8
  35. package/rcf/fbs/fbs-016.json +1 -1
  36. package/rcf/fbs/fbs-017.json +1 -1
  37. package/rcf/fbs/fbs-018.json +2 -2
  38. package/rcf/requirements/req-008.json +1 -1
  39. package/rcf/requirements/req-009.json +2 -2
  40. package/rcf/requirements/req-010.json +1 -1
  41. package/rcf/test-suites/PENDING.md +2 -2
  42. package/rcf/test-suites/ts-004.json +1 -1
  43. package/rcf/test-suites/ts-006.json +3 -3
  44. package/rcf/test-suites/ts-008.json +2 -2
  45. package/rcf/test-suites/ts-009.json +2 -2
  46. package/rcf/test-suites/ts-017.json +1 -1
  47. package/rcf/test-suites/ts-024.json +1 -1
  48. package/rcf/test-suites/ts-025.json +32 -18
  49. package/rcf/test-suites/ts-026.json +1 -1
  50. package/rcf/test-suites/ts-027.json +7 -7
  51. package/rcf/test-suites/ts-028.json +1 -1
  52. package/rcf/user-stories/us-1001.json +10 -10
  53. package/rcf/user-stories/us-1002.json +12 -12
  54. package/rcf/user-stories/us-1003.json +6 -6
  55. package/rcf/user-stories/us-805.json +2 -2
  56. package/rcf/user-stories/us-901.json +13 -13
  57. package/src/blueprint/conflicts.js +15 -15
  58. package/src/blueprint/supersede.js +6 -6
  59. package/src/build/bundle.js +3 -3
  60. package/src/build/formatters/markdown.js +9 -9
  61. package/src/build/mark.js +3 -3
  62. package/src/build/queue.js +1 -1
  63. package/src/cli/blueprint.js +3 -3
  64. package/src/cli/browser-verify.js +1 -1
  65. package/src/cli/build.js +139 -69
  66. package/src/cli/coverage.js +1 -1
  67. package/src/cli/create.js +3 -3
  68. package/src/cli/delete.js +2 -2
  69. package/src/cli/design.js +10 -10
  70. package/src/cli/fbs.js +1 -1
  71. package/src/cli/finalise.js +22 -20
  72. package/src/cli/help.js +267 -95
  73. package/src/cli/impact.js +1 -1
  74. package/src/cli/intake.js +3 -3
  75. package/src/cli/link.js +3 -3
  76. package/src/cli/preflight.js +2 -2
  77. package/src/cli/read.js +1 -1
  78. package/src/cli/req-baseline.js +2 -2
  79. package/src/cli/req-classify.js +3 -3
  80. package/src/cli/review.js +1 -1
  81. package/src/cli/standards.js +1 -1
  82. package/src/cli/test-suite.js +1 -1
  83. package/src/cli/trace.js +1 -1
  84. package/src/cli/ui-baseline.js +3 -3
  85. package/src/cli/ui-classify.js +4 -4
  86. package/src/cli/update.js +2 -2
  87. package/src/cli/validate.js +2 -2
  88. package/src/cli/view.js +12 -10
  89. package/src/core/store/writer.js +1 -1
  90. package/src/design/writer.js +3 -3
  91. package/src/finalise/detect.js +32 -38
  92. package/src/finalise/index.js +0 -1
  93. package/src/finalise/install.js +9 -8
  94. package/src/finalise/spawn.js +14 -10
  95. package/src/mcp/tools.js +1 -1
  96. package/src/req-baseline/gate.js +1 -1
  97. package/src/ui-baseline/manifest-writer.js +2 -2
  98. package/src/verify/cli/cleanup.js +1 -1
  99. package/src/verify/cli/mcp.js +1 -1
  100. package/src/verify/cli/provision.js +1 -1
  101. package/src/verify/cli/report.js +1 -1
  102. package/src/verify/cli/run.js +1 -1
  103. package/src/view-supervisor/manifest-writer.js +2 -2
  104. package/bin/rcf-verify.js +0 -122
  105. package/src/verify/cli/help.js +0 -56
package/CHANGELOG.md CHANGED
@@ -4,6 +4,38 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Pre-1.0, breaking changes are signalled by a minor version bump.
6
6
 
7
+ ## [0.10.0] - 2026-08-26
8
+
9
+ The CLI is reorganised around the five RCF tool groups. Every previously-shipped verb keeps its handler; only the invocation shape changes. Clean break: no aliases, no deprecation shims, no back-compat fallbacks. The legacy standalone `rcf-verify` bin is deleted; the verify suite lives at `rcf verify <verb>` inside the umbrella `rcf` CLI.
10
+
11
+ ### Changed (BREAKING)
12
+
13
+ Every top-level verb is now grouped. Old flat forms exit 2 with a stderr hint at the new grouped form (a usage-error affordance, not a fallback; the invocation still fails). Highlights:
14
+
15
+ - **discover** (six verbs): `rcf intake` -> `rcf discover intake`; likewise for `preflight`, `req-classify`, `req-baseline`, `ui-classify`, `ui-baseline`.
16
+ - **define** (ten verbs): `rcf create` -> `rcf define create`; likewise for `read`, `update`, `delete`, `link`, `unlink`, `validate`, `design`, `blueprint`, `standards`.
17
+ - **build** (mode collapse into explicit sub-verbs): `rcf build` -> `rcf build queue`; `rcf build <fbs-id>` -> `rcf build bundle <fbs-id>` (or `bundle --next`); `rcf build <fbs-id> --mark <status>` -> `rcf build mark <fbs-id> <status>`. Plus `rcf finalise` -> `rcf build finalise`; `rcf review` -> `rcf build review`; `rcf fbs` -> `rcf build fbs`; `rcf test-suite` -> `rcf build test-suite`.
18
+ - **verify** (six verbs): `rcf verify <run|report|provision|cleanup|mcp>` stays as-is, now inside the grouped CLI. `rcf browser-verify <fbs-id>` -> `rcf verify browser <fbs-id>`.
19
+ - **audit** (four verbs): `rcf view` -> `rcf audit view`; `rcf coverage` -> `rcf audit coverage`; `rcf trace` -> `rcf audit trace`; `rcf impact` -> `rcf audit impact`.
20
+ - **core** (five verbs, unchanged): `rcf init`, `rcf doctor`, `rcf guidance`, `rcf mcp`, `rcf help` stay at the top level.
21
+
22
+ ### Removed
23
+
24
+ - `bin/rcf-verify.js` is deleted from the package. `package.json:bin` now exposes only `rcf`. The 0.7.1 transition-grace alias is gone.
25
+ - The legacy `@stravica-ai/rcf-verify-lite` package-resolution fallback path in the finalise gate's install detection is deleted; only `rcf-lite` resolves.
26
+
27
+ ### Unchanged
28
+
29
+ - MCP tool names (`rcf_validate`, `rcf_coverage`, `rcf_trace`, `rcf_impact`, `rcf_read`, `rcf_create`, `rcf_update`, `rcf_delete`, `rcf_link`, `rcf_unlink`, `rcf_build`) do NOT move in step with the CLI. Agent-client consumers keep their tool names; the reasoning is in the reorganisation spec (`2026-08-26_cli-group-design.md`).
30
+ - Resource URIs (`rcf://tree`, `rcf://doc/<id>`, `rcf://docs/<slug>`, `rcf://guidance/<slug>`) are unaffected.
31
+ - Per-verb handler semantics: every handler retains its previous behaviour; only the argv layer moved.
32
+
33
+ ### Migration
34
+
35
+ - Rewrite every shell script and doc that invokes the CLI to the grouped form. The dispatcher's hint on the old form names the new form; scripting against those hint messages is unsupported.
36
+ - `rcf build finalise` launches `rcf verify run` as a fresh subprocess (was `rcf-verify run`); if you scripted the finalise subprocess invocation independently, prepend the `verify` group token.
37
+ - The old `.rcf-verify-report.json` filename is retained as the default `--out` for `rcf build finalise` so existing artefact readers keep working.
38
+
7
39
  ## [0.9.0] - 2026-08-25
8
40
 
9
41
  The blueprint-mechanism release. Ships Phase 1 blueprint composition (standards + selective retrieval), Phase 3.5 conflict-resolution verbs, the 0.4.4 prefix-family id grammar seam, and the first content packages built on the mechanism: SPA (v1.0.0 through v1.3.0 including the styled-under-CSP and deployment-gate cures), auth (v1.0.0 magic-link + opaque cookie sessions), persistence (v1.0.0 SQLite-first store), ci-pipeline (v1.0.0), observability (v1.0.0), and REST (16 REQ / 20 US / 117 AC doc set). Consumes `@stravica-ai/rcf-schemas@0.4.5` (exact pin, additive-only from 0.4.3 through 0.4.5).
@@ -31,11 +63,11 @@ Detailed entries below cover the Phase 3.5 verbs (PR #90). The following merges
31
63
  ### Added (blueprint conflict-resolution verbs, w-2026-08-19-008)
32
64
 
33
65
  - **`src/blueprint/resolutions.js`.** Pure helpers on top of the new `manifest.resolutions[]` seam (rcf-schemas 0.4.5): `nextResolutionId(manifest, now)` mints monotonic `res-YYYY-MM-DD-NNN` ids; `matchingResolution(manifest, { topic, incoming, existing })` returns the resolution record that resolves a specific globalAdrTopic conflict pair, matching on kind + topic + both `{slug, adrId}` pairs listed in `supersedes[]` (superset OK, subset not).
34
- - **`src/blueprint/supersede.js` + `rcf blueprint supersede <topic>` verb.** Scaffolds a project-level ADR at `rcf/adrs/adr-NNN-<topic>.json` (well-formed against `adr.schema.json`, editable stubs) that supersedes every applied blueprint's scope:global ADR on the topic, and appends a `manifest.resolutions[]` record so the detector honours the resolution on subsequent `rcf blueprint add` runs. Refuses when the topic has fewer than two applied scope:global ADRs, when the manifest is missing prd or tad, and when a file already exists at the scaffolded ADR path. Optional `--reason` operator note; whitespace-only reasons are refused at the writer edge.
35
- - **`rcf blueprint add --resolve <topic>=project:<ADR-id>` flag.** Repeatable per conflicted topic. Records a `manifest.resolutions[]` entry in-memory BEFORE conflict detection runs, so a freshly declared resolution is honoured; the record then persists via the manifest write alongside the applied-blueprint update. Refuses declarations whose topic does not match a scope:global ADR on the incoming blueprint, whose topic has no matching applied blueprint, whose `resolvedByAdrId` is mis-shaped, or whose `--reason` is whitespace-only.
66
+ - **`src/blueprint/supersede.js` + `rcf define blueprint supersede <topic>` verb.** Scaffolds a project-level ADR at `rcf/adrs/adr-NNN-<topic>.json` (well-formed against `adr.schema.json`, editable stubs) that supersedes every applied blueprint's scope:global ADR on the topic, and appends a `manifest.resolutions[]` record so the detector honours the resolution on subsequent `rcf define blueprint add` runs. Refuses when the topic has fewer than two applied scope:global ADRs, when the manifest is missing prd or tad, and when a file already exists at the scaffolded ADR path. Optional `--reason` operator note; whitespace-only reasons are refused at the writer edge.
67
+ - **`rcf define blueprint add --resolve <topic>=project:<ADR-id>` flag.** Repeatable per conflicted topic. Records a `manifest.resolutions[]` entry in-memory BEFORE conflict detection runs, so a freshly declared resolution is honoured; the record then persists via the manifest write alongside the applied-blueprint update. Refuses declarations whose topic does not match a scope:global ADR on the incoming blueprint, whose topic has no matching applied blueprint, whose `resolvedByAdrId` is mis-shaped, or whose `--reason` is whitespace-only.
36
68
  - **`src/blueprint/conflicts.js` reshape.** `detectGlobalAdrConflicts` accepts an optional manifest and honours matching resolutions. `renderConflictReport` now leads each conflict block with per-side `blueprint <slug>: <title> — <decision>` headers (falls back to `<id> at <path>` when the tree did not carry the ADR body), footers ids + paths under `refs:`, and names four honest resolution paths (adopt-incoming, keep-existing, supersede via project ADR, declare-on-add via `--resolve`) with the actual conflicting blueprint slugs already substituted — no `<slug>` placeholders. New `conflictReportJson(conflicts)` returns the same shape as a machine-readable object for agent-driven composition.
37
69
  - **`src/blueprint/apply.js` enrichment.** ADR title + one-sentence decision are pulled off `tree.byId` for the existing side and threaded onto the conflict record so the renderer's per-side headers render cleanly.
38
- - **`src/blueprint/diff.js` + `rcf blueprint diff <topic>` verb.** Read-only side-by-side view of every applied blueprint's scope:global ADR on a topic: id, path, title, status, decision.
70
+ - **`src/blueprint/diff.js` + `rcf define blueprint diff <topic>` verb.** Read-only side-by-side view of every applied blueprint's scope:global ADR on a topic: id, path, title, status, decision.
39
71
  - **`src/cli/blueprint.js` wiring.** New verbs `supersede`, `diff`; new flags `--resolve` (repeatable), `--reason`, `--json` on `add`.
40
72
  - **`src/blueprint/index.js` re-exports** the new APIs: `nextResolutionId`, `matchingResolution`, `supersedeBlueprintTopic`, `diffBlueprintTopic`, `renderDiff`, `conflictReportJson`, `detectCrossBlueprintClaims`.
41
73
  - **New tests.** `test/blueprint/resolutions.test.js` (10), `test/blueprint/supersede.test.js` (5), `test/blueprint/coresidence.test.js` (3 — the SPA + REST co-residence money paths via supersede-first, declare-on-add, and honour-idempotency), `test/blueprint/diff.test.js` (4), plus 5 new cases on `test/blueprint/cli.test.js` (--resolve success + reshape refuse, --json refuse + apply, supersede verb, diff verb, --resolve validation) and 2 updated cases on `test/blueprint/conflicts.test.js` (reshape assertions).
@@ -51,7 +83,7 @@ Detailed entries below cover the Phase 3.5 verbs (PR #90). The following merges
51
83
 
52
84
  - **`src/verify/chain/index.js` (`readChain`) surfaces AC `scope` and per-TC `boundTcs`.** Every flattened AC now carries the schema-declared scope tag (or `undefined` for bootstrap-era ACs) plus a `boundTcs[]` list of `{ tsId, tcId, scope }` for every bound TC. Verify's downstream verdict layer runs the scope check off this shape, matching the shape the admissibility lint's `scanTcScopeVsAc` consumes at build stage.
53
85
  - **`src/verify/verdict/index.js` (`scopePerAcVerdict`, extended `derivePerAcVerdicts`).** New `SCOPE-MISMATCH` per-AC verdict (NV-BL-GATE-01 + NV-BL-ADM-03). Fires when an AC declares a scope tag AND every bound TC's scope is narrower than the AC's scope AND at least one bound TC declares a scope tag (bootstrap-era TCs without a tag are silent -- the admissibility lint handles that class). At least one TC at or wider than the AC scope clears the mismatch (a wider TC is always admissible). Emitted alongside the four 0.7.0 per-AC verdict classes on `report.perAcVerdicts[]`. Existing report validator (`src/verify/report/index.js`) admits the new class via the widened `PER_AC_VERDICTS` enum.
54
- - **`src/finalise/ingest.js` (`findScopeMismatchAcs`, `reportHasScopeMismatch`; extended `summariseReport`).** NV-BL-GATE-01 REVIEW-stage consumers (`rcf review <fbs-id>`, `rcf finalise`) read the same shape via new predicates. `summariseReport` renders a dedicated `scope mismatches (N)` section when the report carries any SCOPE-MISMATCH entries (silent when none, so pre-0.8.0 reports remain byte-identical in the summary output).
86
+ - **`src/finalise/ingest.js` (`findScopeMismatchAcs`, `reportHasScopeMismatch`; extended `summariseReport`).** NV-BL-GATE-01 REVIEW-stage consumers (`rcf build review <fbs-id>`, `rcf build finalise`) read the same shape via new predicates. `summariseReport` renders a dedicated `scope mismatches (N)` section when the report carries any SCOPE-MISMATCH entries (silent when none, so pre-0.8.0 reports remain byte-identical in the summary output).
55
87
  - **`src/finalise/index.js` re-exports** `findScopeMismatchAcs` and `reportHasScopeMismatch` alongside the pre-existing mock-only-declared predicates.
56
88
  - **`test/verify/chain/scope-derivation-0-8-0.test.js` (2 tests):** chain reader hands through AC.scope + per-TC scope on boundTcs; leaves AC.scope undefined when the AC has no tag.
57
89
  - **`test/verify/verdict/scope-mismatch-0-8-0.test.js` (7 tests):** narrower TC surfaces SCOPE-MISMATCH; wider or equal TC clears; deployed TC covering runtime AC is fine; AC without scope is silent; TC without scope is silent; AC with no bound TCs is silent; `derivePerAcVerdicts` emits SCOPE-MISMATCH alongside MOCK-ONLY-DECLARED on the same AC when both apply.
@@ -90,7 +122,7 @@ Four landmine fixes lifted from `w-2026-07-28-012` (design notes + d-2026-07-28-
90
122
 
91
123
  - **Landmine 1: `packages/rcf-lite/src/core/store/walker.js` (loadChildKind).** The walker used to derive tree ids by upper-casing the whole filename stem (`stem.toUpperCase()`). Lossless while every id was `<PREFIX>-<digits>`; the moment a slug lands (`fbs-004-user-login.json` -> body `fbsId: "FBS-004-user-login"`), the whole-stem fold produced `FBS-004-USER-LOGIN` in `tree.byId` / `kindById` / `parentByChild` while every inbound reference used the lower-case form. The graph silently detached. The fix (`idFromFilenameStem`) upper-cases the prefix segment only and leaves the slug tail verbatim. Regression: `test/store/walker.test.js` `walkTree preserves case on slug tails when deriving id from filename`; also asserts a valid inbound `dependsOnFbsIds` reference resolves to the byId entry.
92
124
  - **Landmine 2: `packages/rcf-lite/src/core/store/writer.js` (nextFlatId).** The allocator's high-water mark used a slug-blind regex `^${prefix}-(\d+)$`; a slugged id was invisible to the max and the allocator reset to 001, re-issuing taken numbers. The fix replaces the local regex with the shared `idNumber(id, prefix)` helper (`ids.js:72-78`, pattern `^${prefix}-(\d+)(?:-|$)`) which parses both numeric-only and slugged shapes into the same numeric key. Regression: `test/core/store/writer.test.js` `nextIdForKind sees slugged ids in the occupancy set` and `nextIdForKind never re-issues a slugged FBS number even without a companion numeric-only FBS at the same slot`.
93
- - **Landmine 3: hardcoded three-digit id patterns across seven walker/writer sites AND three consumer-path stragglers.** The moment a TS crosses 999 (rcf-schemas 0.4.3 admits `TS-1000`), the walker's `idPrefixMatchesParent` rule silently skipped every inline TC under that TS, and the writer's `nextIdForKind tc` refused with "unrecognised TS id" even though the schema admitted the parent. Widened all sites to `\d{3,}` in lockstep with the schema. Walker + writer sites (7 total): `packages/rcf-lite/src/core/store/walker.js:851/854` and `writer.js:252/908/1163` (four regex sites), plus the corresponding comments and the resolveInlineId TC branch on writer.js widened for consistency. Consumer-path stragglers (3 sites, folded in as a rework commit after the initial car 3.1 landed): `packages/rcf-lite/src/cli/test-suite.js:85` (hard-refused any TS >= 1000 in `rcf test-suite <id> {provenance|approve}` with an error text citing "TS-015"; widened + error text updated to cite both "TS-015" and "TS-1000"), `packages/rcf-lite/src/cli/read.js:122` (`rcf read TC-1000-x` silently fell through to null even when the TC existed under a widened TS; widened the inline-TC guard), and `packages/rcf-lite/src/mcp/tools.js:765` (same silent-null pattern via the MCP read tool). Regressions: `test/core/store/writer.test.js` `nextIdForKind tc admits a TS beyond 999` + `walker inline-TC prefix-match rule fires for TS beyond 999`; `test/cli/test-suite-cli.test.js` `rcf test-suite TS-1000 provenance accepts a four-digit TS id` + `rcf test-suite TS-1000 approve accepts a four-digit TS id` + `rcf test-suite refuses a non-TS positional and cites both three-digit and four-digit shapes`; `test/cli/read.test.js` `rcf read TC-1000-first-case resolves the inline TC on a widened (four-digit) TS`; `test/mcp/tools.test.js` `rcf_read: resolves an inline TC on a widened (four-digit) TS`.
125
+ - **Landmine 3: hardcoded three-digit id patterns across seven walker/writer sites AND three consumer-path stragglers.** The moment a TS crosses 999 (rcf-schemas 0.4.3 admits `TS-1000`), the walker's `idPrefixMatchesParent` rule silently skipped every inline TC under that TS, and the writer's `nextIdForKind tc` refused with "unrecognised TS id" even though the schema admitted the parent. Widened all sites to `\d{3,}` in lockstep with the schema. Walker + writer sites (7 total): `packages/rcf-lite/src/core/store/walker.js:851/854` and `writer.js:252/908/1163` (four regex sites), plus the corresponding comments and the resolveInlineId TC branch on writer.js widened for consistency. Consumer-path stragglers (3 sites, folded in as a rework commit after the initial car 3.1 landed): `packages/rcf-lite/src/cli/test-suite.js:85` (hard-refused any TS >= 1000 in `rcf build test-suite <id> {provenance|approve}` with an error text citing "TS-015"; widened + error text updated to cite both "TS-015" and "TS-1000"), `packages/rcf-lite/src/cli/read.js:122` (`rcf define read TC-1000-x` silently fell through to null even when the TC existed under a widened TS; widened the inline-TC guard), and `packages/rcf-lite/src/mcp/tools.js:765` (same silent-null pattern via the MCP read tool). Regressions: `test/core/store/writer.test.js` `nextIdForKind tc admits a TS beyond 999` + `walker inline-TC prefix-match rule fires for TS beyond 999`; `test/cli/test-suite-cli.test.js` `rcf build test-suite TS-1000 provenance accepts a four-digit TS id` + `rcf build test-suite TS-1000 approve accepts a four-digit TS id` + `rcf build test-suite refuses a non-TS positional and cites both three-digit and four-digit shapes`; `test/cli/read.test.js` `rcf define read TC-1000-first-case resolves the inline TC on a widened (four-digit) TS`; `test/mcp/tools.test.js` `rcf_read: resolves an inline TC on a widened (four-digit) TS`.
94
126
  - **Landmine 4: `packages/rcf-lite/src/core/store/writer.js` (`deriveSlug`).** Returned the literal `'tc'` on empty derivation. Every non-TC caller (FBS, CN, ADR, TAC create paths) would silently produce an id ending in `-tc` when the source description derived to empty -- wrong kind label baked in as a slug. The fix returns `''` at the source and moves the `|| 'tc'` fallback to the TC-specific callers (`writer.js` `createInlineTc`, `mcp/tools.js`, `cli/create.js`). Regression: `deriveSlug lowercases and squashes non-alphanumeric runs` updated (`!!!` now maps to `''`, not `'tc'`) and a new `0.8.0 slug-train (landmine 4): deriveSlug returns empty string, not the "tc" literal` test.
95
127
 
96
128
  ### Added (Car 2, shared standards ruleset)
@@ -107,46 +139,46 @@ The cold-run response train. Four tracks land together in one release, each clos
107
139
 
108
140
  ### Added (Track C+D, 2026-07-31)
109
141
 
110
- - **`rcf req-classify <req-id> [--all] [--json]` verb (spec §4.4).** Runs the REQ-shape classifier (`packages/build/src/req-detection/classifier.js`) against one requirement or the whole tree and writes the resulting `shapeClassification` block back onto the REQ. Deterministic keyword-scan over `title`, `description`, `rationale` plus parent PRD `intent`/`problem` as fallback context; signals from `@stravica-ai/rcf-lite-core/patterns/req-shapes` (single source of truth, `webUi` composed from Track B's `UI_SEED_PATTERNS_V1`). Multi-shape verdicts are legitimate; `[none]` records a pure business-rule REQ; `content-pending` marks a REQ whose description is empty or a TODO. Preserves any prior `operatorOverride` block on subsequent runs.
111
- - **Automatic classification on write.** `rcf create req --description "..."` and `rcf update req <id> --description|--title|--rationale ...` fire the classifier post-write; the resulting shapes line prints alongside the create/update confirmation. Best-effort - a classification error surfaces on stderr but never fails the write (the REQ was already persisted; classification is provenance layered on top).
112
- - **`rcf req-baseline <verb>` (spec §5).** Baseline-AC sweep and opt-out ledger for classified REQs. `rcf req-baseline sweep --req <id> | --all [--dry-run] [--yes] [--json] [--status]` walks every US under the target and proposes any baseline AC not yet present; interactive by default on a TTY, `--yes` accepts every candidate non-interactively. `rcf req-baseline opt-out --req <id> --key <baselineKey> --reason "..." [--scope req|project]` writes a `baselineAcOptOuts[]` entry (reason floor 20 characters per schema); `--remove` deletes an existing entry. The C+D-native verb sits alongside Track A's preflight design-shape write path so both surfaces feed the same ledger.
113
- - **Silence-refuses-build (Stage-1) gate wired into `rcf build`.** `rcf build --next` and `rcf build <fbs-id>` refuse (exit 4) when the selected FBS binds an AC on a US that still has open baseline sweep candidates. The refusal message matches spec §5.4 verbatim in shape (candidate count, per-key description, `Resolve:` line pointing at `rcf req-baseline sweep --req ...`). Open-candidate state is derived from the tree (`packages/build/src/req-baseline/open-candidates.js`); a fresh US under a classified REQ auto-surfaces its unresolved baseline keys without any explicit queue-write (spec §5.3 moment 4).
114
- - **Moment-4 signal on `rcf create us`.** Non-interactive US creation under a shape-classified REQ prints the list of baseline keys that are OPEN and names `rcf req-baseline sweep --req <id>` as the resolution command. The Stage-1 refusal is the single unbypassable gate; this signal surfaces the queue immediately rather than waiting for the operator to hit `rcf build --next`.
115
- - **`rcf intake` verb (spec §6).** Variable-fidelity intake stage that runs BEFORE elicitation-playbook §3 when the operator supplies content. Three-phase flow: classify by fidelity (`none | napkin | briefLight | briefStrong | prd | prdPlusTad`), validate + challenge with deterministic scans (`impliedButNotStated`, `contradiction`, `missingLoadBearingConstraint`), record an `intakeClassification` on the manifest. `--artefact <path>` (repeatable / comma-separated), `--kind <hint>`, `--input <config.json>` (non-interactive), `--dry-run`, `--json`. The intake-worker subagent dispatch (spec §6.6) is fenced for a follow-up ship; the deterministic scans plus operator `otherDeclared` findings via `--input` cover phase 2 in v1.
142
+ - **`rcf discover req-classify <req-id> [--all] [--json]` verb (spec §4.4).** Runs the REQ-shape classifier (`packages/build/src/req-detection/classifier.js`) against one requirement or the whole tree and writes the resulting `shapeClassification` block back onto the REQ. Deterministic keyword-scan over `title`, `description`, `rationale` plus parent PRD `intent`/`problem` as fallback context; signals from `@stravica-ai/rcf-lite-core/patterns/req-shapes` (single source of truth, `webUi` composed from Track B's `UI_SEED_PATTERNS_V1`). Multi-shape verdicts are legitimate; `[none]` records a pure business-rule REQ; `content-pending` marks a REQ whose description is empty or a TODO. Preserves any prior `operatorOverride` block on subsequent runs.
143
+ - **Automatic classification on write.** `rcf define create req --description "..."` and `rcf define update req <id> --description|--title|--rationale ...` fire the classifier post-write; the resulting shapes line prints alongside the create/update confirmation. Best-effort - a classification error surfaces on stderr but never fails the write (the REQ was already persisted; classification is provenance layered on top).
144
+ - **`rcf discover req-baseline <verb>` (spec §5).** Baseline-AC sweep and opt-out ledger for classified REQs. `rcf discover req-baseline sweep --req <id> | --all [--dry-run] [--yes] [--json] [--status]` walks every US under the target and proposes any baseline AC not yet present; interactive by default on a TTY, `--yes` accepts every candidate non-interactively. `rcf discover req-baseline opt-out --req <id> --key <baselineKey> --reason "..." [--scope req|project]` writes a `baselineAcOptOuts[]` entry (reason floor 20 characters per schema); `--remove` deletes an existing entry. The C+D-native verb sits alongside Track A's preflight design-shape write path so both surfaces feed the same ledger.
145
+ - **Silence-refuses-build (Stage-1) gate wired into `rcf build`.** `rcf build --next` and `rcf build <fbs-id>` refuse (exit 4) when the selected FBS binds an AC on a US that still has open baseline sweep candidates. The refusal message matches spec §5.4 verbatim in shape (candidate count, per-key description, `Resolve:` line pointing at `rcf discover req-baseline sweep --req ...`). Open-candidate state is derived from the tree (`packages/build/src/req-baseline/open-candidates.js`); a fresh US under a classified REQ auto-surfaces its unresolved baseline keys without any explicit queue-write (spec §5.3 moment 4).
146
+ - **Moment-4 signal on `rcf define create us`.** Non-interactive US creation under a shape-classified REQ prints the list of baseline keys that are OPEN and names `rcf discover req-baseline sweep --req <id>` as the resolution command. The Stage-1 refusal is the single unbypassable gate; this signal surfaces the queue immediately rather than waiting for the operator to hit `rcf build --next`.
147
+ - **`rcf discover intake` verb (spec §6).** Variable-fidelity intake stage that runs BEFORE elicitation-playbook §3 when the operator supplies content. Three-phase flow: classify by fidelity (`none | napkin | briefLight | briefStrong | prd | prdPlusTad`), validate + challenge with deterministic scans (`impliedButNotStated`, `contradiction`, `missingLoadBearingConstraint`), record an `intakeClassification` on the manifest. `--artefact <path>` (repeatable / comma-separated), `--kind <hint>`, `--input <config.json>` (non-interactive), `--dry-run`, `--json`. The intake-worker subagent dispatch (spec §6.6) is fenced for a follow-up ship; the deterministic scans plus operator `otherDeclared` findings via `--input` cover phase 2 in v1.
116
148
  - **Intake-worker subagent (spec §6.6) fenced for a follow-up ship.** The reader-driven findings the spec names (a worker reads the artefact end-to-end and adds findings the pattern scan cannot) are NOT produced by any LLM path in v1. What ships now: the three deterministic scans above cover the concrete failure classes cold-run analysis §6 named (Resend-key gap, negated-login vs admin-UI contradiction, web-UI without sign-in surface), and the seam for the worker is in place - `runIntakePhases({ input })` accepts operator-declared `otherDeclared` findings via `--input`, so wiring an Opus 4.7-pinned worker later is a code addition at that merge point, not a schema or record-shape change. This mirrors the mock-canary posture in the register-regression driver: an unwired driver honestly declared over silent theatre.
117
149
  - **Register-regression canary (spec §7).** `npm run canary:register` in `packages/build/`, backed by `packages/build/scripts/canary-register.mjs` and the runner module under `packages/build/src/register-canary/`. Loads the fixture pack from `@stravica-ai/rcf-lite-core/fixtures/register-canary/*.json` (three v1 fixtures), injects the shipping build's guidance content (elicitation + build-cycle playbooks) as system context for a subagent driver, grades every first response with core's `REGISTER_CANARY_DIMENSIONS_V1` (five dimensions: `internalRuleCitation`, `unglossedJargon`, `redundantPermissionAsk`, `bypassOffer`, `wordCountBudget`), and appends one `registerCanary` record per fixture to `packages/build/fixtures/canary-manifest.json`. Exit 0 on aggregate pass, exit 4 on any fail; `--accept-fail --reason "..."` is the ship-despite-fail path for live-driver fails.
118
150
  - **Distinguishable mock-driver runs.** The MOCK subagent driver produces a durably distinguishable record from any live-driver run: `buildVersion` is suffixed `-mockdriver`, the top-level verdict is FORCED to `fail`, and `shipDespiteFailReason` is stamped with `MOCK_DRIVER_MARKER`. This mirrors Track A N-2 (unwired mutation runner returning PASS is theatre) and Track B flagged-call-2 (stub browser driver forced to warn-never-pass). `--accept-fail` is refused on mock-driver runs so a release engineer cannot paper over the mock marker; the ship-despite-fail path is for live-driver fails only. Production driver (Anthropic Agent SDK dispatch pinned to Opus 4.7 per estate ladder) is fenced for a separate ship - the runner accepts a `driverMode: 'mock' | 'live'` argument so wiring the live driver is a one-line change at the CLI seam without touching the record contract.
119
151
  - **`never-skip-RCF` platform invariant (spec §8).** The canonical paragraph lands byte-identically in three surfaces: `packages/build/guidance/elicitation-playbook.md` §11.1, `packages/build/guidance/build-cycle-playbook.md` §13 (opens the bug-triage section), and `packages/build/guidance/manifest.json` `platformInvariants[0].text`. A locking test (`packages/build/test/guidance/never-skip-rcf.test.js`) asserts byte-identity across all three; a wording edit in one surface without the same edit in the others fails CI. `rcf guidance invariants` prints the platform-invariants block from the manifest as a numbered list; editor guard-rail comments at the top of both playbooks name the release-block posture.
120
152
  - **Elicitation-playbook additions.** New §5.5 ("REQ shape and its baselines") between §5 and §6 with sub-sections §5.5.1 (five shapes) and §5.5.2 (sweep + opt-out ledger). §8 done-bar gains item 6 (every classified REQ has completed its baseline sweep). §11.1 carries the never-skip-RCF invariant paragraph verbatim. Cross-references to Track A preflight and Track B `uiBaseline.defaults`.
121
- - **Build-cycle-playbook additions.** §13 opens with the never-skip-RCF invariant paragraph verbatim (existing bug-triage text preserved after the invariant). §16 gains a cross-reference to the review-surface persistence (`rcf view start | status | stop | logs`).
122
- - **`rcf view start | status | stop | logs` sub-verbs (spec §9).** `packages/build/src/view-supervisor/` with `bin/view-supervisor-child.mjs` as the detached child entry point. `rcf view start --detach` (default on an interactive TTY; explicit `--foreground` preserves the pre-0.7.0 behaviour for non-interactive callers) forks a supervised background process via `child_process.spawn({ detached: true, stdio: 'ignore' })`, writes `reviewSurface.viewServer` to the manifest, runs a 30-second heartbeat, and installs SIGTERM/SIGINT handlers that clear the manifest record and remove the pid file on shutdown. `rcf view status` reports `running | stale | not-started` (a heartbeat older than 2× the interval or a dead pid classifies as `stale`). `rcf view stop` sends SIGTERM and waits for clean shutdown. `rcf view logs [--tail <n>]` tails the supervisor log. `--persist-until <duration|iso>` keeps the supervisor alive across foreground-session death until the named deadline: accepts durations (`4h`, `30m`, `2h30m` - matches the spec §9.2 sample) or ISO timestamps (`2026-07-31T18:00:00Z`); unrecognised values refuse (exit 2) rather than silently no-op.
153
+ - **Build-cycle-playbook additions.** §13 opens with the never-skip-RCF invariant paragraph verbatim (existing bug-triage text preserved after the invariant). §16 gains a cross-reference to the review-surface persistence (`rcf audit view start | status | stop | logs`).
154
+ - **`rcf audit view start | status | stop | logs` sub-verbs (spec §9).** `packages/build/src/view-supervisor/` with `bin/view-supervisor-child.mjs` as the detached child entry point. `rcf audit view start --detach` (default on an interactive TTY; explicit `--foreground` preserves the pre-0.7.0 behaviour for non-interactive callers) forks a supervised background process via `child_process.spawn({ detached: true, stdio: 'ignore' })`, writes `reviewSurface.viewServer` to the manifest, runs a 30-second heartbeat, and installs SIGTERM/SIGINT handlers that clear the manifest record and remove the pid file on shutdown. `rcf audit view status` reports `running | stale | not-started` (a heartbeat older than 2× the interval or a dead pid classifies as `stale`). `rcf audit view stop` sends SIGTERM and waits for clean shutdown. `rcf audit view logs [--tail <n>]` tails the supervisor log. `--persist-until <duration|iso>` keeps the supervisor alive across foreground-session death until the named deadline: accepts durations (`4h`, `30m`, `2h30m` - matches the spec §9.2 sample) or ISO timestamps (`2026-07-31T18:00:00Z`); unrecognised values refuse (exit 2) rather than silently no-op.
123
155
  - **`.rcf/view-server.pid` and `.rcf/view-server.log` gitignored** via the 0.6.0 aggregator seam (`viewServerGitignoreEntry`, `viewServerLogGitignoreEntry` exported from `packages/build/src/view-supervisor/manifest-writer.js`, folded into `managedGitignoreEntries()`). Doctor's stale-hash check catches drift; the two-file addition follows the same one-import-two-lines pattern the 0.6.0 spec ratified.
124
156
  - **`packages/build/guidance/persona-programme.md` new file (spec §10).** Ships the tail-interview template (five questions: response length + density, silent non-asks, first-impression posture, register drift, catch-all) as discoverable guidance. Registered on `guidance/manifest.json` as `slug: persona-programme`; `rcf guidance persona-programme` prints it and MCP-wired agents reach it via `rcf://docs/persona-programme`. No v1 runtime consumer wired inside build-lite; operator-side persona role definitions inherit the template in a separate ship (spec §12 seams claimed).
125
157
  - **`rcf guidance invariants` verb.** Prints the platform-invariants block from `guidance/manifest.json` as a numbered list. Documented in the `guidance --help` under a distinct "Platform-invariant printer (Track C+D)" heading (kept out of the standard topic list so the byte-faithful topic-serving contract remains intact - `invariants` is a virtual verb, not a manifest doc topic).
126
158
 
127
159
  ### Added (Track B, 2026-07-31)
128
160
 
129
- - **`rcf ui-classify <fbs-id> [--json]` verb.** Runs the UI-bearing classifier on demand against one FBS. Deterministic keyword scan over the FBS summary, in-scope AC prose, parent US iWant / soThat, ancestor REQ description / rationale, plus a `dependsOnServices[]` auth-shape signal. No writes; prints the verdict and matched signals with source anchors, and (when the verdict is `ui`) names the ratification command `rcf update <fbs-id> --set uiBearing=true`. Falls out of `rcf build --next` and `rcf build <fbs-id>` as a `[info]` line ahead of the bundle so the operator always sees the classifier's reasoning. Backed by `packages/build/src/ui-detection/classifier.js` and the shared `patterns/ui-shapes` seed set exported by `@stravica-ai/rcf-lite-core@0.3.0`. Spec sections 4.2, 4.4.
130
- - **`rcf ui-baseline <verb>` verb (init | show | opt-out).** Manages the project's ruled UI defaults captured once as a `uiBaseline` record on the manifest (monotonic id `uib-YYYY-MM-DD-NNN`). `init` runs an enter-to-accept-all summary session (or `--input <file>` non-interactive) walking the eighteen ruled defaults from spec section 6.1 (theme mode, shared layout module, design tokens module, hex-in-view-files ban, contrast target, contrast-test-before-palette, focus rings, hover states, component vocabulary, single badge shape, typography defaults, interaction defaults, auth flow); every opt-out lands on `operatorOptOuts[]` with a plain-text reason of at least twenty characters, matching the spec's "explicit never by silence" property. `--reset` appends the previous record to `uiBaselineHistory[]`. The init flow reads the preflight seam (spec section 3.2): any Track A `preFlightConfig[].designShapeAnswers` entry whose `uiBaselineWritePath` targets `defaults.*` lands as a seeded override (newest answer wins), while the corresponding `baselineAcOptOuts[]` ledger entry stays put as the durable record. `show` prints the current record; `opt-out --field <path> --reason "..."` appends an entry idempotently.
131
- - **`rcf design <fbs-id> [<sub-verb> ...]` verb.** Design substage (Stage 1.5) for UI-bearing FBSes. Sub-verbs write to the FBS's `designStage` block: `journeys add --id <slug> --actor "..." --goal "..." --step "..."` (repeated 2 to 8 times), `nav set --shape <shape> --route <path=label:auth> [--signed-in-as-affordance ...] [--notes ...]`, `theme-a11y set --mode <themeMode> --tokens <path> --contrast-test <path> --contrast-before-palette true|false`, `--mark-complete` (sets `designStageComplete: true` once all three artefacts are present and non-empty). `rcf design <fbs-id>` with no sub-verb prints the current state and names the next-move options. Positional-grammar constraint: the FBS positional MUST match `/^FBS-\d+$/`, and any positional in slot 0 that fails the pattern is a usage error, not an FBS lookup miss (spec section 5.5). Refuses any Design substage write when the FBS is uiBearing but no `uiBaseline` record exists on the manifest (spec section 6.2).
132
- - **`rcf browser-verify <fbs-id>` verb.** Stage 5 browser-verification gate for a UI-bearing FBS. Two modes: `operatorSession` records the operator's ack (no captures required; the operator's ack is the evidence per spec section 8.2), `agentScreenshotCritique` drives an injectable browser driver over every enumerated route x theme, runs the versioned `UI_INVARIANTS_V1` set (spec section 8.3 - `sharedNavPresent`, `activeNavMarked`, `signedInAsAffordance`, `themeToggleVisible` with the widened recognition set, `themeDefaultsToLight`, `focusRingsVisible`, `sharedLayoutModule` structural compare) and the auth-REQ smoke pack (`GET /login` expects 200 + text/html, `POST /logout` expects 200 / 302 / 303, `GET /login/verify?token=` refuses the empty-token accept as a regression). Writes a `browserVerification[]` record on the manifest with a monotonic per-FBS id `bv-<fbsId>-<n>` and an aggregated verdict per spec section 8.5. `--ack` clears a warn verdict; `--dry-run` prints the record without writing. The stub driver default emits zero captures and surfaces a `warn` on a synthetic `agentDriverWired` invariant so the CLI is testable without a live Playwright, and production wiring lands via `deps.browserDriver` on the CLI. Rubric surfaced on stdout so the "quite frankly the UI looked really dated" defect is visible ahead of the invariant tables. Spec sections 8, 9.
133
- - **`rcf build --mark complete` gates.** Refuses when the FBS is uiBearing and `designStageComplete` is not true (spec section 5.5), when `designStage.themeAndA11y.contrastTestAuthoredBeforePalette` is false (mandate 10, spec section 7), when the baseline disagrees with a paired `designStage` field and no `operatorOptOuts[]` entry excuses it (spec section 6.2 belt-and-braces), and when the browser-verification verdict is `block` or `warn` without operator ack. `--accept-block --reason "..."` (reason at least twenty characters) is the ship-without-verified escape hatch (spec section 8.6); the reason lands on `browserVerification.operatorShipDespiteBlockReason`. `--mark inProgress` on a uiBearing FBS with no `designStage` emits a soft `[warn]` naming `rcf design <fbs-id>` (spec section 5.2). Powered by three pure helpers on `packages/build/src/build/mark.js` (`checkDesignGate`, `checkContrastBeforePaletteGate`, existing CN gate).
134
- - **REVIEW-stage `uiBaselineDrift` audit kind.** The Track A `rcf review <fbs-id>` audit now runs the Stage-3 UI-baseline drift check alongside the test-theatre findings for uiBearing FBSes: hex literals in view files (default glob `src/ui/**` minus the tokens module, configurable via `uiBaseline.defaults.viewFileGlobs`), and route files that fail to import the shared layout module (default glob `src/routes/**`). Findings land on the same `reviewAudit[].testTheatreFindings[]` record with `kind: uiBaselineDrift` (schema enum extension in `@stravica-ai/rcf-schemas@0.4.0`), severity `block` by default and demoted to `advisory` when `operatorOptOuts[]` names the field. One brief, one worker, one record per FBS - the same audit run emits findings across Track A and Track B kinds (spec section 12 O-12). Spec section 3.4.
161
+ - **`rcf discover ui-classify <fbs-id> [--json]` verb.** Runs the UI-bearing classifier on demand against one FBS. Deterministic keyword scan over the FBS summary, in-scope AC prose, parent US iWant / soThat, ancestor REQ description / rationale, plus a `dependsOnServices[]` auth-shape signal. No writes; prints the verdict and matched signals with source anchors, and (when the verdict is `ui`) names the ratification command `rcf define update <fbs-id> --set uiBearing=true`. Falls out of `rcf build --next` and `rcf build <fbs-id>` as a `[info]` line ahead of the bundle so the operator always sees the classifier's reasoning. Backed by `packages/build/src/ui-detection/classifier.js` and the shared `patterns/ui-shapes` seed set exported by `@stravica-ai/rcf-lite-core@0.3.0`. Spec sections 4.2, 4.4.
162
+ - **`rcf discover ui-baseline <verb>` verb (init | show | opt-out).** Manages the project's ruled UI defaults captured once as a `uiBaseline` record on the manifest (monotonic id `uib-YYYY-MM-DD-NNN`). `init` runs an enter-to-accept-all summary session (or `--input <file>` non-interactive) walking the eighteen ruled defaults from spec section 6.1 (theme mode, shared layout module, design tokens module, hex-in-view-files ban, contrast target, contrast-test-before-palette, focus rings, hover states, component vocabulary, single badge shape, typography defaults, interaction defaults, auth flow); every opt-out lands on `operatorOptOuts[]` with a plain-text reason of at least twenty characters, matching the spec's "explicit never by silence" property. `--reset` appends the previous record to `uiBaselineHistory[]`. The init flow reads the preflight seam (spec section 3.2): any Track A `preFlightConfig[].designShapeAnswers` entry whose `uiBaselineWritePath` targets `defaults.*` lands as a seeded override (newest answer wins), while the corresponding `baselineAcOptOuts[]` ledger entry stays put as the durable record. `show` prints the current record; `opt-out --field <path> --reason "..."` appends an entry idempotently.
163
+ - **`rcf define design <fbs-id> [<sub-verb> ...]` verb.** Design substage (Stage 1.5) for UI-bearing FBSes. Sub-verbs write to the FBS's `designStage` block: `journeys add --id <slug> --actor "..." --goal "..." --step "..."` (repeated 2 to 8 times), `nav set --shape <shape> --route <path=label:auth> [--signed-in-as-affordance ...] [--notes ...]`, `theme-a11y set --mode <themeMode> --tokens <path> --contrast-test <path> --contrast-before-palette true|false`, `--mark-complete` (sets `designStageComplete: true` once all three artefacts are present and non-empty). `rcf define design <fbs-id>` with no sub-verb prints the current state and names the next-move options. Positional-grammar constraint: the FBS positional MUST match `/^FBS-\d+$/`, and any positional in slot 0 that fails the pattern is a usage error, not an FBS lookup miss (spec section 5.5). Refuses any Design substage write when the FBS is uiBearing but no `uiBaseline` record exists on the manifest (spec section 6.2).
164
+ - **`rcf verify browser <fbs-id>` verb.** Stage 5 browser-verification gate for a UI-bearing FBS. Two modes: `operatorSession` records the operator's ack (no captures required; the operator's ack is the evidence per spec section 8.2), `agentScreenshotCritique` drives an injectable browser driver over every enumerated route x theme, runs the versioned `UI_INVARIANTS_V1` set (spec section 8.3 - `sharedNavPresent`, `activeNavMarked`, `signedInAsAffordance`, `themeToggleVisible` with the widened recognition set, `themeDefaultsToLight`, `focusRingsVisible`, `sharedLayoutModule` structural compare) and the auth-REQ smoke pack (`GET /login` expects 200 + text/html, `POST /logout` expects 200 / 302 / 303, `GET /login/verify?token=` refuses the empty-token accept as a regression). Writes a `browserVerification[]` record on the manifest with a monotonic per-FBS id `bv-<fbsId>-<n>` and an aggregated verdict per spec section 8.5. `--ack` clears a warn verdict; `--dry-run` prints the record without writing. The stub driver default emits zero captures and surfaces a `warn` on a synthetic `agentDriverWired` invariant so the CLI is testable without a live Playwright, and production wiring lands via `deps.browserDriver` on the CLI. Rubric surfaced on stdout so the "quite frankly the UI looked really dated" defect is visible ahead of the invariant tables. Spec sections 8, 9.
165
+ - **`rcf build --mark complete` gates.** Refuses when the FBS is uiBearing and `designStageComplete` is not true (spec section 5.5), when `designStage.themeAndA11y.contrastTestAuthoredBeforePalette` is false (mandate 10, spec section 7), when the baseline disagrees with a paired `designStage` field and no `operatorOptOuts[]` entry excuses it (spec section 6.2 belt-and-braces), and when the browser-verification verdict is `block` or `warn` without operator ack. `--accept-block --reason "..."` (reason at least twenty characters) is the ship-without-verified escape hatch (spec section 8.6); the reason lands on `browserVerification.operatorShipDespiteBlockReason`. `--mark inProgress` on a uiBearing FBS with no `designStage` emits a soft `[warn]` naming `rcf define design <fbs-id>` (spec section 5.2). Powered by three pure helpers on `packages/build/src/build/mark.js` (`checkDesignGate`, `checkContrastBeforePaletteGate`, existing CN gate).
166
+ - **REVIEW-stage `uiBaselineDrift` audit kind.** The Track A `rcf build review <fbs-id>` audit now runs the Stage-3 UI-baseline drift check alongside the test-theatre findings for uiBearing FBSes: hex literals in view files (default glob `src/ui/**` minus the tokens module, configurable via `uiBaseline.defaults.viewFileGlobs`), and route files that fail to import the shared layout module (default glob `src/routes/**`). Findings land on the same `reviewAudit[].testTheatreFindings[]` record with `kind: uiBaselineDrift` (schema enum extension in `@stravica-ai/rcf-schemas@0.4.0`), severity `block` by default and demoted to `advisory` when `operatorOptOuts[]` names the field. One brief, one worker, one record per FBS - the same audit run emits findings across Track A and Track B kinds (spec section 12 O-12). Spec section 3.4.
135
167
  - **FBS view renderer surfaces Design substage state.** `packages/build/src/view/doc-renderers/fbs.js` renders a "Design substage" section with `uiBearing`, `designStageComplete`, journey count, nav shape + route count, and themeMode for any FBS whose design block is populated (or where `uiBearing: true`). Renders nothing for non-UI FBSes (byte-preserving vs the pre-Track-B layout baseline).
136
168
 
137
169
  ### Fixed (Track B review pass, 2026-07-31)
138
170
 
139
- - **B-1 blocker: `rcf build --mark complete --accept-block --reason "..."` on a uiBearing FBS with NO existing `browserVerification` record now refuses cleanly (exit 4).** The previous shape wrote the error to stderr, fell through the missing-record branch (because `if (!acceptBlock) return 4` did not short-circuit when acceptBlock was true), and marched the FBS to `complete` with only an ephemeral stderr line as evidence of the operator's ship-anyway reason. That reintroduced the exact "durable record or it did not happen" defect Track A's B-1 closed for `rcf finalise --ship-without-verified`. The fix returns 4 unconditionally in the missing-record branch and names the two-command dance in the refusal message (run `rcf browser-verify` first, then re-run `--accept-block --reason` against the resulting record so the ack anchors on a real verdict rather than inventing a synthetic one). Covered by a new dedicated CLI-level test file `packages/build/test/cli/build-browser-verify-gate.test.js` with four end-to-end tests: refuse-cleanly with `--accept-block` (the fix), refuse-cleanly without `--accept-block` (context-pin on the branch ordering), positive path with an existing block-verdict `bvRecord` (ack lands on `browserVerification.operatorShipDespiteBlockReason` as spec section 8.6 mandates), and warn-cleared-via-`--ack` context. `packages/build/src/cli/build.js:433-437`.
171
+ - **B-1 blocker: `rcf build --mark complete --accept-block --reason "..."` on a uiBearing FBS with NO existing `browserVerification` record now refuses cleanly (exit 4).** The previous shape wrote the error to stderr, fell through the missing-record branch (because `if (!acceptBlock) return 4` did not short-circuit when acceptBlock was true), and marched the FBS to `complete` with only an ephemeral stderr line as evidence of the operator's ship-anyway reason. That reintroduced the exact "durable record or it did not happen" defect Track A's B-1 closed for `rcf build finalise --ship-without-verified`. The fix returns 4 unconditionally in the missing-record branch and names the two-command dance in the refusal message (run `rcf verify browser` first, then re-run `--accept-block --reason` against the resulting record so the ack anchors on a real verdict rather than inventing a synthetic one). Covered by a new dedicated CLI-level test file `packages/build/test/cli/build-browser-verify-gate.test.js` with four end-to-end tests: refuse-cleanly with `--accept-block` (the fix), refuse-cleanly without `--accept-block` (context-pin on the branch ordering), positive path with an existing block-verdict `bvRecord` (ack lands on `browserVerification.operatorShipDespiteBlockReason` as spec section 8.6 mandates), and warn-cleared-via-`--ack` context. `packages/build/src/cli/build.js:433-437`.
140
172
  - **N-1: `packages/build/CHANGELOG.md` no longer carries the duplicate `### Fixed (Track A review pass, 2026-07-31)` heading** at line 23 (bare double-paste on the Track A pass).
141
- - **N-3: unreachable `|| bvRecord.verdict === 'warn'` disjunct dropped** from the ship-without-verified ack writer at `packages/build/src/cli/build.js:451`. The earlier guard returns 4 on `warn && !operatorAckAt`, so acceptBlock could never reach the ack path on a warn record; the disjunct was live spec-drift, not code. The sanctioned warn clear remains `rcf browser-verify --ack` (spec section 8.5).
173
+ - **N-3: unreachable `|| bvRecord.verdict === 'warn'` disjunct dropped** from the ship-without-verified ack writer at `packages/build/src/cli/build.js:451`. The earlier guard returns 4 on `warn && !operatorAckAt`, so acceptBlock could never reach the ack path on a warn record; the disjunct was live spec-drift, not code. The sanctioned warn clear remains `rcf verify browser --ack` (spec section 8.5).
142
174
  - **N-4: `preflightSeamOverrides` generalised across the design-shape catalogue.** The pickup now reads `uiBaselineWritePath` / `uiBaselineWriteValue` off the catalogue's per-choice metadata via a resolved question lookup (default `CATALOGUE_V1`, injectable for tests) rather than switching on `questionId === 'auth.htmlLoginPage'`. Any answered question whose selected choice carries a write path targeting `defaults.*` flows through the seam automatically; a future Track C+D catalogue addition needs no wiring change here. Test coverage extended with three new cases in `test/ui-baseline/preflight-seam.test.js`: a synthetic second-question catalogue that proves generality (auth + ui.themePolicy both landing), an unknown-questionId no-op, and a choice-without-writePath no-op. `packages/build/src/ui-baseline/manifest-writer.js:98-115`.
143
175
  - **N-5: `uiBaselineDrift` findings now anchor on `anchorId` instead of smuggling the FBS id through the `tsId` slot** (rcf-schemas 0.4.2 additive change). The Track A schema required `tsId` on every finding, so the drift check populated `tsId: fbs.fbsId` to satisfy the constraint; a downstream reader assuming `tsId` names a real TS would then follow a broken pointer. Fixed contract-first: `@stravica-ai/rcf-schemas@0.4.2` adds an optional `anchorId` field and relaxes `tsId` from required for `uiBaselineDrift` (still required for the five test-theatre kinds). This repo's `@stravica-ai/rcf-schemas` dep bumped from `^0.4.1` to `^0.4.2` (`packages/build/package.json`, `packages/core/package.json`). The drift writer at `packages/build/src/review/ui-baseline-drift.js:73, 102` now emits `anchorId: fbs.fbsId` (no `tsId`), with three new tests in `test/review/ui-baseline-drift.test.js` covering the hex-literal + shared-layout paths and a schema-validation smoke on a drift-only reviewAudit record. Legacy 0.4.0/0.4.1 shapes with `tsId` on drift findings remain schema-valid (back-compat is spec-tested on the schemas side).
144
176
  - **N-6: two em-dashes in newly-added Track B code comments swapped for ASCII punctuation** at `packages/build/src/cli/review.js:129, 234` (estate baseline: no em-dashes in any new prose, code comments included).
145
177
 
146
178
  ### Fixed (Track A review pass, 2026-07-31)
147
179
 
148
- - **B-1 blocker: `rcf finalise --ship-without-verified` now records the operator ack on the manifest.** Previously the ack landed on stdout only, which is neither durable across sessions nor greppable at ship time. Ships a new `manifest.shipWithoutVerified[]` array (schema addition in `@stravica-ai/rcf-schemas` 0.4.1, this repo's `@stravica-ai/rcf-schemas` dep bumped from `^0.3.1`/`^0.4.0` to `^0.4.1` accordingly) with a monotonic per-FBS id `swv-<fbsId>-<n>`, the `fbsId`, `ackedAt`, the acknowledged per-AC verdicts (`MOCK-ONLY-DECLARED` or `BLOCKED-BY-DECLARATION` only), and the report path. The finalise confirmation line now names the ack id so an operator can grep the manifest for a specific record. Wired via a new `packages/build/src/finalise/ship-without-verified.js` module with the same atomic-write discipline the preflight writer uses; on manifest validation failure the on-disk manifest stays untouched and finalise exits 3. Spec section 5.2.
149
- - **N-1: `rcf build --next` emits the preflight warning the elicitation and build-cycle playbooks already advertised.** When the selected FBS carries `dependsOnServices[]` entries whose service `id` is not named in any `preFlightConfig[].servicesInScope[].id`, the CLI prints one `[warn]` line naming the unbacked services and pointing at `rcf preflight`. Warn only (not exit 4): the Stage 1 warn-only ruling stands, the hard refuse still lives on `coverage --strict` at Stage 4. Powered by a new `scanUnbackedServices(tree, fbsId)` helper in `packages/build/src/query/attestation.js`.
180
+ - **B-1 blocker: `rcf build finalise --ship-without-verified` now records the operator ack on the manifest.** Previously the ack landed on stdout only, which is neither durable across sessions nor greppable at ship time. Ships a new `manifest.shipWithoutVerified[]` array (schema addition in `@stravica-ai/rcf-schemas` 0.4.1, this repo's `@stravica-ai/rcf-schemas` dep bumped from `^0.3.1`/`^0.4.0` to `^0.4.1` accordingly) with a monotonic per-FBS id `swv-<fbsId>-<n>`, the `fbsId`, `ackedAt`, the acknowledged per-AC verdicts (`MOCK-ONLY-DECLARED` or `BLOCKED-BY-DECLARATION` only), and the report path. The finalise confirmation line now names the ack id so an operator can grep the manifest for a specific record. Wired via a new `packages/build/src/finalise/ship-without-verified.js` module with the same atomic-write discipline the preflight writer uses; on manifest validation failure the on-disk manifest stays untouched and finalise exits 3. Spec section 5.2.
181
+ - **N-1: `rcf build --next` emits the preflight warning the elicitation and build-cycle playbooks already advertised.** When the selected FBS carries `dependsOnServices[]` entries whose service `id` is not named in any `preFlightConfig[].servicesInScope[].id`, the CLI prints one `[warn]` line naming the unbacked services and pointing at `rcf discover preflight`. Warn only (not exit 4): the Stage 1 warn-only ruling stands, the hard refuse still lives on `coverage --strict` at Stage 4. Powered by a new `scanUnbackedServices(tree, fbsId)` helper in `packages/build/src/query/attestation.js`.
150
182
  - **N-2: default mutation-sampling runner (`mode: 'agent-v1-not-wired'`) now aggregates to `warn`, not `pass`.** An unwired runner was indistinguishable at the exit-code layer from a wired runner that killed every mutant, both landing `verdict: pass`. Warn forces the operator to wire a runner or pass `--skip-mutation` (mode `skipped`), which remains `pass` as an explicit operator choice. `aggregateVerdict` in `packages/build/src/review/index.js`; a `block` finding still wins over the warn.
151
183
  - **N-3: `coverage --strict` warns when a preFlightConfig service has empty `affectedFbsIds`.** `findAttestationMissing` intentionally skips services without the back-reference (there is nothing to cross-check), but the operator saw no signal that the service was uncovered by the strict detector. One `[warn]` line per empty back-reference, additive (never turns a passing strict run into a failing one). New `findServicesWithEmptyAffectedFbsIds(tree)` helper alongside the other detectors.
152
184
  - **N-5: `notShipped × mixed` matrix row now covered.** The `mixed` short-circuit runs before any attestation branch, so `notShipped × mixed` refuses like every other `× mixed` cell. Added to the MATRIX loop at `test/query/attestation.test.js` with a comment explaining why the anti-pattern discipline wins over the "notShipped does not gate ship" rule.
@@ -156,26 +188,26 @@ Total across the review pass: +17 tests on top of the Track A baseline (build su
156
188
 
157
189
  ### Added
158
190
 
159
- - **`rcf preflight` verb - elicit the pre-flight service-attestation record and any applicable design-shape answers before the build cycle starts.** Scans the PRD (and optionally a TAD) via the shared `patterns/services` seed set exported from `@stravica-ai/rcf-lite-core@0.3.0`, surfaces per-service candidates with provenance (doc id + section anchor + matched phrase), and forces one of the five attestation modes per candidate (`live` / `sandboxed` / `mocked` / `declaredMockOnly` / `notShipped`). Interactive by default when running on a TTY; non-interactive mode via `--input <file>` for CI and automation. The session writes a `preFlightConfig[]` record onto the manifest, monotonic id `pfc-YYYY-MM-DD-NNN`. Operator-added candidates are supported with zero source refs (`schemas@0.4.0` minItems:0). Credentials NEVER enter the chain: the session prompts for env-var names only and writes name-metadata plus a presence boolean to `.rcf/preflight-secrets.local.json`, the credentials side-file whose gitignore path rides the 0.6.0 managed-block aggregator seam as a genuine two-line extension (one import from `../preflight/secrets.js` and one array entry in `managedGitignoreEntries()`, exactly the shape the 0.6.0 spec §4.1 D-4 promised).
191
+ - **`rcf discover preflight` verb - elicit the pre-flight service-attestation record and any applicable design-shape answers before the build cycle starts.** Scans the PRD (and optionally a TAD) via the shared `patterns/services` seed set exported from `@stravica-ai/rcf-lite-core@0.3.0`, surfaces per-service candidates with provenance (doc id + section anchor + matched phrase), and forces one of the five attestation modes per candidate (`live` / `sandboxed` / `mocked` / `declaredMockOnly` / `notShipped`). Interactive by default when running on a TTY; non-interactive mode via `--input <file>` for CI and automation. The session writes a `preFlightConfig[]` record onto the manifest, monotonic id `pfc-YYYY-MM-DD-NNN`. Operator-added candidates are supported with zero source refs (`schemas@0.4.0` minItems:0). Credentials NEVER enter the chain: the session prompts for env-var names only and writes name-metadata plus a presence boolean to `.rcf/preflight-secrets.local.json`, the credentials side-file whose gitignore path rides the 0.6.0 managed-block aggregator seam as a genuine two-line extension (one import from `../preflight/secrets.js` and one array entry in `managedGitignoreEntries()`, exactly the shape the 0.6.0 spec §4.1 D-4 promised).
160
192
 
161
- - **Design-shape sub-surface inside `rcf preflight` (ADDENDUM §A).** The v1 catalogue carries exactly one question: `auth.htmlLoginPage` ("HTML login page or API-only?"), posed against any REQ whose `shapeClassification.shapes[]` contains `auth` (or, when the classifier has not yet run on the tree, against any REQ whose title / description / rationale prose fires the shared `matchReqShapeSignals` matcher on the auth patterns). An "HTML login page" answer records a `designShapeAnswers[]` entry; an "API-only" answer additionally writes a `baselineAcOptOuts[]` entry with a `linkedPreFlightConfigRef` pointing at the pre-flight record's id plus the question path, so Track C+D's opt-out ledger inherits the ruling without a separate verb. The `uiBaseline.defaults.authFlow.htmlLoginPageRequired` write is fenced pending Track B; the linked-ref is the seam Track B will read when its train-car lands.
193
+ - **Design-shape sub-surface inside `rcf discover preflight` (ADDENDUM §A).** The v1 catalogue carries exactly one question: `auth.htmlLoginPage` ("HTML login page or API-only?"), posed against any REQ whose `shapeClassification.shapes[]` contains `auth` (or, when the classifier has not yet run on the tree, against any REQ whose title / description / rationale prose fires the shared `matchReqShapeSignals` matcher on the auth patterns). An "HTML login page" answer records a `designShapeAnswers[]` entry; an "API-only" answer additionally writes a `baselineAcOptOuts[]` entry with a `linkedPreFlightConfigRef` pointing at the pre-flight record's id plus the question path, so Track C+D's opt-out ledger inherits the ruling without a separate verb. The `uiBaseline.defaults.authFlow.htmlLoginPageRequired` write is fenced pending Track B; the linked-ref is the seam Track B will read when its train-car lands.
162
194
 
163
- - **`rcf fbs <fbs-id> depends-on --service <id> --mode <mode> --acs <acIds>` verb.** Writes a `dependsOnServices[]` entry on the FBS at Define stage; idempotent by service id (a second call replaces the entry). Refuses `--acs` that reference an AC not on the FBS (bypass with `--force` for repair paths). `--preflight <pfc-id>` expands a bare pre-flight id into the composite `preFlightRef` (`pfc-...#services.<id>`) so the FBS binding traces back to the pre-flight ruling.
195
+ - **`rcf build fbs <fbs-id> depends-on --service <id> --mode <mode> --acs <acIds>` verb.** Writes a `dependsOnServices[]` entry on the FBS at Define stage; idempotent by service id (a second call replaces the entry). Refuses `--acs` that reference an AC not on the FBS (bypass with `--force` for repair paths). `--preflight <pfc-id>` expands a bare pre-flight id into the composite `preFlightRef` (`pfc-...#services.<id>`) so the FBS binding traces back to the pre-flight ruling.
164
196
 
165
- - **`rcf test-suite <ts-id> provenance --profile <profile> [--tc <tc-id>] [--env-var VAR ...] [--host host ...] [--notes ...]` verb.** Records `runtimeProvenance` on one TC or on every TC in the TS. Refuses to overwrite an existing block without `--force`. Refuses `--notes` that look like they contain a token or secret (a belt-and-braces grep, not a real secret scanner; the notes field is prose-only, never quotes credential material).
197
+ - **`rcf build test-suite <ts-id> provenance --profile <profile> [--tc <tc-id>] [--env-var VAR ...] [--host host ...] [--notes ...]` verb.** Records `runtimeProvenance` on one TC or on every TC in the TS. Refuses to overwrite an existing block without `--force`. Refuses `--notes` that look like they contain a token or secret (a belt-and-braces grep, not a real secret scanner; the notes field is prose-only, never quotes credential material).
166
198
 
167
- - **`rcf test-suite <ts-id> approve [--force]` verb, plus opt-in `rcf coverage --strict --require-approved` CI gate.** Writes `authoringStatus: approved` on the TS. Refuses to promote a `superseded` or `needsRevision` TS without `--force`. Stage-4 rule in the build-cycle playbook wires this into the automatic end-of-stage promotion; the CI gate refuses any TS still `draft` after Stage 4.
199
+ - **`rcf build test-suite <ts-id> approve [--force]` verb, plus opt-in `rcf audit coverage --strict --require-approved` CI gate.** Writes `authoringStatus: approved` on the TS. Refuses to promote a `superseded` or `needsRevision` TS without `--force`. Stage-4 rule in the build-cycle playbook wires this into the automatic end-of-stage promotion; the CI gate refuses any TS still `draft` after Stage 4.
168
200
 
169
- - **`rcf coverage --strict` extended with the §3.5 attestation × profile matrix.** Three additional exit-4 refusal classes on top of the existing per-AC coverage gate: (a) attestation missing (FBSes listed in a pre-flight record's `affectedFbsIds` but with no matching `dependsOnServices` entry - v1 stays honest and skips services whose `affectedFbsIds` is empty), (b) runtime provenance missing (a TC covers an AC that binds a service but has no `runtimeProvenance` block), (c) attestation drift (a TC's profile contradicts its AC's aggregated attestation per the matrix - `live × mock` / `sandboxed × fixture` / `mixed × anything` all refuse). `declaredMockOnly × live` is recorded as `passWithWarn` and surfaced by the review-stage audit rather than blocking coverage.
201
+ - **`rcf audit coverage --strict` extended with the §3.5 attestation × profile matrix.** Three additional exit-4 refusal classes on top of the existing per-AC coverage gate: (a) attestation missing (FBSes listed in a pre-flight record's `affectedFbsIds` but with no matching `dependsOnServices` entry - v1 stays honest and skips services whose `affectedFbsIds` is empty), (b) runtime provenance missing (a TC covers an AC that binds a service but has no `runtimeProvenance` block), (c) attestation drift (a TC's profile contradicts its AC's aggregated attestation per the matrix - `live × mock` / `sandboxed × fixture` / `mixed × anything` all refuse). `declaredMockOnly × live` is recorded as `passWithWarn` and surfaced by the review-stage audit rather than blocking coverage.
170
202
 
171
- - **`rcf review <fbs-id>` verb - REVIEW-stage test-theatre audit + mutation-sampling coordinator.** Runs four deterministic detectors (mockOnlyIntegrationClaim, testPointerBroken, acIdsCoverageDrift, attestationDrift-as-otherDeclared) plus the mutation-sampling agent dispatch. The mutation runner is injected via `deps.mutationRunner`; the CLI default emits a valid schema record with a `notes` explaining that no runner was wired, so the reviewAudit record always validates. `--skip-mutation` records the skip explicitly. `--dry-run` runs the audit without writing. Writes a `reviewAudit[]` record on the manifest with a monotonic per-FBS id `ra-<fbsId>-<n>`, aggregates a verdict (block if any finding severity is block or any mutation survivor exists; warn if any warn; pass otherwise), and exits 0 on pass or 4 on warn / block, so the audit is a Stage 3 to Stage 4 gate.
203
+ - **`rcf build review <fbs-id>` verb - REVIEW-stage test-theatre audit + mutation-sampling coordinator.** Runs four deterministic detectors (mockOnlyIntegrationClaim, testPointerBroken, acIdsCoverageDrift, attestationDrift-as-otherDeclared) plus the mutation-sampling agent dispatch. The mutation runner is injected via `deps.mutationRunner`; the CLI default emits a valid schema record with a `notes` explaining that no runner was wired, so the reviewAudit record always validates. `--skip-mutation` records the skip explicitly. `--dry-run` runs the audit without writing. Writes a `reviewAudit[]` record on the manifest with a monotonic per-FBS id `ra-<fbsId>-<n>`, aggregates a verdict (block if any finding severity is block or any mutation survivor exists; warn if any warn; pass otherwise), and exits 0 on pass or 4 on warn / block, so the audit is a Stage 3 to Stage 4 gate.
172
204
 
173
- - **`rcf finalise --ship-without-verified` flag, plus MOCK-ONLY-DECLARED disclosure in the finalise summary.** The gate reads per-AC verdicts from the verify report: any AC that comes back `MOCK-ONLY-DECLARED` / `BLOCKED-BY-DECLARATION` refuses the `complete -> verified` promotion unless the operator passes `--ship-without-verified`. The summary always discloses these verdicts (whether the FBS ships or not). Older verify reports without a `perAcVerdicts` field are handled gracefully - verify's train car may land later; older reports flow through with the pre-0.7.0 gate behaviour.
205
+ - **`rcf build finalise --ship-without-verified` flag, plus MOCK-ONLY-DECLARED disclosure in the finalise summary.** The gate reads per-AC verdicts from the verify report: any AC that comes back `MOCK-ONLY-DECLARED` / `BLOCKED-BY-DECLARATION` refuses the `complete -> verified` promotion unless the operator passes `--ship-without-verified`. The summary always discloses these verdicts (whether the FBS ships or not). Older verify reports without a `perAcVerdicts` field are handled gracefully - verify's train car may land later; older reports flow through with the pre-0.7.0 gate behaviour.
174
206
 
175
207
  ### Documentation
176
208
 
177
209
  - **Elicitation playbook §8.5 "Pre-flight config".** New section sits between §8 "When to stop" and §9 "What this playbook deliberately does not do", covering what the pre-flight session is, why it belongs before the build starts, the five modes with one-line decision guidance, what "declared mock-only" costs at ship time, and the credentials-never-in-the-chain contract.
178
- - **Build-cycle playbook additions.** Stage 1 (Define): a "third-party service dependencies belong on the FBS at Define" section wiring the `rcf fbs depends-on` verb into the plan. Stage 3 (Review): a second-gate section covering the `rcf review` audit's five finding categories, severities, and the exit-4-on-warn-or-block gate discipline. Stage 4 (Test): "runtime provenance is authored, not remembered" plus the TS-approval-as-Stage-4-outcome rule. Stage 5 (Finalise): "the finalise gate reads the attestation, not just the exit code" covering the MOCK-ONLY-DECLARED refusal and the `--ship-without-verified` acknowledgement path. Section 15 (Runtime-provenance): a paragraph on the chain being the source of truth from 0.7.0 onward, with the PR body as its rendering.
210
+ - **Build-cycle playbook additions.** Stage 1 (Define): a "third-party service dependencies belong on the FBS at Define" section wiring the `rcf build fbs depends-on` verb into the plan. Stage 3 (Review): a second-gate section covering the `rcf build review` audit's five finding categories, severities, and the exit-4-on-warn-or-block gate discipline. Stage 4 (Test): "runtime provenance is authored, not remembered" plus the TS-approval-as-Stage-4-outcome rule. Stage 5 (Finalise): "the finalise gate reads the attestation, not just the exit code" covering the MOCK-ONLY-DECLARED refusal and the `--ship-without-verified` acknowledgement path. Section 15 (Runtime-provenance): a paragraph on the chain being the source of truth from 0.7.0 onward, with the PR body as its rendering.
179
211
 
180
212
  ### Internal
181
213
 
@@ -191,7 +223,7 @@ Init hygiene release ([#70](https://github.com/Stravica/rcf-lite/pull/70)). The
191
223
 
192
224
  ### Added
193
225
 
194
- - **`rcf doctor` - new subcommand that detects and repairs init-hygiene drift.** Warn-only by default (exits 0 clean, 3 on drift); `--fix` rewrites the safe minimum. Four checks: `agent-instructions` (the managed CLAUDE.md/AGENTS.md block), `gitignore` (the managed `.gitignore` block), `knowledge` (`rcf/knowledge/` shape), `identity` (the seeded `rcf/.identity/profile.md`). The marker-driven checks share a drift enum: `missing-block`, `stale-hash`, `legacy-markers`, `orphan-marker`, `duplicate-block`. `--fix` rewrites only the marked region; every byte outside the markers is preserved verbatim, line endings and trailing whitespace included. Refused for `orphan-marker` and `duplicate-block` - structural corruption is hand-repair only. `--check <name>[,name]` runs a subset; `--json` emits `{ ok, drift[] }`; `--quiet` prints a summary line plus the first three drift items. The verb never auto-fires from `rcf init`, `rcf validate`, an MCP tool, or any hook: the only paths that mutate agent-instructions or `.gitignore` files are `rcf init` (bootstrap) and `rcf doctor --fix` (operator-invoked repair).
226
+ - **`rcf doctor` - new subcommand that detects and repairs init-hygiene drift.** Warn-only by default (exits 0 clean, 3 on drift); `--fix` rewrites the safe minimum. Four checks: `agent-instructions` (the managed CLAUDE.md/AGENTS.md block), `gitignore` (the managed `.gitignore` block), `knowledge` (`rcf/knowledge/` shape), `identity` (the seeded `rcf/.identity/profile.md`). The marker-driven checks share a drift enum: `missing-block`, `stale-hash`, `legacy-markers`, `orphan-marker`, `duplicate-block`. `--fix` rewrites only the marked region; every byte outside the markers is preserved verbatim, line endings and trailing whitespace included. Refused for `orphan-marker` and `duplicate-block` - structural corruption is hand-repair only. `--check <name>[,name]` runs a subset; `--json` emits `{ ok, drift[] }`; `--quiet` prints a summary line plus the first three drift items. The verb never auto-fires from `rcf init`, `rcf define validate`, an MCP tool, or any hook: the only paths that mutate agent-instructions or `.gitignore` files are `rcf init` (bootstrap) and `rcf doctor --fix` (operator-invoked repair).
195
227
 
196
228
  - **Managed CLAUDE.md/AGENTS.md block contract.** The agent-instructions fragment `rcf init` writes now sits inside `<!-- rcf:managed:begin -->` / `<!-- rcf:managed:end -->` markers and can drift as the package upgrades. `rcf doctor` surfaces the drift; `rcf doctor --fix` rewrites the block wholesale from the canonical text shipped in the package, leaving operator content outside the markers byte-identical. The block adds RULE 8 (never skip the method for speed), RULE 9 (write what you learn, pointing at `rcf/knowledge/`) and RULE 10 (read `rcf/.identity/profile.md` at session start if present), and carries the 0.5.2 operator-communication register forward alongside the existing rules. A SHA-256 of the canonical block ships in the tarball at `guidance/managed/agent-instructions-block.hash`; doctor uses it to detect a block whose inner content no longer matches the current package's canonical text (`stale-hash`).
197
229
 
@@ -241,11 +273,11 @@ Dependency-only fix release. Re-packs 0.5.0's runtime against `@stravica-ai/rcf-
241
273
 
242
274
  ## [0.5.0] - 2026-07-29
243
275
 
244
- Coverage becomes honest end-to-end (a "covered" AC now requires a resolving test), the build queue surfaces parallel-safe tiers, and forward trace stops conflating intent chains with FBS dependency graphs. Behaviour changes to `rcf coverage`, `rcf trace --forward`, `rcf read`, `rcf validate`, and the `rcf build` queue overview + JSON envelope. All work is in this package; `@stravica-ai/rcf-lite-core` and `@stravica-ai/rcf-verify-lite` are unchanged. Minor bump under the pre-1.0 breaking-is-minor convention.
276
+ Coverage becomes honest end-to-end (a "covered" AC now requires a resolving test), the build queue surfaces parallel-safe tiers, and forward trace stops conflating intent chains with FBS dependency graphs. Behaviour changes to `rcf audit coverage`, `rcf audit trace --forward`, `rcf define read`, `rcf define validate`, and the `rcf build` queue overview + JSON envelope. All work is in this package; `@stravica-ai/rcf-lite-core` and `@stravica-ai/rcf-verify-lite` are unchanged. Minor bump under the pre-1.0 breaking-is-minor convention.
245
277
 
246
278
  ### Added
247
279
 
248
- - **Coverage is now resolution-gated: a Test Case counts as covering its AC only when its `testPointer` resolves to a real test in the working tree** ([#61](https://github.com/Stravica/rcf-lite/pull/61)). Previously "covered" meant only that a TC row with a matching `acId` existed on the tree; the pointer was accepted everywhere and checked nowhere, so a tree of stub TCs could report full coverage while pointing at nothing. Resolution is deterministic (file exists, plus a declaration-anchor regex finds the named test; JS/TS anchors ship now, structured so another language is one table entry) and runs on every coverage surface: CLI (`rcf coverage`), MCP (`rcf_coverage`), all three formats. A TC whose pointer does not resolve is reported as its own class, `covered-unresolved`, never silently counted either way: it appears in the summary counters, as `unresolved` in the per-REQ and per-AC table cells, as a `[unresolved]` marker on the TC id, in an "Unresolved test pointers" footer naming each pointer and why it failed (`file-missing`, `test-missing`, `malformed-pointer`, `unsupported-file-type`, `missing-pointer`), and with the `broken` class in mermaid output. `covered-unresolved` fails `--strict` (exit 4) exactly as uncovered does. The inherited honest limit, stated in the same terms as the Code Node check: a renamed test is caught, a gutted test that kept its name is not.
280
+ - **Coverage is now resolution-gated: a Test Case counts as covering its AC only when its `testPointer` resolves to a real test in the working tree** ([#61](https://github.com/Stravica/rcf-lite/pull/61)). Previously "covered" meant only that a TC row with a matching `acId` existed on the tree; the pointer was accepted everywhere and checked nowhere, so a tree of stub TCs could report full coverage while pointing at nothing. Resolution is deterministic (file exists, plus a declaration-anchor regex finds the named test; JS/TS anchors ship now, structured so another language is one table entry) and runs on every coverage surface: CLI (`rcf audit coverage`), MCP (`rcf_coverage`), all three formats. A TC whose pointer does not resolve is reported as its own class, `covered-unresolved`, never silently counted either way: it appears in the summary counters, as `unresolved` in the per-REQ and per-AC table cells, as a `[unresolved]` marker on the TC id, in an "Unresolved test pointers" footer naming each pointer and why it failed (`file-missing`, `test-missing`, `malformed-pointer`, `unsupported-file-type`, `missing-pointer`), and with the `broken` class in mermaid output. `covered-unresolved` fails `--strict` (exit 4) exactly as uncovered does. The inherited honest limit, stated in the same terms as the Code Node check: a renamed test is caught, a gutted test that kept its name is not.
249
281
 
250
282
  - **Parallel-safe tiers in the build queue** ([#64](https://github.com/Stravica/rcf-lite/pull/64)). `rcf build` queue overview gains a `tier` column and an explicit "Parallel-safe tiers" block (md); the JSON envelope carries `tier` on each item and a `tiers` array of parallel-safe groups. Tier computation ported from the platform's `computeTiers` (rcf-common build-graph) and reimplemented idiomatically as an iterative memoised longest-chain DFS in the queue module's own style. Cycle posture: cycle members and anything depending on them get tier `null`, join no group, and the walk cannot loop on a cyclic graph. Delivers FBS-010 / TAC-005 (specified since Phase 6, unbuilt in build-lite until now) and binds AC-502-2.
251
283
 
@@ -257,15 +289,15 @@ Coverage becomes honest end-to-end (a "covered" AC now requires a resolving test
257
289
 
258
290
  ### Changed
259
291
 
260
- - **`testPointer` is required on every Test Case** ([#61](https://github.com/Stravica/rcf-lite/pull/61)). The published `@stravica-ai/rcf-schemas` bundle still declares it optional; Build Lite registers a documented strictness overlay (required, `minLength` 1) under the bundle's own `$id`, so every validation path (tree walk, post-write gate, write verbs) refuses a TC without a pointer. `rcf create tc` and the MCP `rcf_create` tool now require `--test-pointer` / `testPointer` and say so in their usage errors. Making the field required upstream in `rcf-schemas` is the durable home for this; the overlay is a pure tightening and will be dropped when that ships.
292
+ - **`testPointer` is required on every Test Case** ([#61](https://github.com/Stravica/rcf-lite/pull/61)). The published `@stravica-ai/rcf-schemas` bundle still declares it optional; Build Lite registers a documented strictness overlay (required, `minLength` 1) under the bundle's own `$id`, so every validation path (tree walk, post-write gate, write verbs) refuses a TC without a pointer. `rcf define create tc` and the MCP `rcf_create` tool now require `--test-pointer` / `testPointer` and say so in their usage errors. Making the field required upstream in `rcf-schemas` is the durable home for this; the overlay is a pure tightening and will be dropped when that ships.
261
293
 
262
294
  - **Coverage envelope shape** ([#61](https://github.com/Stravica/rcf-lite/pull/61)). `totals` gains `coveredUnresolved`; each requirement gains `coverageClass` (`covered` / `covered-unresolved` / `uncovered`); each AC gains `unresolvedTestCases`; the envelope gains a top-level `unresolvedTestPointers` list. `ok` is true only when every requirement in scope is covered by resolving test cases.
263
295
 
264
- - **`rcf trace --forward` treats FBS as a leaf by default** ([#63](https://github.com/Stravica/rcf-lite/pull/63)). Forward trace was BFS-ing straight through the FBS dependency graph (`rcf trace AC-101-1 --forward` fanned out to dependent FBSs at depths 2-4), conflating intent-chain traversal with impact-blast semantics. Per the AC-402-3 ruling ("implementation tracks the AC"), `computeTrace` now surfaces delivering FBS as cross-link **leaves** rather than walking through them. `computeImpact` alone opts into the fan-out via `expandFbsDependents` - the D7 blast-radius semantics (including "a dependent of an affected FBS") are unchanged and now explicitly asserted. Trace goldens regenerated accordingly.
296
+ - **`rcf audit trace --forward` treats FBS as a leaf by default** ([#63](https://github.com/Stravica/rcf-lite/pull/63)). Forward trace was BFS-ing straight through the FBS dependency graph (`rcf audit trace AC-101-1 --forward` fanned out to dependent FBSs at depths 2-4), conflating intent-chain traversal with impact-blast semantics. Per the AC-402-3 ruling ("implementation tracks the AC"), `computeTrace` now surfaces delivering FBS as cross-link **leaves** rather than walking through them. `computeImpact` alone opts into the fan-out via `expandFbsDependents` - the D7 blast-radius semantics (including "a dependent of an affected FBS") are unchanged and now explicitly asserted. Trace goldens regenerated accordingly.
265
297
 
266
- - **`rcf read` reports schema validity honestly instead of hiding it** ([#63](https://github.com/Stravica/rcf-lite/pull/63)). Validity is reported on stderr (stdout stays pure JSON, safe for pipes); a schema-invalid but present document is returned together with its validation errors, exit 0 - `read` is a retrieval verb, not a gate. Use `rcf validate` when you want failure on invalid state.
298
+ - **`rcf define read` reports schema validity honestly instead of hiding it** ([#63](https://github.com/Stravica/rcf-lite/pull/63)). Validity is reported on stderr (stdout stays pure JSON, safe for pipes); a schema-invalid but present document is returned together with its validation errors, exit 0 - `read` is a retrieval verb, not a gate. Use `rcf define validate` when you want failure on invalid state.
267
299
 
268
- - **`rcf validate` now fails on duplicate ids** ([#55](https://github.com/Stravica/rcf-lite/pull/55)) - exit 3, `duplicateId`, rule `globallyUniqueIds`. Previously a tree with colliding ids validated clean, however the collision got there. The check covers standalone documents, inline acceptance criteria and inline test cases, and treats leading-zero spellings as one id (`REQ-001` and `REQ-0001` both name requirement 1, which the schema pattern `^REQ-\d{3,}$` legitimately permits). Every colliding location produces its own error naming the id and each claiming file, so a CI log identifies the whole collision rather than half of it:
300
+ - **`rcf define validate` now fails on duplicate ids** ([#55](https://github.com/Stravica/rcf-lite/pull/55)) - exit 3, `duplicateId`, rule `globallyUniqueIds`. Previously a tree with colliding ids validated clean, however the collision got there. The check covers standalone documents, inline acceptance criteria and inline test cases, and treats leading-zero spellings as one id (`REQ-001` and `REQ-0001` both name requirement 1, which the schema pattern `^REQ-\d{3,}$` legitimately permits). Every colliding location produces its own error naming the id and each claiming file, so a CI log identifies the whole collision rather than half of it:
269
301
 
270
302
  ```
271
303
  [error] duplicateId US-101: Duplicate id AC-101-1: claimed by 2 locations: AC-101-1 in
@@ -273,7 +305,7 @@ Coverage becomes honest end-to-end (a "covered" AC now requires a resolving test
273
305
  rcf/user-stories/us-101.json (acceptanceCriteria[1].id).
274
306
  ```
275
307
 
276
- Detection lives in the walker, not the schema: the schema is right to permit variable-width numeric runs, and it is uniqueness *after normalisation* that is being violated. `rcf validate --json` carries the same issue under `kind: "duplicateId"`.
308
+ Detection lives in the walker, not the schema: the schema is right to permit variable-width numeric runs, and it is uniqueness *after normalisation* that is being violated. `rcf define validate --json` carries the same issue under `kind: "duplicateId"`.
277
309
 
278
310
  - **`rcf help <subcommand>` and `rcf <subcommand> --help` stay byte-identical** ([#57](https://github.com/Stravica/rcf-lite/pull/57)). The help surface kept a private duplicate of most subcommand help blocks; nine had drifted and every divergence ran the same direction - the `rcf help` path (the discovery path an agent reaches for first) was the stale copy. `rcf help create` was hiding the `cn` kind and all four Code Node options, so a shipped feature was invisible from the help surface. Every subcommand's help now imports from the module's exported `HELP`, and a parity test makes that structural rather than lucky.
279
311
 
@@ -281,7 +313,7 @@ Coverage becomes honest end-to-end (a "covered" AC now requires a resolving test
281
313
 
282
314
  - **Guidance no longer points at unreachable `guidance/*.md` paths** ([#56](https://github.com/Stravica/rcf-lite/pull/56)). The agent-instructions fragment `rcf init` writes, the "Deep guidance" footer on every `rcf build --next` spec bundle, and the `rcf init --no-agent-setup` manual instructions all named bare `guidance/elicitation-playbook.md` / `guidance/build-cycle-playbook.md` paths. Those files exist only inside a clone of this repository, so the instruction was dead on arrival in a consumer project. All of them now name `rcf guidance <topic>`.
283
315
 
284
- - **The build-cycle playbook no longer claims the independent verification gate is unbuilt** ([#56](https://github.com/Stravica/rcf-lite/pull/56)). Section 16 described the fresh-context self-review as an "interim stopgap until rcf-verify-lite exists", which stopped being true when `rcf-verify-lite` shipped and `rcf finalise` began running it. The playbook contradicted its own section 7, and the false claim was inside the fragment written into every initialised project's `CLAUDE.md`. The self-review is now positioned as what it actually is: the cheap in-loop check that runs between builds, subordinate to the `rcf finalise` gate rather than a placeholder for it, and never evidence for a `verified` mark. AC-805-4 and its drift test moved with the prose, and the test now fails if either file reacquires the stale claim.
316
+ - **The build-cycle playbook no longer claims the independent verification gate is unbuilt** ([#56](https://github.com/Stravica/rcf-lite/pull/56)). Section 16 described the fresh-context self-review as an "interim stopgap until rcf-verify-lite exists", which stopped being true when `rcf-verify-lite` shipped and `rcf build finalise` began running it. The playbook contradicted its own section 7, and the false claim was inside the fragment written into every initialised project's `CLAUDE.md`. The self-review is now positioned as what it actually is: the cheap in-loop check that runs between builds, subordinate to the `rcf build finalise` gate rather than a placeholder for it, and never evidence for a `verified` mark. AC-805-4 and its drift test moved with the prose, and the test now fails if either file reacquires the stale claim.
285
317
 
286
318
  ### Documentation
287
319
 
@@ -289,9 +321,9 @@ Coverage becomes honest end-to-end (a "covered" AC now requires a resolving test
289
321
 
290
322
  ### Internal
291
323
 
292
- - **`rcf validate` + `rcf coverage --strict` now gate CI** ([#64](https://github.com/Stravica/rcf-lite/pull/64)). Both run from `packages/build` as required steps in `ci.yml`, before the suite. From this release on, a stub TC or a new uncovered AC fails CI; the whole 76/76 dogfood coverage state is locked in.
324
+ - **`rcf define validate` + `rcf audit coverage --strict` now gate CI** ([#64](https://github.com/Stravica/rcf-lite/pull/64)). Both run from `packages/build` as required steps in `ci.yml`, before the suite. From this release on, a stub TC or a new uncovered AC fails CI; the whole 76/76 dogfood coverage state is locked in.
293
325
 
294
- - **Dogfood coverage authored end-to-end** ([#62](https://github.com/Stravica/rcf-lite/pull/62), [#63](https://github.com/Stravica/rcf-lite/pull/63)). 76 acceptance criteria audited against the existing test corpus; 24 Test Suites + 76 Test Cases authored through `rcf create ts` / `rcf create tc --test-pointer` (the tool proving itself against itself). Final `rcf coverage --with-code`: `implemented-and-covered=76 implemented-uncovered=0 unimplemented=0`, `covered-unresolved=0`.
326
+ - **Dogfood coverage authored end-to-end** ([#62](https://github.com/Stravica/rcf-lite/pull/62), [#63](https://github.com/Stravica/rcf-lite/pull/63)). 76 acceptance criteria audited against the existing test corpus; 24 Test Suites + 76 Test Cases authored through `rcf define create ts` / `rcf define create tc --test-pointer` (the tool proving itself against itself). Final `rcf audit coverage --with-code`: `implemented-and-covered=76 implemented-uncovered=0 unimplemented=0`, `covered-unresolved=0`.
295
327
 
296
328
  ## [0.4.0] - 2026-07-22
297
329
 
@@ -299,9 +331,9 @@ Hardens the `verified` state so it can only be reached through the independent s
299
331
 
300
332
  ### Changed
301
333
 
302
- - **`rcf build <fbs-id> --mark verified` is now refused** ([#53](https://github.com/Stravica/rcf-lite/pull/53)): the `--mark` ladder caps at `complete`. Previously any forward lifecycle jump was legal, so `--mark verified` promoted `complete → verified` with no verify run at all - a one-flag bypass of the finalise gate's independence guarantee (spec §9). `--mark verified` now exits 4 (the mark-refusal family), writes nothing, and points to `rcf finalise`. `verified` is written only by the finalise gate, or by the sanctioned explicit override `rcf update <fbs-id> --set executionStatus=verified` (unchanged). **Migration:** anywhere you scripted `rcf build <id> --mark verified`, switch to `rcf finalise <id> --url <deploy-url>` (the ship gate) or, for a deliberate manual override with no verify run, `rcf update <id> --set executionStatus=verified`.
303
- - **`rcf finalise` now gates on ship authority, not just exit code** ([#53](https://github.com/Stravica/rcf-lite/pull/53)): promotion to `verified` previously required only that the spawned `rcf-verify` subprocess exit 0, so a correctness-only pass (e.g. a bare `--profile ci` run) could write `verified`. Promotion now additionally requires the ingested report's `verdictAuthority === 'ship'` (spec §4) - a `deployed`-profile run, or a `ci`/`local-dev` run with `--parity-env`. A passing non-ship run, or an unreadable report on a pass, produces a clean explicit **HOLD** (state unchanged, exit 4), never a silent promotion and never an error. Re-verify of an already-verified item is unchanged.
304
- - **`completionContract.markVerified` renamed to `completionContract.finalise`** ([#53](https://github.com/Stravica/rcf-lite/pull/53)): the JSON build bundle (`rcf build --format json`) and the MCP `rcf_build` result emit a `completionContract` object. Its `markVerified` key - which carried a `rcf build … --mark verified` command that is now refused - is renamed to `finalise` and carries `rcf finalise <id> --url <deploy-url>`. **This is a breaking change for any consumer that reads `completionContract.markVerified` from the JSON/MCP output** (the MCP `BUILD_OUTPUT_SCHEMA` `required` list changed to match). Read `completionContract.finalise` instead. The Stage-5 markdown runbook and the guidance pack (`build-cycle.md`, `build-cycle-playbook.md`, `getting-started.md`) are reworded to route ship through `rcf finalise`.
334
+ - **`rcf build <fbs-id> --mark verified` is now refused** ([#53](https://github.com/Stravica/rcf-lite/pull/53)): the `--mark` ladder caps at `complete`. Previously any forward lifecycle jump was legal, so `--mark verified` promoted `complete → verified` with no verify run at all - a one-flag bypass of the finalise gate's independence guarantee (spec §9). `--mark verified` now exits 4 (the mark-refusal family), writes nothing, and points to `rcf build finalise`. `verified` is written only by the finalise gate, or by the sanctioned explicit override `rcf define update <fbs-id> --set executionStatus=verified` (unchanged). **Migration:** anywhere you scripted `rcf build <id> --mark verified`, switch to `rcf build finalise <id> --url <deploy-url>` (the ship gate) or, for a deliberate manual override with no verify run, `rcf define update <id> --set executionStatus=verified`.
335
+ - **`rcf build finalise` now gates on ship authority, not just exit code** ([#53](https://github.com/Stravica/rcf-lite/pull/53)): promotion to `verified` previously required only that the spawned `rcf-verify` subprocess exit 0, so a correctness-only pass (e.g. a bare `--profile ci` run) could write `verified`. Promotion now additionally requires the ingested report's `verdictAuthority === 'ship'` (spec §4) - a `deployed`-profile run, or a `ci`/`local-dev` run with `--parity-env`. A passing non-ship run, or an unreadable report on a pass, produces a clean explicit **HOLD** (state unchanged, exit 4), never a silent promotion and never an error. Re-verify of an already-verified item is unchanged.
336
+ - **`completionContract.markVerified` renamed to `completionContract.finalise`** ([#53](https://github.com/Stravica/rcf-lite/pull/53)): the JSON build bundle (`rcf build --format json`) and the MCP `rcf_build` result emit a `completionContract` object. Its `markVerified` key - which carried a `rcf build … --mark verified` command that is now refused - is renamed to `finalise` and carries `rcf build finalise <id> --url <deploy-url>`. **This is a breaking change for any consumer that reads `completionContract.markVerified` from the JSON/MCP output** (the MCP `BUILD_OUTPUT_SCHEMA` `required` list changed to match). Read `completionContract.finalise` instead. The Stage-5 markdown runbook and the guidance pack (`build-cycle.md`, `build-cycle-playbook.md`, `getting-started.md`) are reworded to route ship through `rcf build finalise`.
305
337
 
306
338
  ### Documentation
307
339
 
@@ -309,11 +341,11 @@ Hardens the `verified` state so it can only be reached through the independent s
309
341
 
310
342
  ## [0.3.0] - 2026-07-22
311
343
 
312
- Deploy-aware, runtime-honest build guidance (Tier-1 hardening, REQ-008) plus the `rcf finalise` ship gate that hands the final verdict to an independent `rcf-verify` run. First release published from the `Stravica/rcf-lite` monorepo, and the first release to depend on the extracted `@stravica-ai/rcf-lite-core` package.
344
+ Deploy-aware, runtime-honest build guidance (Tier-1 hardening, REQ-008) plus the `rcf build finalise` ship gate that hands the final verdict to an independent `rcf-verify` run. First release published from the `Stravica/rcf-lite` monorepo, and the first release to depend on the extracted `@stravica-ai/rcf-lite-core` package.
313
345
 
314
346
  ### Added
315
347
 
316
- - **`rcf finalise <fbs-id> --url <deploy-url>` - the finalise gate** ([#50](https://github.com/Stravica/rcf-lite/pull/50)): promotes an FBS from `complete` to `verified` only when an independent `rcf-verify` run against the deployed app passes. `rcf-verify` is spawned as a **fresh OS subprocess** (never imported in-process) under `@stravica-ai/rcf-lite-core`'s isolation env (§7.3), so the verifier agent starts cold with zero build context. Exit code is the gate (0 → promote, non-zero → FBS left unchanged and findings surfaced); findings flow via a chain-node-addressed `--out` report file, not stdout scraping. Install-together posture (§8.3): if `rcf-verify` is absent, `finalise` prompts to install it on an interactive TTY or accepts an explicit `--install-verify` flag off a TTY - it never silently skips the gate and never silently auto-installs.
348
+ - **`rcf build finalise <fbs-id> --url <deploy-url>` - the finalise gate** ([#50](https://github.com/Stravica/rcf-lite/pull/50)): promotes an FBS from `complete` to `verified` only when an independent `rcf-verify` run against the deployed app passes. `rcf-verify` is spawned as a **fresh OS subprocess** (never imported in-process) under `@stravica-ai/rcf-lite-core`'s isolation env (§7.3), so the verifier agent starts cold with zero build context. Exit code is the gate (0 → promote, non-zero → FBS left unchanged and findings surfaced); findings flow via a chain-node-addressed `--out` report file, not stdout scraping. Install-together posture (§8.3): if `rcf-verify` is absent, `finalise` prompts to install it on an interactive TTY or accepts an explicit `--install-verify` flag off a TTY - it never silently skips the gate and never silently auto-installs.
317
349
 
318
350
  ### Changed
319
351
 
@@ -339,16 +371,16 @@ Deploy-aware, runtime-honest build guidance (Tier-1 hardening, REQ-008) plus the
339
371
 
340
372
  ## [0.2.0] - 2026-07-10
341
373
 
342
- The spec-to-code bridge (X2): `CN-*` Code Nodes make source code a first-class node in the same graph the spec chain already lives in, so `rcf validate` catches a dangling spec-to-code link the same way it catches a dangling spec-to-spec one.
374
+ The spec-to-code bridge (X2): `CN-*` Code Nodes make source code a first-class node in the same graph the spec chain already lives in, so `rcf define validate` catches a dangling spec-to-code link the same way it catches a dangling spec-to-spec one.
343
375
 
344
376
  ### Added
345
377
 
346
378
  - **Code Node document kind** (`CN-*`, `rcf/code-nodes/`), the 11th RCF document type, delivered via [`@stravica-ai/rcf-schemas@0.3.1`](https://github.com/Stravica/rcf-schemas). Identity is a working-tree path, optionally `#symbol`-suffixed; granularity (file vs symbol) is derived, never stored.
347
- - **Staleness detection**: `rcf validate` fails (exit 3, `staleCode`) when a Code Node's path or symbol no longer resolves against the working tree; `--no-code` skips the pass.
348
- - **Queries**: `rcf trace <path>` and `rcf trace <path>#symbol` walk backward from source to the requirements it serves; `--to-code` on `trace` / `impact` extends the forward fan-out into implementing and dependent Code Nodes; a Code Node id is a uniform pivot like any other. `rcf view` renders Code Nodes as a distinct cosmetic class.
349
- - **CRUD**: `rcf create/update/delete cn`, mirroring the existing writer patterns; delete refused while another Code Node depends on it; post-write validation as with every other kind. `--derive-deps` optionally shells out to `dependency-cruiser` for file-level dependency auto-derivation - a dev-time-only assist, never a runtime dependency.
379
+ - **Staleness detection**: `rcf define validate` fails (exit 3, `staleCode`) when a Code Node's path or symbol no longer resolves against the working tree; `--no-code` skips the pass.
380
+ - **Queries**: `rcf audit trace <path>` and `rcf audit trace <path>#symbol` walk backward from source to the requirements it serves; `--to-code` on `trace` / `impact` extends the forward fan-out into implementing and dependent Code Nodes; a Code Node id is a uniform pivot like any other. `rcf audit view` renders Code Nodes as a distinct cosmetic class.
381
+ - **CRUD**: `rcf define create/update/delete cn`, mirroring the existing writer patterns; delete refused while another Code Node depends on it; post-write validation as with every other kind. `--derive-deps` optionally shells out to `dependency-cruiser` for file-level dependency auto-derivation - a dev-time-only assist, never a runtime dependency.
350
382
  - **The mark-complete gate**: `rcf build --mark complete` refuses (exit 3, `missingCodeNodes`) when any acceptance criterion of the completed build spec carries no Code Node; `--no-code-nodes` declares a genuinely no-code (docs-only, config-only) spec, recorded on the FBS.
351
- - **`rcf coverage --with-code`**: informational four-class code axis per acceptance criterion (`implemented-and-covered` / `implemented-uncovered` / `unimplemented` / `CN-orphaned`), never blocking.
383
+ - **`rcf audit coverage --with-code`**: informational four-class code axis per acceptance criterion (`implemented-and-covered` / `implemented-uncovered` / `unimplemented` / `CN-orphaned`), never blocking.
352
384
  - **MCP adapter**: `rcf_create` accepts kind `cn`; `rcf_trace` / `rcf_impact` gain `toCode` and path-mode; `rcf_coverage` gains `withCode`; `rcf_validate` gains `noCode`.
353
385
  - **Guidance pack + build bundle**: the five-stage runbook and the build-cycle guidance now direct CN authoring during Stage 2 (Build) and name the Stage 5 gate.
354
386
  - **Docs**: `docs/code-nodes.md` - the concept, authoring guidance, and an explicit honest-limits section (semantic drift, symbol rot, namesake false-cleans).
@@ -366,7 +398,7 @@ First public release.
366
398
 
367
399
  - Schema-validated document chain: every RCF document type (PRD, requirements, user stories, acceptance criteria, TAD, build sequence) lives as JSON in your repository, validated against the [`@stravica-ai/rcf-schemas`](https://github.com/Stravica/rcf-schemas) contract.
368
400
  - Dogfooded `rcf/` tree: this repository's own PRD, requirements, stories, acceptance criteria, TAD and build queue, built and maintained with the tool itself.
369
- - `rcf view`: live HTML rendering of the full document graph in the browser.
401
+ - `rcf audit view`: live HTML rendering of the full document graph in the browser.
370
402
  - Unified `rcf` CLI with full create, read, update and delete coverage across the document chain.
371
403
  - Traceability queries: coverage, trace forward and back through the chain, and impact analysis from any node.
372
404
  - `rcf build`: SDD adapter that turns the build queue into staged, executable spec bundles.
package/README.md CHANGED
@@ -9,7 +9,7 @@ Build software with an AI coding agent without losing the plot.
9
9
 
10
10
  Anyone who has shipped with a coding agent knows the failure mode: the code arrives fast, but what the product is supposed to do lives in prompt history, and prompt history is not a spec. Three weeks later nobody can say what is covered, what is tested, or what breaks when something changes.
11
11
 
12
- RCF Lite keeps those answers machine-checkable. It maintains a live chain from what you asked for, through requirements, user stories, acceptance criteria and tests, into the code itself, as plain JSON files in your own repository. Your agent works the chain instead of improvising; `rcf validate` catches drift the moment it happens; and when your app is deployed, `rcf verify` sends an independent verifier at it before anything gets called done.
12
+ RCF Lite keeps those answers machine-checkable. It maintains a live chain from what you asked for, through requirements, user stories, acceptance criteria and tests, into the code itself, as plain JSON files in your own repository. Your agent works the chain instead of improvising; `rcf define validate` catches drift the moment it happens; and when your app is deployed, `rcf verify` sends an independent verifier at it before anything gets called done.
13
13
 
14
14
  It is the tooling for the [Requirements Confidence Framework (RCF)](https://stravica.ai/rcf-methodology): a method for keeping AI-built software honest.
15
15
 
@@ -50,7 +50,7 @@ If your project pinned one of the pre-consolidation packages, replace it with `r
50
50
 
51
51
  Invocation:
52
52
 
53
- - `rcf init` / `rcf validate` / `rcf build` / `rcf finalise` are unchanged.
53
+ - `rcf init` / `rcf define validate` / `rcf build` / `rcf build finalise` are unchanged.
54
54
  - Prefer `rcf verify <run|report|provision|cleanup|mcp>` over the legacy `rcf-verify` bin. The alias still works (identical dispatch, one-line stderr deprecation notice on direct invocation; suppress with `RCF_QUIET=1`).
55
55
 
56
56
  The full migration story, the ratified ruling sheet and the registry runbook live at the repo root under [`docs/`](../../docs/).
@@ -65,7 +65,7 @@ See them the way you'd see your own project's:
65
65
  git clone https://github.com/Stravica/rcf-lite.git
66
66
  cd rcf-lite && pnpm install
67
67
  cd packages/rcf-lite
68
- pnpm rcf view # the tree this tool was built from, rendered live in your browser
68
+ pnpm rcf audit view # the tree this tool was built from, rendered live in your browser
69
69
  ```
70
70
 
71
71
  Then scaffold your own: [docs/getting-started.md](docs/getting-started.md).
@@ -85,7 +85,11 @@ Then scaffold your own: [docs/getting-started.md](docs/getting-started.md).
85
85
 
86
86
  ## Under the hood
87
87
 
88
- The chain does not stop at the tests. **Code Nodes** make source files first-class nodes in the same graph, so a dangling spec-to-code link fails `rcf validate` exactly the way a dangling spec-to-spec one does. `rcf trace` walks backward from a source file to the requirements it serves; `rcf impact` extends a change's blast radius into the code that implements it. Spec-only trees work unchanged; the code layer is additive. Full detail, deliberate limits and the roadmap beyond them: [docs/code-nodes.md](docs/code-nodes.md).
88
+ The chain does not stop at the tests. **Code Nodes** make source files first-class nodes in the same graph, so a dangling spec-to-code link fails `rcf define validate` exactly the way a dangling spec-to-spec one does. `rcf audit trace` walks backward from a source file to the requirements it serves; `rcf audit impact` extends a change's blast radius into the code that implements it. Spec-only trees work unchanged; the code layer is additive. Full detail, deliberate limits and the roadmap beyond them: [docs/code-nodes.md](docs/code-nodes.md).
89
+
90
+ ## Known issues
91
+
92
+ - **npm 11.6.x may install `rcf-lite` empty.** Running `npm install rcf-lite` on its own with npm 11.6.x reports `up to date, audited 1 package` and performs no file operations, leaving the package unusable. This is an upstream npm reify bug, not an rcf-lite defect. Workarounds: install rcf-lite alongside any other package in the same command, use npm 10, or use pnpm. The note will clear once npm ships the fix.
89
93
 
90
94
  ## Contributing
91
95