rcf-lite 0.28.2 → 0.29.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 (327) hide show
  1. package/CHANGELOG.md +317 -0
  2. package/bin/rcf.js +8 -0
  3. package/fixtures/canary-manifest.json +9 -9
  4. package/guidance/define-draft-shapes.md +89 -0
  5. package/guidance/define-intake.md +97 -0
  6. package/guidance/define-intent.md +107 -0
  7. package/guidance/discover-prototype.md +67 -0
  8. package/guidance/discovery-prototypes.md +69 -0
  9. package/guidance/harness-template.md +34 -0
  10. package/guidance/managed/agent-instructions-block.hash +1 -1
  11. package/guidance/managed/agent-instructions-block.md +34 -0
  12. package/guidance/manifest.json +9 -2
  13. package/package.json +1 -1
  14. package/rcf/adrs/adr-4120-canonical-json-hashing.json +30 -0
  15. package/rcf/adrs/adr-4121-define-sidecars.json +30 -0
  16. package/rcf/adrs/adr-4122-define-gates-blocking-and-optout.json +30 -0
  17. package/rcf/adrs/adr-4123-readiness-single-object.json +30 -0
  18. package/rcf/adrs/adr-4124-define-freeze-verb.json +30 -0
  19. package/rcf/adrs/adr-4126-persona-tagged-blockers-two-levels.json +40 -0
  20. package/rcf/adrs/adr-4127-readiness-tab-order-and-register.json +40 -0
  21. package/rcf/adrs/adr-4129-questions-harness-driven-loop.json +25 -0
  22. package/rcf/adrs/adr-4130-intake-harness-split-tool-scanned.json +20 -0
  23. package/rcf/adrs/adr-4132-prototypes-in-discovery-as-evidence.json +20 -0
  24. package/rcf/adrs/adr-4133-dag-dependency-depth-layout.json +40 -0
  25. package/rcf/adrs/adr-4134-index-json-route.json +35 -0
  26. package/rcf/adrs/adr-4135-readiness-po-register-and-adapter.json +35 -0
  27. package/rcf/adrs/adr-4136-embedding-host-fixture.json +35 -0
  28. package/rcf/code-nodes/cn-700.json +23 -0
  29. package/rcf/code-nodes/cn-701.json +14 -0
  30. package/rcf/code-nodes/cn-702.json +14 -0
  31. package/rcf/code-nodes/cn-703.json +16 -0
  32. package/rcf/code-nodes/cn-720.json +15 -0
  33. package/rcf/code-nodes/cn-721.json +15 -0
  34. package/rcf/code-nodes/cn-722.json +15 -0
  35. package/rcf/code-nodes/cn-723.json +18 -0
  36. package/rcf/code-nodes/cn-724.json +14 -0
  37. package/rcf/code-nodes/cn-725.json +37 -0
  38. package/rcf/code-nodes/cn-726.json +25 -0
  39. package/rcf/code-nodes/cn-727.json +23 -0
  40. package/rcf/code-nodes/cn-728.json +37 -0
  41. package/rcf/code-nodes/cn-729.json +26 -0
  42. package/rcf/code-nodes/cn-730.json +24 -0
  43. package/rcf/code-nodes/cn-731.json +20 -0
  44. package/rcf/code-nodes/cn-732.json +14 -0
  45. package/rcf/code-nodes/cn-733.json +14 -0
  46. package/rcf/code-nodes/cn-734.json +14 -0
  47. package/rcf/code-nodes/cn-735.json +20 -0
  48. package/rcf/code-nodes/cn-736.json +14 -0
  49. package/rcf/code-nodes/cn-737.json +14 -0
  50. package/rcf/code-nodes/cn-738.json +14 -0
  51. package/rcf/code-nodes/cn-739.json +18 -0
  52. package/rcf/code-nodes/cn-740.json +19 -0
  53. package/rcf/code-nodes/cn-741.json +16 -0
  54. package/rcf/code-nodes/cn-742.json +16 -0
  55. package/rcf/code-nodes/cn-743.json +15 -0
  56. package/rcf/code-nodes/cn-744.json +22 -0
  57. package/rcf/code-nodes/cn-745.json +16 -0
  58. package/rcf/code-nodes/cn-746.json +14 -0
  59. package/rcf/code-nodes/cn-747.json +18 -0
  60. package/rcf/code-nodes/cn-748.json +16 -0
  61. package/rcf/code-nodes/cn-749.json +16 -0
  62. package/rcf/code-nodes/cn-750.json +15 -0
  63. package/rcf/code-nodes/cn-751.json +18 -0
  64. package/rcf/code-nodes/cn-752.json +14 -0
  65. package/rcf/fbs/fbs-190.json +37 -0
  66. package/rcf/fbs/fbs-191.json +34 -0
  67. package/rcf/fbs/fbs-192.json +17 -0
  68. package/rcf/fbs/fbs-193.json +37 -0
  69. package/rcf/fbs/fbs-194.json +35 -0
  70. package/rcf/fbs/fbs-195.json +37 -0
  71. package/rcf/fbs/fbs-196.json +45 -0
  72. package/rcf/fbs/fbs-197.json +41 -0
  73. package/rcf/fbs/fbs-198.json +42 -0
  74. package/rcf/fbs/fbs-199.json +59 -0
  75. package/rcf/fbs/fbs-200.json +46 -0
  76. package/rcf/fbs/fbs-201.json +49 -0
  77. package/rcf/fbs/fbs-202.json +110 -0
  78. package/rcf/fbs/fbs-203.json +100 -0
  79. package/rcf/requirements/req-001.json +2 -1
  80. package/rcf/requirements/req-002.json +3 -2
  81. package/rcf/requirements/req-003.json +2 -1
  82. package/rcf/requirements/req-004.json +2 -1
  83. package/rcf/requirements/req-005.json +2 -1
  84. package/rcf/requirements/req-006.json +2 -1
  85. package/rcf/requirements/req-007.json +2 -1
  86. package/rcf/requirements/req-008.json +2 -1
  87. package/rcf/requirements/req-009.json +2 -1
  88. package/rcf/requirements/req-010.json +5 -2
  89. package/rcf/requirements/req-011.json +2 -1
  90. package/rcf/requirements/req-012.json +2 -1
  91. package/rcf/requirements/req-013.json +2 -1
  92. package/rcf/requirements/req-014.json +2 -1
  93. package/rcf/requirements/req-015.json +2 -1
  94. package/rcf/requirements/req-016.json +5 -2
  95. package/rcf/requirements/req-017.json +6 -2
  96. package/rcf/requirements/req-018.json +5 -2
  97. package/rcf/requirements/req-019.json +5 -2
  98. package/rcf/requirements/req-020.json +5 -2
  99. package/rcf/requirements/req-021.json +5 -2
  100. package/rcf/requirements/req-022.json +5 -2
  101. package/rcf/requirements/req-023.json +6 -4
  102. package/rcf/requirements/req-024.json +5 -2
  103. package/rcf/requirements/req-025.json +5 -2
  104. package/rcf/requirements/req-026.json +5 -2
  105. package/rcf/requirements/req-027.json +5 -2
  106. package/rcf/requirements/req-028.json +5 -2
  107. package/rcf/requirements/req-029.json +5 -2
  108. package/rcf/requirements/req-030.json +5 -2
  109. package/rcf/requirements/req-040.json +5 -2
  110. package/rcf/requirements/req-041.json +5 -2
  111. package/rcf/requirements/req-042.json +5 -2
  112. package/rcf/requirements/req-043.json +5 -2
  113. package/rcf/requirements/req-050.json +5 -2
  114. package/rcf/requirements/req-051.json +5 -2
  115. package/rcf/requirements/req-052.json +5 -2
  116. package/rcf/requirements/req-053.json +5 -2
  117. package/rcf/requirements/req-054.json +5 -2
  118. package/rcf/requirements/req-060.json +5 -2
  119. package/rcf/requirements/req-061.json +5 -2
  120. package/rcf/requirements/req-062.json +5 -2
  121. package/rcf/requirements/req-063.json +5 -2
  122. package/rcf/requirements/req-070.json +5 -2
  123. package/rcf/requirements/req-071.json +5 -2
  124. package/rcf/requirements/req-072.json +5 -2
  125. package/rcf/requirements/req-073.json +5 -2
  126. package/rcf/requirements/req-074.json +5 -2
  127. package/rcf/requirements/req-075.json +5 -2
  128. package/rcf/requirements/req-076.json +5 -2
  129. package/rcf/requirements/req-077.json +5 -2
  130. package/rcf/requirements/req-080.json +5 -2
  131. package/rcf/requirements/req-081.json +5 -2
  132. package/rcf/requirements/req-082.json +5 -2
  133. package/rcf/requirements/req-083.json +5 -2
  134. package/rcf/requirements/req-084.json +5 -2
  135. package/rcf/requirements/req-085.json +5 -2
  136. package/rcf/requirements/req-086.json +5 -2
  137. package/rcf/requirements/req-090.json +5 -2
  138. package/rcf/requirements/req-091.json +5 -2
  139. package/rcf/requirements/req-092.json +5 -2
  140. package/rcf/requirements/req-093.json +5 -2
  141. package/rcf/requirements/req-094.json +5 -2
  142. package/rcf/requirements/req-100.json +5 -2
  143. package/rcf/requirements/req-101.json +5 -2
  144. package/rcf/requirements/req-102.json +5 -2
  145. package/rcf/requirements/req-103.json +5 -2
  146. package/rcf/requirements/req-104.json +5 -2
  147. package/rcf/requirements/req-110.json +5 -2
  148. package/rcf/requirements/req-111.json +5 -2
  149. package/rcf/requirements/req-112.json +5 -2
  150. package/rcf/requirements/req-113.json +5 -2
  151. package/rcf/requirements/req-114.json +5 -2
  152. package/rcf/requirements/req-115.json +5 -2
  153. package/rcf/requirements/req-120.json +5 -2
  154. package/rcf/requirements/req-121.json +5 -2
  155. package/rcf/requirements/req-122.json +5 -2
  156. package/rcf/requirements/req-123.json +5 -2
  157. package/rcf/requirements/req-124.json +5 -2
  158. package/rcf/requirements/req-125.json +5 -2
  159. package/rcf/requirements/req-130.json +6 -2
  160. package/rcf/requirements/req-131.json +6 -2
  161. package/rcf/requirements/req-132.json +6 -2
  162. package/rcf/requirements/req-133.json +6 -2
  163. package/rcf/requirements/req-134.json +6 -2
  164. package/rcf/requirements/req-140.json +5 -2
  165. package/rcf/requirements/req-145.json +5 -2
  166. package/rcf/requirements/req-150.json +6 -2
  167. package/rcf/requirements/req-151.json +6 -2
  168. package/rcf/requirements/req-152.json +6 -2
  169. package/rcf/requirements/req-153.json +6 -2
  170. package/rcf/requirements/req-154.json +5 -2
  171. package/rcf/requirements/req-155.json +4 -1
  172. package/rcf/requirements/req-156.json +4 -1
  173. package/rcf/requirements/req-157.json +4 -1
  174. package/rcf/requirements/req-158.json +4 -1
  175. package/rcf/requirements/req-159.json +4 -1
  176. package/rcf/requirements/req-160.json +4 -1
  177. package/rcf/requirements/req-161.json +4 -1
  178. package/rcf/requirements/req-162.json +4 -1
  179. package/rcf/requirements/req-163.json +5 -2
  180. package/rcf/requirements/req-164.json +5 -2
  181. package/rcf/requirements/req-165.json +5 -2
  182. package/rcf/requirements/req-170.json +33 -0
  183. package/rcf/requirements/req-171.json +44 -0
  184. package/rcf/requirements/req-172.json +25 -0
  185. package/rcf/requirements/req-173.json +25 -0
  186. package/rcf/requirements/req-174.json +31 -0
  187. package/rcf/requirements/req-175.json +31 -0
  188. package/rcf/requirements/req-176.json +25 -0
  189. package/rcf/requirements/req-180.json +31 -0
  190. package/rcf/requirements/req-181.json +31 -0
  191. package/rcf/requirements/req-182.json +25 -0
  192. package/rcf/requirements/req-186.json +25 -0
  193. package/rcf/requirements/req-187.json +31 -0
  194. package/rcf/requirements/req-188.json +25 -0
  195. package/rcf/tacs/tac-4120-define-delta-and-freeze.json +21 -0
  196. package/rcf/tacs/tac-4121-define-ledgers.json +35 -0
  197. package/rcf/tacs/tac-4122-define-gates.json +61 -0
  198. package/rcf/tacs/tac-4123-define-readiness.json +61 -0
  199. package/rcf/tacs/tac-4124-define-freeze.json +28 -0
  200. package/rcf/tacs/tac-4127-readiness-view.json +49 -0
  201. package/rcf/tacs/tac-4128-define-guidance.json +53 -0
  202. package/rcf/tacs/tac-4129-define-questions.json +45 -0
  203. package/rcf/tacs/tac-4130-define-intake-brief.json +35 -0
  204. package/rcf/tacs/tac-4131-build-dag-view.json +39 -0
  205. package/rcf/tacs/tac-4132-id-lookup.json +42 -0
  206. package/rcf/tacs/tac-4133-readiness-po-layer.json +37 -0
  207. package/rcf/tacs/tac-4134-wespa-host-fixture.json +54 -0
  208. package/rcf/test-suites/ts-016.json +3 -3
  209. package/rcf/test-suites/ts-204.json +30 -0
  210. package/rcf/test-suites/ts-205.json +22 -0
  211. package/rcf/test-suites/ts-206.json +22 -0
  212. package/rcf/test-suites/ts-207.json +22 -0
  213. package/rcf/test-suites/ts-208.json +22 -0
  214. package/rcf/test-suites/ts-209.json +22 -0
  215. package/rcf/test-suites/ts-210.json +22 -0
  216. package/rcf/test-suites/ts-211.json +22 -0
  217. package/rcf/test-suites/ts-212.json +22 -0
  218. package/rcf/test-suites/ts-213.json +50 -0
  219. package/rcf/test-suites/ts-214.json +74 -0
  220. package/rcf/test-suites/ts-215.json +211 -0
  221. package/rcf/test-suites/ts-216.json +94 -0
  222. package/rcf/test-suites/ts-217.json +62 -0
  223. package/rcf/test-suites/ts-218.json +78 -0
  224. package/rcf/test-suites/ts-219.json +70 -0
  225. package/rcf/test-suites/ts-220.json +78 -0
  226. package/rcf/test-suites/ts-221.json +78 -0
  227. package/rcf/test-suites/ts-222.json +100 -0
  228. package/rcf/test-suites/ts-223.json +94 -0
  229. package/rcf/test-suites/ts-224.json +70 -0
  230. package/rcf/test-suites/ts-225.json +78 -0
  231. package/rcf/test-suites/ts-226.json +158 -0
  232. package/rcf/test-suites/ts-227.json +38 -0
  233. package/rcf/test-suites/ts-228.json +86 -0
  234. package/rcf/test-suites/ts-229.json +83 -0
  235. package/rcf/user-stories/us-15601.json +4 -4
  236. package/rcf/user-stories/us-17001.json +27 -0
  237. package/rcf/user-stories/us-17002.json +22 -0
  238. package/rcf/user-stories/us-17003.json +22 -0
  239. package/rcf/user-stories/us-17004.json +22 -0
  240. package/rcf/user-stories/us-17005.json +22 -0
  241. package/rcf/user-stories/us-17006.json +21 -0
  242. package/rcf/user-stories/us-17007.json +21 -0
  243. package/rcf/user-stories/us-17008.json +22 -0
  244. package/rcf/user-stories/us-17101.json +22 -0
  245. package/rcf/user-stories/us-17102.json +22 -0
  246. package/rcf/user-stories/us-17201.json +38 -0
  247. package/rcf/user-stories/us-17301.json +38 -0
  248. package/rcf/user-stories/us-17302.json +50 -0
  249. package/rcf/user-stories/us-17401.json +70 -0
  250. package/rcf/user-stories/us-17402.json +55 -0
  251. package/rcf/user-stories/us-17403.json +60 -0
  252. package/rcf/user-stories/us-17501.json +48 -0
  253. package/rcf/user-stories/us-17502.json +60 -0
  254. package/rcf/user-stories/us-17503.json +65 -0
  255. package/rcf/user-stories/us-17601.json +58 -0
  256. package/rcf/user-stories/us-18001.json +55 -0
  257. package/rcf/user-stories/us-18002.json +35 -0
  258. package/rcf/user-stories/us-18101.json +35 -0
  259. package/rcf/user-stories/us-18202.json +40 -0
  260. package/rcf/user-stories/us-18601.json +55 -0
  261. package/rcf/user-stories/us-18602.json +50 -0
  262. package/rcf/user-stories/us-18603.json +45 -0
  263. package/rcf/user-stories/us-18701.json +50 -0
  264. package/rcf/user-stories/us-18702.json +50 -0
  265. package/rcf/user-stories/us-18801.json +45 -0
  266. package/rcf/user-stories/us-204.json +59 -0
  267. package/rcf/user-stories/us-205.json +64 -0
  268. package/rcf/user-stories/us-206.json +54 -0
  269. package/releases/releases.yaml +31 -1
  270. package/src/cli/feedback.js +126 -10
  271. package/src/cli/freeze.js +730 -0
  272. package/src/cli/help.js +23 -0
  273. package/src/cli/ledger.js +615 -0
  274. package/src/cli/questions.js +288 -0
  275. package/src/cli/readiness.js +498 -0
  276. package/src/cli/view.js +29 -0
  277. package/src/core/store/writer.js +86 -6
  278. package/src/define/freeze-record.js +247 -0
  279. package/src/define/ledgers.js +712 -0
  280. package/src/feedback/redact.js +105 -19
  281. package/src/intake/index.js +1 -1
  282. package/src/intake/orchestrator.js +117 -0
  283. package/src/mcp/tools.js +354 -0
  284. package/src/query/delta.js +282 -0
  285. package/src/query/gates.js +1107 -0
  286. package/src/query/index.js +50 -0
  287. package/src/query/questions.js +438 -0
  288. package/src/query/readiness.js +574 -0
  289. package/src/server/index.js +60 -1
  290. package/src/server/routes.js +133 -4
  291. package/src/setup/identity-seed.js +17 -0
  292. package/src/view/build-dag.js +563 -0
  293. package/src/view/components/badge.js +41 -0
  294. package/src/view/components/diff.js +72 -0
  295. package/src/view/components/doc-row.js +62 -0
  296. package/src/view/components/empty-state.js +35 -0
  297. package/src/view/components/entity-selector.js +137 -0
  298. package/src/view/components/filter-bar.js +97 -0
  299. package/src/view/components/findings-list.js +49 -0
  300. package/src/view/components/index.js +12 -0
  301. package/src/view/components/pill.js +46 -0
  302. package/src/view/components/sub-tab-strip.js +43 -0
  303. package/src/view/components/toast.js +25 -0
  304. package/src/view/components-fixture.js +265 -0
  305. package/src/view/doc-renderers/architecture.js +304 -0
  306. package/src/view/doc-renderers/build.js +160 -0
  307. package/src/view/doc-renderers/helpers.js +2 -2
  308. package/src/view/doc-renderers/index.js +2 -0
  309. package/src/view/doc-renderers/prd.js +35 -6
  310. package/src/view/doc-renderers/req.js +10 -2
  311. package/src/view/doc-renderers/user-story.js +10 -5
  312. package/src/view/html-page.js +717 -300
  313. package/src/view/index.js +109 -3
  314. package/src/view/live-client.js +61 -8
  315. package/src/view/needs-work.js +79 -0
  316. package/src/view/page-init.js +2193 -0
  317. package/src/view/product-map.js +751 -0
  318. package/src/view/readiness/phrasebook.js +115 -0
  319. package/src/view/readiness/po-layer.js +288 -0
  320. package/src/view/readiness/question-adapter.js +329 -0
  321. package/src/view/readiness/stage-legend.js +71 -0
  322. package/src/view/readiness.js +505 -0
  323. package/src/view/style.css +2279 -266
  324. package/src/view/test-host/embed-client.js +121 -0
  325. package/src/view/test-host/fixture.js +183 -0
  326. package/src/view/test-host/lints.js +220 -0
  327. package/src/view/view-index.js +323 -0
