rcf-lite 0.32.5 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/fixtures/canary-manifest.json +9 -9
  3. package/guidance/discovery/discovery-viewer-command-page-1-layout.html +191 -0
  4. package/guidance/discovery/discovery-viewer-command-page-2-coverage.html +193 -0
  5. package/guidance/discovery/discovery-viewer-command-page-3-trace-matrix.html +195 -0
  6. package/guidance/discovery/discovery-viewer-command-page-4-tables.html +196 -0
  7. package/guidance/discovery/discovery-viewer-command-page-5-subtabs.html +224 -0
  8. package/guidance/discovery/discovery-viewer-command-page-6-one-page.html +280 -0
  9. package/guidance/discovery/discovery-viewer-command-page.md +301 -0
  10. package/package.json +1 -1
  11. package/rcf/adrs/adr-4139-readiness-tables-read-only-no-commands.json +30 -0
  12. package/rcf/adrs/adr-4140-view-query-routes-per-pivot-matrix-no-mermaid.json +30 -0
  13. package/rcf/adrs/adr-4141-verify-modes.json +25 -0
  14. package/rcf/code-nodes/cn-771.json +3 -2
  15. package/rcf/code-nodes/cn-779.json +19 -0
  16. package/rcf/code-nodes/cn-780.json +14 -0
  17. package/rcf/code-nodes/cn-781.json +14 -0
  18. package/rcf/code-nodes/cn-782.json +15 -0
  19. package/rcf/code-nodes/cn-783.json +17 -0
  20. package/rcf/code-nodes/cn-784.json +20 -0
  21. package/rcf/code-nodes/cn-785.json +14 -0
  22. package/rcf/code-nodes/cn-786.json +14 -0
  23. package/rcf/code-nodes/cn-787.json +18 -0
  24. package/rcf/code-nodes/cn-788.json +14 -0
  25. package/rcf/code-nodes/cn-789.json +14 -0
  26. package/rcf/code-nodes/cn-790.json +15 -0
  27. package/rcf/code-nodes/cn-791.json +14 -0
  28. package/rcf/code-nodes/cn-792.json +17 -0
  29. package/rcf/code-nodes/cn-793.json +17 -0
  30. package/rcf/code-nodes/cn-794.json +18 -0
  31. package/rcf/code-nodes/cn-795.json +15 -0
  32. package/rcf/code-nodes/cn-796.json +14 -0
  33. package/rcf/code-nodes/cn-797.json +14 -0
  34. package/rcf/code-nodes/cn-798.json +14 -0
  35. package/rcf/define/brief-ledger.json +47 -0
  36. package/rcf/define/decisions-ledger.json +113 -0
  37. package/rcf/fbs/fbs-204.json +46 -0
  38. package/rcf/fbs/fbs-205.json +46 -0
  39. package/rcf/fbs/fbs-206.json +49 -0
  40. package/rcf/fbs/fbs-207.json +43 -0
  41. package/rcf/fbs/fbs-208.json +47 -0
  42. package/rcf/fbs/fbs-209.json +22 -0
  43. package/rcf/requirements/req-002.json +5 -2
  44. package/rcf/requirements/req-180.json +6 -3
  45. package/rcf/tacs/tac-4127-readiness-view.json +2 -2
  46. package/rcf/tacs/tac-4133-readiness-po-layer.json +2 -2
  47. package/rcf/tacs/tac-4135-readiness-command-page.json +94 -0
  48. package/rcf/tacs/tac-4136-view-query-routes.json +68 -0
  49. package/rcf/tacs/tac-4141-verify-modes.json +25 -0
  50. package/rcf/test-suites/ts-223.json +7 -7
  51. package/rcf/test-suites/ts-237.json +78 -0
  52. package/rcf/test-suites/ts-238.json +70 -0
  53. package/rcf/test-suites/ts-239.json +78 -0
  54. package/rcf/test-suites/ts-240.json +70 -0
  55. package/rcf/test-suites/ts-241.json +70 -0
  56. package/rcf/test-suites/ts-242.json +46 -0
  57. package/rcf/user-stories/us-16503.json +51 -0
  58. package/rcf/user-stories/us-18001.json +4 -4
  59. package/rcf/user-stories/us-18003.json +79 -0
  60. package/rcf/user-stories/us-18004.json +70 -0
  61. package/rcf/user-stories/us-18005.json +79 -0
  62. package/rcf/user-stories/us-208.json +74 -0
  63. package/rcf/user-stories/us-209.json +70 -0
  64. package/releases/releases.yaml +10 -1
  65. package/src/cli/finalise.js +141 -2
  66. package/src/cli/view.js +13 -1
  67. package/src/finalise/spawn.js +2 -0
  68. package/src/server/index.js +104 -39
  69. package/src/server/routes.js +169 -0
  70. package/src/verify/cli/run.js +40 -0
  71. package/src/verify/engine/deterministic.js +150 -0
  72. package/src/verify/engine/index.js +27 -4
  73. package/src/view/doc-renderers/req.js +11 -9
  74. package/src/view/html-page.js +15 -11
  75. package/src/view/index.js +7 -1
  76. package/src/view/live-client.js +4 -0
  77. package/src/view/page-init.js +842 -1
  78. package/src/view/readiness/composite-id.js +35 -0
  79. package/src/view/readiness/coverage-summary.js +293 -0
  80. package/src/view/readiness/delta-counts.js +156 -0
  81. package/src/view/readiness/grid.js +175 -0
  82. package/src/view/readiness/po-layer.js +110 -220
  83. package/src/view/readiness/tables.js +500 -0
  84. package/src/view/readiness/thin-reqs.js +237 -0
  85. package/src/view/readiness/trace-matrix.js +392 -0
  86. package/src/view/readiness.js +359 -370
  87. package/src/view/style.css +594 -0
  88. package/src/watch/index.js +75 -17
