rcf-lite 0.28.1 → 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 (219) hide show
  1. package/CHANGELOG.md +310 -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-602.json +3 -2
  10. package/rcf/code-nodes/cn-700.json +23 -0
  11. package/rcf/code-nodes/cn-701.json +14 -0
  12. package/rcf/code-nodes/cn-702.json +14 -0
  13. package/rcf/code-nodes/cn-703.json +16 -0
  14. package/rcf/code-nodes/cn-720.json +15 -0
  15. package/rcf/code-nodes/cn-721.json +15 -0
  16. package/rcf/code-nodes/cn-722.json +15 -0
  17. package/rcf/code-nodes/cn-723.json +18 -0
  18. package/rcf/code-nodes/cn-724.json +14 -0
  19. package/rcf/code-nodes/cn-725.json +21 -0
  20. package/rcf/code-nodes/cn-726.json +17 -0
  21. package/rcf/code-nodes/cn-727.json +14 -0
  22. package/rcf/code-nodes/cn-728.json +21 -0
  23. package/rcf/code-nodes/cn-729.json +18 -0
  24. package/rcf/code-nodes/cn-730.json +14 -0
  25. package/rcf/fbs/fbs-181.json +3 -2
  26. package/rcf/fbs/fbs-190.json +37 -0
  27. package/rcf/fbs/fbs-191.json +34 -0
  28. package/rcf/fbs/fbs-192.json +17 -0
  29. package/rcf/fbs/fbs-193.json +37 -0
  30. package/rcf/fbs/fbs-194.json +35 -0
  31. package/rcf/fbs/fbs-195.json +37 -0
  32. package/rcf/fbs/fbs-196.json +37 -0
  33. package/rcf/fbs/fbs-197.json +41 -0
  34. package/rcf/requirements/req-001.json +2 -1
  35. package/rcf/requirements/req-002.json +2 -1
  36. package/rcf/requirements/req-003.json +2 -1
  37. package/rcf/requirements/req-004.json +2 -1
  38. package/rcf/requirements/req-005.json +2 -1
  39. package/rcf/requirements/req-006.json +2 -1
  40. package/rcf/requirements/req-007.json +2 -1
  41. package/rcf/requirements/req-008.json +2 -1
  42. package/rcf/requirements/req-009.json +2 -1
  43. package/rcf/requirements/req-010.json +5 -2
  44. package/rcf/requirements/req-011.json +2 -1
  45. package/rcf/requirements/req-012.json +2 -1
  46. package/rcf/requirements/req-013.json +2 -1
  47. package/rcf/requirements/req-014.json +2 -1
  48. package/rcf/requirements/req-015.json +2 -1
  49. package/rcf/requirements/req-016.json +5 -2
  50. package/rcf/requirements/req-017.json +6 -2
  51. package/rcf/requirements/req-018.json +5 -2
  52. package/rcf/requirements/req-019.json +5 -2
  53. package/rcf/requirements/req-020.json +5 -2
  54. package/rcf/requirements/req-021.json +5 -2
  55. package/rcf/requirements/req-022.json +5 -2
  56. package/rcf/requirements/req-023.json +6 -4
  57. package/rcf/requirements/req-024.json +5 -2
  58. package/rcf/requirements/req-025.json +5 -2
  59. package/rcf/requirements/req-026.json +5 -2
  60. package/rcf/requirements/req-027.json +5 -2
  61. package/rcf/requirements/req-028.json +5 -2
  62. package/rcf/requirements/req-029.json +5 -2
  63. package/rcf/requirements/req-030.json +5 -2
  64. package/rcf/requirements/req-040.json +5 -2
  65. package/rcf/requirements/req-041.json +5 -2
  66. package/rcf/requirements/req-042.json +5 -2
  67. package/rcf/requirements/req-043.json +5 -2
  68. package/rcf/requirements/req-050.json +5 -2
  69. package/rcf/requirements/req-051.json +5 -2
  70. package/rcf/requirements/req-052.json +5 -2
  71. package/rcf/requirements/req-053.json +5 -2
  72. package/rcf/requirements/req-054.json +5 -2
  73. package/rcf/requirements/req-060.json +5 -2
  74. package/rcf/requirements/req-061.json +5 -2
  75. package/rcf/requirements/req-062.json +5 -2
  76. package/rcf/requirements/req-063.json +5 -2
  77. package/rcf/requirements/req-070.json +5 -2
  78. package/rcf/requirements/req-071.json +5 -2
  79. package/rcf/requirements/req-072.json +5 -2
  80. package/rcf/requirements/req-073.json +5 -2
  81. package/rcf/requirements/req-074.json +5 -2
  82. package/rcf/requirements/req-075.json +5 -2
  83. package/rcf/requirements/req-076.json +5 -2
  84. package/rcf/requirements/req-077.json +5 -2
  85. package/rcf/requirements/req-080.json +5 -2
  86. package/rcf/requirements/req-081.json +5 -2
  87. package/rcf/requirements/req-082.json +5 -2
  88. package/rcf/requirements/req-083.json +5 -2
  89. package/rcf/requirements/req-084.json +5 -2
  90. package/rcf/requirements/req-085.json +5 -2
  91. package/rcf/requirements/req-086.json +5 -2
  92. package/rcf/requirements/req-090.json +5 -2
  93. package/rcf/requirements/req-091.json +5 -2
  94. package/rcf/requirements/req-092.json +5 -2
  95. package/rcf/requirements/req-093.json +5 -2
  96. package/rcf/requirements/req-094.json +5 -2
  97. package/rcf/requirements/req-100.json +5 -2
  98. package/rcf/requirements/req-101.json +5 -2
  99. package/rcf/requirements/req-102.json +5 -2
  100. package/rcf/requirements/req-103.json +5 -2
  101. package/rcf/requirements/req-104.json +5 -2
  102. package/rcf/requirements/req-110.json +5 -2
  103. package/rcf/requirements/req-111.json +5 -2
  104. package/rcf/requirements/req-112.json +5 -2
  105. package/rcf/requirements/req-113.json +5 -2
  106. package/rcf/requirements/req-114.json +5 -2
  107. package/rcf/requirements/req-115.json +5 -2
  108. package/rcf/requirements/req-120.json +5 -2
  109. package/rcf/requirements/req-121.json +5 -2
  110. package/rcf/requirements/req-122.json +5 -2
  111. package/rcf/requirements/req-123.json +5 -2
  112. package/rcf/requirements/req-124.json +5 -2
  113. package/rcf/requirements/req-125.json +5 -2
  114. package/rcf/requirements/req-130.json +6 -2
  115. package/rcf/requirements/req-131.json +6 -2
  116. package/rcf/requirements/req-132.json +6 -2
  117. package/rcf/requirements/req-133.json +6 -2
  118. package/rcf/requirements/req-134.json +6 -2
  119. package/rcf/requirements/req-140.json +5 -2
  120. package/rcf/requirements/req-145.json +5 -2
  121. package/rcf/requirements/req-150.json +6 -2
  122. package/rcf/requirements/req-151.json +6 -2
  123. package/rcf/requirements/req-152.json +6 -2
  124. package/rcf/requirements/req-153.json +6 -2
  125. package/rcf/requirements/req-154.json +5 -2
  126. package/rcf/requirements/req-155.json +4 -1
  127. package/rcf/requirements/req-156.json +4 -1
  128. package/rcf/requirements/req-157.json +4 -1
  129. package/rcf/requirements/req-158.json +4 -1
  130. package/rcf/requirements/req-159.json +4 -1
  131. package/rcf/requirements/req-160.json +4 -1
  132. package/rcf/requirements/req-161.json +4 -1
  133. package/rcf/requirements/req-162.json +4 -1
  134. package/rcf/requirements/req-163.json +5 -2
  135. package/rcf/requirements/req-164.json +5 -2
  136. package/rcf/requirements/req-165.json +5 -2
  137. package/rcf/requirements/req-170.json +33 -0
  138. package/rcf/requirements/req-171.json +44 -0
  139. package/rcf/requirements/req-172.json +25 -0
  140. package/rcf/requirements/req-173.json +25 -0
  141. package/rcf/requirements/req-174.json +25 -0
  142. package/rcf/requirements/req-175.json +25 -0
  143. package/rcf/tacs/tac-4103-feedback-redact.json +2 -2
  144. package/rcf/tacs/tac-4120-define-delta-and-freeze.json +21 -0
  145. package/rcf/tacs/tac-4121-define-ledgers.json +23 -0
  146. package/rcf/tacs/tac-4122-define-gates.json +25 -0
  147. package/rcf/tacs/tac-4123-define-readiness.json +25 -0
  148. package/rcf/test-suites/ts-188.json +10 -2
  149. package/rcf/test-suites/ts-191.json +19 -5
  150. package/rcf/test-suites/ts-204.json +30 -0
  151. package/rcf/test-suites/ts-205.json +22 -0
  152. package/rcf/test-suites/ts-206.json +22 -0
  153. package/rcf/test-suites/ts-207.json +22 -0
  154. package/rcf/test-suites/ts-208.json +22 -0
  155. package/rcf/test-suites/ts-209.json +22 -0
  156. package/rcf/test-suites/ts-210.json +22 -0
  157. package/rcf/test-suites/ts-211.json +22 -0
  158. package/rcf/test-suites/ts-212.json +22 -0
  159. package/rcf/test-suites/ts-213.json +50 -0
  160. package/rcf/test-suites/ts-214.json +74 -0
  161. package/rcf/test-suites/ts-215.json +67 -0
  162. package/rcf/test-suites/ts-216.json +78 -0
  163. package/rcf/test-suites/ts-217.json +62 -0
  164. package/rcf/user-stories/us-15601.json +10 -1
  165. package/rcf/user-stories/us-15701.json +4 -4
  166. package/rcf/user-stories/us-17001.json +27 -0
  167. package/rcf/user-stories/us-17002.json +22 -0
  168. package/rcf/user-stories/us-17003.json +22 -0
  169. package/rcf/user-stories/us-17004.json +22 -0
  170. package/rcf/user-stories/us-17005.json +22 -0
  171. package/rcf/user-stories/us-17006.json +21 -0
  172. package/rcf/user-stories/us-17007.json +21 -0
  173. package/rcf/user-stories/us-17008.json +22 -0
  174. package/rcf/user-stories/us-17101.json +22 -0
  175. package/rcf/user-stories/us-17102.json +22 -0
  176. package/rcf/user-stories/us-17201.json +38 -0
  177. package/rcf/user-stories/us-17301.json +38 -0
  178. package/rcf/user-stories/us-17401.json +58 -0
  179. package/rcf/user-stories/us-17501.json +48 -0
  180. package/releases/releases.yaml +31 -1
  181. package/src/browser-verify/manifest-writer.js +9 -5
  182. package/src/cli/browser-verify.js +9 -1
  183. package/src/cli/feedback.js +316 -32
  184. package/src/cli/help.js +11 -0
  185. package/src/cli/intake.js +25 -4
  186. package/src/cli/ledger.js +399 -0
  187. package/src/cli/preflight.js +24 -3
  188. package/src/cli/readiness.js +280 -0
  189. package/src/cli/review.js +9 -1
  190. package/src/cli/ui-baseline.js +14 -1
  191. package/src/core/store/index.js +5 -0
  192. package/src/core/store/validate-composed-record.js +95 -0
  193. package/src/core/store/writer.js +86 -6
  194. package/src/define/freeze-record.js +232 -0
  195. package/src/define/ledgers.js +513 -0
  196. package/src/feedback/gh.js +27 -1
  197. package/src/feedback/redact-allowlist.json +7 -0
  198. package/src/feedback/redact.js +126 -11
  199. package/src/feedback/render.js +34 -12
  200. package/src/intake/manifest-writer.js +6 -3
  201. package/src/intake/orchestrator.js +44 -7
  202. package/src/preflight/manifest-writer.js +11 -11
  203. package/src/query/delta.js +276 -0
  204. package/src/query/gates.js +832 -0
  205. package/src/query/index.js +36 -0
  206. package/src/query/readiness.js +333 -0
  207. package/src/req-baseline/sweep.js +34 -5
  208. package/src/server/index.js +37 -0
  209. package/src/server/routes.js +65 -3
  210. package/src/ui-baseline/manifest-writer.js +11 -10
  211. package/src/view/doc-renderers/helpers.js +39 -2
  212. package/src/view/doc-renderers/req.js +10 -2
  213. package/src/view/doc-renderers/tad.js +67 -6
  214. package/src/view/doc-renderers/user-story.js +10 -5
  215. package/src/view/html-page.js +347 -28
  216. package/src/view/index.js +14 -1
  217. package/src/view/live-client.js +22 -2
  218. package/src/view/product-map.js +751 -0
  219. package/src/view/style.css +383 -0