@@ -0,0 +1,40 @@
1
+ {
2
+ "createdAt": "2026-10-01T18:00:00Z",
3
+ "updatedAt": "2026-10-01T18:00:00Z",
4
+ "adrId": "ADR-4127-readiness-tab-order-and-register",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "accepted",
9
+ "title": "Readiness is tab 1 always and renders the same object as the CLI; the register orders persona groups and never hides one",
10
+ "context": "DEFINE step 2 (0.29.0) exposes two verdicts on the one readiness ladder. The CLI prints them (PR B); the viewer must carry the same object so the two surfaces cannot disagree. Overview today is just the PRD body (phase 3.6 dropped the diagram), so it never earns first-tab status, and operators asked for a landing surface that leads with 'can I build and whose questions stand in the way'. The persona register from profile.md orders the CLI text report; the tab must honour the same register without ever hiding the non-register group.",
11
+ "decision": "Readiness is tab 1 of the HTML review surface, always, and renders the computeReadiness result directly. The former Overview tab is renamed PRD with no content change; the data-tab=overview and id=tab-overview identifiers are preserved so every existing `#tab-overview` hash link still resolves. The viewer's renderer is pure: it reads the result object and the profile text and emits HTML; it never recomputes readiness or hits disk. The profile register from pickRegister orders the persona groups (productOwner-first by default; engineer-first when the register says engineer; unstated prints the pair in default order). The non-register group is always rendered with its count visible; collapsed by default, never omitted from the DOM. The Freeze now control is enabled iff levels.readyToBuild.ok; disabled, it names the failing gates from levels.readyToBuild.blockedBy. There is no intent-complete button at L1 (nothing to record; ADR-4126). The 0.29.0 diff component in the delta list is document-level only; criterion-level before-and-after is deferred to the UX review (open question 7 of the step 2 spec).",
12
+ "consequences": "One readiness object renders three ways (CLI text, CLI JSON, viewer tab) with no second register of truth; AC-18001-7 asserts the identity by walking the rendered anchors and matching them to the --json for the same tree hash. The Overview-to-PRD rename is cosmetic for hash routing and content; existing anchors keep working. Shared components land once, pay down the drift between tabs, and are additive on the Product Map and Requirements tabs in 0.29.0 (re-pointing is a follow-up where the swap is not mechanical). Deferring the criterion-level diff keeps PR C small and leaves the AC-class before-and-after question to the UX review it belongs to.",
13
+ "alternativesConsidered": [
14
+ {
15
+ "name": "Readiness as the fifth tab, Overview stays first",
16
+ "summary": "Keep Overview as the landing tab and add Readiness at the end.",
17
+ "reasonNotChosen": "Overview is one block today (the PRD body); it does not earn the landing. Making an operator click to see the verdict is friction for the thing the surface exists to answer."
18
+ },
19
+ {
20
+ "name": "Rename to data-tab=prd and migrate hash anchors",
21
+ "summary": "Rename both the label and the identifier so the surface reads consistently.",
22
+ "reasonNotChosen": "Every existing `#tab-overview` link in documents and in commit messages breaks. The label is for humans; the identifier is for links. Keeping the identifier is cheaper than migrating every link."
23
+ },
24
+ {
25
+ "name": "Hide the non-register persona group",
26
+ "summary": "When the register is productOwner, omit the engineer group from the DOM entirely (and vice versa).",
27
+ "reasonNotChosen": "AC-18001-5 is must-not for a reason: hiding a persona breaks the parity the CLI --json offers. The tab is a diagnostic, not a filter; the collapse-with-count-visible pattern honours the register without losing visibility."
28
+ },
29
+ {
30
+ "name": "Recompute readiness in the browser",
31
+ "summary": "Ship the raw tree to the client and compute readiness there.",
32
+ "reasonNotChosen": "Two implementations of the same compute are two chances to disagree. The server already walks the tree; piping the computed object into the HTML keeps the no-recompute invariant and makes the live-SSE path trivial."
33
+ },
34
+ {
35
+ "name": "Ship criterion-level diff in 0.29.0",
36
+ "summary": "Render AC-level before-and-after inside the delta list, with a per-AC collapsible pane.",
37
+ "reasonNotChosen": "Open question 7 of the step 2 spec parks the AC-class before-and-after question for a UX review. Shipping without that review risks a design the review would reject; document-level diff carries the signal 0.29.0 needs."
38
+ }
39
+ ]
40
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "adrId": "ADR-4129-questions-harness-driven-loop",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "0.1.0",
6
+ "status": "accepted",
7
+ "title": "The intent-complete loop is harness-driven; rcf-lite computes questions with write-backs and never holds conversation state",
8
+ "context": "DEFINE step 3 (spec 2026-10-01, section 1.1) requires a product owner to reach `levels.intentComplete.ok === true` without an engineer. The question is whether rcf-lite should hold a conversation state (a stored loop file, a conversation engine) or stay stateless and let the harness (Claude Code or any MCP-wired agent) drive.",
9
+ "decision": "the loop is harness-driven; rcf-lite computes questions with write-backs and never holds conversation state. computeQuestions is a pure function of (readiness, tree, ledgers, profileText, persona); the harness runs the write-back a question carries, calls readiness again and asks the next question.",
10
+ "consequences": "The loop is monotone (each answer removes its item), the compute is testable in isolation, and the harness can be swapped without changing rcf-lite. The cost is that the harness must run readiness and questions each turn, which is a cheap pure call.",
11
+ "alternativesConsidered": [
12
+ {
13
+ "name": "A conversation engine in rcf-lite",
14
+ "summary": "Hold an LLM-driven loop inside rcf-lite that elicits the PO directly.",
15
+ "reasonNotChosen": "An LLM dependency inside rcf-lite reverses the method's shape: rcf-lite is the referee the harness consults, not an agent of its own. It also forces a model and provider choice that the harness has already made."
16
+ },
17
+ {
18
+ "name": "A stored loop file",
19
+ "summary": "Keep a loop state file under rcf/define/ that tracks which question was asked last.",
20
+ "reasonNotChosen": "The stale state the detection model removed. A loop file would drift from the tree the moment any writer edits a ledger outside the loop, and would need its own validate pass; the four ledgers plus the profile already carry every state the loop needs."
21
+ }
22
+ ],
23
+ "createdAt": "2026-10-02T18:00:00Z",
24
+ "updatedAt": "2026-10-02T18:00:00Z"
25
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "adrId": "ADR-4130-intake-harness-split-tool-scanned",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "0.1.0",
6
+ "status": "accepted",
7
+ "title": "Intake is harness-split and tool-scanned; findings become open-question statements",
8
+ "context": "DEFINE step 3 (spec 2026-10-01, section 2.1) requires a document intake path where a product owner hands over a folder and reaches `Intent-complete: yes` without an engineer driving. Splitting a source into kinded statements is the agent's job (the harness writes one statement file per source, operator corrects); the mechanical scans and the follow-up questions are rcf-lite's. The question is how finding-level signals (duplicates, contradictions, ambiguities) reach the loop.",
9
+ "decision": "intake is harness-split and tool-scanned; findings become open-question statements. The harness writes `<source>.statements.md` beside each source; `brief add --from` mints the statements and `runIntakeScansOnDelta` runs over the new ids against the existing body; every finding is appended as one `openQuestion` statement whose source names the scan and the ids involved. The loop then reads one check (`brief:openQuestions`) and the owner answers in one place.",
10
+ "consequences": "The owner sees findings as questions, not a separate list. One place carries intent (the brief ledger) and one check reads it. The harness can be swapped: any agent that writes the statement file shape and runs `brief add --from` reaches the same loop. The cost is that a finding's full detail lives inside the openQuestion text and its `scan:` source marker; a reviewer who wants a flat findings list builds it from `kind === 'openQuestion' && source startsWith 'scan:'`.",
11
+ "alternativesConsidered": [
12
+ {
13
+ "name": "A separate findings list on the manifest",
14
+ "summary": "Keep an array of findings on rcf/manifest.json alongside the ledgers, read by the loop as a parallel surface.",
15
+ "reasonNotChosen": "A second place the loop would have to read, and a second place that could drift from the brief ledger. Making findings statements collapses two surfaces into the one the loop already needs."
16
+ }
17
+ ],
18
+ "createdAt": "2026-10-02T18:00:00Z",
19
+ "updatedAt": "2026-10-02T18:00:00Z"
20
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "adrId": "ADR-4132-prototypes-in-discovery-as-evidence",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "0.1.0",
6
+ "status": "accepted",
7
+ "title": "Prototypes live in DISCOVERY as evidence; code is burned; salvage is re-derivation",
8
+ "context": "DEFINE step 3 (spec 2026-10-01, section 5) asks where a learning prototype belongs. A prototype is useful for the questions it provokes; its code is a product of exploration under a different set of rules than the project proper, so promoting it would smuggle unowned decisions past the chain.",
9
+ "decision": "prototypes live in DISCOVERY as evidence; code is burned; salvage is re-derivation. The prototype is recorded in the intake classification as an artefact of kind `other`; its outputs are brief statements with source spans `prototype:<name>:<observation>`; the burn is a single `constraint` statement naming the prototype, its commit sha and burn date. The only middle path is salvage by re-derivation: extract requirements from the prototype as evidence, run them through intake and the PO loop like any document, rebuild from the tree.",
10
+ "consequences": "Provenance stays whole: every shape in the tree has a chain back to a PO-sanctioned statement, not a prototype's incidental choices. The cost is one managed-block rule the harness picks up, one guidance file (`guidance/discovery-prototypes.md`) and the honest message that a prototype's code is reference only; nothing in the build consumes a prototype directly.",
11
+ "alternativesConsidered": [
12
+ {
13
+ "name": "A prototype mode or promotion path (inverts provenance)",
14
+ "summary": "Keep the prototype as a privileged tree that can be promoted into the project tree, or run a prototype mode that bypasses parts of the chain while the prototype is live.",
15
+ "reasonNotChosen": "Promotion inverts provenance: a shape would reach the project tree without the PO loop that owns every settled shape. The method's whole worth is that every requirement, story, AC and shape has a known origin; a promotion path would let an incidental prototype choice bypass that trace, and no amount of auditing after the fact can restore the owner."
16
+ }
17
+ ],
18
+ "createdAt": "2026-10-03T00:00:00Z",
19
+ "updatedAt": "2026-10-03T00:00:00Z"
20
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "createdAt": "2026-10-02T18:00:00Z",
3
+ "updatedAt": "2026-10-02T18:00:00Z",
4
+ "adrId": "ADR-4133-dag-dependency-depth-layout",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "accepted",
9
+ "title": "The Build/DAG canvas lays FBS by dependency depth, orders rows by buildOrder, measures the critical path by node count and parks unconnected FBS in a hidden lane with a count",
10
+ "context": "The viewer UI refresh (design doc section 7) names a DAG for the Build tab's second sub-tab. The dogfood tree has 87 FBS: 46 declare no dependency, 36 are unconnected both ways, 1 is in progress and `estimatedHours` is sparse. A force-directed layout is unstable across reloads (the review surface is a diff-worthy artefact; identical trees must render identical pages), and grouping purely by `domain` hides the thing the DAG exists to show (which FBS feeds which). The v1 rcf-tools DAG proved the tiered layout works for the engineer audience the Build tab serves (decision 14); its gaps - dark-only palette, no inspector, orphan pile-up swamping the graph, no deep links, horizontal page scroll - are the ones this PR fixes.",
11
+ "decision": "Columns in the DAG are dependency-depth tiers computed from `dependsOnFbsIds` (0 for an FBS that declares no dependency; max over `depth(dep) + 1` otherwise). Rows inside a column are `buildOrder` ascending with `fbsId` lexicographic tie-break. Critical path is the longest chain by node count across the DAG, because `estimatedHours` is sparse on the dogfood tree (section 7 gap b); the critical-path chip highlights every member of that longest chain. Unconnected FBS - no incoming and no outgoing `dependsOnFbsIds` edge - sit in a lane below the main graph, hidden by default, with the count on the toggle. Cycle members (should any appear) are excluded from the depth layout and surfaced as a shared `findings-list` finding on the toolbar; non-cycle FBS still render so one bad edge never blocks the view (AC-204-6). The selection round-trips `#tab=build&sub=dag&entity=<id>` through the existing writeHash so a node is a link (decision 11, extended).",
12
+ "consequences": "The DAG is deterministic across reloads: same tree, same page. The depth tiers reflect what actually gates work, so the leftmost column is the \"start here\" set and buildable-now items concentrate on its right edge. Hiding unconnected FBS by default stops the 36-row pile-up swamping the dogfood tree - the toggle count makes them discoverable. Choosing node count for the critical path means the chip stays meaningful on the dogfood tree today and will only sharpen when `estimatedHours` fills in; the ADR explicitly leaves the hours-weighted critical path to a later change. Deep-link parity with Specs (`#tab=build&sub=specs&entity=<id>`) lets the inspector's Open-in-Specs link move cleanly across the two sub-tabs.",
13
+ "alternativesConsidered": [
14
+ {
15
+ "name": "Force-directed layout",
16
+ "summary": "Lay nodes with a physics simulation and let the dependency edges settle.",
17
+ "reasonNotChosen": "Position jitters across reloads so screenshots and visual diffs are noisy; the review surface is diff-worthy by contract, and a layout that is not deterministic cannot be screenshotted into design evidence or compared across runs."
18
+ },
19
+ {
20
+ "name": "Group by domain only",
21
+ "summary": "Columns are areas (view, cli, build, ...) and rows are FBS in each area.",
22
+ "reasonNotChosen": "Hides the dependency structure that the DAG exists to show. Area is already a FilterBar facet on the Specs sub-tab and a badge on each node; adding a second encoding of it costs the dependency axis."
23
+ },
24
+ {
25
+ "name": "Hours-weighted critical path",
26
+ "summary": "Measure critical path by sum of `estimatedHours` along a chain.",
27
+ "reasonNotChosen": "`estimatedHours` is sparse on the dogfood tree (section 7 gap b); a weight that is missing on most nodes would silently bias the chip toward whichever subtree happens to have estimates. Node count is the honest signal today; the hours-weighted path becomes an opt-in once estimates are populated across the tree."
28
+ },
29
+ {
30
+ "name": "Render unconnected FBS inline (no lane)",
31
+ "summary": "Lay unconnected FBS as a 36-row column 0 alongside the connected tier 0.",
32
+ "reasonNotChosen": "Doubles the visual height of the leftmost column with nodes that have no edges, swamping the dependency graph the DAG exists to show. The toggled lane with the count keeps them one click away without the pile-up."
33
+ },
34
+ {
35
+ "name": "Block the DAG on any cycle",
36
+ "summary": "If the tree carries a `dependsOnFbsIds` cycle, refuse to render the DAG and show an error.",
37
+ "reasonNotChosen": "A bad edge on a 90-node graph should not take the view down; the `rcf validate` CLI is the enforcement surface. The DAG reports the cycle as a shared finding and renders the rest so the engineer can see what still composes."
38
+ }
39
+ ]
40
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "createdAt": "2026-10-02T19:00:00Z",
3
+ "updatedAt": "2026-10-02T19:00:00Z",
4
+ "adrId": "ADR-4134-index-json-route",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "accepted",
9
+ "title": "The ID lookup index is served as a new ./index.json route, built from BuiltTreeModel on every rewalk and fetched lazily on first open",
10
+ "context": "The viewer UI refresh (design doc section 6) adds a type-ahead ID lookup over every document id plus every AC. On the dogfood tree that is roughly 1,600 rows and well under 300 KB of JSON. The viewer is served through wespa's reverse proxy; new routes 404 until wespa's allow-list is extended (Dex, 2026-10-02; design section 9), so every new route must be named in the release notes alongside the version that introduces it. The viewer page is already live-updated through an SSE tree-update event, so an index that mirrors the tree needs to refresh on the same event. The alternatives considered were inlining the index into `./scope.json` or rebuilding the index in the browser; both have load-bearing downsides.",
11
+ "decision": "The index is a new `./index.json` route served from the viewer, built once per rewalk from BuiltTreeModel and returned as `{ rows: Array<{ id, kind, title, snippet, parent, tab }> }`. The route is fetched lazily on first LookupModal open and refreshed when the next SSE `tree-update` event lands (the client marks its cache dirty and refetches on the next open). The route is named in the release notes so wespa's proxy allow-list is extended before the version pin bump; it is the fourth or later such release-noted viewer route after PR 1's `./page-init.js`.",
12
+ "consequences": "The browser pays one small fetch on the first lookup and nothing on subsequent opens until the tree changes; the server owns the index projection once per rewalk, so the model-to-rows mapping cannot drift between the viewer's doc renderers and the lookup results. The release-notes contract means every new viewer route the embedding host must allow is discoverable from CHANGELOG. The index body is `{ rows }` rather than a bare array so a future field (version, generatedAt, counts) can be added without a breaking shape change.",
13
+ "alternativesConsidered": [
14
+ {
15
+ "name": "Inline the index into ./scope.json",
16
+ "summary": "Extend the existing deep-link scope endpoint to carry the lookup rows.",
17
+ "reasonNotChosen": "`./scope.json` is a hot path on every page load and is parsed by the deep-link client; bloating it with ~1,600 lookup rows pays the fetch cost even for sessions that never open the modal, and couples two independent features."
18
+ },
19
+ {
20
+ "name": "Rebuild the index in the browser from the rendered DOM",
21
+ "summary": "Walk the DOM after each SSE innerHTML swap to collect every data-doc-id and AC li.",
22
+ "reasonNotChosen": "Duplicates the BuiltTreeModel projection in the viewer client, misses the kind, parent and tab metadata the DOM does not carry in a canonical shape, and silently drifts when a renderer stops emitting a given document."
23
+ },
24
+ {
25
+ "name": "Inline the index into the shell HTML as a <script type=\"application/json\">",
26
+ "summary": "Server-render the rows directly inside the page so no second request is needed.",
27
+ "reasonNotChosen": "Grows every first paint by ~300 KB even when the lookup is never opened and leaks straight into the HTML stream the live client swaps on every SSE tree-update, so the cost is paid again per update. The modal is a cold path; the lazy fetch is cheaper."
28
+ },
29
+ {
30
+ "name": "Keep the index unversioned and refetch on a wall-clock timer",
31
+ "summary": "Have the client poll `./index.json` every N seconds instead of listening to the SSE event.",
32
+ "reasonNotChosen": "Breaks the viewer's live-swap discipline: the rest of the surface refreshes on `tree-update` with no polling anywhere, so an index that polls would be the only timer-driven feature and race the SSE swap during edits."
33
+ }
34
+ ]
35
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "createdAt": "2026-10-02T19:30:00Z",
3
+ "updatedAt": "2026-10-02T19:30:00Z",
4
+ "adrId": "ADR-4135-readiness-po-register-and-adapter",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "accepted",
9
+ "title": "The Readiness tab fixes its two-audience split by the profile register and uses a feature-detected adapter seam for the PO question block",
10
+ "context": "The viewer UI refresh (design doc section 1 decision 1 and section 8) splits the Readiness tab into a product-owner-first layer and a For-engineers DocRow at the foot. The chain's DEFINE step 3 PR 2 reserves a new `readiness.questions[]` shape built by `computeQuestions`; until that lands the viewer still has to render the PO question block from today's `personas.productOwner.blockers`. The design doc section 11 recommendation 4 asks for the phrasebook now so the pilot does not wait on DEFINE step 3. Baz's amendment 12 of 2026-10-02T13:40Z requires both VerdictCards to render at equal height with the chain term muted beside the plain label on both cards (`intent-complete`, `ready-to-build`).",
11
+ "decision": "The Readiness tab fixes its two-audience split by the `profile.register`: `productOwner` or absent / unstated opens the four PO blocks and keeps the For-engineers DocRow closed; `engineer` opens the For-engineers DocRow at the foot. No tab changes shape or wording by where the tree sits in DEFINE (decision 14). The phrasebook ships now (one entry per row of design doc section 8's PO question table, keyed by check id) and the adapter seam `toQuestions(readiness)` is a pure module exported from src/view/readiness/question-adapter.js: it reads blockers today and switches to `readiness.questions` when the shape is present (feature-detected via `preferReadinessQuestions(readiness)`); the call site in the tab reads only the adapter output. The engineer DOM is never removed, only collapsed under the DocRow (AC-18002-3). The CLI verdict strings keep rendering inside the For-engineers DocRow verbatim.",
12
+ "consequences": "The pilot can land PR 8 ahead of DEFINE step 3 PR 2 without a second rewrite when the questions shape arrives; the switch is one feature-detect line and the shape is drop-in. The two-audience split is deterministic (the register alone decides opening state) so the Remi pilot never sees a tab that reshapes under its feet as the tree moves through DEFINE. The engineer DOM invariant guards parity with `rcf define readiness --json`: every id the CLI prints is still in the viewer's DOM, just one level down. Equal-height VerdictCards and the muted chain term land once, from CSS, so no scripted measurement is needed.",
13
+ "alternativesConsidered": [
14
+ {
15
+ "name": "Wait for DEFINE step 3 PR 2 to land `readiness.questions` before shipping PR 8",
16
+ "summary": "Postpone PR 8 until the questions compute is in place so the viewer never reads blockers directly.",
17
+ "reasonNotChosen": "Blocks the Remi pilot and the hand-over for a chain PR whose timing is not in this surface's control. The adapter seam is the smallest possible layer that lets both sources flow through one shape, so the extra module today is a one-day cost that buys weeks of parallel progress."
18
+ },
19
+ {
20
+ "name": "Re-render the tab by DEFINE progression state (open For-engineers once the PO blocks pass)",
21
+ "summary": "Have the Readiness tab change shape based on where the tree sits in DEFINE (so the engineer view only appears when the PO view is empty).",
22
+ "reasonNotChosen": "Contradicts decision 14 (`two audiences, fixed by tab, never by progression state`). An engineer looking at the tab mid-DEFINE loses the engineer view they rely on, and a product owner who reaches intent-complete suddenly sees a block that was never there before. The register is the audience; the state is not."
23
+ },
24
+ {
25
+ "name": "Remove the engineer blockers from the DOM in the productOwner register",
26
+ "summary": "Hide the engineer-level findings entirely when `profile.register === 'productOwner'` so the page is quieter.",
27
+ "reasonNotChosen": "AC-18002-3 is must-not for a reason: hiding a persona breaks parity with `rcf define readiness --json`. A collapsed DocRow carries the signal the diagnostic needs without crowding the PO view."
28
+ },
29
+ {
30
+ "name": "Script equal-height cards with ResizeObserver",
31
+ "summary": "Measure both VerdictCards after render and copy the taller height onto the shorter one.",
32
+ "reasonNotChosen": "The relative-script policy (PR 1) keeps the shell free of inline script and keeps the DOM a server render that the SSE path can swap wholesale. CSS grid or flex with `align-items: stretch` on a shared parent gives equal height for free and survives theme swaps."
33
+ }
34
+ ]
35
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "createdAt": "2026-10-02T20:00:00Z",
3
+ "updatedAt": "2026-10-02T20:00:00Z",
4
+ "adrId": "ADR-4136-embedding-host-fixture",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "accepted",
9
+ "title": "The embedding contract is pinned by a CI host fixture; three lints keep framing, target='_top' and localStorage namespace in place; the fixture does not persist theme itself so a passing run proves the iframe reads the message",
10
+ "context": "Dex's wespa requirements (2026-10-02; `docs/2026-10-02_dex-wespa-embedding-requirements.md`) and the viewer UI refresh design section 9 pin the viewer's embedding contract: same-origin iframe behind wespa's reverse proxy under `/rcf-viewer/`, `embed=1` suppresses the title block and footer, `theme=light|dark|auto` on first paint, `{ type: \"rcf-view-theme\", theme }` postMessage with `event.origin` checked for live changes, viewer fills 100% viewport height and owns its scrolling, no framing-blocking headers, no `target=\"_top\"` links, localStorage namespaced under `rcf-view:v1:`, no outbound postMessage from the viewer. Wespa smoke on the live wespa shell is Dex's side; a regression there would reach wespa before the viewer saw it, so the viewer needs its own proof in CI. Unit-testing the message handler without an iframe does not prove framing works (a `X-Frame-Options` or wrong CSP would break the live embed and pass the unit test).",
11
+ "decision": "Ship a same-origin test-host fixture served by the viewer's own dev server at `/test-host.html` (plus `/test-host.js` for the client-side wiring) that mimics the wespa shell: a 56px header carrying a Light/Dark/Auto theme toggle, a resizable sidebar (64px collapsed, 200-400px expanded, user-resizable live via a drag handle), a content pane carrying a single `<iframe src=\"./?embed=1&theme=light\">`. The fixture is off the default route surface - gated on a `--test-host` CLI flag or the `RCF_VIEW_TEST_HOST=1` env var and 404s otherwise - so wespa's proxy allow-list is unaffected. Three lints run in the test suite: (1) framing lint on `GET /` (no `X-Frame-Options`, no CSP `frame-ancestors` that forbids same-origin); (2) `target=\"_top\"` grep over every rendered page (zero occurrences); (3) localStorage namespace grep over `src/view/**` (every key starts with `rcf-view:v1:`). The fixture does not persist its theme choice to localStorage so a passing run proves the iframe reads the postMessage (and not the host's own storage).",
12
+ "consequences": "The viewer's suite catches a framing regression (headers, `target=\"_top\"`, un-namespaced localStorage key) before it reaches wespa. The fixture is the first production use of the test-host surface, so a `--test-host` flag plus env guard is established as the pattern for every future embedding-shell fixture (adding a `/test-host/<other>.html` becomes a one-liner on the router). Keeping the fixture off the default route surface means the release notes do not need to extend wespa's proxy allow-list for every lint test; a later PR that wants to ship the host alongside the viewer can do so without re-platforming the fixture itself. The lints run on the dogfood tree today and fail on main if a future PR adds a framing-blocking header or writes to localStorage with a non-namespaced key, so the viewer PR that introduces the regression carries the fix in the same commit.",
13
+ "alternativesConsidered": [
14
+ {
15
+ "name": "Wespa-side-only smoke",
16
+ "summary": "Let wespa test the viewer's embedding against the live wespa shell; the viewer ships no proof of its own.",
17
+ "reasonNotChosen": "A regression lands on main before wespa sees it; wespa's smoke runs on a different repo on a different cadence, so a viewer refactor that drops `embed=1` or writes an un-namespaced localStorage key breaks every embedder and the viewer's own CI is clean. The lints run in the viewer's suite catch the regression at the PR that introduces it."
18
+ },
19
+ {
20
+ "name": "Unit-test the message handler without an iframe",
21
+ "summary": "Drive the viewer's `rcf-view-theme` postMessage handler through `node:vm` or a jsdom shim and assert the handler flips `data-theme`.",
22
+ "reasonNotChosen": "Does not prove framing. A later PR that adds `X-Frame-Options: DENY` to `GET /` would pass the unit test and break every embedder. The host fixture exercises the whole stack: HTTP headers, iframe mount, postMessage, data-theme stamp."
23
+ },
24
+ {
25
+ "name": "Ship the test host under the default route surface",
26
+ "summary": "Serve `/test-host.html` unconditionally; a developer can hit it directly to eyeball the embed.",
27
+ "reasonNotChosen": "Wespa's proxy allow-lists routes; a new default-on route needs an allow-list extension and a release-note version pin. The fixture is a test-suite proof, not a shipped UI; keeping it behind `--test-host` / `RCF_VIEW_TEST_HOST=1` means the production route surface stays `/`, `/events`, `/live-client.js`, `/page-init.js`, `/style.css`, `/mermaid.min.js`, `/product-map/*`, `/scope.json`, `/index.json`, `/_fixtures/components`."
28
+ },
29
+ {
30
+ "name": "Persist the theme in the host fixture itself",
31
+ "summary": "Store the Light/Dark choice in `localStorage` on the host so a reload keeps the toggle state.",
32
+ "reasonNotChosen": "A passing run would then not prove the iframe honours the postMessage: the host could set `data-theme` on the iframe URL and the iframe would never need to read the message. By refusing to persist, the fixture forces every theme flip through the postMessage path."
33
+ }
34
+ ]
35
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "cnId": "CN-700",
3
+ "path": "src/view/product-map.js",
4
+ "title": "Product Map re-bucketing over the review-surface tree model",
5
+ "description": "Pure functions groupByShape, groupByComponent, groupByTraceCoverage, groupByCapability, groupByBlueprint plus the panel renderer that composes them. No schema change; consumes shapeClassification.shapes[], usByTacId, tsByAcId/tcsByAcId, REQ.tags[] for capability:<slug> tags, and manifest.blueprints[].contributions[] for blueprint attribution (fallback: reqId kebab-slug prefix). Emits a pm-blueprint-badge on every REQ row an applied blueprint contributed.",
6
+ "implementsAcIds": [
7
+ "AC-17001-1",
8
+ "AC-17001-2",
9
+ "AC-17002-1",
10
+ "AC-17003-1",
11
+ "AC-17004-1",
12
+ "AC-17005-1",
13
+ "AC-17006-1",
14
+ "AC-17008-1"
15
+ ],
16
+ "dependencies": [
17
+ "CN-017"
18
+ ],
19
+ "version": "0.1.0",
20
+ "status": "draft",
21
+ "createdAt": "2026-09-21T17:00:00.000Z",
22
+ "updatedAt": "2026-09-22T11:59:39Z"
23
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "cnId": "CN-701",
3
+ "path": "scripts/apply-capability-tags.mjs",
4
+ "title": "Apply capability:<slug> tags to every dogfood REQ",
5
+ "description": "One authoritative REQ id -> slugs mapping applied to every requirement file under rcf/requirements/. Idempotent; overwrites existing capability:<slug> tags while preserving non-capability tags. Ships as the tool of record until a rcf discover req-capability-classify verb lands.",
6
+ "implementsAcIds": [
7
+ "AC-17101-1"
8
+ ],
9
+ "dependencies": [],
10
+ "version": "0.1.0",
11
+ "status": "draft",
12
+ "createdAt": "2026-09-21T17:00:00.000Z",
13
+ "updatedAt": "2026-09-21T17:00:00.000Z"
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "cnId": "CN-702",
3
+ "path": "docs/capability-tags.md",
4
+ "title": "Capability tag convention and taxonomy for the rcf-lite chain",
5
+ "description": "Documents the capability:<slug> convention, the initial twelve-slug taxonomy and the rule for adding a new capability. Linked from docs/README.md; consumed by the Product Map by-capability grouping.",
6
+ "implementsAcIds": [
7
+ "AC-17102-1"
8
+ ],
9
+ "dependencies": [],
10
+ "version": "0.1.0",
11
+ "status": "draft",
12
+ "createdAt": "2026-09-21T17:00:00.000Z",
13
+ "updatedAt": "2026-09-21T17:00:00.000Z"
14
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "cnId": "CN-703",
3
+ "path": "src/server/routes.js",
4
+ "implementsAcIds": [
5
+ "AC-17007-1"
6
+ ],
7
+ "dependencies": [
8
+ "CN-700"
9
+ ],
10
+ "version": "0.1.0",
11
+ "status": "draft",
12
+ "createdAt": "2026-09-21T19:44:46.452Z",
13
+ "updatedAt": "2026-09-21T19:44:52.245Z",
14
+ "title": "Product Map partial-render endpoint",
15
+ "description": "Adds a /product-map/<group> HTTP route that serves the pre-rendered non-lazy panel body for the named grouping. Consumed by the inline scripts hydrateLazyGroup which fetches on first activation and caches the response in memory; the live-client invalidates the cache on every SSE tree-update swap."
16
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "cnId": "CN-720",
3
+ "path": "src/query/delta.js",
4
+ "title": "Delta detection: canonical-JSON hashing and computeDelta",
5
+ "description": "Pure module. canonicaliseJson (recursive key sort, no whitespace), hashDocument (sha256:<hex> over the canonical form of a parsed document), computeTreeHash (over sorted (id, docHash) pairs), and computeDelta (returns the proposal 2026-09-22 section 2.2 shape: frozen, frozenAt, treeHash, currentTreeHash, changed, added, removed, briefSince, unchanged). Ledger docHashes are added under ledger:<name> ids per sidecar the caller passes.",
6
+ "implementsAcIds": [
7
+ "AC-17201-1",
8
+ "AC-17201-2"
9
+ ],
10
+ "dependencies": [],
11
+ "version": "0.1.0",
12
+ "status": "draft",
13
+ "createdAt": "2026-09-24T12:00:00Z",
14
+ "updatedAt": "2026-09-24T12:00:00Z"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "cnId": "CN-721",
3
+ "path": "src/define/freeze-record.js",
4
+ "title": "Freeze record I/O: load, save, validate",
5
+ "description": "Owns rcf/define/freeze.json. FREEZE_RECORD_REL_PATH, FreezeRecordError (coded parseFailure | schemaFailure | ioFailure with a named field), validateFreezeRecord, loadFreezeRecord (returns null on ENOENT), saveFreezeRecord (validates first; refuses to persist an invalid record; creates rcf/define/ on demand; writes pretty JSON with a trailing newline). The record's local schema is documented at the top of this file and covered by test/define/freeze-record.test.js.",
6
+ "implementsAcIds": [
7
+ "AC-17201-3",
8
+ "AC-17201-4"
9
+ ],
10
+ "dependencies": [],
11
+ "version": "0.1.0",
12
+ "status": "draft",
13
+ "createdAt": "2026-09-24T12:00:00Z",
14
+ "updatedAt": "2026-09-24T12:00:00Z"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "cnId": "CN-722",
3
+ "path": "src/define/ledgers.js",
4
+ "title": "Four sidecar ledgers: load, save, validate, add, resolve",
5
+ "description": "Pure module. LEDGER_NAMES (brief, decisions, concerns, probes), BRIEF_KINDS closed vocabulary, LedgerError with coded reason and named field, ledgerRelPath / ledgerPath, emptyLedger, loadLedger (empty on ENOENT), saveLedger (validates first), validateLedger, addEntry (mints next id, stamps addedAt, defaults status open, validates the whole body), resolveEntry (flips status to resolved, stamps resolvedAt, refuses unknown id), parseBriefFromFile (splits by non-empty lines, strips '- ', '* ', '<n>. ' bullets), loadAllLedgers (returns a LedgerBundle for computeDelta).",
6
+ "implementsAcIds": [
7
+ "AC-17301-1",
8
+ "AC-17301-3"
9
+ ],
10
+ "dependencies": [],
11
+ "version": "0.1.0",
12
+ "status": "draft",
13
+ "createdAt": "2026-09-24T12:00:00Z",
14
+ "updatedAt": "2026-09-24T12:00:00Z"
15
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "cnId": "CN-723",
3
+ "path": "src/cli/ledger.js",
4
+ "title": "rcf define ledger CLI verb",
5
+ "description": "Wraps src/define/ledgers.js with parseArgs and per-verb usage text. Dispatches ledger <name> <verb> where name is brief | decisions | concerns | probes and verb is add | resolve | list. renderDecisionsList prints the numbered #241 format (one decision per item, letter) options, default:, blocks:). list --json emits { ledger, <arrayKey>: [...] }. bin/rcf.js registers this under GROUPS.define.ledger; src/cli/help.js wires HELP into HELP_MAP.define.ledger and extends the DEFINE_HELP and top-level listing blocks so 'rcf help define ledger' and 'rcf define ledger --help' render the same text by construction.",
6
+ "implementsAcIds": [
7
+ "AC-17301-1",
8
+ "AC-17301-2",
9
+ "AC-17301-3"
10
+ ],
11
+ "dependencies": [
12
+ "CN-722"
13
+ ],
14
+ "version": "0.1.0",
15
+ "status": "draft",
16
+ "createdAt": "2026-09-24T12:00:00Z",
17
+ "updatedAt": "2026-09-24T12:00:00Z"
18
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "cnId": "CN-724",
3
+ "path": "test/store/walker-invisible-define.test.js",
4
+ "title": "Invariant test: walker never surfaces rcf/define/ files",
5
+ "description": "One-scenario test that seeds a fresh project (initProject), writes rcf/define/freeze.json, the four ledger files and a bug-bait req-999.json under rcf/define/, then runs walkTree and asserts no error path starts with rcf/define/ and no such file lands in tree.byId. The invariant is what keeps the sidecars out of every existing tree consumer (proposal 2026-09-22 section 2.1).",
6
+ "implementsAcIds": [
7
+ "AC-17301-4"
8
+ ],
9
+ "dependencies": [],
10
+ "version": "0.1.0",
11
+ "status": "draft",
12
+ "createdAt": "2026-09-24T12:00:00Z",
13
+ "updatedAt": "2026-09-24T12:00:00Z"
14
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "cnId": "CN-725",
3
+ "path": "src/query/gates.js",
4
+ "title": "DEFINE stage gates D1..D8 pure check functions",
5
+ "description": "Pure module. One check function per stage D1..D8 (checkD1Brief..checkD8Freeze) plus STAGE_ORDER, STAGE_GATES, STAGE_SHORT_NAMES, INTERFACE_KINDS. Each takes (tree, context) and returns { stage, gate, state, checks[] } in the section 2.4 shape.",
6
+ "implementsAcIds": [
7
+ "AC-17401-1",
8
+ "AC-17401-2",
9
+ "AC-17401-3",
10
+ "AC-17401-4",
11
+ "AC-17401-5",
12
+ "AC-17401-6",
13
+ "AC-17401-7",
14
+ "AC-17401-8",
15
+ "AC-17401-9",
16
+ "AC-17402-1",
17
+ "AC-17402-2",
18
+ "AC-17402-3",
19
+ "AC-17402-4",
20
+ "AC-17402-5",
21
+ "AC-17402-6",
22
+ "AC-17402-7",
23
+ "AC-17403-1",
24
+ "AC-17403-2",
25
+ "AC-17403-3",
26
+ "AC-17403-4",
27
+ "AC-17403-5",
28
+ "AC-17403-6",
29
+ "AC-17403-7",
30
+ "AC-17403-8"
31
+ ],
32
+ "dependencies": [],
33
+ "version": "0.1.0",
34
+ "status": "draft",
35
+ "createdAt": "2026-09-24T14:30:00Z",
36
+ "updatedAt": "2026-10-01T14:30:00Z"
37
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "cnId": "CN-726",
3
+ "path": "src/query/readiness.js",
4
+ "title": "computeReadiness composes delta + fan-out + gates + coverage + decisions",
5
+ "description": "Pure module. computeReadiness returns the section 2.4 object; internal composeStages calls TAC-4122's gate functions in D1..D8 order; deriveNextAction picks the first failing check; freezeable folds the eight stage states.",
6
+ "implementsAcIds": [
7
+ "AC-17501-1",
8
+ "AC-17501-2",
9
+ "AC-17501-3",
10
+ "AC-17501-4",
11
+ "AC-17502-1",
12
+ "AC-17502-2",
13
+ "AC-17502-3",
14
+ "AC-17502-4",
15
+ "AC-17502-5",
16
+ "AC-17502-6",
17
+ "AC-17502-7",
18
+ "AC-17502-8"
19
+ ],
20
+ "dependencies": [],
21
+ "version": "0.1.0",
22
+ "status": "draft",
23
+ "createdAt": "2026-09-24T14:30:00Z",
24
+ "updatedAt": "2026-10-01T14:30:00Z"
25
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "cnId": "CN-727",
3
+ "path": "src/cli/readiness.js",
4
+ "title": "rcf define readiness [--json] [--check <stage>|all] [--level intent|build] [--persona productOwner|engineer]",
5
+ "description": "CLI handler. Parses --json / --check / --level / --persona / --help; walks the tree; loads freeze + all four ledgers; resolves test pointers; wraps computeReadiness with runWithAdmissibilityGate; emits text (with the two verdict lines + persona-grouped blocker sections ordered by the profile.md register) or JSON (with _meta.level carrying the invocation); the exit matrix in spec section 3.2 folds --check and --level per decision 4.",
6
+ "implementsAcIds": [
7
+ "AC-17501-5",
8
+ "AC-17503-1",
9
+ "AC-17503-2",
10
+ "AC-17503-3",
11
+ "AC-17503-4",
12
+ "AC-17503-5",
13
+ "AC-17503-6",
14
+ "AC-17503-7",
15
+ "AC-17503-8",
16
+ "AC-17503-9"
17
+ ],
18
+ "dependencies": [],
19
+ "version": "0.1.0",
20
+ "status": "draft",
21
+ "createdAt": "2026-09-24T14:30:00Z",
22
+ "updatedAt": "2026-10-01T16:00:00Z"
23
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "cnId": "CN-728",
3
+ "path": "test/query/gates.test.js",
4
+ "title": "Unit tests for stage gates D1..D8",
5
+ "description": "Covers AC-17401-1..8: return shape, blocking-vs-warn-with-ack fold, notApplicable rules, D4 floors + per-class opt-outs, D3 interface kind vocabulary, D5 cheap checks, D6 validate + probe count, D8 tree-wide freeze checks.",
6
+ "implementsAcIds": [
7
+ "AC-17401-1",
8
+ "AC-17401-2",
9
+ "AC-17401-3",
10
+ "AC-17401-4",
11
+ "AC-17401-5",
12
+ "AC-17401-6",
13
+ "AC-17401-7",
14
+ "AC-17401-8",
15
+ "AC-17401-9",
16
+ "AC-17402-1",
17
+ "AC-17402-2",
18
+ "AC-17402-3",
19
+ "AC-17402-4",
20
+ "AC-17402-5",
21
+ "AC-17402-6",
22
+ "AC-17402-7",
23
+ "AC-17403-1",
24
+ "AC-17403-2",
25
+ "AC-17403-3",
26
+ "AC-17403-4",
27
+ "AC-17403-5",
28
+ "AC-17403-6",
29
+ "AC-17403-7",
30
+ "AC-17403-8"
31
+ ],
32
+ "dependencies": [],
33
+ "version": "0.1.0",
34
+ "status": "draft",
35
+ "createdAt": "2026-09-24T14:30:00Z",
36
+ "updatedAt": "2026-10-01T14:30:00Z"
37
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "cnId": "CN-729",
3
+ "path": "test/query/readiness.test.js",
4
+ "title": "Unit tests for computeReadiness",
5
+ "description": "Covers AC-17501-1..4 and AC-17501-6: section 2.4 shape, fan-out impacted/impactedFbs, coverage tree + per-REQ, nextAction pick, loadAllLedgers-omits-absent.",
6
+ "implementsAcIds": [
7
+ "AC-17501-1",
8
+ "AC-17501-2",
9
+ "AC-17501-3",
10
+ "AC-17501-4",
11
+ "AC-17501-6",
12
+ "AC-17502-1",
13
+ "AC-17502-2",
14
+ "AC-17502-3",
15
+ "AC-17502-4",
16
+ "AC-17502-5",
17
+ "AC-17502-6",
18
+ "AC-17502-7",
19
+ "AC-17502-8"
20
+ ],
21
+ "dependencies": [],
22
+ "version": "0.1.0",
23
+ "status": "draft",
24
+ "createdAt": "2026-09-24T14:30:00Z",
25
+ "updatedAt": "2026-10-01T14:30:00Z"
26
+ }