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,78 @@
1
+ {
2
+ "createdAt": "2026-10-03T00:00:00Z",
3
+ "updatedAt": "2026-10-03T00:00:00Z",
4
+ "purpose": "Cover US-205 (engineer jumps to any document or AC by id): id-first ranking (exact / prefix / contains), word-match title above snippet, single-char ids-only, hash pick lands and expands the entity, mount URL round-trip, embed/theme preservation, empty-state on fetch failure, no outbound postMessage / storage / cookies. Points at the 22 new unit tests under test/view/id-lookup.test.js.",
5
+ "testLevel": "unit",
6
+ "acIds": [
7
+ "AC-205-1",
8
+ "AC-205-2",
9
+ "AC-205-3",
10
+ "AC-205-4",
11
+ "AC-205-5",
12
+ "AC-205-6",
13
+ "AC-205-7",
14
+ "AC-205-8"
15
+ ],
16
+ "id": "TS-225",
17
+ "usId": "US-205",
18
+ "title": "ID lookup: ranking, hash pick, embed/theme round-trip, empty state, no outbound side effects",
19
+ "testCases": [
20
+ {
21
+ "id": "TC-225-id-exact-prefix-first",
22
+ "acId": "AC-205-1",
23
+ "description": "rankLookupRows ranks an exact id match first, a prefix match second and a contains match third; the first ranked row is pre-highlighted in the LookupModal shell.",
24
+ "status": "pending",
25
+ "testPointer": "test/view/id-lookup.test.js::rankLookupRows: exact id ranks first (AC-205-1)"
26
+ },
27
+ {
28
+ "id": "TC-225-word-title-above-snippet",
29
+ "acId": "AC-205-2",
30
+ "description": "rankLookupRows ranks a word match in the title above a word match in the snippet when the id does not match; every query word must match title or snippet.",
31
+ "status": "pending",
32
+ "testPointer": "test/view/id-lookup.test.js::rankLookupRows: a title match ranks above a snippet-only match (AC-205-2)"
33
+ },
34
+ {
35
+ "id": "TC-225-single-char-ids-only",
36
+ "acId": "AC-205-3",
37
+ "description": "rankLookupRows with a one-character query returns only id matches; the title / snippet word-match pass is skipped.",
38
+ "status": "pending",
39
+ "testPointer": "test/view/id-lookup.test.js::rankLookupRows: single-character queries search ids only (AC-205-3)"
40
+ },
41
+ {
42
+ "id": "TC-225-pick-writes-hash",
43
+ "acId": "AC-205-4",
44
+ "description": "wireLookup pick writes `#tab=<tab>&entity=<id>` through writeHash; the Router activates the tab, opens every ancestor DocRow and the entity, scrolls into view and flashes the target.",
45
+ "status": "pending",
46
+ "testPointer": "test/view/id-lookup.test.js::page-init.js carries the wireLookup contract: Cmd/Ctrl+F binding, lazy fetch, up/down/Enter/Esc"
47
+ },
48
+ {
49
+ "id": "TC-225-mount-url-round-trip",
50
+ "acId": "AC-205-5",
51
+ "description": "A mount URL of `?tab=requirements&entity=US-304` lands on the same position at first paint via the Router (PR 1 contract), independent of the LookupModal.",
52
+ "status": "pending",
53
+ "testPointer": "test/view/id-lookup.test.js::page-init.js urlWithHash preserves ?tab=&entity= + any other query at mount (AC-205-5)"
54
+ },
55
+ {
56
+ "id": "TC-225-embed-theme-preserved",
57
+ "acId": "AC-205-6",
58
+ "description": "A pick with `?embed=1&theme=dark` on the URL leaves both query params in place; only the hash fragment changes. Verified against writeHash + the hash-preservation helpers.",
59
+ "status": "pending",
60
+ "testPointer": "test/view/id-lookup.test.js::page-init.js writeHash preserves ?embed= and ?theme= through every pick (AC-205-6)"
61
+ },
62
+ {
63
+ "id": "TC-225-empty-state-on-fetch-failure",
64
+ "acId": "AC-205-7",
65
+ "description": "A non-2xx / malformed `./index.json` response renders the empty state (The index could not be loaded.) with a visible Reload index button; no result rows show; the reload affordance re-fetches on click.",
66
+ "status": "pending",
67
+ "testPointer": "test/view/id-lookup.test.js::renderLookupModal carries the empty-state + reload affordance markers (AC-205-7)"
68
+ },
69
+ {
70
+ "id": "TC-225-no-outbound-side-effects",
71
+ "acId": "AC-205-8",
72
+ "description": "wireLookup never calls window.postMessage, never writes to localStorage / sessionStorage / document.cookie, and makes no outbound fetch other than `./index.json`; asserted by a source-level scan of page-init.js (no postMessage, no storage, no cookie writes in wireLookup).",
73
+ "status": "pending",
74
+ "testPointer": "test/view/id-lookup.test.js::page-init.js does not add outbound postMessage, localStorage, sessionStorage or cookies in the lookup wiring (AC-205-8)"
75
+ }
76
+ ],
77
+ "status": "draft"
78
+ }
@@ -0,0 +1,158 @@
1
+ {
2
+ "createdAt": "2026-10-02T18:00:00Z",
3
+ "updatedAt": "2026-10-02T18:00:00Z",
4
+ "purpose": "Cover US-18601 (the question set), US-18602 (the verb and the tools) and US-18603 (write-back and termination): computeQuestions turns every failing PO item into a question carrying its write-back; the CLI verb and the MCP tool emit the same object; the scripted loop converges; engineer edits never re-open the PO loop.",
5
+ "testLevel": "unit",
6
+ "acIds": [
7
+ "AC-18601-1",
8
+ "AC-18601-2",
9
+ "AC-18601-3",
10
+ "AC-18601-4",
11
+ "AC-18601-5",
12
+ "AC-18601-6",
13
+ "AC-18601-7",
14
+ "AC-18602-1",
15
+ "AC-18602-2",
16
+ "AC-18602-3",
17
+ "AC-18602-4",
18
+ "AC-18602-5",
19
+ "AC-18602-6",
20
+ "AC-18603-1",
21
+ "AC-18603-2",
22
+ "AC-18603-3",
23
+ "AC-18603-4",
24
+ "AC-18603-5"
25
+ ],
26
+ "id": "TS-226",
27
+ "usId": "US-18601",
28
+ "title": "DEFINE step 3 PR 2: computeQuestions, the questions verb, MCP tools and loop convergence",
29
+ "testCases": [
30
+ {
31
+ "id": "TC-226-q-shape",
32
+ "acId": "AC-18601-1",
33
+ "description": "On a fixture failing brief:openQuestions, skeleton:resolvedBy and stories:reqHasUs, computeQuestions(persona=productOwner) returns one entry per failing item with id, stage, check, itemId, heading, ask, answerKinds, writeBack and blocks intent.",
34
+ "status": "pending",
35
+ "testPointer": "test/query/questions.test.js::AC-18601-1: openQuestions + resolvedBy + reqHasUs failing items each produce one question with the full shape"
36
+ },
37
+ {
38
+ "id": "TC-226-q-deterministic",
39
+ "acId": "AC-18601-2",
40
+ "description": "Two consecutive computeQuestions runs on the same tree return deep-equal questions[] ordered D1, D2, D4, D7 then by source span.",
41
+ "status": "pending",
42
+ "testPointer": "test/query/questions.test.js::AC-18601-2: two runs are deep-equal and ordered D1, D2, D4, D7 then by source span"
43
+ },
44
+ {
45
+ "id": "TC-226-q-optional-decisions",
46
+ "acId": "AC-18601-3",
47
+ "description": "An open decision with two options and a default appears in optional[] with blocks build and not in questions[].",
48
+ "status": "pending",
49
+ "testPointer": "test/query/questions.test.js::AC-18601-3: a two-option decision with a default appears in optional[], not questions[]"
50
+ },
51
+ {
52
+ "id": "TC-226-q-empty-when-ok",
53
+ "acId": "AC-18601-4",
54
+ "description": "On a tree where levels.intentComplete.ok, computeQuestions(persona=productOwner) returns ok:true and empty questions[].",
55
+ "status": "pending",
56
+ "testPointer": "test/query/questions.test.js::AC-18601-4: ok tree returns ok:true and empty questions[]"
57
+ },
58
+ {
59
+ "id": "TC-226-q-engineer-persona",
60
+ "acId": "AC-18601-5",
61
+ "description": "computeQuestions(persona=engineer) returns one entry per failing engineer item in failing stages with blocks build.",
62
+ "status": "pending",
63
+ "testPointer": "test/query/questions.test.js::AC-18601-5: persona engineer returns engineer blockers in failing stages, blocks: build"
64
+ },
65
+ {
66
+ "id": "TC-226-q-usage-error",
67
+ "acId": "AC-18601-6",
68
+ "description": "computeQuestions throws a usage error naming productOwner and engineer when given any other persona value.",
69
+ "status": "pending",
70
+ "testPointer": "test/query/questions.test.js::AC-18601-6: unknown persona throws naming the accepted values"
71
+ },
72
+ {
73
+ "id": "TC-226-q-writes-nothing",
74
+ "acId": "AC-18601-7",
75
+ "description": "computeQuestions never creates or modifies a file under rcf/ (mtime guard); every writeBack.command names a verb registered in the CLI help.",
76
+ "status": "pending",
77
+ "testPointer": "test/query/questions.test.js::AC-18601-7: computeQuestions is pure; every writeBack.command names a registered verb"
78
+ },
79
+ {
80
+ "id": "TC-226-cli-json",
81
+ "acId": "AC-18602-1",
82
+ "description": "rcf define questions --json on a failing tree prints the computeQuestions object to stdout and exits 0.",
83
+ "status": "pending",
84
+ "testPointer": "test/cli/questions.test.js::AC-18602-1: --json on a failing tree prints the questions object and exits 0"
85
+ },
86
+ {
87
+ "id": "TC-226-cli-persona-default",
88
+ "acId": "AC-18602-2",
89
+ "description": "Without --persona, the verb picks engineer on a profile whose register is engineer, else productOwner.",
90
+ "status": "pending",
91
+ "testPointer": "test/cli/questions.test.js::AC-18602-2: persona defaults from profile register"
92
+ },
93
+ {
94
+ "id": "TC-226-cli-stage-limit",
95
+ "acId": "AC-18602-3",
96
+ "description": "--stage skeleton --limit 2 caps questions[] to two D2 entries and reports remaining counting every D2 item.",
97
+ "status": "pending",
98
+ "testPointer": "test/cli/questions.test.js::AC-18602-3: --stage brief --limit 1 caps the output but keeps remaining honest"
99
+ },
100
+ {
101
+ "id": "TC-226-mcp-parity",
102
+ "acId": "AC-18602-4",
103
+ "description": "MCP tool rcf_define_questions returns structuredContent deep-equal to the verb's --json output on the same tree.",
104
+ "status": "pending",
105
+ "testPointer": "test/mcp/define-questions.test.js::AC-18602-4: rcf_define_questions structuredContent deep-equals the verb --json"
106
+ },
107
+ {
108
+ "id": "TC-226-cli-usage-persona",
109
+ "acId": "AC-18602-5",
110
+ "description": "--persona architect exits 2 with the usage line naming productOwner and engineer.",
111
+ "status": "pending",
112
+ "testPointer": "test/cli/questions.test.js::AC-18602-5: --persona architect exits 2 naming productOwner and engineer"
113
+ },
114
+ {
115
+ "id": "TC-226-cli-writes-nothing",
116
+ "acId": "AC-18602-6",
117
+ "description": "No file under rcf/ is created or modified during any invocation of rcf define questions.",
118
+ "status": "pending",
119
+ "testPointer": "test/cli/questions.test.js::AC-18602-6: questions writes nothing under rcf/"
120
+ },
121
+ {
122
+ "id": "TC-226-loop-convergence",
123
+ "acId": "AC-18603-1",
124
+ "description": "Scripted loop fixture: applying each writeBack and recomputing drives questions count strictly down each turn and to zero.",
125
+ "status": "pending",
126
+ "testPointer": "test/query/questions-loop.test.js::AC-18603-1: scripted loop converges strictly to zero within a bounded number of turns"
127
+ },
128
+ {
129
+ "id": "TC-226-loop-new-req",
130
+ "acId": "AC-18603-2",
131
+ "description": "A statement resolved to a new REQ carrying description and domain passes skeleton:reqIntent and raises stories:reqHasUs once.",
132
+ "status": "pending",
133
+ "testPointer": "test/query/questions-loop.test.js::AC-18603-2: a new REQ with description and domain passes reqIntent and asks reqHasUs once"
134
+ },
135
+ {
136
+ "id": "TC-226-loop-l1-draft-stable",
137
+ "acId": "AC-18603-3",
138
+ "description": "At L1, adding [draft] entities, TAC stubs and interfaces leaves intentComplete.ok true and the PO question set empty.",
139
+ "status": "pending",
140
+ "testPointer": "test/query/questions-loop.test.js::AC-18603-3: at L1 adding engineer-only draft entities keeps the PO question set empty"
141
+ },
142
+ {
143
+ "id": "TC-226-loop-decision-wellformed",
144
+ "acId": "AC-18603-4",
145
+ "description": "An openQuestion promoted to a decision with one option and no default makes decisions:wellFormed ask once and brief:openQuestions pass.",
146
+ "status": "pending",
147
+ "testPointer": "test/query/questions-loop.test.js::AC-18603-4: an openQuestion promoted to a one-option decision asks decisions:wellFormed once"
148
+ },
149
+ {
150
+ "id": "TC-226-loop-engineer-isolation",
151
+ "acId": "AC-18603-5",
152
+ "description": "No product-owner check changes from ok to failing after an engineer-only edit.",
153
+ "status": "pending",
154
+ "testPointer": "test/query/questions-loop.test.js::AC-18603-5: an engineer-only edit never changes the PO question set"
155
+ }
156
+ ],
157
+ "status": "draft"
158
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "createdAt": "2026-10-03T09:30:00Z",
3
+ "updatedAt": "2026-10-03T09:30:00Z",
4
+ "purpose": "Cover US-18002 (the Readiness tab renders question cards grouped by source span): the questions block lists each group with its asks and every id is in the JSON (AC-18002-1), the empty questions set renders the intent-complete verdict line (AC-18002-2), and engineer blockers are never removed from the DOM in any register (AC-18002-3). Points at the three AC tests under test/view/readiness-po-layer.test.js.",
5
+ "testLevel": "unit",
6
+ "acIds": [
7
+ "AC-18002-1",
8
+ "AC-18002-2",
9
+ "AC-18002-3"
10
+ ],
11
+ "id": "TS-227",
12
+ "usId": "US-18002",
13
+ "title": "Readiness PO question cards: groups and asks, intent-complete empty state, engineer DOM preserved across registers",
14
+ "testCases": [
15
+ {
16
+ "id": "TC-227-groups-and-asks",
17
+ "acId": "AC-18002-1",
18
+ "description": "renderReadinessPO is given a readiness object with questions[] non-empty; every group label, every ask and every question id appears in the panel, and data-rcf-source=\"readiness.questions\" is stamped so the source path is unambiguous.",
19
+ "status": "pending",
20
+ "testPointer": "test/view/readiness-po-layer.test.js::PO layer: AC-18002-1 given questions[] non-empty, the block lists each group with its asks and every id is in the JSON"
21
+ },
22
+ {
23
+ "id": "TC-227-empty-questions-verdict",
24
+ "acId": "AC-18002-2",
25
+ "description": "renderReadinessPO is given a clean readiness object with both levels ok and no blockers; the panel renders the exact intent-complete verdict line (formatVerdictLines(result).intentComplete) inside the questions block and marks the section data-rcf-empty=\"yes\".",
26
+ "status": "pending",
27
+ "testPointer": "test/view/readiness-po-layer.test.js::PO layer: AC-18002-2 given questions[] empty, the block shows the intent-complete verdict line"
28
+ },
29
+ {
30
+ "id": "TC-227-engineer-dom-preserved",
31
+ "acId": "AC-18002-3",
32
+ "description": "renderReadinessPanel across every register (null / productOwner / engineer) keeps every engineer blocker id in the DOM, keeps every PO blocker id in the DOM and renders the CLI verdict strings verbatim inside the For-engineers DocRow; the For-engineers DocRow is closed under productOwner / unstated and open under engineer.",
33
+ "status": "pending",
34
+ "testPointer": "test/view/readiness-po-layer.test.js::PO layer: AC-18002-3 engineer blockers are not removed from the DOM in any register"
35
+ }
36
+ ],
37
+ "status": "draft"
38
+ }
@@ -0,0 +1,86 @@
1
+ {
2
+ "createdAt": "2026-10-03T00:00:00Z",
3
+ "updatedAt": "2026-10-03T00:00:00Z",
4
+ "purpose": "Cover US-18801 (the draft homes) and US-18202 (the DEFINE-stage guidance pack): parseInterfaceDraft and shapes:draftSettled extended to TAC.purpose, coreEntities[].description and TAC.interfaces[].description with the three id forms; a draft-only tree is L1-neutral; the guidance manifest advertises the two new slugs and five prompts; the register scan rejects an em-dash in any new guidance file; the managed block regeneration keeps every existing rule.",
5
+ "testLevel": "unit",
6
+ "acIds": [
7
+ "AC-18801-1",
8
+ "AC-18801-2",
9
+ "AC-18801-3",
10
+ "AC-18801-4",
11
+ "AC-18801-5",
12
+ "AC-18202-1",
13
+ "AC-18202-2",
14
+ "AC-18202-3",
15
+ "AC-18202-4"
16
+ ],
17
+ "id": "TS-228",
18
+ "usId": "US-18801",
19
+ "title": "DEFINE step 3 PR 3: draft marker extension, DISCOVERY prototypes guidance and prompts",
20
+ "testCases": [
21
+ {
22
+ "id": "TC-228-draft-coreentity",
23
+ "acId": "AC-18801-1",
24
+ "description": "A coreEntities entry whose description starts with [draft] fails shapes:draftSettled with id TAD.entity:<name>.",
25
+ "status": "pending",
26
+ "testPointer": "test/query/gates.test.js::gates (REQ-188, AC-18801-1): draft on coreEntity description fails draftSettled with TAD.entity:<name>"
27
+ },
28
+ {
29
+ "id": "TC-228-draft-tac-purpose",
30
+ "acId": "AC-18801-2",
31
+ "description": "A TAC whose purpose starts with [draft] fails shapes:draftSettled with id <tacId>; shapes:tacHasInterface counts the TAC as present.",
32
+ "status": "pending",
33
+ "testPointer": "test/query/gates.test.js::gates (REQ-188, AC-18801-2): draft on TAC.purpose fails draftSettled with <tacId>, tacHasInterface counts it present"
34
+ },
35
+ {
36
+ "id": "TC-228-draft-cleared",
37
+ "acId": "AC-18801-3",
38
+ "description": "When every [draft] marker across the three homes is cleared, shapes:draftSettled passes.",
39
+ "status": "pending",
40
+ "testPointer": "test/query/gates.test.js::gates (REQ-188, AC-18801-3): three draft homes cleared, draftSettled passes"
41
+ },
42
+ {
43
+ "id": "TC-228-draft-template-markers",
44
+ "acId": "AC-18801-4",
45
+ "description": "A [draft] recordShape without fields: fails shapes:templateMarkers exactly as a settled one. (shapes:templateMarkers lands in PR 5; this TC is pending PR 5.)",
46
+ "status": "pending",
47
+ "testPointer": "test/query/gates.test.js::gates (REQ-188, AC-18801-4 pending PR 5): draft recordShape without fields fails templateMarkers"
48
+ },
49
+ {
50
+ "id": "TC-228-draft-no-po-read",
51
+ "acId": "AC-18801-5",
52
+ "description": "No productOwner check reads any [draft] content: every productOwner check on a draft-only tree is ok (L1-neutral).",
53
+ "status": "pending",
54
+ "testPointer": "test/query/gates.test.js::gates (REQ-188, AC-18801-5): draft-only tree keeps every productOwner check ok (L1-neutral)"
55
+ },
56
+ {
57
+ "id": "TC-228-guidance-prints",
58
+ "acId": "AC-18202-1",
59
+ "description": "rcf guidance define-intent and rcf guidance discovery-prototypes each print their file byte-faithfully.",
60
+ "status": "pending",
61
+ "testPointer": "test/guidance/verbs.test.js::guidance (REQ-182, AC-18202-1): rcf guidance define-intent and discovery-prototypes each print their file"
62
+ },
63
+ {
64
+ "id": "TC-228-manifest-prompts",
65
+ "acId": "AC-18202-2",
66
+ "description": "The manifest lists rcf_define_intent, rcf_define_intake, rcf_define_draft_shapes and rcf_discover_prototype as argument-free; rcf_define_litmus is recorded as pending PR 7.",
67
+ "status": "pending",
68
+ "testPointer": "test/guidance/inventory.test.js::guidance (REQ-182, AC-18202-2): four argument-free DEFINE prompts present, litmus pending PR 7"
69
+ },
70
+ {
71
+ "id": "TC-228-register-scan-em-dash",
72
+ "acId": "AC-18202-3",
73
+ "description": "A new guidance file containing an em-dash fails the register scan.",
74
+ "status": "pending",
75
+ "testPointer": "test/guidance/canonical-text-lint.test.js::guidance (REQ-182, AC-18202-3): em-dash in a new guidance file fails the register scan"
76
+ },
77
+ {
78
+ "id": "TC-228-managed-block-preserves-rules",
79
+ "acId": "AC-18202-4",
80
+ "description": "The managed block regeneration keeps every existing rule: regenerating never removes a line.",
81
+ "status": "pending",
82
+ "testPointer": "test/guidance/fragment.test.js::guidance (REQ-182, AC-18202-4): managed-block regeneration keeps every existing rule"
83
+ }
84
+ ],
85
+ "status": "draft"
86
+ }
@@ -0,0 +1,83 @@
1
+ {
2
+ "createdAt": "2026-10-03T09:55:00Z",
3
+ "updatedAt": "2026-10-03T10:00:00Z",
4
+ "purpose": "Cover US-206 (wespa's embedding contract is proved by a CI host fixture): the fixture renders a 56px header + resizable sidebar around an iframe pointed at ./?embed=1&theme=light with no inline script (AC-206-1), theme flips via postMessage without a reload (AC-206-2, source invariants + Playwright live proof in the lane evidence), embed and theme survive a hash-only in-iframe navigation (AC-206-3, source invariants + Playwright live proof), cross-origin messages are ignored by the viewer listener (AC-206-4), the three lints keep framing headers, target=_top and localStorage namespace in place (AC-206-5), and no new outbound postMessage shape is introduced by the viewer or the fixture (AC-206-6). All TCs resolve to node tests under test/view/wespa-host-fixture.test.js.",
5
+ "testLevel": "unit",
6
+ "acIds": [
7
+ "AC-206-1",
8
+ "AC-206-2",
9
+ "AC-206-3",
10
+ "AC-206-4",
11
+ "AC-206-5",
12
+ "AC-206-6"
13
+ ],
14
+ "id": "TS-229",
15
+ "usId": "US-206",
16
+ "title": "Wespa host fixture: iframe shell, theme postMessage, hash-nav embed preservation, cross-origin refusal, three lints, no new outbound postMessage shape",
17
+ "testCases": [
18
+ {
19
+ "id": "TC-229-fixture-shell",
20
+ "acId": "AC-206-1",
21
+ "description": "renderTestHostPage returns an HTML5 document with a 56px wespa-header, data-rcf-host-sidebar + data-rcf-host-sidebar-drag markers, data-rcf-host-theme light/dark toggles, an iframe whose src is literally './?embed=1&theme=light' (HTML-entity encoded), a '<script src=./test-host.js defer>' line and zero inline <script> blocks.",
22
+ "status": "pending",
23
+ "testPointer": "test/view/wespa-host-fixture.test.js::renderTestHostPage: HTML5 document, no inline script, iframe src is ./?embed=1&theme=light"
24
+ },
25
+ {
26
+ "id": "TC-229-test-host-http-roundtrip",
27
+ "acId": "AC-206-1",
28
+ "description": "The router serves /test-host.html with the fixture body (iframe src + script tag) and /test-host.js with application/javascript when testHost: true; both routes return 404 by default so wespa's reverse-proxy allow-list is unaffected.",
29
+ "status": "pending",
30
+ "testPointer": "test/view/wespa-host-fixture.test.js::/test-host.html is served when testHost: true; iframe src and no inline script round-trip over HTTP"
31
+ },
32
+ {
33
+ "id": "TC-229-theme-postmessage-source",
34
+ "acId": "AC-206-2",
35
+ "description": "src/view/test-host/embed-client.js posts { type: 'rcf-view-theme', theme } to iframe.contentWindow targeting window.location.origin, and its code (comments stripped) contains no localStorage/sessionStorage write (ADR-4136 does-not-persist invariant). The live postMessage round-trip is captured in Playwright evidence (agents/dave/remote-lane/inbox/r-20261002-161325-e2a2/output/evidence/test-host-dark-iframe-flipped.png).",
36
+ "status": "pending",
37
+ "testPointer": "test/view/wespa-host-fixture.test.js::AC-206-2: embed-client posts { type: rcf-view-theme, theme } to the iframe targeting window.location.origin"
38
+ },
39
+ {
40
+ "id": "TC-229-hash-nav-embed-preserved",
41
+ "acId": "AC-206-3",
42
+ "description": "src/view/page-init.js exposes writeHash (reassigns location.hash only, preserving the search string by construction) and stamps data-embed + data-theme on load. Live hash-nav proof lives in Playwright evidence (hash-nav-proof.txt + test-host-iframe-hash-nav-embed-preserved.png).",
43
+ "status": "pending",
44
+ "testPointer": "test/view/wespa-host-fixture.test.js::AC-206-3: page-init preserves embed=1 and theme= across hash writes (urlWithHash + writeHash)"
45
+ },
46
+ {
47
+ "id": "TC-229-cross-origin-ignored",
48
+ "acId": "AC-206-4",
49
+ "description": "src/view/page-init.js registers an addEventListener('message', ...) whose first guard returns early when ev.origin !== window.location.origin; cross-origin messages never reach the applyTheme path and <html data-theme> is unchanged.",
50
+ "status": "pending",
51
+ "testPointer": "test/view/wespa-host-fixture.test.js::AC-206-4: page-init theme message listener rejects messages whose origin differs from window.location.origin"
52
+ },
53
+ {
54
+ "id": "TC-229-lint-framing",
55
+ "acId": "AC-206-5",
56
+ "description": "assertFramingHeaders over GET / returns ok: true (no X-Frame-Options; no CSP frame-ancestors forbidding same-origin). Positive + negative unit cases also pass (refuses on X-Frame-Options present, refuses on frame-ancestors 'none', accepts frame-ancestors 'self').",
57
+ "status": "pending",
58
+ "testPointer": "test/view/wespa-host-fixture.test.js::lint 1 (framing): GET / carries no X-Frame-Options and no CSP frame-ancestors that forbids same-origin"
59
+ },
60
+ {
61
+ "id": "TC-229-lint-no-target-top",
62
+ "acId": "AC-206-5",
63
+ "description": "assertNoTargetTop returns count: 0 across every rendered page (renderPage shell, renderComponentsFixturePage, renderTestHostPage). Also lints the populated dogfood tree render (renderModelToPage) so per-document conditional output cannot smuggle a target=\"_top\" past the test. Positive + negative unit cases cover target=\"_top\" / target='_top' / target=_top.",
64
+ "status": "pending",
65
+ "testPointer": "test/view/wespa-host-fixture.test.js::lint 2 (target=\"_top\"): zero occurrences across every rendered page (shell, components fixture, test-host fixture, populated dogfood tree)"
66
+ },
67
+ {
68
+ "id": "TC-229-lint-localstorage-namespace",
69
+ "acId": "AC-206-5",
70
+ "description": "assertLocalStorageNamespace over the src/view tree returns ok: true; every localStorage/sessionStorage call site resolves to a key starting with 'rcf-view:v1:'.",
71
+ "status": "pending",
72
+ "testPointer": "test/view/wespa-host-fixture.test.js::lint 3 (localStorage namespace): every storage call site under src/view uses an rcf-view:v1: key"
73
+ },
74
+ {
75
+ "id": "TC-229-no-new-outbound-postmessage-shape",
76
+ "acId": "AC-206-6",
77
+ "description": "A grep over src/view/** (excluding test-host/embed-client.js, the sanctioned emitter) finds zero .postMessage( call sites; the embed-client itself posts exactly one shape, 'rcf-view-theme'.",
78
+ "status": "pending",
79
+ "testPointer": "test/view/wespa-host-fixture.test.js::AC-206-6: no outbound postMessage shape beyond rcf-view-theme is introduced by the viewer or the fixture"
80
+ }
81
+ ],
82
+ "status": "draft"
83
+ }
@@ -56,14 +56,14 @@
56
56
  },
