rcf-lite 0.0.1 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/CHANGELOG.md +288 -0
  2. package/LICENSE +202 -0
  3. package/README.md +92 -4
  4. package/bin/rcf-verify.js +122 -0
  5. package/bin/rcf.js +174 -0
  6. package/bin/view-supervisor-child.mjs +14 -0
  7. package/fixtures/canary-manifest.json +103 -0
  8. package/guidance/README.md +25 -0
  9. package/guidance/build-cycle-playbook.md +492 -0
  10. package/guidance/build-cycle.md +40 -0
  11. package/guidance/document-model.md +45 -0
  12. package/guidance/elicitation-playbook.md +442 -0
  13. package/guidance/harness-template.md +246 -0
  14. package/guidance/managed/README.md +63 -0
  15. package/guidance/managed/agent-instructions-block.hash +1 -0
  16. package/guidance/managed/agent-instructions-block.md +220 -0
  17. package/guidance/managed/legacy-fragment-hashes.json +22 -0
  18. package/guidance/manifest.json +21 -0
  19. package/guidance/overview.md +48 -0
  20. package/guidance/persona-programme.md +44 -0
  21. package/package.json +69 -8
  22. package/rcf/adrs/adr-001.json +25 -0
  23. package/rcf/adrs/adr-002.json +25 -0
  24. package/rcf/adrs/adr-003.json +25 -0
  25. package/rcf/adrs/adr-004.json +20 -0
  26. package/rcf/adrs/adr-005.json +20 -0
  27. package/rcf/adrs/adr-006.json +25 -0
  28. package/rcf/adrs/adr-007.json +25 -0
  29. package/rcf/adrs/adr-008.json +25 -0
  30. package/rcf/adrs/adr-009.json +25 -0
  31. package/rcf/build-sequence.json +11 -0
  32. package/rcf/code-nodes/cn-001.json +14 -0
  33. package/rcf/code-nodes/cn-002.json +14 -0
  34. package/rcf/code-nodes/cn-003.json +15 -0
  35. package/rcf/code-nodes/cn-004.json +17 -0
  36. package/rcf/code-nodes/cn-005.json +19 -0
  37. package/rcf/code-nodes/cn-006.json +17 -0
  38. package/rcf/code-nodes/cn-007.json +16 -0
  39. package/rcf/code-nodes/cn-008.json +19 -0
  40. package/rcf/code-nodes/cn-009.json +16 -0
  41. package/rcf/code-nodes/cn-010.json +18 -0
  42. package/rcf/code-nodes/cn-011.json +18 -0
  43. package/rcf/code-nodes/cn-012.json +18 -0
  44. package/rcf/code-nodes/cn-013.json +19 -0
  45. package/rcf/code-nodes/cn-014.json +16 -0
  46. package/rcf/code-nodes/cn-015.json +16 -0
  47. package/rcf/code-nodes/cn-016.json +16 -0
  48. package/rcf/code-nodes/cn-017.json +16 -0
  49. package/rcf/code-nodes/cn-018.json +15 -0
  50. package/rcf/code-nodes/cn-019.json +16 -0
  51. package/rcf/code-nodes/cn-020.json +16 -0
  52. package/rcf/code-nodes/cn-021.json +16 -0
  53. package/rcf/code-nodes/cn-022.json +16 -0
  54. package/rcf/code-nodes/cn-023.json +16 -0
  55. package/rcf/code-nodes/cn-024.json +16 -0
  56. package/rcf/code-nodes/cn-025.json +16 -0
  57. package/rcf/code-nodes/cn-026.json +16 -0
  58. package/rcf/code-nodes/cn-027.json +16 -0
  59. package/rcf/code-nodes/cn-028.json +16 -0
  60. package/rcf/code-nodes/cn-029.json +19 -0
  61. package/rcf/code-nodes/cn-030.json +14 -0
  62. package/rcf/code-nodes/cn-031.json +14 -0
  63. package/rcf/code-nodes/cn-032.json +14 -0
  64. package/rcf/code-nodes/cn-033.json +14 -0
  65. package/rcf/code-nodes/cn-034.json +14 -0
  66. package/rcf/code-nodes/cn-035.json +14 -0
  67. package/rcf/code-nodes/cn-036.json +14 -0
  68. package/rcf/code-nodes/cn-037.json +14 -0
  69. package/rcf/code-nodes/cn-038.json +14 -0
  70. package/rcf/code-nodes/cn-039.json +14 -0
  71. package/rcf/code-nodes/cn-040.json +14 -0
  72. package/rcf/code-nodes/cn-041.json +14 -0
  73. package/rcf/code-nodes/cn-042.json +14 -0
  74. package/rcf/code-nodes/cn-043.json +14 -0
  75. package/rcf/code-nodes/cn-044.json +14 -0
  76. package/rcf/code-nodes/cn-045.json +14 -0
  77. package/rcf/code-nodes/cn-046.json +14 -0
  78. package/rcf/code-nodes/cn-047.json +14 -0
  79. package/rcf/code-nodes/cn-048.json +14 -0
  80. package/rcf/code-nodes/cn-049.json +14 -0
  81. package/rcf/code-nodes/cn-050.json +14 -0
  82. package/rcf/code-nodes/cn-051.json +14 -0
  83. package/rcf/code-nodes/cn-052.json +14 -0
  84. package/rcf/code-nodes/cn-053.json +14 -0
  85. package/rcf/code-nodes/cn-054.json +14 -0
  86. package/rcf/code-nodes/cn-055.json +14 -0
  87. package/rcf/code-nodes/cn-056.json +14 -0
  88. package/rcf/code-nodes/cn-057.json +14 -0
  89. package/rcf/fbs/fbs-001.json +49 -0
  90. package/rcf/fbs/fbs-002.json +42 -0
  91. package/rcf/fbs/fbs-003.json +37 -0
  92. package/rcf/fbs/fbs-004.json +39 -0
  93. package/rcf/fbs/fbs-005.json +38 -0
  94. package/rcf/fbs/fbs-006.json +48 -0
  95. package/rcf/fbs/fbs-007.json +39 -0
  96. package/rcf/fbs/fbs-008.json +40 -0
  97. package/rcf/fbs/fbs-009.json +36 -0
  98. package/rcf/fbs/fbs-010.json +41 -0
  99. package/rcf/fbs/fbs-011.json +36 -0
  100. package/rcf/fbs/fbs-012.json +46 -0
  101. package/rcf/fbs/fbs-013.json +42 -0
  102. package/rcf/fbs/fbs-014.json +49 -0
  103. package/rcf/fbs/fbs-015.json +32 -0
  104. package/rcf/manifest.json +17 -0
  105. package/rcf/prd.json +47 -0
  106. package/rcf/requirements/req-001.json +19 -0
  107. package/rcf/requirements/req-002.json +19 -0
  108. package/rcf/requirements/req-003.json +19 -0
  109. package/rcf/requirements/req-004.json +19 -0
  110. package/rcf/requirements/req-005.json +19 -0
  111. package/rcf/requirements/req-006.json +19 -0
  112. package/rcf/requirements/req-007.json +19 -0
  113. package/rcf/requirements/req-008.json +19 -0
  114. package/rcf/requirements/req-009.json +19 -0
  115. package/rcf/tacs/tac-001.json +45 -0
  116. package/rcf/tacs/tac-002.json +109 -0
  117. package/rcf/tacs/tac-003.json +40 -0
  118. package/rcf/tacs/tac-004.json +51 -0
  119. package/rcf/tacs/tac-005.json +52 -0
  120. package/rcf/tacs/tac-006.json +104 -0
  121. package/rcf/tacs/tac-007.json +38 -0
  122. package/rcf/tacs/tac-008.json +51 -0
  123. package/rcf/tad.json +51 -0
  124. package/rcf/test-suites/PENDING.md +23 -0
  125. package/rcf/test-suites/ts-001.json +38 -0
  126. package/rcf/test-suites/ts-002.json +38 -0
  127. package/rcf/test-suites/ts-003.json +43 -0
  128. package/rcf/test-suites/ts-004.json +44 -0
  129. package/rcf/test-suites/ts-005.json +30 -0
  130. package/rcf/test-suites/ts-006.json +36 -0
  131. package/rcf/test-suites/ts-007.json +43 -0
  132. package/rcf/test-suites/ts-008.json +37 -0
  133. package/rcf/test-suites/ts-009.json +38 -0
  134. package/rcf/test-suites/ts-010.json +38 -0
  135. package/rcf/test-suites/ts-011.json +44 -0
  136. package/rcf/test-suites/ts-012.json +36 -0
  137. package/rcf/test-suites/ts-013.json +38 -0
  138. package/rcf/test-suites/ts-014.json +38 -0
  139. package/rcf/test-suites/ts-015.json +38 -0
  140. package/rcf/test-suites/ts-016.json +37 -0
  141. package/rcf/test-suites/ts-017.json +38 -0
  142. package/rcf/test-suites/ts-018.json +38 -0
  143. package/rcf/test-suites/ts-019.json +37 -0
  144. package/rcf/test-suites/ts-020.json +46 -0
  145. package/rcf/test-suites/ts-021.json +46 -0
  146. package/rcf/test-suites/ts-022.json +46 -0
  147. package/rcf/test-suites/ts-023.json +46 -0
  148. package/rcf/test-suites/ts-024.json +46 -0
  149. package/rcf/test-suites/ts-025.json +52 -0
  150. package/rcf/user-stories/us-101.json +40 -0
  151. package/rcf/user-stories/us-102.json +40 -0
  152. package/rcf/user-stories/us-201.json +40 -0
  153. package/rcf/user-stories/us-202.json +40 -0
  154. package/rcf/user-stories/us-203.json +32 -0
  155. package/rcf/user-stories/us-301.json +40 -0
  156. package/rcf/user-stories/us-302.json +40 -0
  157. package/rcf/user-stories/us-303.json +40 -0
  158. package/rcf/user-stories/us-304.json +40 -0
  159. package/rcf/user-stories/us-401.json +40 -0
  160. package/rcf/user-stories/us-402.json +40 -0
  161. package/rcf/user-stories/us-403.json +40 -0
  162. package/rcf/user-stories/us-501.json +40 -0
  163. package/rcf/user-stories/us-502.json +40 -0
  164. package/rcf/user-stories/us-503.json +40 -0
  165. package/rcf/user-stories/us-601.json +40 -0
  166. package/rcf/user-stories/us-602.json +40 -0
  167. package/rcf/user-stories/us-701.json +40 -0
  168. package/rcf/user-stories/us-702.json +40 -0
  169. package/rcf/user-stories/us-801.json +49 -0
  170. package/rcf/user-stories/us-802.json +49 -0
  171. package/rcf/user-stories/us-803.json +49 -0
  172. package/rcf/user-stories/us-804.json +49 -0
  173. package/rcf/user-stories/us-805.json +49 -0
  174. package/rcf/user-stories/us-901.json +40 -0
  175. package/src/.gitkeep +0 -0
  176. package/src/browser-verify/auth-smoke.js +109 -0
  177. package/src/browser-verify/index.js +29 -0
  178. package/src/browser-verify/invariants.js +336 -0
  179. package/src/browser-verify/manifest-writer.js +189 -0
  180. package/src/browser-verify/runner.js +171 -0
  181. package/src/build/bundle.js +198 -0
  182. package/src/build/formatters/json.js +18 -0
  183. package/src/build/formatters/markdown.js +406 -0
  184. package/src/build/index.js +14 -0
  185. package/src/build/mark.js +177 -0
  186. package/src/build/queue.js +285 -0
  187. package/src/cli/browser-verify.js +231 -0
  188. package/src/cli/build.js +584 -0
  189. package/src/cli/coverage.js +219 -0
  190. package/src/cli/create.js +355 -0
  191. package/src/cli/delete.js +127 -0
  192. package/src/cli/design.js +353 -0
  193. package/src/cli/doctor.js +548 -0
  194. package/src/cli/fbs.js +171 -0
  195. package/src/cli/finalise.js +377 -0
  196. package/src/cli/guidance.js +206 -0
  197. package/src/cli/help.js +156 -0
  198. package/src/cli/impact.js +119 -0
  199. package/src/cli/init.js +282 -0
  200. package/src/cli/intake.js +153 -0
  201. package/src/cli/link.js +128 -0
  202. package/src/cli/mcp.js +160 -0
  203. package/src/cli/preflight.js +220 -0
  204. package/src/cli/read.js +162 -0
  205. package/src/cli/req-baseline.js +269 -0
  206. package/src/cli/req-classify.js +135 -0
  207. package/src/cli/review.js +295 -0
  208. package/src/cli/test-suite.js +216 -0
  209. package/src/cli/trace.js +172 -0
  210. package/src/cli/ui-baseline.js +292 -0
  211. package/src/cli/ui-classify.js +108 -0
  212. package/src/cli/update.js +197 -0
  213. package/src/cli/validate.js +168 -0
  214. package/src/cli/view.js +452 -0
  215. package/src/core/baseline-catalog/data/auth.json +42 -0
  216. package/src/core/baseline-catalog/data/http-api.json +42 -0
  217. package/src/core/baseline-catalog/data/notifications.json +33 -0
  218. package/src/core/baseline-catalog/data/persistence.json +33 -0
  219. package/src/core/baseline-catalog/data/web-ui.json +60 -0
  220. package/src/core/baseline-catalog/index.js +121 -0
  221. package/src/core/errors/index.js +167 -0
  222. package/src/core/fixtures/register-canary/canary-prompt-01.json +18 -0
  223. package/src/core/fixtures/register-canary/canary-prompt-02.json +21 -0
  224. package/src/core/fixtures/register-canary/canary-prompt-03.json +17 -0
  225. package/src/core/isolation/index.js +60 -0
  226. package/src/core/mcp/framing.js +103 -0
  227. package/src/core/mcp/index.js +8 -0
  228. package/src/core/mcp/server.js +228 -0
  229. package/src/core/patterns/register-canary.js +209 -0
  230. package/src/core/patterns/req-shapes.js +158 -0
  231. package/src/core/patterns/services.js +358 -0
  232. package/src/core/patterns/ui-shapes.js +166 -0
  233. package/src/core/store/cn-resolve.js +134 -0
  234. package/src/core/store/derive-deps.js +93 -0
  235. package/src/core/store/ids.js +78 -0
  236. package/src/core/store/index.js +20 -0
  237. package/src/core/store/init.js +255 -0
  238. package/src/core/store/loader.js +211 -0
  239. package/src/core/store/tp-resolve.js +176 -0
  240. package/src/core/store/validator.js +191 -0
  241. package/src/core/store/walker.js +898 -0
  242. package/src/core/store/writer.js +1849 -0
  243. package/src/design/index.js +11 -0
  244. package/src/design/writer.js +271 -0
  245. package/src/finalise/detect.js +129 -0
  246. package/src/finalise/index.js +17 -0
  247. package/src/finalise/ingest.js +119 -0
  248. package/src/finalise/install.js +119 -0
  249. package/src/finalise/ship-without-verified.js +131 -0
  250. package/src/finalise/spawn.js +84 -0
  251. package/src/intake/fidelity.js +105 -0
  252. package/src/intake/index.js +6 -0
  253. package/src/intake/manifest-writer.js +100 -0
  254. package/src/intake/orchestrator.js +138 -0
  255. package/src/intake/validate.js +80 -0
  256. package/src/mcp/map-errors.js +131 -0
  257. package/src/mcp/prompts.js +49 -0
  258. package/src/mcp/resources.js +244 -0
  259. package/src/mcp/tools.js +1204 -0
  260. package/src/preflight/design-shapes.js +185 -0
  261. package/src/preflight/index.js +90 -0
  262. package/src/preflight/manifest-writer.js +264 -0
  263. package/src/preflight/scanner.js +206 -0
  264. package/src/preflight/secrets.js +134 -0
  265. package/src/preflight/session.js +246 -0
  266. package/src/query/attestation.js +285 -0
  267. package/src/query/coverage.js +308 -0
  268. package/src/query/formatters/json.js +21 -0
  269. package/src/query/formatters/mermaid.js +209 -0
  270. package/src/query/formatters/table.js +203 -0
  271. package/src/query/impact.js +173 -0
  272. package/src/query/index.js +9 -0
  273. package/src/query/trace.js +345 -0
  274. package/src/register-canary/fixture-loader.js +87 -0
  275. package/src/register-canary/index.js +10 -0
  276. package/src/register-canary/record-writer.js +132 -0
  277. package/src/register-canary/runner.js +156 -0
  278. package/src/req-baseline/gate.js +86 -0
  279. package/src/req-baseline/index.js +27 -0
  280. package/src/req-baseline/open-candidates.js +143 -0
  281. package/src/req-baseline/opt-out.js +195 -0
  282. package/src/req-baseline/sweep.js +230 -0
  283. package/src/req-detection/classifier.js +181 -0
  284. package/src/req-detection/index.js +9 -0
  285. package/src/req-detection/persist.js +55 -0
  286. package/src/review/index.js +325 -0
  287. package/src/review/mutation.js +117 -0
  288. package/src/review/ui-baseline-drift.js +138 -0
  289. package/src/server/index.js +178 -0
  290. package/src/server/routes.js +110 -0
  291. package/src/server/sse.js +118 -0
  292. package/src/setup/agent-setup.js +362 -0
  293. package/src/setup/identity-seed.js +104 -0
  294. package/src/setup/knowledge-seed.js +123 -0
  295. package/src/setup/managed-block.js +193 -0
  296. package/src/setup/managed-gitignore.js +166 -0
  297. package/src/setup/managed-markers.js +49 -0
  298. package/src/ui-baseline/defaults.js +119 -0
  299. package/src/ui-baseline/index.js +25 -0
  300. package/src/ui-baseline/manifest-writer.js +282 -0
  301. package/src/ui-baseline/session.js +178 -0
  302. package/src/ui-detection/classifier.js +192 -0
  303. package/src/verify/chain/index.js +190 -0
  304. package/src/verify/cli/cleanup.js +61 -0
  305. package/src/verify/cli/help.js +56 -0
  306. package/src/verify/cli/mcp.js +98 -0
  307. package/src/verify/cli/provision.js +71 -0
  308. package/src/verify/cli/report.js +71 -0
  309. package/src/verify/cli/run.js +155 -0
  310. package/src/verify/engine/brief.js +87 -0
  311. package/src/verify/engine/index.js +177 -0
  312. package/src/verify/engine/launcher.js +307 -0
  313. package/src/verify/mcp/tools.js +107 -0
  314. package/src/verify/profile/index.js +146 -0
  315. package/src/verify/provision/index.js +256 -0
  316. package/src/verify/report/index.js +139 -0
  317. package/src/verify/report/renderer.js +118 -0
  318. package/src/verify/verdict/index.js +246 -0
  319. package/src/view/doc-renderers/adr.js +44 -0
  320. package/src/view/doc-renderers/build-sequence.js +40 -0
  321. package/src/view/doc-renderers/fbs.js +128 -0
  322. package/src/view/doc-renderers/helpers.js +159 -0
  323. package/src/view/doc-renderers/index.js +12 -0
  324. package/src/view/doc-renderers/prd.js +45 -0
  325. package/src/view/doc-renderers/req.js +43 -0
  326. package/src/view/doc-renderers/tac.js +38 -0
  327. package/src/view/doc-renderers/tad.js +74 -0
  328. package/src/view/doc-renderers/test-suite.js +45 -0
  329. package/src/view/doc-renderers/user-story.js +63 -0
  330. package/src/view/html-page.js +462 -0
  331. package/src/view/index.js +63 -0
  332. package/src/view/live-client.js +338 -0
  333. package/src/view/mermaid-diagram.js +178 -0
  334. package/src/view/style.css +735 -0
  335. package/src/view/tree-model.js +152 -0
  336. package/src/view/vendored/mermaid.min.js +2607 -0
  337. package/src/view-supervisor/index.js +26 -0
  338. package/src/view-supervisor/logs.js +32 -0
  339. package/src/view-supervisor/manifest-writer.js +178 -0
  340. package/src/view-supervisor/persist-until.js +85 -0
  341. package/src/view-supervisor/supervisor.js +276 -0
  342. package/src/watch/index.js +152 -0