package/CHANGELOG.md CHANGED
@@ -6,6 +6,316 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.28.4] - 2026-09-30
10
+
11
+ Note: 0.28.3 (2026-09-22 below) was never tagged or published; its entries ship in 0.28.4 together with the ones here.
12
+ ### Added
13
+
14
+ - **Product Map tab on the review surface (#238).** Merged on `main` at
15
+ `ab35dd7d`; grouped by shape, component, trace coverage, capability,
16
+ blueprint. This entry backfills the changelog now that the following
17
+ DEFINE-train slice rides on top of it.
18
+ - **DEFINE workflow: delta detection foundation (REQ-172, slice 1 of
19
+ the 0.29.0 train).** New pure module `src/query/delta.js` exports
20
+ `hashDocument`, `computeTreeHash` and `computeDelta`. `hashDocument`
21
+ is SHA-256 over the canonical JSON of the parsed document (keys
22
+ sorted recursively, no whitespace), so a hand reformat is not a
23
+ change; the tree hash is over the sorted `(id, docHash)` pairs.
24
+ `computeDelta(tree, freeze, ledgers)` returns the exact
25
+ `frozen / frozenAt / treeHash / currentTreeHash / changed / added /
26
+ removed / briefSince / unchanged` shape proposal 2026-09-22 v3
27
+ section 2.2 pins. Before the first freeze the delta is the whole
28
+ tree (`frozen: false`, everything in `added`). Reused unchanged:
29
+ `computeImpact`, `computeTrace`, `computeCoverage`.
30
+ - **DEFINE workflow: freeze record I/O (REQ-172, slice 1).** New
31
+ module `src/define/freeze-record.js` owns `rcf/define/freeze.json`
32
+ with a small local schema (until schemas 0.7 promotes it to
33
+ `manifest.freeze` per ADR-4121). `loadFreezeRecord` returns `null`
34
+ on a missing file; a malformed body raises `FreezeRecordError` with
35
+ a coded reason (`parseFailure | schemaFailure | ioFailure`) and a
36
+ named field. `saveFreezeRecord` validates first, so a bad
37
+ in-memory record never lands on disk; extension fields survive a
38
+ save / load round trip. The `rcf define freeze` verb is a
39
+ later-slice consumer; this slice ships the writer so the verb can
40
+ be built without a second refactor.
41
+ - **DEFINE workflow: four sidecar ledgers and `rcf define ledger`
42
+ (REQ-173, slice 1).** Four ledgers live under `rcf/define/`, one
43
+ JSON file each, each with one numbered sequence per project:
44
+ `brief-ledger.json` (D1 statements, kinded from the closed set
45
+ `capability | constraint | actor | entity | externalSystem |
46
+ surface | outOfScope | openQuestion | amendment`),
47
+ `decisions-ledger.json` (D7, printed in the numbered #241 format),
48
+ `concern-ledger.json` (D5, `applied | waived`),
49
+ `probe-ledger.json` (D6, `low | medium | high`).
50
+ `rcf define ledger <name> <verb>` covers `add`, `resolve` and
51
+ `list`; `brief add --from <file>` splits statements one per
52
+ non-empty line and mints sequential ids (the
53
+ intake-scan-against-frozen-statements hook is a named seam this
54
+ slice leaves for slice 2). `decisions list` prints the #241
55
+ numbered format. `list --json` emits `{ ledger, <arrayKey>: [...] }`.
56
+ A dedicated test proves the walker never surfaces any `rcf/define/`
57
+ path in `walkTree` errors or `tree.byId`.
58
+ - **DEFINE workflow: stage gates D1..D8 (REQ-174, slice 2 of the
59
+ 0.29.0 train).** New pure module `src/query/gates.js` exports one
60
+ check function per stage (`checkD1Brief`..`checkD8Freeze`) plus
61
+ the `STAGE_ORDER`, `STAGE_GATES`, `STAGE_SHORT_NAMES` and
62
+ `INTERFACE_KINDS` constants. Every function returns the exact
63
+ `{ stage, gate, state, checks[] }` shape proposal 2026-09-22 v3
64
+ section 2.4 specifies, with `checks[i]` in the `{ name, ok, over,
65
+ pass, total, failing }` form. Blocking in 0.29.0: D1 (define.brief),
66
+ D2 (define.skeleton), D4 floors (define.stories), D7
67
+ (define.decisions), D8 (define.freeze). Warn-with-ack in 0.29.0:
68
+ D3 (define.shapes), D5 (define.crosscut), D6 (define.consistency)
69
+ \- the state folds to `acknowledged` when the freeze record's
70
+ `gates[<gate>]` entry names `acknowledged` at the current tree
71
+ hash (ADR-4122). D4 per-class opt-outs reuse the existing
72
+ `manifest.baselineAcOptOuts[]` array with a baselineKey of
73
+ `'defineD4:failure'` or `'defineD4:mustNot'`; no new sidecar is
74
+ introduced. The template parser, entity join, D5 catalogue, D6
75
+ contradiction / unsatisfiable scans and probe runner remain as
76
+ named seams for 0.30.
77
+ - **DEFINE workflow: readiness compute and `rcf define readiness`
78
+ CLI (REQ-175, slice 2).** New pure module `src/query/readiness.js`
79
+ exports `computeReadiness(tree, { freeze, ledgers, profile,
80
+ testPointers })` returning the section 2.4 object exactly:
81
+ `{ tree, delta, stages, nextAction, coverage, decisions,
82
+ freezeable }`. The composer runs `computeDelta` once, calls
83
+ `computeImpact` per id in the union of changed and added (skipping
84
+ the fan-out on the unfrozen whole-tree case where the delta IS the
85
+ tree), runs the eight gate check functions over
86
+ `scope = changed union added union impacted`, calls
87
+ `computeCoverage` tree-wide plus once per REQ ancestor of the
88
+ delta, lists open decisions, and folds to `freezeable` and
89
+ `nextAction`. New verb `rcf define readiness [--json] [--check
90
+ <stage>|all]` (stage accepts D1..D8 or the short names
91
+ `brief | skeleton | shapes | stories | crosscut | consistency |
92
+ decisions | freeze`); text output = tree line, next action, chip
93
+ line, delta list, failing items; `--check` exits 4 on a
94
+ blocking-stage failure, exits 0 with a visible `[warn]` line on an
95
+ unacknowledged warn-with-ack failure. The producer is wrapped with
96
+ `runWithAdmissibilityGate` per the NV-BL-SR-03 addendum; on
97
+ admissibility refusal the CLI prints a `[warn]` note and still
98
+ runs the compute, because readiness is a diagnostic and refusing
99
+ to render is the opposite of what it exists to say (ADR-4123).
100
+ Slice 3's `rcf define freeze` will read `freezeable` and refuse
101
+ on any failing stage.
102
+
103
+ ### Fixed
104
+
105
+ - **US auto-mint extends flat legacy sequences instead of jumping to
106
+ the encoded slot (#256).** `nextIdForKind(tree, 'userStory', ...)`
107
+ in `src/core/store/writer.js` assumed every US id encodes
108
+ `US-<reqNum><localTwoDigit>`. On a legacy tree whose USes carry
109
+ flat sequential ids (`US-1`, ..., `US-163`, migrated in from a
110
+ WESPA v1 project) that decode failed and the allocator fell back
111
+ to `US-<reqNumLabel>01`, minting shapes like `US-5301` under
112
+ `REQ-053` next to the existing `US-163`. Classification now runs
113
+ before allocation: every US id is labelled ENCODED (id decomposes
114
+ as `<declaredReqNum><local in 01..99>`) or FLAT. An all-flat tree
115
+ extends past the tree-wide high-water mark (`US-164`), an
116
+ all-encoded tree preserves the historical rule, and a mixed tree
117
+ refuses with a message that names both example ids and asks the
118
+ operator to normalise one shape before retrying. AC auto-mint is
119
+ unchanged.
120
+ - **Slice-1 P3: `loadAllLedgers` omits absent ledger files
121
+ (AC-17501-6).** The `LedgerBundle` typedef on `computeDelta` said
122
+ "if a project has not authored any decisions, the caller passes
123
+ no `decisions` key and the delta records no `ledger:decisions`
124
+ docHash"; the previous implementation always returned the four
125
+ empty bodies. `loadAllLedgers` now `stat`s each ledger file first
126
+ and omits the key on ENOENT, so a project with no ledgers gets
127
+ zero `ledger:<name>` docHashes in the delta and a project with
128
+ only a brief ledger gets exactly one. `loadLedger` still returns
129
+ an empty ledger on ENOENT so single-ledger callers keep the
130
+ ergonomic default.
131
+ - **Slice-2 P3: D2 / D4 scaffold-TODO matcher narrowed to the literal
132
+ `TODO:` placeholder.** `src/query/gates.js`'s `TODO_RE` used to be
133
+ the whole-word `/\btodo\b/i`, which false-positived on prose
134
+ mentioning the word ("no AC description contains the scaffold TODO
135
+ placeholder" on US-17401 / AC-17401-4 tripped D4 against itself).
136
+ The regex is now `/\bTODO:/` -- case-sensitive uppercase `TODO`
137
+ followed by a colon, matching every scaffold string
138
+ `src/core/store/init.js` and `src/core/store/writer.js` write for a
139
+ fresh `rcf init` / `rcf define create`. `src/cli/validate.js` keeps
140
+ its broader `\btodo\b/i` matcher for the informational tree-wide
141
+ notice, so incidental prose still surfaces there without failing
142
+ the D2 / D4 gates.
143
+
144
+ ## [0.28.3] - 2026-09-22
145
+
146
+ Batched fix release for the residuals Dex's 0.28.2 round-trip surfaced
147
+ and one HQ-side viewer bug caught the same day. Ships one chain
148
+ amendment (three design amendments on the same redactor AC) and four
149
+ build diffs behind it, plus one viewer-side snapshot fix.
150
+
151
+ ### Fixed
152
+
153
+ - **Feedback redactor no longer folds the git protocol sentinel
154
+ `git@<host>` inside git+ssh, ssh or git URLs as an email (#243).**
155
+ A `--evidence` pointer copied verbatim from a git remote or a clone
156
+ URL survives verbatim, so the operator can re-run the exact command
157
+ they hit. The bare-remote shape `git@<host>:<owner>/<repo>` is
158
+ covered by the same guard. Real emails in surrounding prose still
159
+ fold to `<email>`. The URL rule 4 also now handles git+ssh, ssh and
160
+ git schemes with optional userinfo, so a non-allowlisted host inside
161
+ a clone URL still folds to `<host>` while the sentinel is preserved.
162
+ - **Feedback redactor now treats single-letter trailing labels as
163
+ filename fragments, not hostnames (#247, Codex P1-5 deferred from
164
+ PR #242).** Tokens like `a.b.c` survive verbatim. Single-letter TLDs
165
+ are not registrable domains in normal prose; folding them yields
166
+ more false positives on short identifiers and abbreviated references
167
+ than legitimate redactions.
168
+ - **Feedback redactor emits a visible stderr warning when identity
169
+ folding is disabled (#246).** When `rcf/.identity/profile.md` is
170
+ missing, has no `## Name` heading, has a blank or placeholder Name
171
+ line, or the Name contains only reserved-noun tokens, one line on
172
+ stderr names the file and states that folding is disabled. The
173
+ operator is no longer left thinking their identity is in force when
174
+ it silently is not. Folding still skips per AC-15601-5.
175
+ - **`rcf feedback status --json` and text now expose the destination
176
+ resolution `source` field, matching preview (#244).** Programmatic
177
+ verifiers can now assert which resolver path picked the destination
178
+ (`library-manifest`, `package-bugs-url`, `derived`,
179
+ `sourceRef-derivation`, `library-slug-inference`) without parsing
180
+ preview text. The field is null when the resolver did not attach
181
+ one, e.g. for an unresolved library.
182
+ - **`rcf feedback add --evidence` pointers now render with the label
183
+ matching their shape, not always `(command)` (#245).** The help
184
+ text promises three labels (`command / path:line / id`); the
185
+ classifier now recognises `path:line` and bare paths ending in a
186
+ known file extension as `file`, canonical RCF ids
187
+ (`AC-15601-6`, `REQ-155`, `FBS-181`, `US-15701`) as `id`, and
188
+ everything else as `command`. Whitespace anywhere in the pointer
189
+ is a hard signal for `command`.
190
+ - **`rcf audit view` snapshots its shipped static assets at server
191
+ startup (#248).** The live-view server rendered the HTML body from
192
+ a tree walk captured at startup, but `/style.css`, `/mermaid.min.js`
193
+ and `/live-client.js` were `readFile`-d per request against the
194
+ on-disk file. A checkout branch switch during the server's lifetime
195
+ silently changed what got served; the 2026-09-22 Product Map
196
+ preview rendered unstyled for exactly this reason. All three
197
+ assets are now read once at startup into memory and served from
198
+ there. The tree walker remains the only path that re-reads disk on
199
+ `rcf/` change.
200
+
201
+ ### Chain
202
+
203
+ - AC-15601-6 amended with design amendments R5e (single-letter TLDs
204
+ are filename fragments), R5f (git protocol sentinel protection on
205
+ rule 3, extended URL rule 4 for git+ssh/ssh/git schemes) and R5g
206
+ (visible stderr warning when identity folding is disabled). All
207
+ three land on the same story so operators see one AC that names
208
+ the redactor's full posture.
209
+ - `rcf define validate`: clean. `rcf audit coverage --strict`: clean
210
+ (103 requirements, 0 uncovered).
211
+
212
+ ### Release mechanics
213
+
214
+ - Version bumped: `packages/rcf-lite/package.json` 0.28.2 -> 0.28.3.
215
+ - Changelog entry under `## [0.28.3]` (this section).
216
+ - Feed entry added under `releases/releases.yaml`; `latest` set to
217
+ 0.28.3.
218
+ - Tag / publish: Dave (not this PR).
219
+
220
+ ## [0.28.2] - 2026-09-22
221
+
222
+ Batched fix release for the seven defects Barry's WSD-lens dogfood on
223
+ 0.28.1 surfaced (issues #229-#235), plus the three unreported dry-run
224
+ gaps Barry folded into the same release on 2026-09-21 (review,
225
+ ui-baseline init, browser-verify). Ships two chain amendments (the fold
226
+ rule on submit and a filename-token narrowing on the redactor's rule 4)
227
+ and five build diffs behind them.
228
+
229
+ ### Fixed
230
+
231
+ - **Submit no longer folds unrelated findings into one issue and no
232
+ longer replaces the report with a "+1" stub (#234).** Barry's
233
+ round-trip filed five distinct findings with four distinct
234
+ fingerprints; every one folded to a single pre-existing issue whose
235
+ fingerprint matched none of them, and every comment carried
236
+ `+1 from another reporter.` with no title, body or evidence. The
237
+ fix, per Barry's 2026-09-21 ruling on the fold rule: submit no
238
+ longer treats a `gh search issues` hit as a fold decision. For
239
+ every returned candidate it now re-reads the issue body via a new
240
+ `ghIssueGetBody` gh-adapter function and folds only when the body
241
+ carries `rcf-feedback-fingerprint: <fp>` on its own line AND the
242
+ anchor / kind / target markers agree with the entry's. The retired
243
+ `+1 from another reporter.` payload is gone: every fold comment
244
+ now carries the full report prefaced with "Apparent duplicate of
245
+ the issue subject; posting the full report so a human can judge."
246
+ Submit's stdout row names the outcome and the matched title so a
247
+ wrong fold is visible on the same line a human already sees
248
+ (`created #N <url>` vs `commented on #N (fingerprint match:
249
+ <title>) <url>`). Chain: US-15701 AC-15701-2 amended.
250
+
251
+ - **Redactor no longer rewrites `.md` filenames as hostnames (#233).**
252
+ Barry's evidence pointer
253
+ `rcf discover intake --artefact rcf/knowledge/docs/brief/Backstory-product-brief.md,...`
254
+ was rendered as `Backstory-<host>`, destroying the exact evidence
255
+ the verb asks for. Rule 4 gets two narrowings per design amendment
256
+ R5d: the bare-hostname regex's negative lookbehind now includes
257
+ `-` (so `product-brief.md` can never start a match after the
258
+ hyphen), and a candidate hostname whose trailing dot-label is on a
259
+ bundled `extensions` shortlist in `src/feedback/redact-allowlist.json`
260
+ is left intact with no ledger row. Bundled list covers the file
261
+ types rcf-lite feedback sees as evidence (md, mdx, json, yml,
262
+ yaml, txt, js, mjs, cjs, ts, tsx, html, css, png, svg, sh, csv,
263
+ log, lock). Operator overlay merges via `allowExtensions` on the
264
+ redact context. Chain: US-15601 AC-15601-6 added; TAC-4103
265
+ responsibility updated.
266
+
267
+ - **Dry-run and write now share one schema pass across the five
268
+ discover verbs (#230 and #232, folding review / ui-baseline init /
269
+ browser-verify per Barry's 2026-09-21 ruling).** Two direct
270
+ reproductions: `intake --input <file> --dry-run` accepted
271
+ `fidelity: briefRich` that the real run refused (#230); `preflight
272
+ --input <file> --dry-run` accepted a kebab-case service id that
273
+ the real run refused (#232). Same class in the three unreported
274
+ verbs. Fix: a shared `validateComposedRecord({ tree, record, verb
275
+ })` helper in `src/core/store/validate-composed-record.js`
276
+ synthesises the same nextManifest each writer builds, runs
277
+ `validateDocument`, and returns null or the same error the writer
278
+ would refuse on. Wired into every dry-run branch; exit code 3 on a
279
+ validation miss in every path. `intake --help` now advertises the
280
+ fidelity enum (`FIDELITY_LEVELS`); `preflight --help` names the
281
+ service id pattern (`^[a-z][a-zA-Z0-9]*$`).
282
+
283
+ - **`discover intake` no longer emits the same finding once per
284
+ artefact (#229).** Two artefacts that both trip the same branch
285
+ (`impliedButNotStated` on a web UI with no sign-in surface) landed
286
+ as two byte-identical findings in the record. Findings are now
287
+ material-scoped (deduped by `<kind>::<detail>` before timestamping)
288
+ and operator-supplied `validationFindings` on `--input` survive
289
+ regardless of kind (previously only `otherDeclared` was folded,
290
+ silently dropping `impliedButNotStated` / `contradiction` /
291
+ `missingLoadBearingConstraint`). A scan hit and an operator-authored
292
+ finding of the same (kind, detail) coalesce with the operator's
293
+ response preserved. Build-only per the RCA.
294
+
295
+ - **`req-baseline sweep --yes` no longer assigns duplicate AC ids
296
+ across a batch (#231).** Accepting two or more baseline candidates
297
+ for one user story in one sweep refused the write with
298
+ `Duplicate id AC-<usNum>-<N>`. Fix: extract the ordinal allocator
299
+ into `makeAcIdAllocator(usDoc)`; `applySweepDecisions` creates one
300
+ allocator per US and passes each allocated id into
301
+ `composeBaselineAc` via a new optional `idOverride` parameter.
302
+ `nextAcId` and the single-candidate call shape stay
303
+ backwards-compatible. Build-only per the RCA.
304
+
305
+ - **TAD renderer no longer dumps `JSON.stringify(x)` for structured
306
+ fields (#235).** Barry's review surface showed 14 raw single-line
307
+ JSON blobs across `dataStores`, `coreEntities` and
308
+ `externalSystems`. A new `fieldObjectTable(label, rows, columns)`
309
+ helper renders each schema-known structured field as a table with
310
+ the columns the TAD schema documents. Empty columns are dropped so
311
+ schema-optional fields do not surface as blank columns; every cell
312
+ is escaped. `renderOptionalSections` picks the table renderer for
313
+ `dataArchitecture.dataStores`, `dataArchitecture.coreEntities`,
314
+ `integrationArchitecture.externalSystems`; everything else keeps
315
+ the scalar / array-of-string treatment. `style.css` gets a
316
+ lightweight `.field-list table.field-table` rule matching the
317
+ existing cadence. Build-only per the RCA.
318
+
9
319
  ## [0.28.1] - 2026-09-17
10
320
 
11
321
  Feedback defects surfaced by the first WSD-lens round-trip on 0.28.0. Fixes both a destination bug that silently misrouted library-owned findings to `Stravica/rcf-lite`, and a redactor over-fold that mangled the docs URL and any prose using the canonical noun `agent`. Includes a package-rename disclosure banner in the README so a colleague hitting `npm i -g @stravica-ai/rcf-build-lite` on the retired scoped name knows what to do.
package/bin/rcf.js CHANGED
@@ -51,6 +51,8 @@ import { main as reqClassifyMain } from '../src/cli/req-classify.js';
51
51
  import { main as reqBaselineMain } from '../src/cli/req-baseline.js';
52
52
  import { main as intakeMain } from '../src/cli/intake.js';
53
53
  import { main as evalMain } from '../src/cli/eval.js';
54
+ import { main as ledgerMain } from '../src/cli/ledger.js';
55
+ import { main as readinessMain } from '../src/cli/readiness.js';
54
56
 
55
57
  // Verify group members are handled by the verify-suite dispatch tree.
56
58
  import { main as verifyRunMain } from '../src/verify/cli/run.js';
@@ -95,6 +97,8 @@ export const GROUPS = {
95
97
  design: designMain,
96
98
  blueprint: blueprintMain,
97
99
  standards: standardsMain,
100
+ ledger: ledgerMain,
101
+ readiness: readinessMain,
98
102
  },
99
103
  build: {
100
104
  // build sub-verbs are dispatched by build.js internally (queue |
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "registerCanary": [
3
3
  {
4
- "id": "rc-2026-09-17-001",
5
- "createdAt": "2026-09-17T14:42:00.660Z",
6
- "buildVersion": "0.28.1-mockdriver",
4
+ "id": "rc-2026-09-30-001",
5
+ "createdAt": "2026-09-30T15:30:02.583Z",
6
+ "buildVersion": "0.28.4-mockdriver",
7
7
  "fixturePromptId": "canary-prompt-01",
8
8
  "responseWordCount": 55,
9
9
  "grades": {
@@ -34,9 +34,9 @@
34
34
  "shipDespiteFailReason": "mock canary driver used; no real subagent was dispatched. This record verifies canary infrastructure only, not the register itself."
35
35
  },
36
36
  {
37
- "id": "rc-2026-09-17-002",
38
- "createdAt": "2026-09-17T14:42:00.661Z",
39
- "buildVersion": "0.28.1-mockdriver",
37
+ "id": "rc-2026-09-30-002",
38
+ "createdAt": "2026-09-30T15:30:02.584Z",
39
+ "buildVersion": "0.28.4-mockdriver",
40
40
  "fixturePromptId": "canary-prompt-02",
41
41
  "responseWordCount": 55,
42
42
  "grades": {
@@ -67,9 +67,9 @@
67
67
  "shipDespiteFailReason": "mock canary driver used; no real subagent was dispatched. This record verifies canary infrastructure only, not the register itself."
68
68
  },
69
69
  {
70
- "id": "rc-2026-09-17-003",
71
- "createdAt": "2026-09-17T14:42:00.661Z",
72
- "buildVersion": "0.28.1-mockdriver",
70
+ "id": "rc-2026-09-30-003",
71
+ "createdAt": "2026-09-30T15:30:02.584Z",
72
+ "buildVersion": "0.28.4-mockdriver",
73
73
  "fixturePromptId": "canary-prompt-03",
74
74
  "responseWordCount": 55,
75
75
  "grades": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rcf-lite",
3
- "version": "0.28.1",
3
+ "version": "0.28.4",
4
4
  "type": "module",
5
5
  "description": "One-install tooling for the Requirements Confidence Framework (RCF): the unified `rcf` CLI grouped into the five RCF tool groups (discover, define, build, verify, audit) plus a small core set (init, doctor, guidance, mcp), an MCP server, the live tree viewer and the fresh-context adversarial ship-gate verifier. Consumes @stravica-ai/rcf-schemas.",
6
6
  "license": "Apache-2.0",
@@ -0,0 +1,30 @@
1
+ {
2
+ "createdAt": "2026-09-24T12:00:00Z",
3
+ "updatedAt": "2026-09-24T12:00:00Z",
4
+ "adrId": "ADR-4120-canonical-json-hashing",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "accepted",
9
+ "title": "Delta detection hashes the parsed document under canonical JSON, at standalone-document granularity",
10
+ "context": "The DEFINE workflow proposal (2026-09-22 v3, section 2.1) has to pick both a hashing scheme and a granularity for docHashes on the freeze record. Two choices matter and are named as residual (proposal section 2 penultimate paragraph): raw bytes versus the parsed canonical form, and criterion-level versus document-level. Raw bytes are the smallest amount of code (one hash per file read); the parsed form costs one canonicalise per document per read (milliseconds at 700 criteria). Criterion granularity gives a finer delta; document granularity keeps the model small and lets git supply the criterion-level diff. Both choices had to be recorded because the detection model is what every later slice (readiness, freeze, bundle, viewer) reads.",
11
+ "decision": "Hash the parsed document under canonical JSON: keys sorted recursively, no whitespace, JSON.stringify escaping. Compute the tree hash as SHA-256 over the sorted (id, docHash) pairs canonically encoded. The docHashes map has one entry per standalone document the walker exposes on tree.byId (PRD, TAD, BS, REQ, US, TAC, ADR, FBS, TS, CN, EVAL) plus one ledger:<name> entry per sidecar the caller passes; inline AC and TC ride inside their parent's hash. Granularity for the delta list is the standalone document.",
12
+ "consequences": "A hand reformat of a document (rewritten by rcf define update, opened in an editor and re-saved) does not surface as a change, which is what an operator expects. An edit to a criterion surfaces as its owning story changed; Readiness renders the criterion-level diff by reading the frozen story out of git (proposal section 2.2). The cost is one canonicalise per document per read; at the backstory scale of 700 criteria that is milliseconds and is only paid on readiness / freeze / bundle --next reads. If document granularity turns out too coarse in practice, ADR replacement or schemas 0.7's criterion-hash extension is the migration path; the change would not affect the wire shape of computeDelta because inline granularity would simply expand tree.byId, not the return shape.",
13
+ "alternativesConsidered": [
14
+ {
15
+ "name": "Hash the raw file bytes",
16
+ "summary": "SHA-256 over readFile output directly; skip canonical JSON entirely.",
17
+ "reasonNotChosen": "A CLI edit that rewrites the file (rcf define update reserialises the object every write) would report drift on a file it did not semantically touch. The operator would learn to ignore the delta list, which is the one signal the freeze exists to give."
18
+ },
19
+ {
20
+ "name": "Criterion granularity: one docHash per AC / TC on top of the document ones",
21
+ "summary": "Add inline-AC and inline-TC entries to docHashes so an edit to AC-207-4 shows in the delta at criterion granularity.",
22
+ "reasonNotChosen": "Doubles the size of docHashes for no readiness improvement: Readiness already renders the criterion-level diff by reading the frozen story from git. The size cost is real (a backstory tree has 700 criteria plus 40 stories); the render cost is not."
23
+ },
24
+ {
25
+ "name": "Hash the pretty-printed JSON body the writer produced",
26
+ "summary": "Hash JSON.stringify(doc, null, 2) plus a trailing newline; the same bytes the writer wrote.",
27
+ "reasonNotChosen": "Whitespace-only reformats (a hand edit that reorders keys or drops a trailing newline) then surface as changes. Canonical hashing is the same code with the whitespace stripped and the keys sorted; the operator sees fewer false-positive deltas."
28
+ }
29
+ ]
30
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "createdAt": "2026-09-24T12:00:00Z",
3
+ "updatedAt": "2026-09-24T12:00:00Z",
4
+ "adrId": "ADR-4121-define-sidecars",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "accepted",
9
+ "title": "Ledgers as rcf-lite-owned sidecars under rcf/define/ with a local schema; promote to manifest records at schemas 0.7",
10
+ "context": "The DEFINE proposal (2026-09-22 v3, section 2.5 and decision 5) needs a place to put four things the workflow reads and writes but the shared schema does not yet know about: brief statements (D1), decisions (D7), (concern, REQ) dispositions (D5), probe findings (D6). @stravica-ai/rcf-schemas 0.6.3 has no manifest.ledgers[] shape and adding one is a schema-package bump; the train's non-goal (train-context: 'no schema change') is to ship the workflow without touching @stravica-ai/rcf-schemas. Precedent already exists in this codebase: .rcf/preflight-secrets.local.json is an rcf-lite-owned file outside the shared schema; the loader reads only its named collection directories, so a directory the walker was not told about is invisible.",
11
+ "decision": "Ledgers live under rcf/define/, one JSON file each (brief-ledger.json, decisions-ledger.json, concern-ledger.json, probe-ledger.json), validated by a local schema inside src/define/ledgers.js. The freeze record sits at rcf/define/freeze.json under the same directory. rcf-lite owns the shape until @stravica-ai/rcf-schemas 0.7, at which point the sidecars promote to a manifest.freeze and manifest.ledgers[] records and the local schema is retired. The walker never reads rcf/define/ (src/core/store/loader.js's subdirFor returns null for anything not in its child-kind list), and a walker-invisibility invariant test proves it stays that way.",
12
+ "consequences": "A DEFINE change lands with no @stravica-ai/rcf-schemas bump: the train ships as 0.29.0 against the shipped 0.6.3 schema. The sidecars are committed alongside the rest of the rcf tree (they carry durable state: the freeze hash, brief statement ids, resolved decisions), so git supplies the history the proposal section 2.1 relies on. Second source of truth is the honest downside: until schemas 0.7 there are two places the DEFINE workflow reads (the shared tree and rcf/define/); the promotion path documented in the proposal (section 5.3) is straightforward because the local shapes were designed to line up with the manifest.freeze and manifest.ledgers[] fields the schema promotion adds. If schemas 0.7 slips past the 0.30 train, the sidecars keep working: the local schema is the contract every DEFINE gate consumes.",
13
+ "alternativesConsidered": [
14
+ {
15
+ "name": "Land the manifest.freeze and manifest.ledgers[] records at schemas 0.6.4 now",
16
+ "summary": "Bump @stravica-ai/rcf-schemas to 0.6.4 as part of this train; add manifest.freeze and manifest.ledgers[] optional records; write to the manifest instead of a sidecar.",
17
+ "reasonNotChosen": "The train-context ('no schema change') is a deliberate constraint on this train, ratified 2026-09-22, so the workflow can ship without another WSD-side schema round trip. Schemas 0.7 is on the roadmap for the 0.30 train; landing the sidecar first proves the shape at zero schema cost, then the promotion carries the same fields."
18
+ },
19
+ {
20
+ "name": "One combined ledger file (rcf/define/ledger.json) with four sections",
21
+ "summary": "Collapse the four ledgers into one file with four top-level keys; the CLI still gates on the name.",
22
+ "reasonNotChosen": "The four ledgers grow independently (a probe-heavy pass writes ten probes with no decisions changed) and merge conflicts on a shared file would surface constantly. Four files with distinct id sequences is the cheaper long-term shape, and it lines up with the four manifest.ledgers[] records schemas 0.7 is expected to add."
23
+ },
24
+ {
25
+ "name": "Sidecars under .rcf/ (like preflight-secrets.local.json)",
26
+ "summary": "Put the four ledgers under .rcf/ so they inherit the preflight-secrets convention.",
27
+ "reasonNotChosen": "The preflight sidecar is gitignored on purpose (credential name-metadata). DEFINE ledgers hold durable state the workflow re-reads on every gate; they must be committed. rcf/define/ keeps them visible to the operator, alongside the tree they annotate."
28
+ }
29
+ ]
30
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "createdAt": "2026-09-24T14:30:00Z",
3
+ "updatedAt": "2026-09-24T14:30:00Z",
4
+ "adrId": "ADR-4122-define-gates-blocking-and-optout",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "accepted",
9
+ "title": "Blocking-vs-warn-with-ack per stage; D4 per-class opt-out reuses manifest.baselineAcOptOuts[]",
10
+ "context": "The DEFINE workflow proposal (2026-09-22 v3, section 3.2, decision 2) blocks where the data exists and warns-with-acknowledgement for D3, D5, D6 until the release that lands the 0.30 (renumbered from 0.31 per Baz 2026-09-24) contradiction-lite, unsatisfiable-lite, template parser and probe runner. Proposal section 3.2 also says D4's failure and must-not floors accept a 'reasoned opt-out per class, as the baseline ledger works (#239)'. The train has to decide whether the D4 opt-out reuses the existing manifest.baselineAcOptOuts[] machinery (nextOptOutId, composeOptOutRecord under src/req-baseline/opt-out.js, boo-YYYY-MM-DD-NNN ids) or gets a new sidecar under rcf/define/. Second decision that had to be recorded because slice 2's gates read it and slice 3's freeze verb writes it: how does the freeze record acknowledge a warn-with-ack gate?",
11
+ "decision": "Blocking in 0.29.0: D1 (define.brief), D2 (define.skeleton), D4 floors (define.stories), D7 (define.decisions), D8 (define.freeze). Warn-with-ack in 0.29.0: D3 (define.shapes), D5 (define.crosscut), D6 (define.consistency). Blocking-vs-warn-with-ack is a stage property, hard-coded in src/query/gates.js. The state fold: a stage is 'passed' when every check.ok is true; 'failing' otherwise. For D3/D5/D6 the fold flips to 'acknowledged' when the freeze record's gates[<gate>] entry names state 'acknowledged' AND at.hash === currentTreeHash. D4 per-class opt-outs reuse manifest.baselineAcOptOuts[]: baselineKey is 'defineD4:failure' or 'defineD4:mustNot'; scope is 'req' (opt-out applies to every US whose reqId matches the record's reqId) or 'project' (applies tree-wide). Slice-3 writes acknowledgements via the freeze verb; the write path is out of scope for slice 2. The opt-out records are authored by an operator through the existing req-baseline surface (or, in slice 3, an optional --optout flag on freeze); slice 2 only reads them.",
12
+ "consequences": "One place to author opt-outs (the existing baselineAcOptOuts array), one place to read them (the same array). No new sidecar until D4's class opt-outs mature (schemas 0.7 will move them if the shape stabilises). The freeze record's gates map becomes the single source of truth for acknowledgements, which is what slice-3's freeze verb writes and every consumer reads. The honest downside: the baselineKey field is loosely typed (free-string convention rather than an enum), so a typo lands silently (the D4 gate simply does not honour the opt-out). Mitigation: the checker prints a stderr [warn] when it sees a record with a baselineKey starting with 'defineD4:' but not matching the two known values, and the CLI docs (slice 3 / slice 6) list the accepted keys. If the shape ever needs to move, existing records can be migrated by rewriting the manifest under the same array without a schema change.",
13
+ "alternativesConsidered": [
14
+ {
15
+ "name": "New sidecar rcf/define/class-optout-ledger.json for D4 per-class opt-outs",
16
+ "summary": "Fifth sidecar under rcf/define/ with entries { id, scope, reqId?, usId?, class: 'failure'|'mustNot', reason, addedAt }; add --class flag to rcf define ledger.",
17
+ "reasonNotChosen": "Adds a fifth sidecar to a proposal (decision 5) that ratified four. Proposal section 3.2 D4 explicitly says 'as the baseline ledger works (#239)', pointing at the existing surface. A fifth ledger costs a schema, a CLI branch and a walker-invisibility test for zero gain over reusing the shape."
18
+ },
19
+ {
20
+ "name": "Block D3, D5, D6 in 0.29.0 (drop warn-with-ack)",
21
+ "summary": "Every stage blocks; --ack is not a thing until 0.30.",
22
+ "reasonNotChosen": "Baz's decision 2 ratifies warn-with-ack for D3/D5/D6 until the template parser, catalogue and scans land. Blocking on cheap presence-only checks (an unknown interface kind, or a probe finding still open) refuses freezes on tree state that 0.30's richer checks would tolerate; the acknowledgement channel is what lets an operator move without inventing the check they need is not there."
23
+ },
24
+ {
25
+ "name": "Encode blocking-vs-warn-with-ack on each check rather than on the stage",
26
+ "summary": "Every check[i] carries a policy tag; the fold reads the tags rather than the stage.",
27
+ "reasonNotChosen": "The proposal is stage-scoped, not check-scoped. Per-check policy tags land as future noise for zero present gain: today every check inside a stage shares a policy. If a stage grows a check with a different policy the fold moves back to per-stage anyway (a 'failing' check on any policy is still 'failing' in the current model)."
28
+ }
29
+ ]
30
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "createdAt": "2026-09-24T14:30:00Z",
3
+ "updatedAt": "2026-09-24T15:48:40Z",
4
+ "adrId": "ADR-4123-readiness-single-object",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "accepted",
9
+ "title": "Readiness is one computed object; no stored stage state, no stored 'stale'",
10
+ "context": "The DEFINE workflow proposal (2026-09-22 v3, section 2.4, section 6, decision 4) is explicit that the CLI and the viewer tab render the same object so they cannot disagree, and that stage state is one of passed | failing | acknowledged | notApplicable with no 'stale' and no 'notStarted' because nothing is stored: every chip is the truth at the moment of the read. The Product Map (PR #238) precedent already ships this pattern for a related surface. Slice 2 has to pick both the computation shape (one pass or many; where the impact fan-out sits) and the storage shape (nothing stored, or a small cache in the freeze record).",
11
+ "decision": "computeReadiness returns one plain-data object per call over the tree, the freeze record and the four ledgers; nothing is stored between calls, and no per-stage 'stale' flag exists. The stage state is a pure fold over the checks[] of that stage: 'passed' when every check.ok is true, 'failing' otherwise, unless the stage is one of the three warn-with-ack stages and the freeze record's gates[<gate>] entry names 'acknowledged' at the current tree hash. The CLI (rcf define readiness) prints the object; slice-5's viewer renders the same JSON envelope over the existing tab wire (proposal section 6.3). runWithAdmissibilityGate wraps the CLI's producer, matching the ruleset toolScope { traceabilityAndQueryTools: true } switch; slice 2 records this decision because the wrap changes the shipping exit path. On refuse the wrap runs first (so the ruleset toolScope guard is honoured, NV-BL-SR-03 addendum), then the readiness compute runs anyway and the CLI prints an informational [warn] on stderr naming the refusal, because readiness is a diagnostic and refusing to report which stage blocks is the opposite of what the verb exists to do; the process still exits 0 (or the --check <stage> code) and never exits 4 solely on refuse.",
12
+ "consequences": "One shape, two renderers: whatever the CLI prints, the viewer prints. Absence of stored state means Readiness is stateless: git carries the history, the freeze record carries the acknowledgements, everything else is derived. The compute cost is one canonicalise per document per read (already ratified in ADR-4120) plus one computeImpact per delta id, which on the dogfood chain (~700 documents, ~30 delta ids on a small feature) is measured in single-digit seconds. If the compute ever costs too much, the mitigation is a per-render cache in the CLI or the viewer (not in the freeze record), so the shape stays the same. Honest downside: the whole-tree unfrozen case (no freeze record; every doc is 'added') computes computeImpact over every id, which on very large trees may be quadratic in fan-out size. Slice 2 mitigates by skipping the per-id fan-out when the delta is the whole tree (the delta IS the fan-out), and the CLI prints wall time so an operator can spot a regression before it becomes a defect.",
13
+ "alternativesConsidered": [
14
+ {
15
+ "name": "Cache the last stage[] under freeze.gates[]; only re-run gates whose delta touches their scope",
16
+ "summary": "Store per-stage checks + fold under freeze.gates[]; readiness reads the cache and only re-runs gates the delta invalidates.",
17
+ "reasonNotChosen": "Two sources of truth: the tree and the cache. Every operator correction to a story would have to invalidate the cache correctly, and any bug in the invalidation would show up as a false green. Proposal section 2.4 explicitly rules this out: 'no stale, no not-started'. Recompute is cheap enough not to earn the complexity."
18
+ },
19
+ {
20
+ "name": "Skip runWithAdmissibilityGate on the readiness verb",
21
+ "summary": "Readiness is a diagnostic; run it even when admissibility refuses so the operator can see what to fix.",
22
+ "reasonNotChosen": "The ruleset toolScope switch { traceabilityAndQueryTools: true } is a runtime-read guard (NV-BL-SR-03 addendum) and matches every other query verb's expected posture; skipping it would be an exception the checker cannot see. Slice 2 wraps the producer; a future train can add an explicit --show-anyway flag if the diagnostic case wins the argument, and record it as an ADR replacement."
23
+ },
24
+ {
25
+ "name": "Compute stages fully in parallel with Promise.all",
26
+ "summary": "Every stage check runs in parallel via Promise.all against a shared context.",
27
+ "reasonNotChosen": "The gate check functions are pure and CPU-bound (no I/O); Promise.all does not parallelise CPU on Node without workers, and worker-thread overhead is far larger than the compute cost. Sequential composition is honest about what the code does."
28
+ }
29
+ ]
30
+ }
@@ -6,11 +6,12 @@
6
6
  "AC-15601-2",
7
7
  "AC-15601-3",
8
8
  "AC-15601-4",
9
- "AC-15601-5"
9
+ "AC-15601-5",
10
+ "AC-15601-6"
10
11
  ],
11
12
  "dependencies": [],
12
13
  "version": "0.1.0",
13
14
  "status": "draft",
14
15
  "createdAt": "2026-09-16T15:27:35Z",
15
- "updatedAt": "2026-09-17T14:14:49Z"
16
+ "updatedAt": "2026-09-22T00:00:00Z"
16
17
  }
@@ -0,0 +1,23 @@
1
+ {
2
+ "cnId": "CN-700",
3
+ "path": "src/view/product-map.js",
4
+ "title": "Product Map re-bucketing over the review-surface tree model",
5
+ "description": "Pure functions groupByShape, groupByComponent, groupByTraceCoverage, groupByCapability, groupByBlueprint plus the panel renderer that composes them. No schema change; consumes shapeClassification.shapes[], usByTacId, tsByAcId/tcsByAcId, REQ.tags[] for capability:<slug> tags, and manifest.blueprints[].contributions[] for blueprint attribution (fallback: reqId kebab-slug prefix). Emits a pm-blueprint-badge on every REQ row an applied blueprint contributed.",
6
+ "implementsAcIds": [
7
+ "AC-17001-1",
8
+ "AC-17001-2",
9
+ "AC-17002-1",
10
+ "AC-17003-1",
11
+ "AC-17004-1",
12
+ "AC-17005-1",
13
+ "AC-17006-1",
14
+ "AC-17008-1"
15
+ ],
16
+ "dependencies": [
17
+ "CN-017"
18
+ ],
19
+ "version": "0.1.0",
20
+ "status": "draft",
21
+ "createdAt": "2026-09-21T17:00:00.000Z",
22
+ "updatedAt": "2026-09-22T11:59:39Z"
23
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "cnId": "CN-701",
3
+ "path": "scripts/apply-capability-tags.mjs",
4
+ "title": "Apply capability:<slug> tags to every dogfood REQ",
5
+ "description": "One authoritative REQ id -> slugs mapping applied to every requirement file under rcf/requirements/. Idempotent; overwrites existing capability:<slug> tags while preserving non-capability tags. Ships as the tool of record until a rcf discover req-capability-classify verb lands.",
6
+ "implementsAcIds": [
7
+ "AC-17101-1"
8
+ ],
9
+ "dependencies": [],
10
+ "version": "0.1.0",
11
+ "status": "draft",
12
+ "createdAt": "2026-09-21T17:00:00.000Z",
13
+ "updatedAt": "2026-09-21T17:00:00.000Z"
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "cnId": "CN-702",
3
+ "path": "docs/capability-tags.md",
4
+ "title": "Capability tag convention and taxonomy for the rcf-lite chain",
5
+ "description": "Documents the capability:<slug> convention, the initial twelve-slug taxonomy and the rule for adding a new capability. Linked from docs/README.md; consumed by the Product Map by-capability grouping.",
6
+ "implementsAcIds": [
7
+ "AC-17102-1"
8
+ ],
9
+ "dependencies": [],
10
+ "version": "0.1.0",
11
+ "status": "draft",
12
+ "createdAt": "2026-09-21T17:00:00.000Z",
13
+ "updatedAt": "2026-09-21T17:00:00.000Z"
14
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "cnId": "CN-703",
3
+ "path": "src/server/routes.js",
4
+ "implementsAcIds": [
5
+ "AC-17007-1"
6
+ ],
7
+ "dependencies": [
8
+ "CN-700"
9
+ ],
10
+ "version": "0.1.0",
11
+ "status": "draft",
12
+ "createdAt": "2026-09-21T19:44:46.452Z",
13
+ "updatedAt": "2026-09-21T19:44:52.245Z",
14
+ "title": "Product Map partial-render endpoint",
15
+ "description": "Adds a /product-map/<group> HTTP route that serves the pre-rendered non-lazy panel body for the named grouping. Consumed by the inline scripts hydrateLazyGroup which fetches on first activation and caches the response in memory; the live-client invalidates the cache on every SSE tree-update swap."
16
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "cnId": "CN-720",
3
+ "path": "src/query/delta.js",
4
+ "title": "Delta detection: canonical-JSON hashing and computeDelta",
5
+ "description": "Pure module. canonicaliseJson (recursive key sort, no whitespace), hashDocument (sha256:<hex> over the canonical form of a parsed document), computeTreeHash (over sorted (id, docHash) pairs), and computeDelta (returns the proposal 2026-09-22 section 2.2 shape: frozen, frozenAt, treeHash, currentTreeHash, changed, added, removed, briefSince, unchanged). Ledger docHashes are added under ledger:<name> ids per sidecar the caller passes.",
6
+ "implementsAcIds": [
7
+ "AC-17201-1",
8
+ "AC-17201-2"
9
+ ],
10
+ "dependencies": [],
11
+ "version": "0.1.0",
12
+ "status": "draft",
13
+ "createdAt": "2026-09-24T12:00:00Z",
14
+ "updatedAt": "2026-09-24T12:00:00Z"
15
+ }