rcf-lite 0.28.2 → 0.28.4

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 (196) hide show
  1. package/CHANGELOG.md +211 -0
  2. package/bin/rcf.js +4 -0
  3. package/fixtures/canary-manifest.json +9 -9
  4. package/package.json +1 -1
  5. package/rcf/adrs/adr-4120-canonical-json-hashing.json +30 -0
  6. package/rcf/adrs/adr-4121-define-sidecars.json +30 -0
  7. package/rcf/adrs/adr-4122-define-gates-blocking-and-optout.json +30 -0
  8. package/rcf/adrs/adr-4123-readiness-single-object.json +30 -0
  9. package/rcf/code-nodes/cn-700.json +23 -0
  10. package/rcf/code-nodes/cn-701.json +14 -0
  11. package/rcf/code-nodes/cn-702.json +14 -0
  12. package/rcf/code-nodes/cn-703.json +16 -0
  13. package/rcf/code-nodes/cn-720.json +15 -0
  14. package/rcf/code-nodes/cn-721.json +15 -0
  15. package/rcf/code-nodes/cn-722.json +15 -0
  16. package/rcf/code-nodes/cn-723.json +18 -0
  17. package/rcf/code-nodes/cn-724.json +14 -0
  18. package/rcf/code-nodes/cn-725.json +21 -0
  19. package/rcf/code-nodes/cn-726.json +17 -0
  20. package/rcf/code-nodes/cn-727.json +14 -0
  21. package/rcf/code-nodes/cn-728.json +21 -0
  22. package/rcf/code-nodes/cn-729.json +18 -0
  23. package/rcf/code-nodes/cn-730.json +14 -0
  24. package/rcf/fbs/fbs-190.json +37 -0
  25. package/rcf/fbs/fbs-191.json +34 -0
  26. package/rcf/fbs/fbs-192.json +17 -0
  27. package/rcf/fbs/fbs-193.json +37 -0
  28. package/rcf/fbs/fbs-194.json +35 -0
  29. package/rcf/fbs/fbs-195.json +37 -0
  30. package/rcf/fbs/fbs-196.json +37 -0
  31. package/rcf/fbs/fbs-197.json +41 -0
  32. package/rcf/requirements/req-001.json +2 -1
  33. package/rcf/requirements/req-002.json +2 -1
  34. package/rcf/requirements/req-003.json +2 -1
  35. package/rcf/requirements/req-004.json +2 -1
  36. package/rcf/requirements/req-005.json +2 -1
  37. package/rcf/requirements/req-006.json +2 -1
  38. package/rcf/requirements/req-007.json +2 -1
  39. package/rcf/requirements/req-008.json +2 -1
  40. package/rcf/requirements/req-009.json +2 -1
  41. package/rcf/requirements/req-010.json +5 -2
  42. package/rcf/requirements/req-011.json +2 -1
  43. package/rcf/requirements/req-012.json +2 -1
  44. package/rcf/requirements/req-013.json +2 -1
  45. package/rcf/requirements/req-014.json +2 -1
  46. package/rcf/requirements/req-015.json +2 -1
  47. package/rcf/requirements/req-016.json +5 -2
  48. package/rcf/requirements/req-017.json +6 -2
  49. package/rcf/requirements/req-018.json +5 -2
  50. package/rcf/requirements/req-019.json +5 -2
  51. package/rcf/requirements/req-020.json +5 -2
  52. package/rcf/requirements/req-021.json +5 -2
  53. package/rcf/requirements/req-022.json +5 -2
  54. package/rcf/requirements/req-023.json +6 -4
  55. package/rcf/requirements/req-024.json +5 -2
  56. package/rcf/requirements/req-025.json +5 -2
  57. package/rcf/requirements/req-026.json +5 -2
  58. package/rcf/requirements/req-027.json +5 -2
  59. package/rcf/requirements/req-028.json +5 -2
  60. package/rcf/requirements/req-029.json +5 -2
  61. package/rcf/requirements/req-030.json +5 -2
  62. package/rcf/requirements/req-040.json +5 -2
  63. package/rcf/requirements/req-041.json +5 -2
  64. package/rcf/requirements/req-042.json +5 -2
  65. package/rcf/requirements/req-043.json +5 -2
  66. package/rcf/requirements/req-050.json +5 -2
  67. package/rcf/requirements/req-051.json +5 -2
  68. package/rcf/requirements/req-052.json +5 -2
  69. package/rcf/requirements/req-053.json +5 -2
  70. package/rcf/requirements/req-054.json +5 -2
  71. package/rcf/requirements/req-060.json +5 -2
  72. package/rcf/requirements/req-061.json +5 -2
  73. package/rcf/requirements/req-062.json +5 -2
  74. package/rcf/requirements/req-063.json +5 -2
  75. package/rcf/requirements/req-070.json +5 -2
  76. package/rcf/requirements/req-071.json +5 -2
  77. package/rcf/requirements/req-072.json +5 -2
  78. package/rcf/requirements/req-073.json +5 -2
  79. package/rcf/requirements/req-074.json +5 -2
  80. package/rcf/requirements/req-075.json +5 -2
  81. package/rcf/requirements/req-076.json +5 -2
  82. package/rcf/requirements/req-077.json +5 -2
  83. package/rcf/requirements/req-080.json +5 -2
  84. package/rcf/requirements/req-081.json +5 -2
  85. package/rcf/requirements/req-082.json +5 -2
  86. package/rcf/requirements/req-083.json +5 -2
  87. package/rcf/requirements/req-084.json +5 -2
  88. package/rcf/requirements/req-085.json +5 -2
  89. package/rcf/requirements/req-086.json +5 -2
  90. package/rcf/requirements/req-090.json +5 -2
  91. package/rcf/requirements/req-091.json +5 -2
  92. package/rcf/requirements/req-092.json +5 -2
  93. package/rcf/requirements/req-093.json +5 -2
  94. package/rcf/requirements/req-094.json +5 -2
  95. package/rcf/requirements/req-100.json +5 -2
  96. package/rcf/requirements/req-101.json +5 -2
  97. package/rcf/requirements/req-102.json +5 -2
  98. package/rcf/requirements/req-103.json +5 -2
  99. package/rcf/requirements/req-104.json +5 -2
  100. package/rcf/requirements/req-110.json +5 -2
  101. package/rcf/requirements/req-111.json +5 -2
  102. package/rcf/requirements/req-112.json +5 -2
  103. package/rcf/requirements/req-113.json +5 -2
  104. package/rcf/requirements/req-114.json +5 -2
  105. package/rcf/requirements/req-115.json +5 -2
  106. package/rcf/requirements/req-120.json +5 -2
  107. package/rcf/requirements/req-121.json +5 -2
  108. package/rcf/requirements/req-122.json +5 -2
  109. package/rcf/requirements/req-123.json +5 -2
  110. package/rcf/requirements/req-124.json +5 -2
  111. package/rcf/requirements/req-125.json +5 -2
  112. package/rcf/requirements/req-130.json +6 -2
  113. package/rcf/requirements/req-131.json +6 -2
  114. package/rcf/requirements/req-132.json +6 -2
  115. package/rcf/requirements/req-133.json +6 -2
  116. package/rcf/requirements/req-134.json +6 -2
  117. package/rcf/requirements/req-140.json +5 -2
  118. package/rcf/requirements/req-145.json +5 -2
  119. package/rcf/requirements/req-150.json +6 -2
  120. package/rcf/requirements/req-151.json +6 -2
  121. package/rcf/requirements/req-152.json +6 -2
  122. package/rcf/requirements/req-153.json +6 -2
  123. package/rcf/requirements/req-154.json +5 -2
  124. package/rcf/requirements/req-155.json +4 -1
  125. package/rcf/requirements/req-156.json +4 -1
  126. package/rcf/requirements/req-157.json +4 -1
  127. package/rcf/requirements/req-158.json +4 -1
  128. package/rcf/requirements/req-159.json +4 -1
  129. package/rcf/requirements/req-160.json +4 -1
  130. package/rcf/requirements/req-161.json +4 -1
  131. package/rcf/requirements/req-162.json +4 -1
  132. package/rcf/requirements/req-163.json +5 -2
  133. package/rcf/requirements/req-164.json +5 -2
  134. package/rcf/requirements/req-165.json +5 -2
  135. package/rcf/requirements/req-170.json +33 -0
  136. package/rcf/requirements/req-171.json +44 -0
  137. package/rcf/requirements/req-172.json +25 -0
  138. package/rcf/requirements/req-173.json +25 -0
  139. package/rcf/requirements/req-174.json +25 -0
  140. package/rcf/requirements/req-175.json +25 -0
  141. package/rcf/tacs/tac-4120-define-delta-and-freeze.json +21 -0
  142. package/rcf/tacs/tac-4121-define-ledgers.json +23 -0
  143. package/rcf/tacs/tac-4122-define-gates.json +25 -0
  144. package/rcf/tacs/tac-4123-define-readiness.json +25 -0
  145. package/rcf/test-suites/ts-204.json +30 -0
  146. package/rcf/test-suites/ts-205.json +22 -0
  147. package/rcf/test-suites/ts-206.json +22 -0
  148. package/rcf/test-suites/ts-207.json +22 -0
  149. package/rcf/test-suites/ts-208.json +22 -0
  150. package/rcf/test-suites/ts-209.json +22 -0
  151. package/rcf/test-suites/ts-210.json +22 -0
  152. package/rcf/test-suites/ts-211.json +22 -0
  153. package/rcf/test-suites/ts-212.json +22 -0
  154. package/rcf/test-suites/ts-213.json +50 -0
  155. package/rcf/test-suites/ts-214.json +74 -0
  156. package/rcf/test-suites/ts-215.json +67 -0
  157. package/rcf/test-suites/ts-216.json +78 -0
  158. package/rcf/test-suites/ts-217.json +62 -0
  159. package/rcf/user-stories/us-15601.json +4 -4
  160. package/rcf/user-stories/us-17001.json +27 -0
  161. package/rcf/user-stories/us-17002.json +22 -0
  162. package/rcf/user-stories/us-17003.json +22 -0
  163. package/rcf/user-stories/us-17004.json +22 -0
  164. package/rcf/user-stories/us-17005.json +22 -0
  165. package/rcf/user-stories/us-17006.json +21 -0
  166. package/rcf/user-stories/us-17007.json +21 -0
  167. package/rcf/user-stories/us-17008.json +22 -0
  168. package/rcf/user-stories/us-17101.json +22 -0
  169. package/rcf/user-stories/us-17102.json +22 -0
  170. package/rcf/user-stories/us-17201.json +38 -0
  171. package/rcf/user-stories/us-17301.json +38 -0
  172. package/rcf/user-stories/us-17401.json +58 -0
  173. package/rcf/user-stories/us-17501.json +48 -0
  174. package/releases/releases.yaml +21 -1
  175. package/src/cli/feedback.js +126 -10
  176. package/src/cli/help.js +11 -0
  177. package/src/cli/ledger.js +399 -0
  178. package/src/cli/readiness.js +280 -0
  179. package/src/core/store/writer.js +86 -6
  180. package/src/define/freeze-record.js +232 -0
  181. package/src/define/ledgers.js +513 -0
  182. package/src/feedback/redact.js +105 -19
  183. package/src/query/delta.js +276 -0
  184. package/src/query/gates.js +832 -0
  185. package/src/query/index.js +36 -0
  186. package/src/query/readiness.js +333 -0
  187. package/src/server/index.js +37 -0
  188. package/src/server/routes.js +65 -3
  189. package/src/view/doc-renderers/helpers.js +2 -2
  190. package/src/view/doc-renderers/req.js +10 -2
  191. package/src/view/doc-renderers/user-story.js +10 -5
  192. package/src/view/html-page.js +347 -28
  193. package/src/view/index.js +14 -1
  194. package/src/view/live-client.js +22 -2
  195. package/src/view/product-map.js +751 -0
  196. package/src/view/style.css +378 -0