@@ -0,0 +1,301 @@
1
+ # Discovery: the viewer Readiness tab as the command page, with traceability on the page
2
+
3
+ Stage: DISCOVERY (design artefact only; no source changes ride with it).
4
+
5
+ Written against `Stravica/rcf-lite` at origin/main 138fa4a8 (0.32.3, 2026-10-07) and read against two real trees: rcf-lite's own chain (1,055 documents, unfrozen) and WESPA main 34ebceba (616 documents, unfrozen, the largest consumer) rendered through the 0.32.3 binary. Every count below comes from either `rcf define readiness --json` or the rendered Readiness panel HTML (the output of `renderModelToPage` in `src/view/index.js`, counted inside `#tab-readiness`).
6
+
7
+ What the reader needs before the RCF detail: the viewer is a read-only web page that renders a project's requirements tree. Its first tab, Readiness, answers two questions (is the requirements set ready for engineers, is the tree ready to build) by running eight checks called D1 to D8 and listing what fails. The same compute feeds the CLI verb `rcf define readiness`, so the tab and the terminal never disagree on the data; this document is about how that data is presented and what is missing around it.
8
+
9
+ Thesis: the Readiness tab already has the right numbers and the wrong shape. The two verdict cards work; everything beneath them is prose, cards or a dump.
10
+
11
+ The question cards carry no link and no write-back, and the one real table reads three of thirty-six checks. The "For engineers" row holds the whole engineer rendering, which on an unfrozen tree is dominated by a delta list that prints every document in the tree (616 rows on WESPA, 1,055 on rcf-lite's own chain) with a diff widget each. Beside it sit a Coverage block that prints blanks because it reads fields the coverage result does not carry, dead links on every composite id, and a Freeze button wired to nothing.
12
+
13
+ The command page is the same readiness object re-expressed as linked tables (questions, blocking items, thin requirements, coverage) with counts that are themselves links, plus three on-demand JSON routes that expose the pure trace, impact and coverage functions the CLI already calls, rendered as a coverage summary chart and a trace matrix for a chosen node. All of it stays server-rendered, GET-only, embed-safe and theme-tokened. The viewer is read-only: it never renders CLI commands, and it never writes. "Resolve in place" means "open the item in full context and surface what settles it in human terms", so every row carries the id, the plain-English ask or finding, where it lives on the chain, a link to the item in full context, and what a resolved state would look like.
14
+
15
+ Infographic sketches (static mocked pages, light theme, no external assets) sit beside this file:
16
+
17
+ - [1 Command page layout](./discovery-viewer-command-page-1-layout.html)
18
+ - [2 Coverage summary](./discovery-viewer-command-page-2-coverage.html)
19
+ - [3 Trace matrix for a selected node](./discovery-viewer-command-page-3-trace-matrix.html)
20
+ - [4 Questions and blocking items tables](./discovery-viewer-command-page-4-tables.html)
21
+
22
+ ## 1. Problem
23
+
24
+ Barry, 2026-10-07, on the Readiness tab:
25
+
26
+ > [Readiness tab] at the moment - apart from the two key stage boxes, its scrappy/messy prose. The user has to try to translate it into the questions its supposed to be answering. E.g. "Questions for you" -> should be a list of the questions in a table perhaps, with a link to where those questions are and can be viewed in full context and/or resolved. If there are say insufficient ACs for a couple of REQs - I would expect to see these specified/listed and links to resolve/view them in place. The "For engineers" section is garbage - its like a scratchpad for the agent - its unhelpful to a human engineer. Lists, tables, counts, links - are whats needed on this whole page - it should serve as THE command page.
27
+
28
+ And on traceability:
29
+
30
+ > Part of the "sell" of RCF is its traceability. So far we barely demo/show that ability via the only exposed visible surface - the viewer. We should helpfully expose available/key query tools to the UI, as well as perhaps a full traceability scan on-demand with a nice chart and table-driven traceability/coverage report. This will help expose key parts of RCF - which helps understanding and adoption.
31
+
32
+ The command page in this project is the Readiness tab: the one screen where a product owner or an engineer sees what blocks the next step, opens each blocking item where it lives, and sees how far the chain from requirement to test actually reaches, with no agent in the loop and no terminal open.
33
+
34
+ ## 2. Users and their questions
35
+
36
+ | # | Role | Question they bring | Current surface answer | The gap |
37
+ |---|---|---|---|---|
38
+ | 1 | Operator | Is my requirements set ready for engineers? | Verdict card "Ready for engineers: Yes / Not yet" with a question count (`po-layer.js` renderVerdictCards) | None on the verdict; the count is text, not a link to the list it counts |
39
+ | 2 | Operator | What do I have to answer, and where does each item live? | "Questions for you" cards: heading, ask, hint, collapsed engineer detail; grouped by source span | No link to the statement or requirement, no table, no write-back shown, no done state |
40
+ | 3 | Operator | Which requirements are thin? | "Requirements that still need work" table (REQ, what is missing, Open) fed by three checks only: `stories:reqHasUs`, `skeleton:reqIntent`, `skeleton:resolvedBy` | Stories missing criteria, criteria with no resolving test and D4 floor failures never reach it; 0 rows on both trees today |
41
+ | 4 | Operator | What happens if I answer these? | "What happens next" paragraph with two counts | Prose; the counts do not link anywhere |
42
+ | 5 | Engineer | What blocks the build, by id, and why? | Blocker cards inside the For-engineers row: stage, check, persona pill, up to 20 ids each with a "why" | Ids past 20 per card are cut (`readiness.js` renderBlockerCard slices to 20; `skeleton:reqShape` has 36 failing, `stories:usFloors` 498 on rcf-lite's chain); composite ids are dead links |
43
+ | 6 | Engineer | What is the next concrete edit? | Next-action line naming stage, check, ids and the re-check command | The command shown is the re-check, not the write-back; no copy control |
44
+ | 7 | Engineer | Which stages pass and what is each one's posture? | D1 to D8 chips with hover text; Stage guide dialog (one real table) | Good; per-stage pass/total only appears deep in the stage detail |
45
+ | 8 | Engineer | What changed since the freeze? | Delta block listing every changed, added or removed document with a hash diff widget | On an unfrozen tree the delta is the whole tree: 616 rows on WESPA, 1,055 on rcf-lite |
46
+ | 9 | Engineer | How much of the tree is covered by resolving tests? | Coverage block: tree column, delta column, re-verify and re-execute counts | Prints `pass / ` (blank) and "No per-REQ delta coverage" on every tree; the CLI line says `15/55 covered (strict)` for WESPA |
47
+ | 10 | Engineer | Which test pointers do not resolve? | Not on the tab; Product Map "Trace coverage" counts structure only | 12 WESPA requirements show Complete on the map while `rcf audit coverage` reports every chain under them unresolved (covered 15, covered-unresolved 12, uncovered 28) |
48
+ | 11 | Engineer | If REQ-001 changes, what has to be re-run or re-approved? | Nothing in the viewer; `rcf audit impact` in the CLI only (74 nodes, 138 edges for REQ-001 on WESPA) | Not exposed |
49
+ | 12 | Engineer, agent | Show me the chain from this requirement down to its tests | (retiring) Per-requirement Mermaid slice under Requirements (REQ, US, AC, FBS); `rcf audit trace` in the CLI; the trace-matrix FBS in Section 9 retires this slice| Test suites, test cases and components are absent from the slice; no node picker; no matrix |
50
+ | 13 | Agent | What exact command answers each question? | `rcf define questions --json` carries `writeBack[].command` per question | Computed and folded onto the readiness object in `src/view/index.js`, never rendered |
51
+ | 14 | Operator inside WESPA | The same answers inside the admin SPA iframe | `?embed=1`, `?theme=`, relative asset URLs, SSE tab resync (0.32.0) all in place | Every new route must be added to the host's proxy allow-list (WESPA added `page-init.js` by hand for 0.29.0) |
52
+ | 15 | Engineer | Can I freeze now? | "Freeze now" button plus the `rcf define freeze` command text | The button has no handler anywhere in `page-init.js` and the server answers 405 to anything but GET and HEAD (`src/server/routes.js`) |
53
+
54
+ ## 3. Current surface audit with counts
55
+
56
+ Method: the Readiness panel was rendered for both trees with the shipped code path and its sections counted with a script. Tree A is rcf-lite's own chain (register unstated, intent-complete no, 15 engineer checks blocking). Tree B is WESPA at 34ebceba under 0.32.3 (register engineer, intent-complete yes, ready-to-build no on D6 and D8).
57
+
58
+ ### 3.1 Two verdict cards (`src/view/readiness/po-layer.js`, renderVerdictCards)
59
+
60
+ - Shape: two equal-height cards, a Yes / Not yet pill, one count line, the CLI verdict line in a code span, the chain term muted.
61
+ - Kinds of information: two verdicts, two counts, one chain term each.
62
+ - Actionable links: 0 (A and B).
63
+ - Items needing a resolve-in-context link: the two counts (they name a list the reader then has to find).
64
+ - Barry's rule: keep the cards; make each count the link into the table it counts.
65
+
66
+ ### 3.2 Questions for you (renderQuestionCards, `question-adapter.js`, `phrasebook.js`)
67
+
68
+ - Shape: one card per question (Q badge, heading, ask, hint, a collapsed "Show the detail (engineer view)" line), grouped by source span, plus an optional dashed group for decisions with a default.
69
+ - Counts: A has 3 questions in 1 group (151 words, 10 paragraphs, 3 details, 0 tables, 0 links); B has 0 questions (30 words, 0 links).
70
+ - Kinds of information: question text, hint, stage and check id in the detail.
71
+ - Items needing a resolve link: all 3 on A; each already carries `writeBack[].command` in the readiness object (for example `rcf define ledger brief add --text "<text>"`) and `itemId` (profile:surface, brief-ledger), neither rendered.
72
+ - Barry's rule: this becomes a table: number, question, where it lives (link), what settles it, the ask in brief-your-agent form (id plus one-line ask, copyable), state. No CLI command rendered on the page.
73
+
74
+ ### 3.3 What happens next (renderNextStep)
75
+
76
+ - Shape: one card, three short paragraphs.
77
+ - Counts: 53 words, 0 links (A and B).
78
+ - Kinds of information: the question count and the engineer item count restated.
79
+ - Items needing a link: both counts.
80
+ - Barry's rule: fold into the verdict cards as linked counts, or keep as one sentence; a paragraph that restates two numbers is not a panel.
81
+
82
+ ### 3.4 Requirements that still need work (renderReqWorkTable)
83
+
84
+ - Shape: a real table (Requirement, What is missing, Open link to `#tab=requirements&entity=REQ-n`).
85
+ - Counts: 0 rows on A and 0 rows on B. Why 0: only three product-owner checks feed it, and on A the failing checks are engineer-persona (`skeleton:reqShape` 36 ids, `stories:usFloors` 498 ids) while on B nothing fails below D6.
86
+ - Kinds of information when populated: REQ id, one reason sentence, one link.
87
+ - Items needing a link: every thin requirement the other checks name.
88
+ - Barry's rule: the right shape, fed by too little. "Insufficient ACs" has no feed at all today; the nearest signals are D4 `stories:usFloors` per story, the Product Map "Missing AC scope" bucket for a story with zero criteria, and coverage `acs[].covered` for criteria with no resolving test.
89
+
90
+ ### 3.5 For engineers (renderForEngineers, a collapsed DocRow carrying the full engineer body from `src/view/readiness.js`)
91
+
92
+ The row is collapsed unless the profile register is engineer; WESPA's register is engineer, so on the showhome tree it is open on first paint. Totals inside it: A 526 KB, 21,639 words, 3,172 paragraphs, 1,437 list items, 1,417 links of which 206 are dead; B 285 KB, 10,949 words, 1,855 paragraphs, 630 list items, 623 links of which 8 are dead. Dead means the href is a composite id (`#AC-060-3:userID`, `#profile:surface`, `#brief-ledger`, `#D6`, `#TAC-4122-define-gates:CHECK_PERSONA`, `#ledger:brief`) that `findByDocId` in `page-init.js` cannot find, so the router falls back to activating the Readiness tab and nothing moves. Sub-blocks:
93
+
94
+ | Block (readiness.js) | A | B | Notes |
95
+ |---|---|---|---|
96
+ | Tree line | 1 paragraph, 11 words | same | Fine |
97
+ | Verdict pills | 2 pills | same | Duplicates the cards above |
98
+ | Next action lines | 2 paragraphs, 2 live anchor links, 2 commands | 2 paragraphs, 1 link | The only working deep links on the page |
99
+ | D1 to D8 chips | 8 chips, hover text, click opens the stage guide | same | Fine; the pass/total per stage is not on the chip |
100
+ | Blockers by persona | 15 cards, 187 ids, 180 links (103 dead) | 2 cards, 3 ids, 3 links (3 dead) | Cards cap at 20 ids; on A, 16 and 478 ids are invisible on two cards |
101
+ | Delta | 1,055 rows, 8,440 diff fragments, 17,938 words, 449 KB (84 percent of the panel) | 616 rows, 4,928 fragments, 10,475 words, 259 KB | Every row reads "added, not in the frozen tree, sha256:..."; this is the scratchpad |
102
+ | Stage detail | 36 checks (21 ok, 15 fail), 187 ids | 36 checks (34 ok, 2 fail), 3 ids | Same ids as the blocker cards, listed a second time |
103
+ | Coverage | `pass / ` and "No per-REQ delta coverage." | identical | Defect: renderCoverage reads `t.pass`, `t.total`, `cv.reqId`; the coverage result carries `totals.{requirements, covered, coveredUnresolved, uncovered}` and `requirements[].id` (`src/query/coverage.js`). The object holds 119/119 on A and 15/55 on B |
104
+ | Decisions outstanding | "No open decisions." | same | Fine when empty; a list, not a table, when not |
105
+ | Freeze now | 1 disabled button, command text | same | No click handler exists; the server is GET and HEAD only |
106
+ | Freeze record | "No freeze record on disk." | same | A has no `rcf/define/` directory at all |
107
+
108
+ - Kinds of information across the row: verdicts, next actions, stage states, failing ids with reasons, delta ids with hashes, coverage counts (not shown), decisions, freeze state.
109
+ - Items needing a resolve link: every failing id (187 on A, 3 on B), every unresolved test pointer (94 on B, not shown), the two coverage counts.
110
+ - Barry's rule: nothing in this row is a table, and the delta is a 616-row list with no filter. The useful engineer content (failing ids with why, next command, coverage) is present but has to be dug out from under the delta.
111
+
112
+ ### 3.6 Stage guide dialog (`stage-legend.js`)
113
+
114
+ - Shape: a dialog with one table (Stage, Name, What it checks, Posture), 8 rows, 238 words, 1 close button.
115
+ - Barry's rule: fine as is; it is the only part of the page that already matches the rule.
116
+
117
+ ### 3.7 Summary across panels
118
+
119
+ | Panel | Words A / B | Tables | Links A / B (dead) | Items needing a resolve link | Verdict against "lists, tables, counts, links" |
120
+ |---|---|---|---|---|---|
121
+ | Verdict cards | 58 / 49 | 0 | 0 / 0 | 2 counts | Keep; link the counts |
122
+ | Questions for you | 151 / 30 | 0 | 0 / 0 | 3 / 0 | Rebuild as a table with write-backs |
123
+ | What happens next | 53 / 54 | 0 | 0 / 0 | 2 counts | Fold into the cards |
124
+ | Requirements that still need work | 44 / 44 | 1 (empty) | 0 / 0 | all thin REQs | Keep the shape, widen the feed |
125
+ | For engineers | 21,639 / 10,949 | 0 | 1,417 (206) / 623 (8) | 187 / 3 ids, 94 pointers, 2 counts | Retire the row; promote named tables |
126
+ | Stage guide | 238 / 238 | 1 | 0 / 0 | 0 | Keep |
127
+ | Whole panel | 22,184 / 11,365 | 1 (+1 in the dialog) | 1,417 / 623 | | |
128
+
129
+ ## 4. Candidate capabilities ranked
130
+
131
+ Rank 1 is highest. Barry value is scored on reader clarity (C), operator action (A) and traceability demonstration (T), each H / M / L; cost is S (a day), M (a few days), L (a week or more, or a decision first).
132
+
133
+ | Rank | Capability | C | A | T | Cost | Evidence and notes |
134
+ |---|---|---|---|---|---|---|
135
+ | 1 | Questions for you as a sortable table: number, question, where it lives (link), what settles it, a copyable brief-your-agent handle (id plus one-line ask), state. No CLI command rendered on the page (Q1 ruling, Section 7). | H | H | L | M | `readiness.questions[]` already carries `itemId`, `context.sourceSpan`, `answerKinds`, `writeBack[].command` (`src/query/questions.js`); the brief handle is composed from `itemId` and the question ask, not from `writeBack[].command` |
136
+ | 2 | Blocking items table for engineers: one row per failing item (stage, check, id as a link, plain-English why, what resolved looks like), filterable by stage and persona, no 20-row cap. No command rendered on the page (Q1 ruling, Section 7). | H | H | M | M | `stages[].checks[].failing[]` has `{id, why}`; composite ids split on the first colon to a real document id plus a fragment |
137
+ | 3 | Coverage block fixed and promoted to a coverage summary: totals, per-requirement rows (covered / unresolved / uncovered), unresolved pointers with reason | H | M | H | S | `coverage.tree.totals`, `requirements[].coverageClass`, `unresolvedTestPointers[]` are already on the readiness object (`src/query/coverage.js`) |
138
+ | 4 | Thin requirements table widened: stories with zero criteria, criteria with no resolving test, D4 floor failures, each with a reason and an Open link | H | H | M | M | Map US ids to REQ via `reqId`; reuse `groupByTraceCoverage` buckets from `src/view/product-map.js` |
139
+ | 5 | Delta as counts with an on-demand list, never 616 rows on first paint | H | L | L | S | `delta.{changed, added, removed, briefSince, impacted, impactedFbs}` are counts already |
140
+ | 6 | Readiness verdict grid: D1 to D8 by scope (tree, delta), each cell pass/total with the failing count as a link into the blocking table | M | M | M | S | `checks[].over` is `tree` or `delta`; `pass` and `total` exist per check |
141
+ | 7 | On-demand full traceability scan: `./trace.json?id=&direction=`, `./impact.json?id=`, `./coverage.json` served from the current state, plus a report sub-view with a chart and a table | M | M | H | L | `computeTrace`, `computeImpact`, `computeCoverage` are pure and already imported by the CLI; the server has no JSON route for them yet |
142
+ | 8 | Trace matrix for a selected node (requirement to story to criterion to suite and case, with components and build specs as columns) | M | M | H | M | Needs 7; matrix chosen over sunburst (section 6.5) |
143
+ | 9 | Global "Trace" action on every id in every table (opens 8 with that pivot) | M | M | H | S | Once 7 and 8 exist, one link pattern |
144
+ | 10 | Impact view for a selected node, action label per row (re-run, re-verify, re-approve, re-execute) | L | M | H | M | `labelFor` in `src/query/impact.js`; defer to a second pass |
145
+ | 11 | Sub-tabs on Readiness: Overview, Questions, Blocking, Coverage, Trace | M | M | L | S | `SubTabStrip` component exists; the router already reads `sub=` for Build |
146
+ | 12 | Search integration: lookup modal hits on readiness rows, `#tab=readiness&sub=blocking&entity=<id>` | L | M | L | S | `./index.json` lookup (TAC-4132) and the hash router already handle `entity=` |
147
+ | 13 | Keyboard shortcuts beyond the existing Cmd or Ctrl+F lookup (row navigation, t for trace) | L | L | L | S | Defer; no evidence anyone asked |
148
+ | 14 | For engineers rewritten as named engineer items (missing coverage, failing checks by id, orphan interfaces, unresolved pointers) and the DocRow wrapper removed | H | H | M | included in 2, 3, 5, 6 | The content survives; the dump does not |
149
+ | 15 | Resolve in place as a write route (POST to a verb such as `rcf define update`) | H | H | L | L, decision first | Server is GET and HEAD only; the host's auth would gate it in WESPA; see question 1 |
150
+ | 16 | Freeze now: remove the dead button; show the state and what resolves it in human terms. No command text on the page (Q4 ruling, Section 7). | M | L | L | S | A dead control on the command page costs trust |
151
+ | 17 | Readiness state timeline (optional) | L | L | M | defer | Needs a persisted snapshot per rewalk; none exists |
152
+
153
+ ## 5. Data already available vs needs computing
154
+
155
+ | Capability | Data source | Viewer compute carries it today | What to compute, and where |
156
+ |---|---|---|---|
157
+ | Questions table (1) | `src/query/questions.js` computeQuestions: `questions[].{id, stage, check, itemId, heading, ask, context.sourceSpan, answerKinds, writeBack[].command, blocks}`, `groups[]`, `optional[]` | Yes; `src/view/index.js` folds it onto `readiness.questions` | Nothing; render it. A "where it lives" link is `#tab=requirements&entity=<REQ>` for REQ items, `#tab=readiness&sub=questions&entity=<itemId>` for brief and profile items |
158
+ | Blocking items table (2) | `readiness.stages[].checks[].failing[].{id, why}`, `personas.engineer.blockers[].{stage, check, ids, failingCount, question}`, `nextAction.command` (`src/query/readiness.js`) | Yes | Nothing; drop the 20-id slice; split composite ids (`AC-060-3:userID` to `AC-060-3` plus `userID`) in a small helper under `src/view/readiness/` |
159
+ | Coverage summary (3) | `readiness.coverage.tree` (CoverageResult: `totals`, `requirements[].{id, coverageClass, acs[].{id, covered, testCases, unresolvedTestCases}}`, `unresolvedTestPointers[].{tsId, tcId, testPointer, reason}`) and `coverage.delta[]` one per requirement | Yes (686 KB of JSON on WESPA, already in memory per rewalk) | Per-family counts (requirements, stories, criteria, suites, cases) from `BuiltTreeModel` lengths; percentage covered from `totals`; a view-side fold only |
160
+ | Thin requirements (4) | D4 `stories:usFloors` failing US ids; `groupByTraceCoverage` buckets (missing stories, missing AC scope, missing component, missing tests) in `src/view/product-map.js`; coverage `acs[].covered` | Partly (checks yes, buckets computed for the Product Map on every rewalk) | Join US to REQ via `us.reqId`; one fold in a new `src/view/readiness/thin-reqs.js`; the Product Map bucket should take the resolution-gated class from coverage instead of counting structure (today 12 WESPA requirements read Complete there and unresolved in coverage) |
161
+ | Delta counts (5) | `readiness.delta` | Yes | Nothing |
162
+ | Verdict grid (6) | `stages[].checks[].{over, pass, total, ok}` | Yes | Group by stage and `over`; view-side |
163
+ | Trace walk REQ, US, AC, TS, TC, FBS (7, 8, 9) | `src/query/trace.js` computeTrace(tree, {id, direction, includeCode}) returning `nodes[].{id, kind, depth, title}` and `edges[].{from, to, kind}`; forward from an AC follows `tsByAcId`, `tcsByAcId`, `fbsByAcId`; FBS is a cross-link leaf; components are not in the walk (US `tacIds` is a view-side join via `usByTacId`) | No; the server keeps the walked tree in `state` but exposes no query route | A GET route per pivot in `src/server/routes.js` (`./trace.json?id=<id>&direction=forward|back|both`), computed from the current state and cached by `state.version`; the client renders the matrix. Full tree on WESPA: PRD-001 forward is 2,972 nodes, 6,337 edges, 1.5 MB as JSON, about half a second end to end from a cold CLI process including the tree walk, so per-pivot responses, not a whole-tree dump |
164
+ | Coverage walk, build-spec queue to criterion closure (3, 6) | D8 `freeze:acFbsOwnership` (every criterion owned by exactly one build spec), `model.fbsByAcId`, `computeQueue` in `src/build/queue.js`, `readiness.tree.{buildAt, fbsTotal}` | Partly (the check's pass/total and failing ids; the queue for the Build tab) | Per build spec: criteria owned, criteria with a resolving test, from `fbs.acIds` joined to coverage `acs[]`; view-side fold |
165
+ | Impact (10) | `src/query/impact.js` computeImpact(tree, {id}) returning `nodes[].{id, kind, role, actionNeeded}` | No | Route `./impact.json?id=<id>` alongside trace; same cache |
166
+ | Unresolved pointers (3) | `coverage.tree.unresolvedTestPointers[]` | Yes | Nothing; render with the reason text the CLI prints |
167
+ | Orphan interfaces, unsatisfiable tokens (2) | D6 failing ids of the form `TAC-x:interface` and `AC-x:token` | Yes | Composite-id split to link the owning TAC or AC |
168
+ | Acknowledged checks (grid) | `stages[].state` and `freezeRecord.gates[gate].reason` today; a sidecar if issue 303 lands | Yes for the state; reason from the freeze record | Read the reason from whichever seam lands; the grid follows `state` unchanged |
169
+
170
+ ## 6. Chart and table proposals with a sketch each
171
+
172
+ Every colour below is a `--sv-*` token from `src/view/style.css`, never a raw value: `--sv-success` and `--sv-successbg` for passed or covered, `--sv-warning` and `--sv-warningbg` for acknowledged or unresolved, `--sv-danger` and `--sv-dangerbg` for failing or uncovered, `--sv-muted` for not applicable, `--sv-link` for row actions, `--sv-border`, `--sv-surface`, `--sv-raised` and `--sv-canvas` for structure. No Mermaid on the page; charts are inline SVG or CSS bars. Sketch 1 shows the whole page with the grid; sketch 4 shows both tables; sketch 2 the coverage summary; sketch 3 the matrix.
173
+
174
+ ### 6.1 Readiness verdict grid (sketch 1)
175
+
176
+ What it shows: the eight stages as rows, the two scopes (tree, delta) as columns, each cell the pass/total of that stage's checks in that scope, coloured by the stage state, the failing count a link into the blocking table filtered to that stage. Data: `stages[].checks[]` grouped by `over`. Shape: a compact table above the fold, replacing the chip row and the stage detail.
177
+
178
+ ```
179
+ Stage Tree Delta State
180
+ D1 Brief 3/3 ok 18/18 ok passed
181
+ D2 Skeleton 4/4 ok 127/127 ok passed
182
+ ...
183
+ D6 Consist. 1194/1194 ok 241/243 2 fail passed? no -> failing [2 items]
184
+ D8 Freeze 6/7 1 fail - failing [1 item]
185
+ ```
186
+
187
+ ### 6.2 Questions table (sketch 4)
188
+
189
+ What it shows: one row per question: number, the plain question, where it lives (a link to the statement, profile field or requirement), what settles it (the hint), a copyable brief-your-agent handle (id plus one-line ask, no command string), and a state column (open; answered appears after the next rewalk). Data: `readiness.questions[]` and `questionGroups[]`; the group label becomes a sortable column, not a heading. Shape: sortable by number, stage, group. Q1 ruling: no CLI command is rendered on the page.
190
+
191
+ ```
192
+ # Question Where Settles it Brief for your agent (copy) State
193
+ 1 What is this project for? brief ledger a document or text brief-ledger: project purpose open
194
+ 2 Where will you look at what we produce? profile:surface pick one profile:surface: viewing surface open
195
+ ```
196
+
197
+ ### 6.3 Thin requirements table, including insufficient criteria (sketch 4)
198
+
199
+ - What it shows: one row per requirement that fails any of: no story, a story with zero criteria, a criterion with no resolving test, a story failing D4 floors, no plain description.
200
+ - Columns: requirement, stories, criteria, criteria with resolving tests, reason, Open link, Trace link.
201
+ - Data: `groupByTraceCoverage` buckets, coverage `requirements[].acs[]`, D4 and D2 failing ids.
202
+ - Shape: sortable, filterable by reason; the row count is the number the verdict card links to.
203
+
204
+ ```
205
+ REQ Stories Criteria Covered Reason Open Trace
206
+ REQ-015 2 10 0 10 criteria have no resolving test -> ->
207
+ REQ-028 1 5 3 pointer does not resolve: file-missing -> ->
208
+ ```
209
+
210
+ ### 6.4 Coverage summary (sketch 2)
211
+
212
+ What it shows: counts per node family (requirements, stories, criteria, suites, cases, build specs) as a row of stat tiles, a stacked bar per requirement class (covered, covered-unresolved, uncovered) with the percentage covered, and a table of unresolved pointers with reason. Data: `coverage.tree.totals`, `requirements[].coverageClass`, `unresolvedTestPointers[]`, family counts from the tree model. Shape: tiles plus one horizontal stacked bar (CSS widths from the three totals) plus a table; on WESPA the bar reads 15 covered, 12 unresolved, 28 uncovered of 55.
213
+
214
+ ```
215
+ [ 55 REQ ] [ 165 US ] [ 1121 AC ] [ 188 TS ] [ 1314 TC ] [ 128 FBS ]
216
+ Covered 27% |#########.......|.............| 15 covered 12 unresolved 28 uncovered
217
+ Unresolved pointers (94): TS-1003 TC-1003-ac-003-1-01 tests/rcf/api/US-003/... test-missing
218
+ ```
219
+
220
+ ### 6.5 Traceability matrix for a selected node (sketch 3), chosen over a sunburst
221
+
222
+ - What it shows: for a chosen pivot (a requirement by default), rows are the stories and their criteria, columns are suites and cases, build specs and components; a cell is filled when the chain reaches it and coloured by resolution (resolving test, pointer unresolved, none).
223
+ - Node picker: reuses the lookup modal; the pivot can be any id.
224
+ - Data: `./trace.json?id=<pivot>&direction=forward` plus `usByTacId` for the component column and coverage `acs[]` for the cell colour.
225
+ - Why a matrix and not a sunburst: a sunburst of WESPA's PRD is 2,972 arcs and unreadable past a few hundred leaves, and it cannot show "reached but unresolved" without a second encoding. A matrix scales by filtering the rows and reads left to right in the same order as the chain (requirement, story, criterion, test), which is the sell. A sunburst can be offered later as a decorative overview; it is not the working view.
226
+
227
+ ```
228
+ Pivot: REQ-001 Natural Language Query Processing [Trace] [Impact] [Open]
229
+ TS-1001 TC-...-01 TC-...-02 FBS-012 TAC-002
230
+ US-001 AC-001-1 x x(ok) . x x
231
+ AC-001-4 x . . x x <- no resolving test
232
+ US-002 AC-002-1 x x(unres) . x x <- pointer test-missing
233
+ ```
234
+
235
+ ### 6.6 Readiness state timeline (optional, named as such)
236
+
237
+ - What it would show: one point per rewalk (tree hash, time, intent-complete, ready-to-build, failing count per stage), so a team sees the tree move towards green over a week.
238
+ - Data: none persisted today; the server would have to append a line per rewalk under `.rcf/` (the per-clone runtime area the managed gitignore already covers, never `rcf/manifest.json`, per issue 236).
239
+ - Shape: a small line or step chart.
240
+ - Status: deferred; named here so the DEFINE scope can say no to it explicitly.
241
+
242
+ ## 7. Risks
243
+
244
+ Decisions received since the brief are listed first; mitigations and open-risks follow.
245
+
246
+ ### 7.1 Decisions received
247
+
248
+ - **Q1 ruled by Barry, 2026-10-07** (relay 9d3dd0a3-e7e5-4c6a-985e-c3962be38d4c, verbatim quote): "I meant provide the info they need to resolve with their agent. Keep the commands out of the view - the user isnt typing those commands - remember - the user operates their agent which uses these commands. Showing them here is little value." The viewer stays read-only, no CLI commands rendered on the page, no write route. Every row carries the item id, the plain-English ask or finding, the chain location (which REQ, US, AC, which gate), a link that opens the item in full context, and what resolved would look like. A per-row brief-your-agent handle (id plus plain-English ask, one line, copyable) is in scope as a design candidate for the wireframe at DEFINE; Barry rules on the render.
249
+ - **Q2 ruled** (delta on an unfrozen tree): counts only, on-demand expand.
250
+ - **Q3 ruled** (thin requirements table): list all three flavours (zero criteria, unresolved tests, D4 floor failures).
251
+ - **Q4 ruled** (freeze-now): no freeze button, no command text on the page; show the state and what resolves it in human terms.
252
+ - **Q5 ruled at DEFINE** (sub-tabs versus one long page): decided on the render; two HTML sketches (same data, both shapes) land in `guidance/discovery/` at DEFINE and Barry rules.
253
+ - **Admissibility note (F2)**: the proposed viewer trace, impact and coverage routes skip `runWithAdmissibilityGate` the same way `src/cli/coverage.js` and `src/cli/impact.js` do (issue 316 and 317 context), so the BUILD worker does not re-apply the gate and refuse on WESPA. The DEFINE TAC states the admissibility statement explicitly.
254
+ - **No Mermaid user-facing**: the per-requirement Mermaid trace slice (Section 2 row 12) is retired by the Trace-matrix FBS in Section 9. No new Mermaid lands on the viewer.
255
+ - **Full traceability scan (ex-Q6, F1 mitigation)**: on an unfrozen tree the scan is on-demand per pivot with the result cached client-side keyed by `state.version`; a whole-tree re-walk per rewalk would be 1.5 MB of JSON every 30 seconds on WESPA and is out of scope. Barry's framing already covers it ("full traceability scan on-demand").
256
+ - **For-engineers DocRow ruled (Dave 2026-10-07, Section 8 Q1)**: retire the wrapper. Section 3.5 described current state; the DEFINE build has no DocRow for engineers. Everything an engineer needs appears as rows in the tables (blocking check id, finding, location, link that opens the item, what resolves it). The Readiness-layout FBS in Section 9 lists `For-engineers retired` on its line already; this bullet is the ruling behind that.
257
+ - **Readiness timeline deferred (Dave 2026-10-07, Section 8 Q2)**: no snapshot log under `.rcf/` in this DEFINE cycle. Recorded as a later candidate in Section 9 (last row, `defer`); not in scope for this build. Reconsidered when the operator surface earns the persistence.
258
+
259
+ ### 7.2 Open risks
260
+
261
+
262
+
263
+ - Embed-mode layout: the page cannot assume the title block, footer or theme control exist (`html[data-embed="1"]` hides them), and tables wider than the WESPA iframe will scroll the page horizontally, which the shell forbids. Mitigation: tables use the existing `.rcf-filterbar` sticky pattern, collapse to stacked rows under 720px, and the sub-tab strip stays inside the panel.
264
+ - Dark-theme contrast: `test/view/contrast-aa.test.js` enumerates every colour pair in both themes and fails the suite below 4.5:1 for text and 3:1 for boundaries, so any new chart colour that is not an existing `--sv-*` token on a known surface breaks the build. Mitigation: no new tokens in the first pass; bars and cells use `--sv-success`, `--sv-warning`, `--sv-danger` on `--sv-surface`.
265
+ - SSE resync (the issue 297 class): every `tree-update` replaces the whole of `#rcf-live-content`, so an on-demand scan rendered inside it is wiped on the next swap, which arrives about every 30 seconds while the heartbeat writes `rcf/manifest.json`. Mitigation: cache the scan result client-side keyed by `state.version` (the Product Map partial cache is the precedent), re-render after `rcfPage.init()`, and mark the result stale rather than dropping it.
266
+ - Heartbeat dirtying the tree (issue 236): any future write route would race the 30-second manifest write and would commit stale liveness fields if an agent commits meanwhile. Mitigation: no write route in the first build; if one lands, it must go through the CLI verbs and never touch the manifest.
267
+ - Full-scan performance on a WESPA-sized tree: PRD-001 forward is 2,972 nodes and 6,337 edges (1.5 MB of JSON), and `rcf define readiness --json` is 687 KB. Mitigation: per-pivot routes only, responses cached by tree version, the matrix capped at one requirement per view with a row filter, no whole-tree trace in the DOM.
268
+ - Ack verb shape (issue 303): a check parked by a future readiness-level ack must show as acknowledged with its reason, not as failing, and the grid must not hard-wire `freezeRecord.gates`. Mitigation: the grid reads `stages[].state` and asks one helper for the reason; the helper is the only place that changes when the sidecar lands.
269
+ - Backwards compatibility with the readiness compute signature: `computeReadiness(tree, {freeze, ledgers, profile, profileText, testPointers, validateErrors, resolvedPaths, probeRunner})` is shared by the CLI, freeze and the viewer, and the tab must not add arguments to it. Mitigation: every new fold lives under `src/view/readiness/` or as a new pure module in `src/query/`, and `renderModelToPage` keeps its return additive (as the issue 307 fix did).
270
+ - Freeze-record staleness after resolve-in-place: a write changes document hashes, so a frozen tree would show "changed" rows and D1 `brief:sinceFreeze` would bite on the next rewalk. Mitigation: the page re-renders on the SSE update after the write and the delta counts say so; this is correct behaviour and the design states it rather than hiding it.
271
+ - Tooling on the consumer laptop: `pnpm exec rcf` from `packages/rcf-lite/` resolves the globally installed 0.31.0 binary, not the clone, while `pnpm rcf` and `node bin/rcf.js` resolve 0.32.3. Mitigation: the hand-back names `pnpm rcf`; the pack's `pnpm exec rcf` wording wants a note.
272
+ - Where this document lives: the guidance pack ships in the npm package (`files` in `package.json` includes `guidance`) and is served to every consumer's agent over MCP and `rcf guidance`, and its tests require a manifest entry for every pack file. Mitigation: this stage adds the manifest entry so the suite stays green; the hand-back asks whether a project discovery doc belongs in the product's guidance pack or under the package docs.
273
+
274
+ ## 8. Open questions for Barry
275
+
276
+ None open. Both remaining items (For-engineers DocRow; Readiness timeline) were ruled by Dave on 2026-10-07 and are recorded in Section 7.1. The deferred timeline is also captured in Section 9 as the final row.
277
+
278
+ ## 9. Proposed DEFINE scope split into FBS-sized pieces
279
+
280
+ Order is the build order. Chain ids are proposals; the DEFINE stage mints them. Test estimates count new `node:test` cases.
281
+
282
+ | FBS title | One-line goal | Capabilities (section 4) | `src/view/` and `src/query/` files | Chain nodes added or edited | Est. tests | Order |
283
+ |---|---|---|---|---|---|---|
284
+ | Readiness tables: questions and blocking items | Replace the question cards and the blocker cards with two linked tables, composite ids resolved, no row cap, no CLI command rendered on the page (Q1); first-paint audience preserves the `profile.register` ordering WESPA relies on (product owner first unless the profile says engineer; folded from ex-Q10 per WESPA precedent) | 1, 2, 16 | `view/readiness.js`, `view/readiness/po-layer.js`, `view/readiness/question-adapter.js`, new `view/readiness/tables.js`, `view/style.css` | US under REQ-180 settled from the discovery draft statement, with criteria; TAC-4133 interfaces; new ADR "tables over prose, composite ids split on the first colon" | +12 in `test/view/readiness-tab.test.js` and `readiness-po-layer.test.js` | 1 |
285
+ | Coverage summary and thin requirements | Fix the blank Coverage block and promote it to the coverage summary; widen the thin requirements feed; align the Product Map trace bucket with resolution-gated coverage | 3, 4, 14 | `view/readiness.js` (renderCoverage), new `view/readiness/coverage-summary.js`, new `view/readiness/thin-reqs.js`, `view/product-map.js`, `view/style.css` | New US under REQ-180; TAC-4127 edit; new ADR "the viewer counts coverage the way the CLI does" | +10 in `test/view/readiness-tab.test.js` and `product-map.test.js` | 2 |
286
+ | Readiness layout: verdict grid, sub-tabs, delta counts, For-engineers retired | Grid above the fold, sub-tab strip with `sub=` in the hash, delta as counts, DocRow removed | 5, 6, 11, 14 | `view/readiness.js`, `view/components/sub-tab-strip.js` (use), `view/page-init.js` (readiness `sub=`), `view/style.css` | US under REQ-180; TAC-4133 edit; ADR-4127 edit (register orders, never hides) | +10 in `tabs.test.js`, `layout-regression.test.js`, `readiness-tab.test.js` | 3 |
287
+ | Query routes: trace, impact, coverage as JSON | Three GET routes computed from the current state and cached by version, relative paths, release-noted for proxy allow-lists | 7 (server half) | `src/server/routes.js`, `src/view/index.js` (state), `src/query/` unchanged | New US under REQ-002 (Visual review surface); new TAC "view query routes" or TAC-003 edit; new ADR "query routes are GET, per pivot, cached by version" | +8 in `test/view/cli.test.js` and `integration.test.js` | 4 |
288
+ | Trace matrix, trace actions, lookup integration (retires the per-requirement Mermaid slice) | Matrix sub-view for a selected node, a Trace action on every id, lookup hits land on readiness rows; the per-requirement Mermaid trace slice (Section 2 row 12) is removed in the same change | 8, 9, 12 | `view/page-init.js`, new `view/readiness/trace-matrix.js`, `view/components/entity-selector.js`, `view/style.css`, remove the per-REQ Mermaid render path in `view/requirements.js` | New US under REQ-180 or REQ-002; TAC-4132 edit; new ADR "matrix, not sunburst; no Mermaid user-facing" | +10 in `readiness-tab.test.js`, `id-lookup.test.js`, `wespa-host-fixture.test.js` | 5 |
289
+ | Impact view and keyboard shortcuts | Impact sub-view with action labels; row navigation keys | 10, 13 | `view/page-init.js`, new `view/readiness/impact-view.js` | New US; ADR as needed | +6 | defer |
290
+ | Readiness timeline | Snapshot per rewalk under `.rcf/`, small chart | 17 | `src/server/`, `view/readiness/` | New US; ADR on persistence | +6 | defer (Dave 2026-10-07, ex-Q2) |
291
+
292
+ Chain-first note for this PR: the discovery artefacts that fit the chain today are brief-ledger statements under `rcf/define/` (none exist on this tree yet; the ledger is created by the CLI on first add). A `[draft]` story was rehearsed and rejected for this PR: `rcf define create us` always writes one placeholder criterion (the schema requires at least one), and CI runs `rcf audit coverage` in strict mode, so a draft story with no resolving test turns REQ-180 uncovered and fails the build. The stories above are minted at DEFINE with their criteria and suites in the same change.
293
+
294
+ ### 9.1 Query modules named in the brief but not in the DEFINE plan (F8)
295
+
296
+ - **gates**: folded. The admissibility gate stays as it is in the CLI (`src/cli/coverage.js`, `src/cli/impact.js`); the viewer routes bypass it the same way (Section 7.1 F2). No gates-specific FBS.
297
+ - **attestation**: deferred. Attestation is a separate build-cycle lane and does not touch the Readiness tab or the three viewer query routes; no FBS in this window.
298
+ - **eval-coverage**: deferred. Readiness coverage counts (Section 6.4) and the trace matrix (Section 6.5) cover the operator's and engineer's needs here; eval-coverage is a separate programme and does not land as its own FBS in this scope.
299
+ - **delta**: folded. On an unfrozen tree the delta is counts only with on-demand expand (Section 7.1 Q2), rendered as part of the Readiness layout FBS (order 3), not its own FBS.
300
+ - **questions**: folded. The computeQuestions path already exists (`src/query/questions.js`); the Readiness tables FBS (order 1) renders it as a table with the brief-your-agent handle. No new query module for questions.
301
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rcf-lite",
3
- "version": "0.32.5",
3
+ "version": "0.33.0",
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-10-07T13:46:28.982Z",
3
+ "updatedAt": "2026-10-08T15:30:03.762Z",
4
+ "context": "Discovery (guidance/discovery/discovery-viewer-command-page.md, sections 3 and 7) measured the Readiness tab on two real trees: 22,184 words and one table on rcf-lite's own chain, 1,417 links of which 206 are dead, a 1,055-row delta list on first paint, blocker cards capped at 20 ids, a Coverage block that prints blanks, and a Freeze now button with no handler on a GET-only server. The product owner's rule for the page is lists, tables, counts and links, and the page is to serve as the command page. The product owner ruled Q1 on 2026-10-07: the viewer stays read-only and shows no CLI commands; the user operates an agent which runs the commands, so a command string on the page is of little value; each row carries what a human needs to brief their agent. Q4 ruled the same way for the freeze control.",
5
+ "decision": "The Readiness tab is rebuilt as linked tables over the readiness object the viewer already has, and the viewer stays read-only with no command text anywhere on the page. Every question and every failing item is a table row carrying the item id, the plain-English ask or finding, the chain location, a link that opens the item in full context, and what resolved looks like; the questions table adds a one-line copy handle holding the id and the ask (no command). Composite finding ids split on the first colon so every link resolves to a real document. There is no freeze button and no write route; the freeze block shows the state and what resolves it in words. The delta on an unfrozen tree is counts only with an on-demand list. Counts on the verdict cards are links into the tables they count.",
6
+ "consequences": "The For-engineers DocRow, the question cards, the blocker cards, the first-paint delta list and the Freeze now control are removed; their content survives in named tables and blocks, so AC-18002-3 (engineer blockers never removed from the DOM) still holds. readiness.questions[].writeBack[].command stays on the object for the CLI and the agent and is never rendered. A row that names an item an agent must act on is briefed through the copy handle, not through a command. The render stays pure and server-side; the CLI and the tab cannot disagree on the same tree hash.",
7
+ "alternativesConsidered": [
8
+ {
9
+ "name": "Show the exact CLI command per row",
10
+ "summary": "Render readiness.questions[].writeBack[].command beside each question and a re-check command beside each blocker.",
11
+ "reasonNotChosen": "Ruled out by the product owner on 2026-10-07 (Q1): the operator does not type those commands, the agent does; showing them on the page is little value and invites a human to paste commands by hand."
12
+ },
13
+ {
14
+ "name": "A POST write route that resolves in place",
15
+ "summary": "Add a write route that runs the CLI verb for a row and re-renders.",
16
+ "reasonNotChosen": "The server is GET and HEAD only by design; a write route races the heartbeat's manifest write (issue 236) and would be gated by the host's auth inside an embedding shell. Ruled out with Q1."
17
+ },
18
+ {
19
+ "name": "Keep the cards and add links",
20
+ "summary": "Keep the question cards and blocker cards, add a link per card.",
21
+ "reasonNotChosen": "Cards do not sort, filter or count; the 20-id cap and the per-card prose are the shape the product owner called scrappy. Tables give counts for free."
22
+ }
23
+ ],
24
+ "adrId": "ADR-4139-readiness-tables-read-only-no-commands",
25
+ "prdId": "PRD-001",
26
+ "tadId": "TAD-001",
27
+ "version": "0.1.0",
28
+ "status": "accepted",
29
+ "title": "The Readiness tab is linked tables over the readiness object; the viewer stays read-only and renders no CLI command text"
30
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "createdAt": "2026-10-07T13:46:29.804Z",
3
+ "updatedAt": "2026-10-08T15:30:04.233Z",
4
+ "context": "The CLI already computes trace, impact and coverage from pure functions in src/query/ (computeTrace, computeImpact, computeCoverage) that the readiness compute and the CLI verbs share. The viewer exposes none of them: the only user-facing trace is a per-requirement Mermaid slice that omits suites, cases and components, and the Product Map counts structure, not resolution. The product owner asked for key query tools exposed in the viewer and a full traceability scan on demand with a chart and a table. On the largest consumer tree a whole-tree forward trace from the PRD is 2,972 nodes, 6,337 edges and 1.5 MB of JSON, and the viewer re-walks about every 30 seconds. The CLI coverage, impact and trace verbs do not call runWithAdmissibilityGate; readiness and freeze do (issues 316 and 317).",
5
+ "decision": "Three GET JSON routes on the viewer server (./trace.json, ./impact.json, ./coverage.json) serve the pure query results per pivot, computed from the current walked state and memoised by state.version; there is no whole-tree dump route. The routes bypass runWithAdmissibilityGate exactly as the CLI coverage, impact and trace verbs do, so the viewer answers on a tree that fails admissibility. The user-facing traceability view is a matrix (rows: stories and criteria under the pivot; columns: suites, cases, build specs, components; cells coloured by resolution), not a sunburst and not Mermaid; the per-requirement Mermaid slice is retired in the same change. The client caches per version in memory and re-renders after an SSE swap rather than dropping the result.",
6
+ "consequences": "Three new release-noted routes that embedding hosts must allow-list, as ./index.json and ./page-init.js were. No new query module and no change to computeReadiness. A sunburst can be added later as a decorative overview; it is not the working view. A tree that fails admissibility shows trace, impact and coverage in the viewer while readiness still reports the refusal, which matches the CLI today. Removing the Mermaid slice drops the last user-facing Mermaid from the Requirements tab; the vendored runtime stays for the diagram tab until a separate decision retires it.",
7
+ "alternativesConsidered": [
8
+ {
9
+ "name": "One whole-tree trace route, rendered as a sunburst",
10
+ "summary": "Serve the full forward trace from the PRD once per rewalk and draw a sunburst.",
11
+ "reasonNotChosen": "1.5 MB of JSON every 30 seconds on the largest consumer tree, 2,972 arcs unreadable past a few hundred leaves, and no way to show reached-but-unresolved without a second encoding."
12
+ },
13
+ {
14
+ "name": "Apply the admissibility gate on the viewer routes",
15
+ "summary": "Call runWithAdmissibilityGate before answering trace, impact and coverage.",
16
+ "reasonNotChosen": "The CLI verbs for the same computes do not gate (issues 316 and 317); gating only the viewer would make the viewer and the terminal disagree on the same tree and refuse the largest consumer tree outright."
17
+ },
18
+ {
19
+ "name": "Keep the per-requirement Mermaid slice and add the matrix beside it",
20
+ "summary": "Leave the Requirements tab slice in place.",
21
+ "reasonNotChosen": "The slice omits suites, cases and components and has no node picker; two traceability views that disagree on reach would confuse the sell. No Mermaid user-facing is a stated ruling."
22
+ }
23
+ ],
24
+ "adrId": "ADR-4140-view-query-routes-per-pivot-matrix-no-mermaid",
25
+ "prdId": "PRD-001",
26
+ "tadId": "TAD-001",
27
+ "version": "0.1.0",
28
+ "status": "accepted",
29
+ "title": "Trace, impact and coverage are GET routes per pivot cached by version that bypass the admissibility gate as the CLI verbs do; the traceability view is a matrix, not a sunburst, and no Mermaid is user-facing"
30
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "createdAt": "2026-10-08T15:39:24.012Z",
3
+ "updatedAt": "2026-10-08T15:39:24.012Z",
4
+ "context": "Issue 330 (2026-10-08). `rcf build finalise` invokes `rcf verify run`, which in the default mode `agentScreenshotCritique` spawns the agent CLI (`claude --print ...`) as a subagent with Playwright MCP. An estate rule forbids a dispatched worker from spawning an agent CLI from a tool call; the surrounding harness terminates any such child, so the current CLI is unusable from a worker. The AI on Record project ran straight into this on FBS-004 through FBS-011: every ship gate was recorded blocked because the worker could not run the finalise gate. The report path was unhelpful too: a verify run that exited before writing a report left finalise reading ENOENT with no mode or reason named.",
5
+ "decision": "Add an explicit `--mode <mode>` flag to `rcf verify run` and `rcf build finalise`, with `agentScreenshotCritique` as the default (the current, model-driven, parent-session path) and `deterministic` as the sister (model-free, in-process, worker-safe). Deterministic mode records diagnostic HTTP reachability on runStats.httpProbe and records every testable AC on blockedAcs[] with reason `critique-only`, so the aggregate verdict is BLOCKED by construction and the finalise gate refuses to promote. The report schema is unchanged; a new runStats.mode + runStats.counts stamp names what the mode did. The finalise gate also writes a `preseed` partial report before launching verify and overwrites it with a `status: aborted` record if verify exits non-zero without replacing it, so finalise never surfaces ENOENT. The ADR-4112 verified invariant is reinforced by a specific finalise refusal line: a deterministic-mode run that skipped any AC refuses promotion with the exact counts (verified/total/skipped) and names the invariant.",
6
+ "consequences": "A dispatched worker can run the finalise gate end-to-end: BLOCKED on today's chain is the HONEST outcome (no AC carries a probe shape yet), and the operator sees the counts rather than ENOENT. Future work can wire chain-level per-AC probe shapes (status, path, header, body substring, selector) and the deterministic mode will consume them without any further CLI change. Agent-mode runs from a parent session are unchanged. No release in this PR; the fix rides the next minor.",
7
+ "alternativesConsidered": [
8
+ {
9
+ "name": "Expose a lower-level `rcf verify browser` for the worker path",
10
+ "summary": "Repurpose the pre-flight browser-verify runner as the worker-safe verify.",
11
+ "reasonNotChosen": "That runner writes a browserVerification record on the manifest and is a REVIEW-time pre-flight, not a ship-gate verifier. Reusing it for finalise would conflate two chain roles and break the §9 independence contract (verify's report is the only input finalise reads)."
12
+ },
13
+ {
14
+ "name": "Keep agentScreenshotCritique as the only mode and run `claude` from inside a tool call",
15
+ "summary": "Document the harness rule and let the operator add an escape hatch.",
16
+ "reasonNotChosen": "The harness rule is a hard estate boundary (nested agent sessions boot a full PA session in the worker's cwd) and will not be relaxed. The gate must have a path that works without spawning an agent CLI."
17
+ }
18
+ ],
19
+ "adrId": "ADR-4141-verify-modes",
20
+ "prdId": "PRD-001",
21
+ "tadId": "TAD-001",
22
+ "version": "0.1.0",
23
+ "status": "proposed",
24
+ "title": "verify modes: parent-session agentScreenshotCritique, worker-safe deterministic"
25
+ }
@@ -5,11 +5,12 @@
5
5
  "description": "Baz 2026-10-04 (w-2026-10-04-dave-001): the FIND SearchButton hover previously fell back to a hard-coded light grey (`var(--sv-surface-hover, #f0f3f8)`) because `--sv-surface-hover` was never declared, which painted dark ink on a light grey in dark mode. The hover + focus-visible + active states now use `--sv-active` with the `--sv-control` border and the `--sv-focus` outline so the pair stays above WCAG AA in both themes (AC-205-9). The three-state theme control (`.rcf-theme`, `.rcf-theme-btn`) sits right of the SearchButton in `.tools`; `aria-pressed=\"true\"` uses `--sv-button` / `--sv-onbutton`, hover and focus-visible use `--sv-active`, and the group is hidden under `html[data-embed=\"1\"]`.",
6
6
  "implementsAcIds": [
7
7
  "AC-202-4",
8
- "AC-205-9"
8
+ "AC-205-9",
9
+ "AC-18005-7"
9
10
  ],
10
11
  "dependencies": [],
11
12
  "version": "0.1.0",
12
13
  "status": "draft",
13
14
  "createdAt": "2026-10-05T09:59:33Z",
14
- "updatedAt": "2026-10-06T10:24:19.675Z"
15
+ "updatedAt": "2026-10-09T12:35:00.856Z"
15
16
  }
@@ -0,0 +1,19 @@
1
+ {
2
+ "cnId": "CN-779",
3
+ "path": "src/view/readiness/tables.js",
4
+ "title": "Readiness command page tables (TAC-4135)",
5
+ "description": "Pure HTML-string renderers (renderQuestionsTable, renderBlockingTable, buildQuestionRows, buildBlockingRows, entityDomId). The questions table has one row per question with number, group as a sortable column, ask, chain location, settles, state, and a Copy for your agent control (AC-18003-1, -2, -5, -8). The blocking table carries every failing item across every failing check across every stage, uncapped, with a FilterBar by stage and persona and composite id splitting via splitCompositeId (AC-18003-3).",
6
+ "implementsAcIds": [
7
+ "AC-18003-1",
8
+ "AC-18003-2",
9
+ "AC-18003-3",
10
+ "AC-18003-5",
11
+ "AC-18003-7",
12
+ "AC-18003-8"
13
+ ],
14
+ "dependencies": [],
15
+ "version": "0.1.0",
16
+ "status": "draft",
17
+ "createdAt": "2026-10-08T16:10:50.697Z",
18
+ "updatedAt": "2026-10-09T11:54:25.548Z"
19
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "cnId": "CN-780",
3
+ "path": "src/view/readiness/composite-id.js",
4
+ "title": "splitCompositeId: first-colon split for composite failing ids",
5
+ "description": "Pure splitter. Given a composite failing id (AC-060-3:userID or TAC-4122-define-gates:checkPersona) returns {docId, fragment} splitting on the FIRST colon so the fragment can keep its own colons. Null and empty inputs are defensive (both fields empty).",
6
+ "implementsAcIds": [
7
+ "AC-18003-4"
8
+ ],
9
+ "dependencies": [],
10
+ "version": "0.1.0",
11
+ "status": "draft",
12
+ "createdAt": "2026-10-08T16:10:51.180Z",
13
+ "updatedAt": "2026-10-08T16:10:51.180Z"
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "cnId": "CN-781",
3
+ "path": "src/view/readiness/po-layer.js",
4
+ "title": "renderReadinessPO composes the register-ordered tables",
5
+ "description": "Composes the two command-page tables into the readiness panel. The register controls paint order (engineer: blocking first; productOwner or null: questions first), both tables carry every blocker id (AC-18003-8, AC-18002-3). Fold readiness.questionOptional into the questions-table synthetic rows via the question-adapter so open decisions reach a row even when readiness.questions is empty.",
6
+ "implementsAcIds": [
7
+ "AC-18003-8"
8
+ ],
9
+ "dependencies": [],
10
+ "version": "0.1.0",
11
+ "status": "draft",
12
+ "createdAt": "2026-10-08T16:10:51.652Z",
13
+ "updatedAt": "2026-10-08T16:10:51.652Z"
14
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "cnId": "CN-782",
3
+ "path": "src/view/readiness.js",
4
+ "title": "Readiness panel: renderFreezeNow retired, next-action command text removed",
5
+ "description": "renderFreezeNow is replaced by renderFreezeState (plain words naming the failing gates from levels.readyToBuild.blockedBy when readyToBuild.ok is false; names what a freeze would record when ok). renderNextActionLine drops the Run <code>command</code> phrase. renderBlockersByPersona and its helpers (renderPersonaGroup, renderBlockerCard, buildIdWhyMap) are retired - the blocking table owns the blocker surface, the engineer DocRow body still carries renderStageDetail with per-check findings-lists so no id leaves the DOM. The null-readiness branch renders only the could-not-be-computed placeholder (AC-18003-6). The composed panel contains no shell command text or Freeze now control (AC-18003-7).",
6
+ "implementsAcIds": [
7
+ "AC-18003-6",
8
+ "AC-18003-7"
9
+ ],
10
+ "dependencies": [],
11
+ "version": "0.1.0",
12
+ "status": "draft",
13
+ "createdAt": "2026-10-08T16:10:52.125Z",
14
+ "updatedAt": "2026-10-08T16:10:52.125Z"
15
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "cnId": "CN-783",
3
+ "path": "src/view/page-init.js",
4
+ "title": "Readiness copy handles, blocking FilterBar wire, entity resolver",
5
+ "description": "wireReadinessCopyHandles copies data-rcf-copy-text through the shared toast (navigator.clipboard with a textarea fallback) - no command text in copy strings (AC-18003-5). wireReadinessBlockingFilterBar applies the stage/persona hiddens on data-rcf-stage / data-rcf-persona rows (AC-18003-3 filter). findByReadinessEntity resolves #tab=readiness&...&entity=<itemId> onto a non-document row carrying data-rcf-entity (AC-18003-2).",
6
+ "implementsAcIds": [
7
+ "AC-18003-1",
8
+ "AC-18003-2",
9
+ "AC-18003-3",
10
+ "AC-18003-5"
11
+ ],
12
+ "dependencies": [],
13
+ "version": "0.1.0",
14
+ "status": "draft",
15
+ "createdAt": "2026-10-08T16:10:52.604Z",
16
+ "updatedAt": "2026-10-09T11:54:26.101Z"
17
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "cnId": "CN-784",
3
+ "path": "src/server/routes.js#handleQueryRoute",
4
+ "title": "Query route handler for trace, impact and coverage (TAC-4136)",
5
+ "description": "handleQueryRoute(req, res, deps, url, route) is the shared handler for GET /trace.json, /impact.json and /coverage.json. Returns 503 before the first walk (AC-208-7), 404 QueryRouteError for an unknown id (AC-208-4), 400 for a bad-direction or bad-scope (AC-208-4), 200 application/json with cache-control: no-store on success (AC-208-1, -2, -3). The handler never imports runWithAdmissibilityGate and never writes under rcf/ (AC-208-5). Serves memoised entries from deps.queryCache keyed by state.version + route + pivot + direction|scope (AC-208-6). Co-sited with createRouter in src/server/routes.js.",
6
+ "implementsAcIds": [
7
+ "AC-208-1",
8
+ "AC-208-2",
9
+ "AC-208-3",
10
+ "AC-208-4",
11
+ "AC-208-5",
12
+ "AC-208-6",
13
+ "AC-208-7"
14
+ ],
15
+ "dependencies": [],
16
+ "version": "0.1.0",
17
+ "status": "draft",
18
+ "createdAt": "2026-10-08T18:39:01.755Z",
19
+ "updatedAt": "2026-10-08T18:39:01.755Z"
20
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "cnId": "CN-785",
3
+ "path": "src/server/index.js#queryCache",
4
+ "title": "Per-version queryCache lifecycle (TAC-4136)",
5
+ "description": "The server closure holds queryCache as a Map keyed by state.version + route + pivot + direction|scope and replaces it wholesale on every rewalk right after state is updated, so a new state.version never serves a previous version's bytes (AC-208-6). The cache is exposed to createRouter through deps.queryCache.{get, set, has, size}; writes stay inside the closure, never persisted and never written under rcf/.",
6
+ "implementsAcIds": [
7
+ "AC-208-6"
8
+ ],
9
+ "dependencies": [],
10
+ "version": "0.1.0",
11
+ "status": "draft",
12
+ "createdAt": "2026-10-08T18:39:02.274Z",
13
+ "updatedAt": "2026-10-08T18:39:02.274Z"
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "cnId": "CN-786",
3
+ "path": "src/view/index.js#renderModelToPage",
4
+ "title": "testPointers kept on the view state (TAC-4136)",
5
+ "description": "renderModelToPage additively returns the resolved testPointers map alongside the walked tree so the server state carries it and GET /coverage.json composes computeCoverage(tree, { strict: true, scopeId, testPointers: state.testPointers }) (AC-208-3). The CLI resolves pointers the same way via resolveTestPointers; the viewer and CLI share one map per walk.",
6
+ "implementsAcIds": [
7
+ "AC-208-3"
8
+ ],
9
+ "dependencies": [],
10
+ "version": "0.1.0",
11
+ "status": "draft",
12
+ "createdAt": "2026-10-08T18:39:07.285Z",
13
+ "updatedAt": "2026-10-08T18:39:07.285Z"
14
+ }