@@ -0,0 +1,190 @@
1
+ // Chain reader (spec §1, §5.2). Verify's ONLY structural input is the RCF
2
+ // chain — the acceptance contract. It reads the acceptance criteria off the
3
+ // chain through the SAME store code build-lite uses (core's walkTree), never
4
+ // the source tree, the test suite, or the builder's self-report (§9
5
+ // independence guarantee 2). It imports core's READ path only — never
6
+ // writer.js / init.js (§7.2 boundary).
7
+ //
8
+ // 0.7.0 additions (verification-integrity-cluster-spec §5.2, ui-design-gate
9
+ // §8.7): the flattened AC read-out is extended with two derived fields —
10
+ // `serviceAttestations` (aggregated from every FBS's `dependsOnServices[]`
11
+ // whose `acIds` include this AC) and `fbsUiBearing` (true when any of the
12
+ // FBSes bound to this AC has `uiBearing: true`). Both derivations live
13
+ // HERE, in verify's chain reader, per the tonight's clarification (Track A
14
+ // changelog 2026-07-31, Track B §18 N2 fold): core's walker surfaces raw
15
+ // document fields only, verify does the AC-level aggregation. Core 0.3.0
16
+ // carries a walker-guard test that asserts no such derivation leaks into
17
+ // core (`packages/rcf-lite/test/core/store/walker-0-7-0-fields.test.js:580`).
18
+ //
19
+ // The manifest fields verify's report and verdict pipelines need
20
+ // (`uiBaseline`, `browserVerification[]`) are read verbatim off `tree.manifest`
21
+ // and surfaced on the read result — verify's downstream code consumes them
22
+ // through the chain reader, not by re-opening the manifest.
23
+
24
+ import { access } from 'node:fs/promises';
25
+ import { dirname, join, resolve } from 'node:path';
26
+
27
+ import { rcfError } from '#core/errors';
28
+ import { walkTree } from '#core/store';
29
+
30
+ /**
31
+ * Resolve a --repo path-or-ref to the project root that holds rcf/manifest.json.
32
+ * v1 treats --repo as a filesystem path (path refs to remote sources are
33
+ * deferred). Walks the given path and its ancestors, like build's finder.
34
+ *
35
+ * @param {string} startPath
36
+ * @returns {Promise<string | null>}
37
+ */
38
+ export async function findProjectRoot(startPath) {
39
+ let dir = resolve(startPath);
40
+ // Walk up until we find rcf/manifest.json or hit the filesystem root.
41
+ // Bounded loop guard: dirname stabilises at the root.
42
+ for (let i = 0; i < 64; i += 1) {
43
+ try {
44
+ await access(join(dir, 'rcf', 'manifest.json'));
45
+ return dir;
46
+ } catch {
47
+ // not here — climb
48
+ }
49
+ const parent = dirname(dir);
50
+ if (parent === dir) break;
51
+ dir = parent;
52
+ }
53
+ return null;
54
+ }
55
+
56
+ /**
57
+ * Aggregate FBS `dependsOnServices[]` into per-AC serviceAttestations. For
58
+ * each AC that an FBS binds (via `fbs.acIds`), every entry in the FBS's
59
+ * `dependsOnServices` whose `acIds` mention the AC produces a
60
+ * `{serviceId, attestationMode}` on that AC's attestation list. An AC bound
61
+ * by several FBSes accumulates every service dependency across all of them,
62
+ * so an `AC-101-2` covered by FBS-011 (`resend` attested `mocked`) and
63
+ * FBS-014 (`stripe` attested `live`) surfaces both attestations on that
64
+ * AC. Order is deterministic: FBSes visited in `fbsItems` order, services
65
+ * in each FBS's declared order.
66
+ *
67
+ * @param {object[]} fbsItems
68
+ * @param {string} acId
69
+ * @returns {Array<{serviceId: string, attestationMode: string}>}
70
+ */
71
+ function serviceAttestationsFor(fbsItems, acId) {
72
+ const out = [];
73
+ for (const fbs of fbsItems ?? []) {
74
+ if (!Array.isArray(fbs.acIds) || !fbs.acIds.includes(acId)) continue;
75
+ for (const dep of fbs.dependsOnServices ?? []) {
76
+ if (!dep || typeof dep.id !== 'string' || typeof dep.attestationMode !== 'string') continue;
77
+ const acIds = Array.isArray(dep.acIds) ? dep.acIds : [];
78
+ if (acIds.length === 0 || acIds.includes(acId)) {
79
+ out.push({ serviceId: dep.id, attestationMode: dep.attestationMode });
80
+ }
81
+ }
82
+ }
83
+ return out;
84
+ }
85
+
86
+ /**
87
+ * True when any FBS bound to `acId` has `uiBearing: true`. Verify uses this
88
+ * flag to gate its UI-baseline verdict emission (§8.7) — a UI-bearing AC is
89
+ * exposed to `UI-BASELINE-UNMET` / `BROWSER-VERIFICATION-MISSING`, while a
90
+ * non-UI AC is not.
91
+ *
92
+ * @param {object[]} fbsItems
93
+ * @param {string} acId
94
+ * @returns {boolean}
95
+ */
96
+ function fbsUiBearingFor(fbsItems, acId) {
97
+ for (const fbs of fbsItems ?? []) {
98
+ if (!Array.isArray(fbs.acIds) || !fbs.acIds.includes(acId)) continue;
99
+ if (fbs.uiBearing === true) return true;
100
+ }
101
+ return false;
102
+ }
103
+
104
+ /**
105
+ * The FBS ids bound to `acId` (via `fbs.acIds`), preserving `fbsItems`
106
+ * order. Verify uses this to resolve the browserVerification entries for
107
+ * an AC when deciding UI-BASELINE-UNMET vs BROWSER-VERIFICATION-MISSING.
108
+ *
109
+ * @param {object[]} fbsItems
110
+ * @param {string} acId
111
+ * @returns {string[]}
112
+ */
113
+ function fbsIdsFor(fbsItems, acId) {
114
+ const out = [];
115
+ for (const fbs of fbsItems ?? []) {
116
+ if (!Array.isArray(fbs.acIds) || !fbs.acIds.includes(acId)) continue;
117
+ if (typeof fbs.fbsId === 'string') out.push(fbs.fbsId);
118
+ }
119
+ return out;
120
+ }
121
+
122
+ /**
123
+ * Read the acceptance contract from the chain. Returns the flattened list of
124
+ * acceptance criteria (each mapped back to its user story + requirement — the
125
+ * chain-node addressing the report carries), plus the resolved chainRef and
126
+ * the manifest fields verify's downstream stages consume.
127
+ *
128
+ * @param {object} opts
129
+ * @param {string} opts.repo - path-or-ref to the RCF chain source
130
+ * @param {string} [opts.chainRef] - which PRD/chain; default = the repo's PRD
131
+ * @returns {Promise<{ acs: Array<object>, chainRef: string, projectRoot: string, manifest: object } | import('#core/errors').RcfError>}
132
+ */
133
+ export async function readChain({ repo, chainRef } = {}) {
134
+ if (typeof repo !== 'string' || repo.length === 0) {
135
+ return rcfError({ kind: 'usage', message: '--repo (the RCF chain source) is required', field: 'repo' });
136
+ }
137
+ const projectRoot = await findProjectRoot(repo);
138
+ if (!projectRoot) {
139
+ return rcfError({
140
+ kind: 'missingFile',
141
+ message: `no RCF chain found at or above "${repo}" (no rcf/manifest.json). Verify needs an existing RCF chain as its acceptance contract.`,
142
+ filePath: repo,
143
+ });
144
+ }
145
+
146
+ const { tree, errors } = await walkTree({ projectRoot });
147
+ if (errors.length > 0) {
148
+ // A chain that does not load is not verifiable — surface the first
149
+ // structural error (errors-as-data; the CLI maps to a chain-load exit).
150
+ return rcfError({
151
+ kind: 'parseFailure',
152
+ message: `RCF chain failed to load: ${errors[0].message}`,
153
+ filePath: errors[0].filePath,
154
+ documentId: errors[0].documentId,
155
+ });
156
+ }
157
+
158
+ const resolvedRef = chainRef ?? tree.prd?.prdId ?? 'PRD-UNKNOWN';
159
+ const fbsItems = tree.fbsItems ?? [];
160
+ const acs = [];
161
+ for (const us of tree.userStories ?? []) {
162
+ for (const ac of us.acceptanceCriteria ?? []) {
163
+ acs.push({
164
+ acId: ac.id,
165
+ usId: us.usId,
166
+ reqId: us.reqId ?? null,
167
+ title: us.title ?? null,
168
+ description: ac.description ?? '',
169
+ given: ac.given ?? '',
170
+ when: ac.when ?? '',
171
+ then: ac.then ?? '',
172
+ testable: ac.testable !== false,
173
+ // 0.7.0 derived fields — verify does the aggregation here per Track A
174
+ // changelog 2026-07-31 and Track B §18 N2 fold; core does not.
175
+ serviceAttestations: serviceAttestationsFor(fbsItems, ac.id),
176
+ fbsUiBearing: fbsUiBearingFor(fbsItems, ac.id),
177
+ fbsIds: fbsIdsFor(fbsItems, ac.id),
178
+ });
179
+ }
180
+ }
181
+
182
+ // Manifest carries the ruled UI baseline and the browser-verification
183
+ // record ledger; verify's report + verdict layers read them from here so
184
+ // downstream never re-opens the manifest. Missing manifest is not a
185
+ // failure at this layer (the walker would have surfaced a load error) —
186
+ // an older chain simply has no 0.7.0 fields, and `manifest` is `{}`.
187
+ const manifest = tree.manifest ?? {};
188
+
189
+ return { acs, chainRef: resolvedRef, projectRoot, manifest };
190
+ }
@@ -0,0 +1,61 @@
1
+ // `rcf-verify cleanup` (spec §3, §6 cleanup contract). Tear down provisioned
2
+ // accounts / sandboxes / data recorded in the --provision file. On a
3
+ // persistent target this stops runs accreting `zzverify-` cruft. Reports what
4
+ // it removed. The concrete teardown route is app-specific (injected).
5
+
6
+ import { parseArgs } from 'node:util';
7
+
8
+ import { formatError, isRcfError } from '#core/errors';
9
+
10
+ import { readProvisionFile, cleanup } from '../provision/index.js';
11
+
12
+ export const HELP = `Usage: rcf-verify cleanup --provision <file> [--url <app-url>]
13
+
14
+ Tear down artefacts recorded in the --provision file (all prefixed
15
+ 'zzverify-'). Reports what was removed and what could not be.
16
+
17
+ Required:
18
+ --provision <file> The provisioning record written by run/provision
19
+
20
+ Exit codes:
21
+ 0 cleanup attempted; see the removed/blocked report
22
+ 2 usage error
23
+ 3 provision file could not be read
24
+ `;
25
+
26
+ /**
27
+ * @param {string[]} argv
28
+ * @param {object} [deps]
29
+ * @returns {Promise<number>}
30
+ */
31
+ export async function main(argv, deps = {}) {
32
+ const stdout = deps.stdout ?? process.stdout;
33
+ const stderr = deps.stderr ?? process.stderr;
34
+
35
+ let parsed;
36
+ try {
37
+ parsed = parseArgs({ args: argv, options: { provision: { type: 'string' }, url: { type: 'string' }, help: { type: 'boolean' } }, allowPositionals: false, strict: true });
38
+ } catch (err) {
39
+ stderr.write(`[error] usage ${err.message}\n`);
40
+ stderr.write(HELP);
41
+ return 2;
42
+ }
43
+ const flags = parsed.values;
44
+ if (flags.help) { stdout.write(HELP); return 0; }
45
+ if (!flags.provision) {
46
+ stderr.write('[error] usage cleanup requires --provision <file>\n');
47
+ stderr.write(HELP);
48
+ return 2;
49
+ }
50
+
51
+ const record = await (deps.readProvisionFile ?? readProvisionFile)(flags.provision);
52
+ if (isRcfError(record)) {
53
+ stderr.write(`${formatError(record, { verbose: true })}\n`);
54
+ return 3;
55
+ }
56
+
57
+ const provisioned = (record.credentials ?? []).map((c) => ({ kind: 'authAccount', ref: c.ref }));
58
+ const result = await cleanup({ provisioned, teardown: deps.teardown });
59
+ stdout.write(`${JSON.stringify(result, null, 2)}\n`);
60
+ return 0;
61
+ }
@@ -0,0 +1,56 @@
1
+ // Top-level and per-subcommand help (mirrors build's cli/help.js). British
2
+ // English, ASCII hyphens only.
3
+
4
+ import { HELP as RUN_HELP } from './run.js';
5
+ import { HELP as REPORT_HELP } from './report.js';
6
+ import { HELP as PROVISION_HELP } from './provision.js';
7
+ import { HELP as CLEANUP_HELP } from './cleanup.js';
8
+ import { HELP as MCP_HELP } from './mcp.js';
9
+
10
+ export const TOP_LEVEL_HELP = `Usage: rcf-verify <command> [options]
11
+
12
+ A fresh-context adversarial verifier. Given an RCF chain (the acceptance
13
+ contract) and a running app under a declared runtime profile, it launches an
14
+ isolated verifier agent that walks real user journeys adversarially and emits a
15
+ structured verdict stamped with the runtime it ran against.
16
+
17
+ This is an independent ship-readiness signal, not a correctness guarantee.
18
+
19
+ Commands:
20
+ run Run adversarial verification and emit a report artifact
21
+ report <path> Re-render a prior report artifact
22
+ provision Stand up prerequisite accounts/sandboxes/data standalone
23
+ cleanup Tear down provisioned artefacts (all prefixed 'zzverify-')
24
+ mcp Serve verify over MCP (local stdio)
25
+ help [command] Print help for a command
26
+
27
+ Options:
28
+ --version Print the package version and exit
29
+ --help Print this help and exit
30
+
31
+ Run 'rcf-verify help <command>' for command-specific help.
32
+ `;
33
+
34
+ const SUB_HELP = {
35
+ run: RUN_HELP,
36
+ report: REPORT_HELP,
37
+ provision: PROVISION_HELP,
38
+ cleanup: CLEANUP_HELP,
39
+ mcp: MCP_HELP,
40
+ };
41
+
42
+ /**
43
+ * @param {string[]} argv
44
+ * @param {object} [deps]
45
+ * @returns {Promise<number>}
46
+ */
47
+ export async function main(argv, deps = {}) {
48
+ const stdout = deps.stdout ?? process.stdout;
49
+ const topic = argv[0];
50
+ if (topic && SUB_HELP[topic]) {
51
+ stdout.write(SUB_HELP[topic]);
52
+ return 0;
53
+ }
54
+ stdout.write(TOP_LEVEL_HELP);
55
+ return 0;
56
+ }
@@ -0,0 +1,98 @@
1
+ // `rcf-verify mcp` subcommand (spec §10 MCP; mirrors build's cli/mcp.js).
2
+ // Wires process stdin/stdout/stderr into the core MCP protocol shell and
3
+ // serves the verify tool registry. stdout carries MCP messages and NOTHING
4
+ // else; all logging goes to stderr.
5
+
6
+ import { readFile } from 'node:fs/promises';
7
+ import { dirname, resolve as resolvePath } from 'node:path';
8
+ import { parseArgs } from 'node:util';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ import { createMcpServer, serveStreams } from '#core/mcp-shell';
12
+
13
+ import { createToolRegistry } from '../mcp/tools.js';
14
+
15
+ const here = dirname(fileURLToPath(import.meta.url));
16
+
17
+ export const HELP = `Usage: rcf-verify mcp [options]
18
+
19
+ Serve verify over the Model Context Protocol (local stdio). Exposes one tool,
20
+ rcf_verify_run, over the same in-process engine the CLI uses. stdout carries
21
+ protocol messages only; logging goes to stderr.
22
+
23
+ Options:
24
+ --verbose Per-request logging on stderr
25
+ --help Print this help
26
+ `;
27
+
28
+ const INSTRUCTIONS = 'Fresh-context adversarial verifier for RCF chains. rcf_verify_run takes an RCF '
29
+ + 'chain (the acceptance contract) and a running app under a declared runtime profile, launches an '
30
+ + 'isolated verifier agent that tries to DISPROVE the app against its acceptance criteria, and '
31
+ + 'returns a structured verdict stamped with the runtime it ran against. Only a "deployed" profile '
32
+ + '(or a declared --parity-env runtime) yields a SHIP verdict. This is an independent ship-readiness '
33
+ + 'signal, not a correctness guarantee.';
34
+
35
+ /**
36
+ * @param {string[]} argv - argv slice after `mcp`
37
+ * @param {object} [deps]
38
+ * @returns {Promise<number>}
39
+ */
40
+ export async function main(argv, deps = {}) {
41
+ const stdout = deps.stdout ?? process.stdout;
42
+ const stderr = deps.stderr ?? process.stderr;
43
+ const stdin = deps.stdin ?? process.stdin;
44
+ const onSignal = deps.onSignal ?? ((sig, fn) => process.on(sig, fn));
45
+
46
+ let parsed;
47
+ try {
48
+ parsed = parseArgs({ args: argv, options: { verbose: { type: 'boolean' }, help: { type: 'boolean' } }, allowPositionals: false, strict: true });
49
+ } catch (err) {
50
+ stderr.write(`[error] usage ${err.message}\n`);
51
+ stderr.write(HELP);
52
+ return 2;
53
+ }
54
+ if (parsed.values.help) {
55
+ stdout.write(HELP);
56
+ return 0;
57
+ }
58
+
59
+ const verbose = Boolean(parsed.values.verbose);
60
+ const log = {
61
+ info: verbose ? (line) => stderr.write(`[rcf-verify mcp] ${line}\n`) : () => {},
62
+ error: (line) => stderr.write(`${line}\n`),
63
+ };
64
+
65
+ const tools = createToolRegistry(deps);
66
+
67
+ const server = createMcpServer({
68
+ serverInfo: { name: 'rcf-verify-lite', version: await readPackageVersion() },
69
+ instructions: INSTRUCTIONS,
70
+ capabilities: { tools: {} },
71
+ handlers: {
72
+ 'tools/list': async () => ({ tools: tools.definitions }),
73
+ 'tools/call': async (params) => {
74
+ log.info(`tools/call ${params.name}`);
75
+ return await tools.call(params.name, params.arguments);
76
+ },
77
+ },
78
+ log,
79
+ });
80
+
81
+ stderr.write('rcf-verify mcp: serving\n');
82
+
83
+ const { done, stop } = serveStreams(server, { input: stdin, output: stdout, log });
84
+ onSignal('SIGINT', () => { log.info('received SIGINT, shutting down'); stop(); });
85
+ onSignal('SIGTERM', () => { log.info('received SIGTERM, shutting down'); stop(); });
86
+
87
+ return await done;
88
+ }
89
+
90
+ async function readPackageVersion() {
91
+ try {
92
+ const pkgPath = resolvePath(here, '..', '..', 'package.json');
93
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
94
+ return pkg.version ?? '0.0.0';
95
+ } catch {
96
+ return '0.0.0';
97
+ }
98
+ }
@@ -0,0 +1,71 @@
1
+ // `rcf-verify provision` (spec §3, §6). Stand up prerequisites standalone and
2
+ // write credentials to the --provision file. Derives what to provision from
3
+ // the chain's ACs (needs --repo). The concrete signup route is app-specific
4
+ // and agent-driven; where it can't be derived, this BLOCKS honestly rather
5
+ // than silently skipping (§6).
6
+
7
+ import { parseArgs } from 'node:util';
8
+
9
+ import { formatError, isRcfError } from '#core/errors';
10
+
11
+ import { readChain } from '../chain/index.js';
12
+ import { runProvisioning } from '../provision/index.js';
13
+
14
+ export const HELP = `Usage: rcf-verify provision --repo <path> --url <app-url> --provision <file>
15
+
16
+ Stand up prerequisite accounts / sandboxes / seed data for a later run.
17
+ Credentials are written to the --provision FILE only, never echoed.
18
+
19
+ Required:
20
+ --repo <path> RCF chain source (to derive what to provision)
21
+ --url <app-url> The running app
22
+ --provision <file> Where to write provisioned credentials
23
+
24
+ Exit codes:
25
+ 0 provisioning attempted; see the report of what was provisioned/blocked
26
+ 2 usage error
27
+ 3 chain could not be loaded
28
+ `;
29
+
30
+ /**
31
+ * @param {string[]} argv
32
+ * @param {object} [deps]
33
+ * @returns {Promise<number>}
34
+ */
35
+ export async function main(argv, deps = {}) {
36
+ const stdout = deps.stdout ?? process.stdout;
37
+ const stderr = deps.stderr ?? process.stderr;
38
+
39
+ let parsed;
40
+ try {
41
+ parsed = parseArgs({ args: argv, options: { repo: { type: 'string' }, url: { type: 'string' }, provision: { type: 'string' }, help: { type: 'boolean' } }, allowPositionals: false, strict: true });
42
+ } catch (err) {
43
+ stderr.write(`[error] usage ${err.message}\n`);
44
+ stderr.write(HELP);
45
+ return 2;
46
+ }
47
+ const flags = parsed.values;
48
+ if (flags.help) { stdout.write(HELP); return 0; }
49
+ if (!flags.repo || !flags.url || !flags.provision) {
50
+ stderr.write('[error] usage provision requires --repo, --url and --provision\n');
51
+ stderr.write(HELP);
52
+ return 2;
53
+ }
54
+
55
+ const chain = await (deps.readChain ?? readChain)({ repo: flags.repo });
56
+ if (isRcfError(chain)) {
57
+ stderr.write(`${formatError(chain, { verbose: true })}\n`);
58
+ return 3;
59
+ }
60
+
61
+ const { provisioning } = await runProvisioning({
62
+ acs: chain.acs,
63
+ url: flags.url,
64
+ provisionPath: flags.provision,
65
+ mode: 'run',
66
+ signup: deps.signup,
67
+ });
68
+
69
+ stdout.write(`${JSON.stringify(provisioning, null, 2)}\n`);
70
+ return 0;
71
+ }
@@ -0,0 +1,71 @@
1
+ // `rcf-verify report <report-path>` (spec §3, parallels `rcf view`).
2
+ // Re-render / summarise a prior report artifact using verify's own
3
+ // self-contained finding-renderer (§7.2 — no core view dependency).
4
+
5
+ import { readFile } from 'node:fs/promises';
6
+ import { resolve as resolvePath } from 'node:path';
7
+ import { parseArgs } from 'node:util';
8
+
9
+ import { formatError, isRcfError } from '#core/errors';
10
+
11
+ import { parseReport } from '../report/index.js';
12
+ import { renderReport } from '../report/renderer.js';
13
+
14
+ export const HELP = `Usage: rcf-verify report <report-path> [--json]
15
+
16
+ Re-render a prior report artifact. Default output is the human-readable
17
+ summary; --json re-emits the parsed artifact as JSON.
18
+
19
+ Exit codes:
20
+ 0 rendered
21
+ 2 usage error (no path)
22
+ 3 the report could not be read / parsed / is an unsupported schema
23
+ `;
24
+
25
+ /**
26
+ * @param {string[]} argv - argv slice after `report`
27
+ * @param {object} [deps]
28
+ * @returns {Promise<number>}
29
+ */
30
+ export async function main(argv, deps = {}) {
31
+ const stdout = deps.stdout ?? process.stdout;
32
+ const stderr = deps.stderr ?? process.stderr;
33
+ const cwd = deps.cwd ?? process.cwd();
34
+
35
+ let parsed;
36
+ try {
37
+ parsed = parseArgs({ args: argv, options: { json: { type: 'boolean' }, help: { type: 'boolean' } }, allowPositionals: true, strict: true });
38
+ } catch (err) {
39
+ stderr.write(`[error] usage ${err.message}\n`);
40
+ stderr.write(HELP);
41
+ return 2;
42
+ }
43
+ if (parsed.values.help) {
44
+ stdout.write(HELP);
45
+ return 0;
46
+ }
47
+ const target = parsed.positionals[0];
48
+ if (!target) {
49
+ stderr.write('[error] usage report: a <report-path> is required\n');
50
+ stderr.write(HELP);
51
+ return 2;
52
+ }
53
+
54
+ const reader = deps.readFile ?? readFile;
55
+ let raw;
56
+ try {
57
+ raw = await reader(resolvePath(cwd, target), 'utf8');
58
+ } catch (err) {
59
+ stderr.write(`[error] missingFile ${target}: ${err.message}\n`);
60
+ return 3;
61
+ }
62
+
63
+ const report = parseReport(raw);
64
+ if (isRcfError(report)) {
65
+ stderr.write(`${formatError(report, { verbose: true })}\n`);
66
+ return 3;
67
+ }
68
+
69
+ stdout.write(parsed.values.json ? `${JSON.stringify(report, null, 2)}\n` : renderReport(report));
70
+ return 0;
71
+ }