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.
- package/CHANGELOG.md +211 -0
- package/bin/rcf.js +4 -0
- package/fixtures/canary-manifest.json +9 -9
- package/package.json +1 -1
- package/rcf/adrs/adr-4120-canonical-json-hashing.json +30 -0
- package/rcf/adrs/adr-4121-define-sidecars.json +30 -0
- package/rcf/adrs/adr-4122-define-gates-blocking-and-optout.json +30 -0
- package/rcf/adrs/adr-4123-readiness-single-object.json +30 -0
- package/rcf/code-nodes/cn-700.json +23 -0
- package/rcf/code-nodes/cn-701.json +14 -0
- package/rcf/code-nodes/cn-702.json +14 -0
- package/rcf/code-nodes/cn-703.json +16 -0
- package/rcf/code-nodes/cn-720.json +15 -0
- package/rcf/code-nodes/cn-721.json +15 -0
- package/rcf/code-nodes/cn-722.json +15 -0
- package/rcf/code-nodes/cn-723.json +18 -0
- package/rcf/code-nodes/cn-724.json +14 -0
- package/rcf/code-nodes/cn-725.json +21 -0
- package/rcf/code-nodes/cn-726.json +17 -0
- package/rcf/code-nodes/cn-727.json +14 -0
- package/rcf/code-nodes/cn-728.json +21 -0
- package/rcf/code-nodes/cn-729.json +18 -0
- package/rcf/code-nodes/cn-730.json +14 -0
- package/rcf/fbs/fbs-190.json +37 -0
- package/rcf/fbs/fbs-191.json +34 -0
- package/rcf/fbs/fbs-192.json +17 -0
- package/rcf/fbs/fbs-193.json +37 -0
- package/rcf/fbs/fbs-194.json +35 -0
- package/rcf/fbs/fbs-195.json +37 -0
- package/rcf/fbs/fbs-196.json +37 -0
- package/rcf/fbs/fbs-197.json +41 -0
- package/rcf/requirements/req-001.json +2 -1
- package/rcf/requirements/req-002.json +2 -1
- package/rcf/requirements/req-003.json +2 -1
- package/rcf/requirements/req-004.json +2 -1
- package/rcf/requirements/req-005.json +2 -1
- package/rcf/requirements/req-006.json +2 -1
- package/rcf/requirements/req-007.json +2 -1
- package/rcf/requirements/req-008.json +2 -1
- package/rcf/requirements/req-009.json +2 -1
- package/rcf/requirements/req-010.json +5 -2
- package/rcf/requirements/req-011.json +2 -1
- package/rcf/requirements/req-012.json +2 -1
- package/rcf/requirements/req-013.json +2 -1
- package/rcf/requirements/req-014.json +2 -1
- package/rcf/requirements/req-015.json +2 -1
- package/rcf/requirements/req-016.json +5 -2
- package/rcf/requirements/req-017.json +6 -2
- package/rcf/requirements/req-018.json +5 -2
- package/rcf/requirements/req-019.json +5 -2
- package/rcf/requirements/req-020.json +5 -2
- package/rcf/requirements/req-021.json +5 -2
- package/rcf/requirements/req-022.json +5 -2
- package/rcf/requirements/req-023.json +6 -4
- package/rcf/requirements/req-024.json +5 -2
- package/rcf/requirements/req-025.json +5 -2
- package/rcf/requirements/req-026.json +5 -2
- package/rcf/requirements/req-027.json +5 -2
- package/rcf/requirements/req-028.json +5 -2
- package/rcf/requirements/req-029.json +5 -2
- package/rcf/requirements/req-030.json +5 -2
- package/rcf/requirements/req-040.json +5 -2
- package/rcf/requirements/req-041.json +5 -2
- package/rcf/requirements/req-042.json +5 -2
- package/rcf/requirements/req-043.json +5 -2
- package/rcf/requirements/req-050.json +5 -2
- package/rcf/requirements/req-051.json +5 -2
- package/rcf/requirements/req-052.json +5 -2
- package/rcf/requirements/req-053.json +5 -2
- package/rcf/requirements/req-054.json +5 -2
- package/rcf/requirements/req-060.json +5 -2
- package/rcf/requirements/req-061.json +5 -2
- package/rcf/requirements/req-062.json +5 -2
- package/rcf/requirements/req-063.json +5 -2
- package/rcf/requirements/req-070.json +5 -2
- package/rcf/requirements/req-071.json +5 -2
- package/rcf/requirements/req-072.json +5 -2
- package/rcf/requirements/req-073.json +5 -2
- package/rcf/requirements/req-074.json +5 -2
- package/rcf/requirements/req-075.json +5 -2
- package/rcf/requirements/req-076.json +5 -2
- package/rcf/requirements/req-077.json +5 -2
- package/rcf/requirements/req-080.json +5 -2
- package/rcf/requirements/req-081.json +5 -2
- package/rcf/requirements/req-082.json +5 -2
- package/rcf/requirements/req-083.json +5 -2
- package/rcf/requirements/req-084.json +5 -2
- package/rcf/requirements/req-085.json +5 -2
- package/rcf/requirements/req-086.json +5 -2
- package/rcf/requirements/req-090.json +5 -2
- package/rcf/requirements/req-091.json +5 -2
- package/rcf/requirements/req-092.json +5 -2
- package/rcf/requirements/req-093.json +5 -2
- package/rcf/requirements/req-094.json +5 -2
- package/rcf/requirements/req-100.json +5 -2
- package/rcf/requirements/req-101.json +5 -2
- package/rcf/requirements/req-102.json +5 -2
- package/rcf/requirements/req-103.json +5 -2
- package/rcf/requirements/req-104.json +5 -2
- package/rcf/requirements/req-110.json +5 -2
- package/rcf/requirements/req-111.json +5 -2
- package/rcf/requirements/req-112.json +5 -2
- package/rcf/requirements/req-113.json +5 -2
- package/rcf/requirements/req-114.json +5 -2
- package/rcf/requirements/req-115.json +5 -2
- package/rcf/requirements/req-120.json +5 -2
- package/rcf/requirements/req-121.json +5 -2
- package/rcf/requirements/req-122.json +5 -2
- package/rcf/requirements/req-123.json +5 -2
- package/rcf/requirements/req-124.json +5 -2
- package/rcf/requirements/req-125.json +5 -2
- package/rcf/requirements/req-130.json +6 -2
- package/rcf/requirements/req-131.json +6 -2
- package/rcf/requirements/req-132.json +6 -2
- package/rcf/requirements/req-133.json +6 -2
- package/rcf/requirements/req-134.json +6 -2
- package/rcf/requirements/req-140.json +5 -2
- package/rcf/requirements/req-145.json +5 -2
- package/rcf/requirements/req-150.json +6 -2
- package/rcf/requirements/req-151.json +6 -2
- package/rcf/requirements/req-152.json +6 -2
- package/rcf/requirements/req-153.json +6 -2
- package/rcf/requirements/req-154.json +5 -2
- package/rcf/requirements/req-155.json +4 -1
- package/rcf/requirements/req-156.json +4 -1
- package/rcf/requirements/req-157.json +4 -1
- package/rcf/requirements/req-158.json +4 -1
- package/rcf/requirements/req-159.json +4 -1
- package/rcf/requirements/req-160.json +4 -1
- package/rcf/requirements/req-161.json +4 -1
- package/rcf/requirements/req-162.json +4 -1
- package/rcf/requirements/req-163.json +5 -2
- package/rcf/requirements/req-164.json +5 -2
- package/rcf/requirements/req-165.json +5 -2
- package/rcf/requirements/req-170.json +33 -0
- package/rcf/requirements/req-171.json +44 -0
- package/rcf/requirements/req-172.json +25 -0
- package/rcf/requirements/req-173.json +25 -0
- package/rcf/requirements/req-174.json +25 -0
- package/rcf/requirements/req-175.json +25 -0
- package/rcf/tacs/tac-4120-define-delta-and-freeze.json +21 -0
- package/rcf/tacs/tac-4121-define-ledgers.json +23 -0
- package/rcf/tacs/tac-4122-define-gates.json +25 -0
- package/rcf/tacs/tac-4123-define-readiness.json +25 -0
- package/rcf/test-suites/ts-204.json +30 -0
- package/rcf/test-suites/ts-205.json +22 -0
- package/rcf/test-suites/ts-206.json +22 -0
- package/rcf/test-suites/ts-207.json +22 -0
- package/rcf/test-suites/ts-208.json +22 -0
- package/rcf/test-suites/ts-209.json +22 -0
- package/rcf/test-suites/ts-210.json +22 -0
- package/rcf/test-suites/ts-211.json +22 -0
- package/rcf/test-suites/ts-212.json +22 -0
- package/rcf/test-suites/ts-213.json +50 -0
- package/rcf/test-suites/ts-214.json +74 -0
- package/rcf/test-suites/ts-215.json +67 -0
- package/rcf/test-suites/ts-216.json +78 -0
- package/rcf/test-suites/ts-217.json +62 -0
- package/rcf/user-stories/us-15601.json +4 -4
- package/rcf/user-stories/us-17001.json +27 -0
- package/rcf/user-stories/us-17002.json +22 -0
- package/rcf/user-stories/us-17003.json +22 -0
- package/rcf/user-stories/us-17004.json +22 -0
- package/rcf/user-stories/us-17005.json +22 -0
- package/rcf/user-stories/us-17006.json +21 -0
- package/rcf/user-stories/us-17007.json +21 -0
- package/rcf/user-stories/us-17008.json +22 -0
- package/rcf/user-stories/us-17101.json +22 -0
- package/rcf/user-stories/us-17102.json +22 -0
- package/rcf/user-stories/us-17201.json +38 -0
- package/rcf/user-stories/us-17301.json +38 -0
- package/rcf/user-stories/us-17401.json +58 -0
- package/rcf/user-stories/us-17501.json +48 -0
- package/releases/releases.yaml +21 -1
- package/src/cli/feedback.js +126 -10
- package/src/cli/help.js +11 -0
- package/src/cli/ledger.js +399 -0
- package/src/cli/readiness.js +280 -0
- package/src/core/store/writer.js +86 -6
- package/src/define/freeze-record.js +232 -0
- package/src/define/ledgers.js +513 -0
- package/src/feedback/redact.js +105 -19
- package/src/query/delta.js +276 -0
- package/src/query/gates.js +832 -0
- package/src/query/index.js +36 -0
- package/src/query/readiness.js +333 -0
- package/src/server/index.js +37 -0
- package/src/server/routes.js +65 -3
- package/src/view/doc-renderers/helpers.js +2 -2
- package/src/view/doc-renderers/req.js +10 -2
- package/src/view/doc-renderers/user-story.js +10 -5
- package/src/view/html-page.js +347 -28
- package/src/view/index.js +14 -1
- package/src/view/live-client.js +22 -2
- package/src/view/product-map.js +751 -0
- package/src/view/style.css +378 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,217 @@ 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
|
+
|
|
9
220
|
## [0.28.2] - 2026-09-22
|
|
10
221
|
|
|
11
222
|
Batched fix release for the seven defects Barry's WSD-lens dogfood on
|
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-
|
|
5
|
-
"createdAt": "2026-09-
|
|
6
|
-
"buildVersion": "0.28.
|
|
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-
|
|
38
|
-
"createdAt": "2026-09-
|
|
39
|
-
"buildVersion": "0.28.
|
|
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-
|
|
71
|
-
"createdAt": "2026-09-
|
|
72
|
-
"buildVersion": "0.28.
|
|
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.
|
|
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
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cnId": "CN-700",
|
|
3
|
+
"path": "src/view/product-map.js",
|
|
4
|
+
"title": "Product Map re-bucketing over the review-surface tree model",
|
|
5
|
+
"description": "Pure functions groupByShape, groupByComponent, groupByTraceCoverage, groupByCapability, groupByBlueprint plus the panel renderer that composes them. No schema change; consumes shapeClassification.shapes[], usByTacId, tsByAcId/tcsByAcId, REQ.tags[] for capability:<slug> tags, and manifest.blueprints[].contributions[] for blueprint attribution (fallback: reqId kebab-slug prefix). Emits a pm-blueprint-badge on every REQ row an applied blueprint contributed.",
|
|
6
|
+
"implementsAcIds": [
|
|
7
|
+
"AC-17001-1",
|
|
8
|
+
"AC-17001-2",
|
|
9
|
+
"AC-17002-1",
|
|
10
|
+
"AC-17003-1",
|
|
11
|
+
"AC-17004-1",
|
|
12
|
+
"AC-17005-1",
|
|
13
|
+
"AC-17006-1",
|
|
14
|
+
"AC-17008-1"
|
|
15
|
+
],
|
|
16
|
+
"dependencies": [
|
|
17
|
+
"CN-017"
|
|
18
|
+
],
|
|
19
|
+
"version": "0.1.0",
|
|
20
|
+
"status": "draft",
|
|
21
|
+
"createdAt": "2026-09-21T17:00:00.000Z",
|
|
22
|
+
"updatedAt": "2026-09-22T11:59:39Z"
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cnId": "CN-701",
|
|
3
|
+
"path": "scripts/apply-capability-tags.mjs",
|
|
4
|
+
"title": "Apply capability:<slug> tags to every dogfood REQ",
|
|
5
|
+
"description": "One authoritative REQ id -> slugs mapping applied to every requirement file under rcf/requirements/. Idempotent; overwrites existing capability:<slug> tags while preserving non-capability tags. Ships as the tool of record until a rcf discover req-capability-classify verb lands.",
|
|
6
|
+
"implementsAcIds": [
|
|
7
|
+
"AC-17101-1"
|
|
8
|
+
],
|
|
9
|
+
"dependencies": [],
|
|
10
|
+
"version": "0.1.0",
|
|
11
|
+
"status": "draft",
|
|
12
|
+
"createdAt": "2026-09-21T17:00:00.000Z",
|
|
13
|
+
"updatedAt": "2026-09-21T17:00:00.000Z"
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cnId": "CN-702",
|
|
3
|
+
"path": "docs/capability-tags.md",
|
|
4
|
+
"title": "Capability tag convention and taxonomy for the rcf-lite chain",
|
|
5
|
+
"description": "Documents the capability:<slug> convention, the initial twelve-slug taxonomy and the rule for adding a new capability. Linked from docs/README.md; consumed by the Product Map by-capability grouping.",
|
|
6
|
+
"implementsAcIds": [
|
|
7
|
+
"AC-17102-1"
|
|
8
|
+
],
|
|
9
|
+
"dependencies": [],
|
|
10
|
+
"version": "0.1.0",
|
|
11
|
+
"status": "draft",
|
|
12
|
+
"createdAt": "2026-09-21T17:00:00.000Z",
|
|
13
|
+
"updatedAt": "2026-09-21T17:00:00.000Z"
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cnId": "CN-703",
|
|
3
|
+
"path": "src/server/routes.js",
|
|
4
|
+
"implementsAcIds": [
|
|
5
|
+
"AC-17007-1"
|
|
6
|
+
],
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"CN-700"
|
|
9
|
+
],
|
|
10
|
+
"version": "0.1.0",
|
|
11
|
+
"status": "draft",
|
|
12
|
+
"createdAt": "2026-09-21T19:44:46.452Z",
|
|
13
|
+
"updatedAt": "2026-09-21T19:44:52.245Z",
|
|
14
|
+
"title": "Product Map partial-render endpoint",
|
|
15
|
+
"description": "Adds a /product-map/<group> HTTP route that serves the pre-rendered non-lazy panel body for the named grouping. Consumed by the inline scripts hydrateLazyGroup which fetches on first activation and caches the response in memory; the live-client invalidates the cache on every SSE tree-update swap."
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cnId": "CN-720",
|
|
3
|
+
"path": "src/query/delta.js",
|
|
4
|
+
"title": "Delta detection: canonical-JSON hashing and computeDelta",
|
|
5
|
+
"description": "Pure module. canonicaliseJson (recursive key sort, no whitespace), hashDocument (sha256:<hex> over the canonical form of a parsed document), computeTreeHash (over sorted (id, docHash) pairs), and computeDelta (returns the proposal 2026-09-22 section 2.2 shape: frozen, frozenAt, treeHash, currentTreeHash, changed, added, removed, briefSince, unchanged). Ledger docHashes are added under ledger:<name> ids per sidecar the caller passes.",
|
|
6
|
+
"implementsAcIds": [
|
|
7
|
+
"AC-17201-1",
|
|
8
|
+
"AC-17201-2"
|
|
9
|
+
],
|
|
10
|
+
"dependencies": [],
|
|
11
|
+
"version": "0.1.0",
|
|
12
|
+
"status": "draft",
|
|
13
|
+
"createdAt": "2026-09-24T12:00:00Z",
|
|
14
|
+
"updatedAt": "2026-09-24T12:00:00Z"
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cnId": "CN-721",
|
|
3
|
+
"path": "src/define/freeze-record.js",
|
|
4
|
+
"title": "Freeze record I/O: load, save, validate",
|
|
5
|
+
"description": "Owns rcf/define/freeze.json. FREEZE_RECORD_REL_PATH, FreezeRecordError (coded parseFailure | schemaFailure | ioFailure with a named field), validateFreezeRecord, loadFreezeRecord (returns null on ENOENT), saveFreezeRecord (validates first; refuses to persist an invalid record; creates rcf/define/ on demand; writes pretty JSON with a trailing newline). The record's local schema is documented at the top of this file and covered by test/define/freeze-record.test.js.",
|
|
6
|
+
"implementsAcIds": [
|
|
7
|
+
"AC-17201-3",
|
|
8
|
+
"AC-17201-4"
|
|
9
|
+
],
|
|
10
|
+
"dependencies": [],
|
|
11
|
+
"version": "0.1.0",
|
|
12
|
+
"status": "draft",
|
|
13
|
+
"createdAt": "2026-09-24T12:00:00Z",
|
|
14
|
+
"updatedAt": "2026-09-24T12:00:00Z"
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cnId": "CN-722",
|
|
3
|
+
"path": "src/define/ledgers.js",
|
|
4
|
+
"title": "Four sidecar ledgers: load, save, validate, add, resolve",
|
|
5
|
+
"description": "Pure module. LEDGER_NAMES (brief, decisions, concerns, probes), BRIEF_KINDS closed vocabulary, LedgerError with coded reason and named field, ledgerRelPath / ledgerPath, emptyLedger, loadLedger (empty on ENOENT), saveLedger (validates first), validateLedger, addEntry (mints next id, stamps addedAt, defaults status open, validates the whole body), resolveEntry (flips status to resolved, stamps resolvedAt, refuses unknown id), parseBriefFromFile (splits by non-empty lines, strips '- ', '* ', '<n>. ' bullets), loadAllLedgers (returns a LedgerBundle for computeDelta).",
|
|
6
|
+
"implementsAcIds": [
|
|
7
|
+
"AC-17301-1",
|
|
8
|
+
"AC-17301-3"
|
|
9
|
+
],
|
|
10
|
+
"dependencies": [],
|
|
11
|
+
"version": "0.1.0",
|
|
12
|
+
"status": "draft",
|
|
13
|
+
"createdAt": "2026-09-24T12:00:00Z",
|
|
14
|
+
"updatedAt": "2026-09-24T12:00:00Z"
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cnId": "CN-723",
|
|
3
|
+
"path": "src/cli/ledger.js",
|
|
4
|
+
"title": "rcf define ledger CLI verb",
|
|
5
|
+
"description": "Wraps src/define/ledgers.js with parseArgs and per-verb usage text. Dispatches ledger <name> <verb> where name is brief | decisions | concerns | probes and verb is add | resolve | list. renderDecisionsList prints the numbered #241 format (one decision per item, letter) options, default:, blocks:). list --json emits { ledger, <arrayKey>: [...] }. bin/rcf.js registers this under GROUPS.define.ledger; src/cli/help.js wires HELP into HELP_MAP.define.ledger and extends the DEFINE_HELP and top-level listing blocks so 'rcf help define ledger' and 'rcf define ledger --help' render the same text by construction.",
|
|
6
|
+
"implementsAcIds": [
|
|
7
|
+
"AC-17301-1",
|
|
8
|
+
"AC-17301-2",
|
|
9
|
+
"AC-17301-3"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"CN-722"
|
|
13
|
+
],
|
|
14
|
+
"version": "0.1.0",
|
|
15
|
+
"status": "draft",
|
|
16
|
+
"createdAt": "2026-09-24T12:00:00Z",
|
|
17
|
+
"updatedAt": "2026-09-24T12:00:00Z"
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cnId": "CN-724",
|
|
3
|
+
"path": "test/store/walker-invisible-define.test.js",
|
|
4
|
+
"title": "Invariant test: walker never surfaces rcf/define/ files",
|
|
5
|
+
"description": "One-scenario test that seeds a fresh project (initProject), writes rcf/define/freeze.json, the four ledger files and a bug-bait req-999.json under rcf/define/, then runs walkTree and asserts no error path starts with rcf/define/ and no such file lands in tree.byId. The invariant is what keeps the sidecars out of every existing tree consumer (proposal 2026-09-22 section 2.1).",
|
|
6
|
+
"implementsAcIds": [
|
|
7
|
+
"AC-17301-4"
|
|
8
|
+
],
|
|
9
|
+
"dependencies": [],
|
|
10
|
+
"version": "0.1.0",
|
|
11
|
+
"status": "draft",
|
|
12
|
+
"createdAt": "2026-09-24T12:00:00Z",
|
|
13
|
+
"updatedAt": "2026-09-24T12:00:00Z"
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cnId": "CN-725",
|
|
3
|
+
"path": "src/query/gates.js",
|
|
4
|
+
"title": "DEFINE stage gates D1..D8 pure check functions",
|
|
5
|
+
"description": "Pure module. One check function per stage D1..D8 (checkD1Brief..checkD8Freeze) plus STAGE_ORDER, STAGE_GATES, STAGE_SHORT_NAMES, INTERFACE_KINDS. Each takes (tree, context) and returns { stage, gate, state, checks[] } in the section 2.4 shape.",
|
|
6
|
+
"implementsAcIds": [
|
|
7
|
+
"AC-17401-1",
|
|
8
|
+
"AC-17401-2",
|
|
9
|
+
"AC-17401-3",
|
|
10
|
+
"AC-17401-4",
|
|
11
|
+
"AC-17401-5",
|
|
12
|
+
"AC-17401-6",
|
|
13
|
+
"AC-17401-7",
|
|
14
|
+
"AC-17401-8"
|
|
15
|
+
],
|
|
16
|
+
"dependencies": [],
|
|
17
|
+
"version": "0.1.0",
|
|
18
|
+
"status": "draft",
|
|
19
|
+
"createdAt": "2026-09-24T14:30:00Z",
|
|
20
|
+
"updatedAt": "2026-09-24T14:30:00Z"
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cnId": "CN-726",
|
|
3
|
+
"path": "src/query/readiness.js",
|
|
4
|
+
"title": "computeReadiness composes delta + fan-out + gates + coverage + decisions",
|
|
5
|
+
"description": "Pure module. computeReadiness returns the section 2.4 object; internal composeStages calls TAC-4122's gate functions in D1..D8 order; deriveNextAction picks the first failing check; freezeable folds the eight stage states.",
|
|
6
|
+
"implementsAcIds": [
|
|
7
|
+
"AC-17501-1",
|
|
8
|
+
"AC-17501-2",
|
|
9
|
+
"AC-17501-3",
|
|
10
|
+
"AC-17501-4"
|
|
11
|
+
],
|
|
12
|
+
"dependencies": [],
|
|
13
|
+
"version": "0.1.0",
|
|
14
|
+
"status": "draft",
|
|
15
|
+
"createdAt": "2026-09-24T14:30:00Z",
|
|
16
|
+
"updatedAt": "2026-09-24T14:30:00Z"
|
|
17
|
+
}
|