57
57
  {
58
58
  "id": "AC-15601-6",
59
- "description": "The rule-4 bare-hostname redactor treats filename tokens carrying a known file extension as NOT hostnames. A candidate hostname match whose trailing dot-label is in a bundled FILE_EXTENSIONS set (initially md, mdx, json, yml, yaml, txt, js, mjs, cjs, ts, tsx, html, css, png, svg, sh, csv, log, lock) is left intact and no ledger row is written for it (design amendment R5d). The negative lookbehind on the hostname regex additionally excludes '-' so that filename fragments embedded in a larger identifier (`Backstory-product-brief.md`) cannot start a match after a hyphen. The extension shortlist is merged at call time from an `extensions` array on src/feedback/redact-allowlist.json alongside the existing `hosts` field, so operators can extend it the same way they extend hosts.",
60
- "given": "a fixture project with a body containing the tokens 'Backstory-product-brief.md', 'intake.json', 'report.log', 'foo.example.com', 'foo.example.com/path', 'Backstory-example.com', and 'a.b.c'",
59
+ "description": "The rule-4 bare-hostname redactor treats filename tokens carrying a known file extension as NOT hostnames. A candidate hostname match whose trailing dot-label is in a bundled FILE_EXTENSIONS set (initially md, mdx, json, yml, yaml, txt, js, mjs, cjs, ts, tsx, html, css, png, svg, sh, csv, log, lock) is left intact and no ledger row is written for it (design amendment R5d). The negative lookbehind on the hostname regex additionally excludes '-' so that filename fragments embedded in a larger identifier (`Backstory-product-brief.md`) cannot start a match after a hyphen. The extension shortlist is merged at call time from an `extensions` array on src/feedback/redact-allowlist.json alongside the existing `hosts` field, so operators can extend it the same way they extend hosts. Design amendment R5e (added 0.28.3 per HQ 2026-09-22 ruling on Codex P1-5 deferred from PR #242): candidate hostnames whose trailing dot-label is a single letter are also treated as filename fragments, not hostnames. Rationale: single-letter TLDs are not registrable domains in normal prose and folding them yields more false positives (short identifiers, abbreviated references) than legitimate hostname redactions. Design amendment R5f (added 0.28.3, #243): the rule-3 email regex is bounded so it does not consume the git protocol sentinel `git@<host>` inside a URL whose scheme is `git+ssh://`, `ssh://` or `git://`, nor the bare git-remote shape `git@<host>:<owner>/<repo>` (colon followed by a non-space path). Rationale: `git@<host>` is a URI userinfo construct, not an email; folding it destroys evidence pointers copied from git remotes and clone URLs. Real email addresses in surrounding prose still fold. Design amendment R5g (added 0.28.3, #246): when the operator identity Name (rcf/.identity/profile.md ## Name) is empty, whitespace only, or contains only reserved-noun tokens (RESERVED_IDENTITY_TOKENS from AC-15601-5), buildRedactionContext emits a one-line visible warning to stderr naming the file and stating that identity folding is disabled. The warning is emitted once per redaction context build. Folding still skips (per AC-15601-5) but the operator is no longer left thinking their identity is in force.",
60
+ "given": "a fixture project with a body containing the tokens 'Backstory-product-brief.md', 'intake.json', 'report.log', 'foo.example.com', 'foo.example.com/path', 'Backstory-example.com', 'a.b.c', the URL 'git+ssh://git@github.com/owner/repo.git#v1.0.0', the URL 'ssh://git@example.internal/owner/repo.git', the bare git remote 'git@github.com:owner/repo.git', and a prose email 'alice@example.com'; and a rcf/.identity/profile.md whose ## Name line is either absent, whitespace only, or reads 'The Agent Operator' (stopword-only)",
61
61
  "when": "the redactor runs over the body with the default allowlist",
62
- "then": "'Backstory-product-brief.md', 'intake.json' and 'report.log' survive verbatim with no ledger row for a hostname substitution on their basename; 'foo.example.com' folds to <host> via rule 4; 'foo.example.com/path' folds via the URL rule; 'Backstory-example.com' folds to '<host>' (the trailing label is not in the extension set); 'a.b.c' folds to <host>. Extending the allowlist with `extensions: [\"xyz\"]` in redact-allowlist.json (or the operator overlay) makes 'file.xyz' also survive.",
62
+ "then": "'Backstory-product-brief.md', 'intake.json' and 'report.log' survive verbatim with no ledger row for a hostname substitution on their basename; 'foo.example.com' folds to <host> via rule 4; 'foo.example.com/path' folds via rule 4 bare-host (the URL rule requires a scheme; the token here has none); 'Backstory-example.com' folds to '<host>' (the trailing label is not in the extension set); 'a.b.c' survives verbatim with no hostname ledger row (single-letter trailing label per R5e); 'git+ssh://git@github.com/owner/repo.git#v1.0.0' survives verbatim - the `git@` sentinel is NOT rewritten by the email rule (R5f) and github.com is on the allowlist; 'ssh://git@example.internal/owner/repo.git' has the `git@` sentinel preserved (R5f stash) and the host `example.internal` folded to '<host>' by the extended URL rule 4 which now matches ssh/git+ssh/git schemes with optional userinfo; 'git@github.com:owner/repo.git' has the `git@` sentinel preserved (R5f) and github.com is on the allowlist so it survives verbatim; 'alice@example.com' in surrounding prose still folds to '<email>' (rule 3 email); and when the identity Name is empty/whitespace/stopword-only, stderr carries exactly one visible warning line naming rcf/.identity/profile.md and stating that identity folding is disabled. Extending the allowlist with `extensions: [\"xyz\"]` in redact-allowlist.json (or the operator overlay) makes 'file.xyz' also survive.",
63
63
  "testable": true,
64
64
  "scope": "runtime"
65
65
  }