@@ -0,0 +1,62 @@
1
+ {
2
+ "createdAt": "2026-09-24T14:30:00Z",
3
+ "updatedAt": "2026-09-24T14:30:00Z",
4
+ "purpose": "Cover computeReadiness and the rcf define readiness CLI (AC-17501-1..6) with unit tests over fixture trees + a CLI spawn, plus a targeted test of the P3 loadAllLedgers fix.",
5
+ "testLevel": "unit",
6
+ "acIds": [
7
+ "AC-17501-1",
8
+ "AC-17501-2",
9
+ "AC-17501-3",
10
+ "AC-17501-4",
11
+ "AC-17501-5",
12
+ "AC-17501-6"
13
+ ],
14
+ "id": "TS-217",
15
+ "usId": "US-17501",
16
+ "title": "computeReadiness and rcf define readiness CLI",
17
+ "testCases": [
18
+ {
19
+ "id": "TC-217-shape",
20
+ "acId": "AC-17501-1",
21
+ "description": "computeReadiness returns { tree, delta, stages, nextAction, coverage, decisions, freezeable } and freezeable is true when every stage state is in { passed, acknowledged, notApplicable }.",
22
+ "status": "pending",
23
+ "testPointer": "test/query/readiness.test.js::readiness: returns the section 2.4 shape and freezeable folds correctly"
24
+ },
25
+ {
26
+ "id": "TC-217-fanout",
27
+ "acId": "AC-17501-2",
28
+ "description": "Fan-out: computeImpact runs once per id in changed union added; impacted is the deduplicated union; impactedFbs is the subset with kind 'fbs' AND actionNeeded 're-execute'.",
29
+ "status": "pending",
30
+ "testPointer": "test/query/readiness.test.js::readiness: fan-out impacted / impactedFbs (frozen delta)"
31
+ },
32
+ {
33
+ "id": "TC-217-coverage",
34
+ "acId": "AC-17501-3",
35
+ "description": "coverage.tree is a tree-wide CoverageResult; coverage.delta is one CoverageResult per unique REQ ancestor of the delta ids.",
36
+ "status": "pending",
37
+ "testPointer": "test/query/readiness.test.js::readiness: coverage tree-wide and per REQ ancestor"
38
+ },
39
+ {
40
+ "id": "TC-217-next-action",
41
+ "acId": "AC-17501-4",
42
+ "description": "nextAction picks the first failing stage in D1..D8 order and the first failing check inside it, listing ids and a rcf define readiness --check <short-name> command; null when no stage is failing.",
43
+ "status": "pending",
44
+ "testPointer": "test/query/readiness.test.js::readiness: nextAction picks first failing stage and check"
45
+ },
46
+ {
47
+ "id": "TC-217-cli",
48
+ "acId": "AC-17501-5",
49
+ "description": "The CLI prints text summary by default and JSON with --json; --check <stage> exits 4 on blocking failure, 0 with a [warn] on warn-with-ack unacknowledged failure, 0 otherwise; admissibility refusal exits 4.",
50
+ "status": "pending",
51
+ "testPointer": "test/cli/readiness.test.js::readiness cli: text output shape and --check exit codes"
52
+ },
53
+ {
54
+ "id": "TC-217-load-all-ledgers",
55
+ "acId": "AC-17501-6",
56
+ "description": "loadAllLedgers omits ledgers whose files are absent so computeDelta records no docHash for absent ledgers; a project with only brief-ledger.json sees exactly one ledger:brief docHash.",
57
+ "status": "pending",
58
+ "testPointer": "test/define/ledgers.test.js::ledgers: loadAllLedgers omits absent ledger files"
59
+ }
60
+ ],
61
+ "status": "draft"
62
+ }
@@ -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
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "createdAt": "2026-09-21T18:36:34.519Z",
3
+ "updatedAt": "2026-09-21T18:36:52.512Z",
4
+ "description": "a reviewer opening the review surface on a slow link the initial HTML payload well under 3 MB by rendering only the active grouping fully; the other three groupings emit only their bucket headings (label + count) and hydrate the REQ list on demand when the reader switches grouping or opens a bucket, so first paint is fast and the tab stays interactive on 100+ REQ trees",
5
+ "usId": "US-17007",
6
+ "prdId": "PRD-001",
7
+ "reqId": "REQ-170",
8
+ "version": "0.1.0",
9
+ "status": "draft",
10
+ "title": "Product Map: lazy first-load payload",
11
+ "asA": "a reviewer opening the review surface on a slow link",
12
+ "iWant": "an initial HTML payload well under 3 MB by rendering only the active grouping fully and hydrating the other groupings on demand from a partial endpoint",
13
+ "soThat": "first paint is fast and the tab stays interactive on 100-plus REQ trees",
14
+ "acceptanceCriteria": [
15
+ {
16
+ "id": "AC-17007-1",
17
+ "description": "The Product Map first-load payload target is well under 3 MB. The server emits the shape (default) grouping fully; the other three groupings emit only their bucket headings (label + total count + per-status mini counts) and each bucket carries a data-pm-lazy marker naming its grouping and bucket id. When the reader switches to a lazy grouping OR opens a lazy bucket, the client fetches that grouping fully once (from a partial endpoint the server exposes) and hydrates the bucket into the DOM; subsequent switches use the cached partial. The pass doc states the before/after first-load bytes.",
18
+ "testable": true
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "usId": "US-17008",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-170",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "Product Map: blueprint badge and by-blueprint grouping",
8
+ "description": "User story under REQ-170. a product reader auditing what came from applied blueprints vs the project's own requirements a visible blueprint badge on every REQ row an applied blueprint contributed, plus a sixth grouping that buckets requirements by contributing blueprint (with an Application bucket for the project's own (non-blueprint) REQs) and a second-level capability split inside each bucket.",
9
+ "asA": "a product reader auditing what came from applied blueprints vs the project's own requirements",
10
+ "iWant": "a blueprint badge on every REQ row that was contributed by a blueprint plus a by-blueprint grouping (one bucket per applied blueprint plus an Application bucket) with capability as the second level inside each bucket",
11
+ "soThat": "I can see at a glance which parts of the tree ride on shared blueprints and which the project owns, and drill from a blueprint to the capabilities it contributed",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-17008-1",
15
+ "description": "Every REQ row inside the Product Map tab that was contributed by an applied blueprint carries a compact blueprint badge (class pm-blueprint-badge) in its summary row, in every grouping (shape, component, trace coverage, capability, and the new by-blueprint grouping itself). The GROUPINGS list on the panel additionally includes 'blueprint'; the by-blueprint grouping enumerates one bucket per entry in manifest.blueprints[] (label: the blueprint's display name or slug, count: number of contributed REQs) plus an Application bucket for the project's own (non-blueprint) REQs (REQs with no blueprint attribution). Inside each blueprint or Application bucket, requirements are split by capability (existing capability:<slug> convention) as a second level, then the usual REQ->US->AC drill-down. The source of truth for blueprint attribution is manifest.blueprints[].contributions[] (parsed by scope.contributionsForBlueprint); the id-prefix fallback (kebab-slug prefix on reqId) is used only when the manifest carries no contributions record for a given blueprint. The hash schema round-trips as #tab=product-map&group=blueprint&status=<s>&open=<ids>, and the /product-map/blueprint partial endpoint serves the grouping body for the client's lazy-render fetch. A project with no applied blueprints renders a single Application bucket cleanly (not an error, not an empty tab).",
16
+ "testable": true
17
+ }
18
+ ],
19
+ "tags": [],
20
+ "createdAt": "2026-09-22T11:59:39Z",
21
+ "updatedAt": "2026-09-22T15:00:00Z"
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "usId": "US-17101",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-171",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "Dogfood chain carries capability tags on every requirement",
8
+ "description": "User story under REQ-171. the maintainer of the rcf-lite dogfood chain every REQ under rcf-lite/rcf to carry at least one capability:<slug> tag drawn from the documented taxonomy",
9
+ "asA": "the maintainer of the rcf-lite dogfood chain",
10
+ "iWant": "every REQ under rcf-lite/rcf to carry at least one capability:<slug> tag drawn from the documented taxonomy",
11
+ "soThat": "the by-capability Product Map view has something to bucket on, and the trial can be evaluated against real data rather than a fixture",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-17101-1",
15
+ "description": "Every REQ under packages/rcf-lite/rcf/requirements/ carries at least one tag matching the regex ^capability:[a-z0-9]+(-[a-z0-9]+)*$, and every slug used maps to an entry in the documented capability taxonomy.",
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-17102",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-171",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "Capability taxonomy is documented in the rcf-lite docs",
8
+ "description": "User story under REQ-171. a reader of the rcf-lite docs deciding whether to adopt the convention the capability:<slug> convention, the initial taxonomy and the rule for adding a new capability documented alongside the rest of the rcf-lite docs",
9
+ "asA": "a reader of the rcf-lite docs deciding whether to adopt the convention",
10
+ "iWant": "the capability:<slug> convention, the initial taxonomy and the rule for adding a new capability documented alongside the rest of the rcf-lite docs",
11
+ "soThat": "a new capability is added deliberately rather than by drift, and a reader can look up what each slug means",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-17102-1",
15
+ "description": "packages/rcf-lite/docs contains a capability tags section listing every slug in the initial taxonomy with a one-line meaning, plus the rule for adding a new capability (single-word verb or noun, kebab-case, must map to a product surface). The section is linked from docs/README.md so it is reachable from the docs entry point.",
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,38 @@
1
+ {
2
+ "usId": "US-17201",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-172",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "Delta detection and freeze record land as reusable primitives",
8
+ "description": "User story under REQ-172. The detection-model foundation lands as pure, importable modules and one durable local record.",
9
+ "asA": "the maintainer of a rcf-lite tree",
10
+ "iWant": "hashDocument, computeTreeHash, computeDelta and the rcf/define/freeze.json reader/writer to land as pure modules exposed on the query surface, with a documented return shape that later DEFINE slices can call unchanged",
11
+ "soThat": "the gates, readiness view, freeze verb and bundle-side read all consume the same detection primitives, and a hand reformat of a document never surfaces as a change",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-17201-1",
15
+ "description": "computeDelta(tree, freeze, ledgers) returns the exact object shape proposal 2026-09-22 v3 section 2.2 specifies (frozen, frozenAt, treeHash, currentTreeHash, changed, added, removed, briefSince, unchanged); given a matching freeze the delta reports empty changed / added / removed and unchanged equal to the number of docHashes; given divergent tree state changed / added / removed classify every id correctly against the freeze record.",
16
+ "testable": true
17
+ },
18
+ {
19
+ "id": "AC-17201-2",
20
+ "description": "hashDocument returns a sha256:<64-hex> string over the canonical JSON form of the parsed document (keys sorted recursively, no whitespace), and is invariant under a whitespace-only reformat or a key reorder: two documents that parse to the same object hash identically.",
21
+ "testable": true
22
+ },
23
+ {
24
+ "id": "AC-17201-3",
25
+ "description": "Loading rcf/define/freeze.json when the file is absent returns null; loading a malformed file (bad JSON, missing docHashes, non-sha256 hash value, negative briefStatements) raises FreezeRecordError with a code (parseFailure or schemaFailure) and a named field so a caller can log the exact failure.",
26
+ "testable": true
27
+ },
28
+ {
29
+ "id": "AC-17201-4",
30
+ "description": "Saving then loading a freeze record round-trips every documented field including extension fields the schema does not police (counts, litmus, versions, note), and saveFreezeRecord refuses to persist an invalid record: no file is written on validation failure.",
31
+ "testable": true
32
+ }
33
+ ],
34
+ "tacIds": ["TAC-4120-define-delta-and-freeze"],
35
+ "tags": [],
36
+ "createdAt": "2026-09-24T12:00:00Z",
37
+ "updatedAt": "2026-09-24T12:00:00Z"
38
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "usId": "US-17301",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-173",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "rcf define ledger reads and writes the four sidecars under rcf/define/",
8
+ "description": "User story under REQ-173. The operator-facing CLI for the four sidecar ledgers lands with add / resolve / list on every ledger name.",
9
+ "asA": "an operator staging or reviewing a DEFINE change",
10
+ "iWant": "rcf define ledger to author brief statements, decisions, concern dispositions and probe findings under rcf/define/ with numbered ids stable for the life of the project, and to leave every ledger invisible to the walker",
11
+ "soThat": "the freeze record's high-water mark, the D7 decisions list, D5 concern coverage and D6 probe findings all have a written record and every later DEFINE slice reads the same seam without discovering a second source of truth",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-17301-1",
15
+ "description": "rcf define ledger brief add --kind <k> --text <t> appends a numbered statement to rcf/define/brief-ledger.json, mints the next id (highest current + 1), stamps status open and addedAt (ISO-8601), refuses an unknown kind with a usage-class error, and refuses --text combined with --from with a usage-class error.",
16
+ "testable": true
17
+ },
18
+ {
19
+ "id": "AC-17301-2",
20
+ "description": "rcf define ledger decisions list prints the numbered #241 format: one decision per item (id. question), options rendered as 'letter) text', the default letter labelled 'default:', and what it blocks labelled 'blocks:'. rcf define ledger <name> list --json emits the machine-readable envelope { ledger, <arrayKey>: [...] }.",
21
+ "testable": true
22
+ },
23
+ {
24
+ "id": "AC-17301-3",
25
+ "description": "rcf define ledger brief add --from <file> reads the file, splits it into one statement per non-empty line (stripping leading '- ', '* ' or '<n>. ' bullets), mints sequential ids for every statement, refuses if the file produced zero non-empty lines, and stamps the file path as source unless --source overrides it. The intake-scan-against-frozen-statements hook is a named seam in the writer, not called in this slice.",
26
+ "testable": true
27
+ },
28
+ {
29
+ "id": "AC-17301-4",
30
+ "description": "walkTree run over a project that carries rcf/define/freeze.json, all four ledger files, and a bug-bait file named req-999.json under rcf/define/ never surfaces any rcf/define/ file as a tree.byId document and reports no walker error naming a rcf/define/ path. The sidecar directory is invisible to every consumer of walkTree.",
31
+ "testable": true
32
+ }
33
+ ],
34
+ "tacIds": ["TAC-4121-define-ledgers"],
35
+ "tags": [],
36
+ "createdAt": "2026-09-24T12:00:00Z",
37
+ "updatedAt": "2026-09-24T12:00:00Z"
38
+ }
@@ -0,0 +1,58 @@
1
+ {
2
+ "usId": "US-17401",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-174",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "Stage gates land as pure check functions with per-stage state",
8
+ "description": "User story under REQ-174. Eight stage-gate check functions land in src/query/gates.js with a stable per-stage return shape that computeReadiness and every later slice can consume unchanged.",
9
+ "asA": "a downstream consumer of DEFINE gates (readiness, freeze, viewer)",
10
+ "iWant": "gates.js to expose one pure check function per stage D1..D8, each returning a { stage, gate, state, checks[] } object with checks in the { name, ok, over: 'delta'|'tree', pass, total, failing: [{ id, why }] } shape",
11
+ "soThat": "computeReadiness composes the eight checks, the freeze verb refuses on any failing state, and the viewer's Readiness tab prints the same rows the CLI does with no schema translation",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-17401-1",
15
+ "description": "src/query/gates.js exports one check function per stage D1..D8 (checkD1Brief, checkD2Skeleton, checkD3Shapes, checkD4Stories, checkD5Crosscut, checkD6Consistency, checkD7Decisions, checkD8Freeze), each taking (tree, { ledgers, profile, scope, freeze, delta, validateErrors, probeOpenCount, coverageTree }) and returning { stage, gate, state, checks[] }; every entry in checks is { name, ok, over: 'delta'|'tree', pass, total, failing: [{ id, why }] } (failing empty on ok).",
16
+ "testable": true
17
+ },
18
+ {
19
+ "id": "AC-17401-2",
20
+ "description": "Blocking stages in 0.29.0 (D1, D2, D4, D7, D8) report state 'passed' when every check ok is true, 'failing' when any check ok is false; warn-with-ack stages (D3, D5, D6) report 'passed' when every check ok is true, 'failing' when any check ok is false and the freeze record does NOT acknowledge that gate at the current tree hash, and 'acknowledged' when the freeze.gates[<gate>] entry names state 'acknowledged' and at.hash === currentTreeHash and any check ok is false.",
21
+ "testable": true
22
+ },
23
+ {
24
+ "id": "AC-17401-3",
25
+ "description": "notApplicable is decided per stage per proposal section 3.2: D2 notApplicable when no statement of a resolving kind (capability, constraint, entity, actor, externalSystem, surface) is in the delta AND no REQ, PRD or TAD is in scope; D3 notApplicable when no TAC and no shaped REQ (httpApi/persistence/auth) is in scope; D5 notApplicable when no REQ is in scope; D7 notApplicable when no open decisions exist AND the profile sets skipReviewFor: singleDocument AND the delta holds exactly one document; D4 and D6 use their own rules; D1 and D8 never notApplicable.",
26
+ "testable": true
27
+ },
28
+ {
29
+ "id": "AC-17401-4",
30
+ "description": "checkD4Stories enforces the D4 floors: every REQ in scope has a US (fail: 'no owning user story'), every US in scope has a testable AC ('missing testable AC'), a failure AC (parsed by the bracketed [failure] prefix on AC description) or a reasoned opt-out per class (manifest.baselineAcOptOuts[] entry with baselineKey 'defineD4:failure' and matching scope), a must-not AC (parsed by [must-not] prefix) or a reasoned opt-out per class (baselineKey 'defineD4:mustNot'), tacIds non-empty and every named tac resolving to a real TAC, and no AC description contains the scaffold TODO placeholder.",
31
+ "testable": true
32
+ },
33
+ {
34
+ "id": "AC-17401-5",
35
+ "description": "checkD3Shapes runs only cheap data-backed checks in 0.29.0: every TAC in scope has at least one interface (fail: 'no interfaces authored'), and every interface's kind is in the closed vocabulary { recordShape, httpRoute, event, cliCommand, uiRoute, port, fileFormat, fixture, other } (fail: 'unknown interface kind <k>'). The template parser, entity-name join, ownerRef and path resolution are named seams (not called this slice) and D3 stays warn-with-ack.",
36
+ "testable": true
37
+ },
38
+ {
39
+ "id": "AC-17401-6",
40
+ "description": "checkD5Crosscut runs only cheap data-backed checks in 0.29.0: TAD.securityArchitecture non-empty when the tree carries any REQ whose shapeClassification.shapes includes 'auth' or 'httpApi'; TAD.operationalConcerns non-empty when any AC description carries the '[deployed]' marker (a cheap presence signal proposal section 3.2 D5 lists as 'deployed-scope AC'); concern-ledger entries in scope disposed as 'applied' or 'waived' (no dangling 'open' concerns on REQs in scope). The full catalogue and per-pair enumeration are named seams for 0.30.",
41
+ "testable": true
42
+ },
43
+ {
44
+ "id": "AC-17401-7",
45
+ "description": "checkD6Consistency runs only cheap data-backed checks in 0.29.0: validate is clean tree-wide (validateErrors is empty; the caller passes the walker + validator's error list), and probe-ledger holds zero 'open' probe entries. Contradiction-lite, unsatisfiable-lite, duplicates and orphan-interface scans, plus the probe runner, are named seams for 0.30.",
46
+ "testable": true
47
+ },
48
+ {
49
+ "id": "AC-17401-8",
50
+ "description": "checkD8Freeze enforces the tree-wide gate: every prior stage state (D1..D7 as computeReadiness composes them) is passed or acknowledged at the current hash; every AC id in the tree is bound by exactly one FBS (walker's fbsByAcId map has length 1); the queue head (computeQueue.nextActionable) is not null; validateErrors is empty. The stage is never notApplicable.",
51
+ "testable": true
52
+ }
53
+ ],
54
+ "tacIds": ["TAC-4122-define-gates"],
55
+ "tags": [],
56
+ "createdAt": "2026-09-24T14:30:00Z",
57
+ "updatedAt": "2026-09-24T14:30:00Z"
58
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "usId": "US-17501",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-175",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "computeReadiness and rcf define readiness render the same object",
8
+ "description": "User story under REQ-175. computeReadiness lands as a pure module and rcf define readiness as its CLI twin. The viewer's Readiness tab (slice 5) will render the same object with no schema translation.",
9
+ "asA": "an operator asking 'can I build, and if not what is in the way'",
10
+ "iWant": "one pure compute that composes delta, fan-out, gates, coverage and decisions into a single object, and one CLI that prints it in a compact form or emits it as JSON",
11
+ "soThat": "the tree line, next action, stage chips, delta list and failing items are the same wherever they are shown, and slice 3's rcf define freeze refuses on the same freezeable flag the CLI reports",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-17501-1",
15
+ "description": "computeReadiness(tree, { freeze, ledgers, profile, testPointers }) returns the exact section 2.4 shape: tree { frozen, frozenAt, treeHash, currentTreeHash, buildAt, fbsTotal }, delta { changed, added, removed, briefSince, impacted, impactedFbs }, stages [{ stage, gate, state, checks }], nextAction { stage, check, ids, command } or null, coverage { tree, delta }, decisions [...open in #241 format], freezeable boolean. freezeable is true when every stage state is 'passed', 'acknowledged' or 'notApplicable'.",
16
+ "testable": true
17
+ },
18
+ {
19
+ "id": "AC-17501-2",
20
+ "description": "Fan-out is computed by calling computeImpact once per id in the union of changed and added; impacted is the union of every impact node id excluding the pivots; impactedFbs is the subset whose kind is 'fbs' and actionNeeded is 're-execute'. Gates run over scope = changed union added union impacted (with duplicates removed).",
21
+ "testable": true
22
+ },
23
+ {
24
+ "id": "AC-17501-3",
25
+ "description": "Coverage is tree-wide plus per REQ ancestor of the delta: coverage.tree is computeCoverage(tree, { testPointers, strict: true }) once over the whole tree; coverage.delta is an array with one CoverageResult per unique REQ id reachable by walking back from every delta id (changed union added) through parentByChild until the reqId is found, using scopeId per invocation. testPointers is the map the CLI produced with resolveTestPointers.",
26
+ "testable": true
27
+ },
28
+ {
29
+ "id": "AC-17501-4",
30
+ "description": "nextAction picks the first stage in D1..D8 order whose state is 'failing', then the first failing check in that stage's checks[] (or the check with the largest failing.length when tie); it names the stage id, the check name, the failing ids (deduped), and a command string like 'rcf define readiness --check <stage-short-name> after editing'. nextAction is null when every stage is passed / acknowledged / notApplicable.",
31
+ "testable": true
32
+ },
33
+ {
34
+ "id": "AC-17501-5",
35
+ "description": "rcf define readiness prints a text summary: the tree line ('Frozen at <shortHash> on <frozenAt>. N documents changed since, M impacted. Build at <fbsId> of <total>; K FBS impacted.' or 'Unfrozen. N documents; the whole tree is the delta.'), the next action line, a chip line 'D1:passed D2:failing D3:acknowledged D4:notApplicable ...', and the failing-items list per stage. --json emits the full computeReadiness object under `${JSON.stringify(result, null, 2)}\\n`. --check <stage> (D1..D8 or short name brief|skeleton|shapes|stories|crosscut|consistency|decisions|freeze) prints only that stage and exits 0 when its state is 'passed' or 'acknowledged' or 'notApplicable', exit 4 on 'failing' for blocking stages (D1, D2, D4, D7, D8), exit 0 with a visible '[warn]' line on 'failing' for warn-with-ack stages (D3, D5, D6). The verb wraps its producer with runWithAdmissibilityGate and, on admissibility refusal, prints the refusal on stderr and exits 4.",
36
+ "testable": true
37
+ },
38
+ {
39
+ "id": "AC-17501-6",
40
+ "description": "loadAllLedgers({ projectRoot }) returns a LedgerBundle that OMITS a ledger key when the sidecar file does not exist (ENOENT), so computeDelta records no docHash for absent ledgers; the docstring on computeDelta (LedgerBundle typedef) and the loader implementation agree. A project that has authored a brief ledger and no others sees exactly one 'ledger:brief' entry in the delta docHashes; a project that has authored no ledgers sees zero 'ledger:*' entries.",
41
+ "testable": true
42
+ }
43
+ ],
44
+ "tacIds": ["TAC-4123-define-readiness"],
45
+ "tags": [],
46
+ "createdAt": "2026-09-24T14:30:00Z",
47
+ "updatedAt": "2026-09-24T14:30:00Z"
48
+ }
@@ -40,8 +40,28 @@
40
40
  # `npm install rcf-lite`.
41
41
 
42
42
  feedVersion: 1
43
- latest: "0.28.2"
43
+ latest: "0.28.4"
44
44
  releases:
45
+ - version: "0.28.4"
46
+ date: "2026-09-30"
47
+ breaking: false
48
+ headlines:
49
+ - "rcf-lite 0.28.4 fixes user story minting on legacy trees that carry flat sequential ids. On a tree migrated in from a v1 project, minting a new user story under a requirement now extends the flat sequence (US-163 to US-164) instead of jumping to an encoded slot such as US-5301; a tree that mixes both id shapes is refused with a message naming one example of each so the operator can normalise it first. Acceptance criteria minting is unchanged."
50
+ - "The 0.28.3 fixes ship in this release, because 0.28.3 was written up on 22 September but never tagged or published. Those are the feedback redactor no longer folding git protocol sentinels as emails, a visible warning when identity folding is disabled, and the live-view server snapshotting its static assets at startup."
51
+ - "The DEFINE workflow foundations arrive ahead of the 0.29.0 train: delta detection, freeze records, four sidecar ledgers with rcf define ledger, stage gates D1 to D8, and rcf define readiness, plus the Product Map tab on the review surface."
52
+ minAgentAction: null
53
+ notesUrl: "https://stravica.ai/docs/rcf/changelog/"
54
+
55
+ - version: "0.28.3"
56
+ date: "2026-09-22"
57
+ breaking: false
58
+ headlines:
59
+ - "rcf-lite 0.28.3 is a small batched fix release for the residuals Dex's 0.28.2 round-trip surfaced and one HQ-side viewer bug caught the same day. The feedback redactor no longer folds the git protocol sentinel git@<host> inside git+ssh, ssh or git URLs as an email, so an evidence pointer copied verbatim from a git remote or clone URL survives as evidence. Bare git remotes and single-letter trailing labels like a.b.c are also protected as URI userinfo and filename fragments; real emails in surrounding prose still fold."
60
+ - "The redactor emits a visible stderr warning when identity folding is disabled, so an operator whose rcf/.identity/profile.md is missing, empty, a placeholder, or contains only reserved nouns is no longer left thinking their identity is in force when it silently is not. Feedback status --json now exposes the destination source field the preview surface already prints, and --evidence pointers now render with the label matching their shape (command / path:line / id) rather than always (command)."
61
+ - "The live-view server snapshots its shipped static assets (style.css, mermaid.min.js, live-client.js) at startup instead of reading them from disk per request, so a checkout branch switch during the server's lifetime can no longer silently change the served CSS."
62
+ minAgentAction: null
63
+ notesUrl: "https://stravica.ai/docs/rcf/changelog/"
64
+
45
65
  - version: "0.28.2"
46
66
  date: "2026-09-22"
47
67
  breaking: false