66
66
  ],
67
67
  "createdAt": "2026-09-16T13:45:23.000Z",
68
- "updatedAt": "2026-09-17T13:47:03Z"
68
+ "updatedAt": "2026-09-22T13:07:00Z"
69
69
  }
@@ -0,0 +1,27 @@
1
+ {
2
+ "usId": "US-17001",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-170",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "Product Map: shape grouping",
8
+ "description": "User story under REQ-170. a product reader reviewing the requirements chain the Product Map tab to bucket requirements by shapeClassification.shapes[], with an \"Unclassified\" bucket for REQs missing the classification and a \"Business rules\" bucket for the \"none\" shape",
9
+ "asA": "a product reader reviewing the requirements chain",
10
+ "iWant": "the Product Map tab to bucket requirements by shapeClassification.shapes[], with an \"Unclassified\" bucket for REQs missing the classification and a \"Business rules\" bucket for the \"none\" shape",
11
+ "soThat": "I can see at a glance which requirements sit in each surface area (webUi, httpApi, auth, persistence, notifications, business rules) without reading every REQ body",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-17001-1",
15
+ "description": "The rendered review-surface HTML carries a \"product-map\" tab (data-tab=\"product-map\") in tab order after Build sequence, with a hidden tabpanel id \"tab-product-map\" containing a shape-grouping section whose bucket headings enumerate every shape the classified REQs use plus \"Unclassified\" and \"Business rules\". A REQ classified into two shapes appears in both buckets. Rendering does not require a schema change.",
16
+ "testable": true
17
+ },
18
+ {
19
+ "id": "AC-17001-2",
20
+ "description": "Selecting the shape grouping drills each bucket to its REQs (rendered with the same doc-details helper and status pill as the Requirements tab), each REQ to its user stories, and each user story to its acceptance criteria, so the reader can go from a shape bucket to an AC in the same tab. Grouping button clicks and status filter changes push a new history entry (Back/Forward traverse them); top-level tab clicks keep their existing behaviour.",
21
+ "testable": true
22
+ }
23
+ ],
24
+ "tags": [],
25
+ "createdAt": "2026-09-21T17:00:00.000Z",
26
+ "updatedAt": "2026-09-21T17:43:06.307Z"
27
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "usId": "US-17002",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-170",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "Product Map: status filter",
8
+ "description": "User story under REQ-170. a reviewer preparing a sign-off pass a status filter (all, draft, review, needsRevision, approved, superseded) on the Product Map tab that applies to whichever grouping is active",
9
+ "asA": "a reviewer preparing a sign-off pass",
10
+ "iWant": "a status filter (all, draft, review, needsRevision, approved, superseded) on the Product Map tab that applies to whichever grouping is active",
11
+ "soThat": "I can narrow the tab to just the \"approved\" or just the \"needsRevision\" REQs without switching tabs or scanning by hand",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-17002-1",
15
+ "description": "The Product Map tab carries a status filter control whose options are the five authoring statuses plus \"all\", and applies to every grouping on the tab. When \"approved\" is selected the tab renders only REQs whose status is approved. Buckets that become empty under the current filter are hidden by default with a summary line (\"N empty\" - clicking reveals them), so the map keeps its shape without rendering a wall of empty headings.",
16
+ "testable": true
17
+ }
18
+ ],
19
+ "tags": [],
20
+ "createdAt": "2026-09-21T17:00:00.000Z",
21
+ "updatedAt": "2026-09-21T18:36:17.135Z"
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "usId": "US-17003",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-170",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "Product Map: component-first grouping",
8
+ "description": "User story under REQ-170. a technical reviewer wanting to see requirements from the architecture down a component grouping rooted at the TAD, showing each TAC (with its dependencies) at level 1, the user stories linked to that TAC at level 2, each story's parent REQ at level 3, and its ACs at level 4, plus an \"Unmapped\" bucket for REQs no component reaches",
9
+ "asA": "a technical reviewer wanting to see requirements from the architecture down",
10
+ "iWant": "a component grouping rooted at the TAD, showing each TAC (with its dependencies) at level 1, the user stories linked to that TAC at level 2, each story's parent REQ at level 3, and its ACs at level 4, plus an \"Unmapped\" bucket for REQs no component reaches",
11
+ "soThat": "I can pivot from a component to the requirements it delivers without leaving the tab",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-17003-1",
15
+ "description": "When the Product Map is grouped by component, the tab renders the TAD as the root, one section per TAC (with a short dependencies[] list drawn from the TAC document), then the user stories under that TAC (from usByTacId), then their parent REQs (from us.reqId), then the ACs. REQs reachable by no TAC are collected under an \"Unmapped\" bucket with a count in its heading.",
16
+ "testable": true
17
+ }
18
+ ],
19
+ "tags": [],
20
+ "createdAt": "2026-09-21T17:00:00.000Z",
21
+ "updatedAt": "2026-09-21T17:00:00.000Z"
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "usId": "US-17004",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-170",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "Product Map: trace-coverage grouping",
8
+ "description": "User story under REQ-170. a reviewer about to sign a chain off a trace-coverage grouping that buckets each REQ by chain completeness (complete, missing stories, missing AC scope or owner, missing component owner, missing tests)",
9
+ "asA": "a reviewer about to sign a chain off",
10
+ "iWant": "a trace-coverage grouping that buckets each REQ by chain completeness (complete, missing stories, missing AC scope or owner, missing component owner, missing tests)",
11
+ "soThat": "I can see which requirements are safe to sign off and which still need work",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-17004-1",
15
+ "description": "The trace-coverage grouping buckets each REQ into one of complete | missing-stories | missing-ac-scope | missing-component | missing-tests, computed from the tree-model maps (storiesByReqId, acIdsByUsId, usByTacId, tsByAcId / tcsByAcId / fbsByAcId). Each REQ row states in plain text exactly what is missing, and links to its own doc anchor so the reader can jump to it.",
16
+ "testable": true
17
+ }
18
+ ],
19
+ "tags": [],
20
+ "createdAt": "2026-09-21T17:00:00.000Z",
21
+ "updatedAt": "2026-09-21T17:00:00.000Z"
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "usId": "US-17005",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-170",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "Product Map: capability grouping",
8
+ "description": "User story under REQ-170. a product reader thinking in capabilities rather than technical shape a capability grouping that reads capability:<slug> entries from REQ.tags[] and buckets requirements per capability",
9
+ "asA": "a product reader thinking in capabilities rather than technical shape",
10
+ "iWant": "a capability grouping that reads capability:<slug> entries from REQ.tags[] and buckets requirements per capability",
11
+ "soThat": "I can see the product as a set of capabilities and drill into each capability's REQs, stories and ACs",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-17005-1",
15
+ "description": "The capability grouping enumerates one bucket per distinct capability:<slug> value present on any REQ across the tree. A REQ carrying multiple capability tags appears under each. REQs whose tags carry no capability:<slug> entry are collected under an \"Unclassified\" bucket. Slugs are rendered as their kebab form in the heading; named capability buckets are ordered by REQ count desc, then alphabetically, and the Unclassified bucket is always pinned last.",
16
+ "testable": true
17
+ }
18
+ ],
19
+ "tags": [],
20
+ "createdAt": "2026-09-21T17:00:00.000Z",
21
+ "updatedAt": "2026-09-21T17:43:06.511Z"
22
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "createdAt": "2026-09-21T18:36:23.922Z",
3
+ "updatedAt": "2026-09-21T18:36:52.280Z",
4
+ "description": "a product reader who returns to the tab across sessions collapsed-by-default buckets, a jump-nav index across the top, per-bucket status mini-counts, hash-encoded open buckets so a bookmarked URL restores tab plus grouping plus filter plus open buckets, and compact REQ cards with drill-down (raw JSON stays on the Requirements tab), so the tab scans in under a minute and survives a live-refresh with open state intact",
5
+ "usId": "US-17006",
6
+ "prdId": "PRD-001",
7
+ "reqId": "REQ-170",
8
+ "version": "0.1.0",
9
+ "status": "draft",
10
+ "title": "Product Map: usable interaction model",
11
+ "asA": "a product reader who returns to the Product Map across sessions",
12
+ "iWant": "collapsed-by-default buckets, a jump-nav index across the top, per-bucket status mini-counts, hash-encoded open-bucket state and compact REQ cards with drill-down (raw JSON stays on the Requirements tab)",
13
+ "soThat": "the tab scans in under a minute and a bookmarked URL restores tab, grouping, filter and the buckets I had open, and my open state survives a live refresh",
14
+ "acceptanceCriteria": [
15
+ {
16
+ "id": "AC-17006-1",
17
+ "description": "The Product Map tab renders every bucket collapsed by default, with a summary row carrying the bucket label, its total REQ count and a mini count per authoring status (draft / review / needsRevision / approved / superseded). A jump-nav rail across the top of the panel lists every bucket in the active grouping with its count; clicking a chip scrolls to that bucket and opens it. The hash schema `#tab=product-map&group=<g>&status=<s>&open=<id1>,<id2>` restores tab, grouping, status filter AND the set of open buckets on return, and every bucket-toggle pushes a new history entry so Back/Forward traverse them. Per-bucket <details> nodes carry a stable data-doc-id (\"pm-bucket:<group>:<id>\") so the live-client persists their open state across SSE refreshes.",
18
+ "testable": true
19
+ }
20
+ ]
21
+ }