rcf-lite 0.28.4 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/CHANGELOG.md +107 -1
  2. package/bin/rcf.js +4 -0
  3. package/fixtures/canary-manifest.json +9 -9
  4. package/guidance/define-draft-shapes.md +89 -0
  5. package/guidance/define-intake.md +97 -0
  6. package/guidance/define-intent.md +107 -0
  7. package/guidance/discover-prototype.md +67 -0
  8. package/guidance/discovery-prototypes.md +69 -0
  9. package/guidance/harness-template.md +34 -0
  10. package/guidance/managed/agent-instructions-block.hash +1 -1
  11. package/guidance/managed/agent-instructions-block.md +34 -0
  12. package/guidance/manifest.json +9 -2
  13. package/package.json +1 -1
  14. package/rcf/adrs/adr-4124-define-freeze-verb.json +30 -0
  15. package/rcf/adrs/adr-4126-persona-tagged-blockers-two-levels.json +40 -0
  16. package/rcf/adrs/adr-4127-readiness-tab-order-and-register.json +40 -0
  17. package/rcf/adrs/adr-4129-questions-harness-driven-loop.json +25 -0
  18. package/rcf/adrs/adr-4130-intake-harness-split-tool-scanned.json +20 -0
  19. package/rcf/adrs/adr-4132-prototypes-in-discovery-as-evidence.json +20 -0
  20. package/rcf/adrs/adr-4133-dag-dependency-depth-layout.json +40 -0
  21. package/rcf/adrs/adr-4134-index-json-route.json +35 -0
  22. package/rcf/adrs/adr-4135-readiness-po-register-and-adapter.json +35 -0
  23. package/rcf/adrs/adr-4136-embedding-host-fixture.json +35 -0
  24. package/rcf/code-nodes/cn-725.json +18 -2
  25. package/rcf/code-nodes/cn-726.json +10 -2
  26. package/rcf/code-nodes/cn-727.json +13 -4
  27. package/rcf/code-nodes/cn-728.json +18 -2
  28. package/rcf/code-nodes/cn-729.json +10 -2
  29. package/rcf/code-nodes/cn-730.json +13 -3
  30. package/rcf/code-nodes/cn-731.json +20 -0
  31. package/rcf/code-nodes/cn-732.json +14 -0
  32. package/rcf/code-nodes/cn-733.json +14 -0
  33. package/rcf/code-nodes/cn-734.json +14 -0
  34. package/rcf/code-nodes/cn-735.json +20 -0
  35. package/rcf/code-nodes/cn-736.json +14 -0
  36. package/rcf/code-nodes/cn-737.json +14 -0
  37. package/rcf/code-nodes/cn-738.json +14 -0
  38. package/rcf/code-nodes/cn-739.json +18 -0
  39. package/rcf/code-nodes/cn-740.json +19 -0
  40. package/rcf/code-nodes/cn-741.json +16 -0
  41. package/rcf/code-nodes/cn-742.json +16 -0
  42. package/rcf/code-nodes/cn-743.json +15 -0
  43. package/rcf/code-nodes/cn-744.json +22 -0
  44. package/rcf/code-nodes/cn-745.json +16 -0
  45. package/rcf/code-nodes/cn-746.json +14 -0
  46. package/rcf/code-nodes/cn-747.json +18 -0
  47. package/rcf/code-nodes/cn-748.json +16 -0
  48. package/rcf/code-nodes/cn-749.json +16 -0
  49. package/rcf/code-nodes/cn-750.json +15 -0
  50. package/rcf/code-nodes/cn-751.json +18 -0
  51. package/rcf/code-nodes/cn-752.json +14 -0
  52. package/rcf/fbs/fbs-196.json +13 -5
  53. package/rcf/fbs/fbs-198.json +42 -0
  54. package/rcf/fbs/fbs-199.json +59 -0
  55. package/rcf/fbs/fbs-200.json +46 -0
  56. package/rcf/fbs/fbs-201.json +49 -0
  57. package/rcf/fbs/fbs-202.json +110 -0
  58. package/rcf/fbs/fbs-203.json +100 -0
  59. package/rcf/requirements/req-002.json +2 -2
  60. package/rcf/requirements/req-173.json +2 -2
  61. package/rcf/requirements/req-174.json +10 -4
  62. package/rcf/requirements/req-175.json +10 -4
  63. package/rcf/requirements/req-176.json +25 -0
  64. package/rcf/requirements/req-180.json +31 -0
  65. package/rcf/requirements/req-181.json +31 -0
  66. package/rcf/requirements/req-182.json +25 -0
  67. package/rcf/requirements/req-186.json +25 -0
  68. package/rcf/requirements/req-187.json +31 -0
  69. package/rcf/requirements/req-188.json +25 -0
  70. package/rcf/tacs/tac-4121-define-ledgers.json +15 -3
  71. package/rcf/tacs/tac-4122-define-gates.json +40 -4
  72. package/rcf/tacs/tac-4123-define-readiness.json +39 -3
  73. package/rcf/tacs/tac-4124-define-freeze.json +28 -0
  74. package/rcf/tacs/tac-4127-readiness-view.json +49 -0
  75. package/rcf/tacs/tac-4128-define-guidance.json +53 -0
  76. package/rcf/tacs/tac-4129-define-questions.json +45 -0
  77. package/rcf/tacs/tac-4130-define-intake-brief.json +35 -0
  78. package/rcf/tacs/tac-4131-build-dag-view.json +39 -0
  79. package/rcf/tacs/tac-4132-id-lookup.json +42 -0
  80. package/rcf/tacs/tac-4133-readiness-po-layer.json +37 -0
  81. package/rcf/tacs/tac-4134-wespa-host-fixture.json +54 -0
  82. package/rcf/test-suites/ts-016.json +3 -3
  83. package/rcf/test-suites/ts-215.json +148 -4
  84. package/rcf/test-suites/ts-216.json +19 -3
  85. package/rcf/test-suites/ts-218.json +78 -0
  86. package/rcf/test-suites/ts-219.json +70 -0
  87. package/rcf/test-suites/ts-220.json +78 -0
  88. package/rcf/test-suites/ts-221.json +78 -0
  89. package/rcf/test-suites/ts-222.json +100 -0
  90. package/rcf/test-suites/ts-223.json +94 -0
  91. package/rcf/test-suites/ts-224.json +70 -0
  92. package/rcf/test-suites/ts-225.json +78 -0
  93. package/rcf/test-suites/ts-226.json +158 -0
  94. package/rcf/test-suites/ts-227.json +38 -0
  95. package/rcf/test-suites/ts-228.json +86 -0
  96. package/rcf/test-suites/ts-229.json +83 -0
  97. package/rcf/user-stories/us-17302.json +50 -0
  98. package/rcf/user-stories/us-17401.json +14 -2
  99. package/rcf/user-stories/us-17402.json +55 -0
  100. package/rcf/user-stories/us-17403.json +60 -0
  101. package/rcf/user-stories/us-17502.json +60 -0
  102. package/rcf/user-stories/us-17503.json +65 -0
  103. package/rcf/user-stories/us-17601.json +58 -0
  104. package/rcf/user-stories/us-18001.json +55 -0
  105. package/rcf/user-stories/us-18002.json +35 -0
  106. package/rcf/user-stories/us-18101.json +35 -0
  107. package/rcf/user-stories/us-18202.json +40 -0
  108. package/rcf/user-stories/us-18601.json +55 -0
  109. package/rcf/user-stories/us-18602.json +50 -0
  110. package/rcf/user-stories/us-18603.json +45 -0
  111. package/rcf/user-stories/us-18701.json +50 -0
  112. package/rcf/user-stories/us-18702.json +50 -0
  113. package/rcf/user-stories/us-18801.json +45 -0
  114. package/rcf/user-stories/us-204.json +59 -0
  115. package/rcf/user-stories/us-205.json +64 -0
  116. package/rcf/user-stories/us-206.json +54 -0
  117. package/releases/releases.yaml +11 -1
  118. package/src/cli/freeze.js +730 -0
  119. package/src/cli/help.js +12 -0
  120. package/src/cli/ledger.js +228 -12
  121. package/src/cli/questions.js +288 -0
  122. package/src/cli/readiness.js +267 -49
  123. package/src/cli/view.js +29 -0
  124. package/src/define/freeze-record.js +30 -15
  125. package/src/define/ledgers.js +210 -11
  126. package/src/intake/index.js +1 -1
  127. package/src/intake/orchestrator.js +117 -0
  128. package/src/mcp/tools.js +354 -0
  129. package/src/query/delta.js +6 -0
  130. package/src/query/gates.js +314 -39
  131. package/src/query/index.js +16 -2
  132. package/src/query/questions.js +438 -0
  133. package/src/query/readiness.js +248 -7
  134. package/src/server/index.js +24 -2
  135. package/src/server/routes.js +68 -1
  136. package/src/setup/identity-seed.js +17 -0
  137. package/src/view/build-dag.js +563 -0
  138. package/src/view/components/badge.js +41 -0
  139. package/src/view/components/diff.js +72 -0
  140. package/src/view/components/doc-row.js +62 -0
  141. package/src/view/components/empty-state.js +35 -0
  142. package/src/view/components/entity-selector.js +137 -0
  143. package/src/view/components/filter-bar.js +97 -0
  144. package/src/view/components/findings-list.js +49 -0
  145. package/src/view/components/index.js +12 -0
  146. package/src/view/components/pill.js +46 -0
  147. package/src/view/components/sub-tab-strip.js +43 -0
  148. package/src/view/components/toast.js +25 -0
  149. package/src/view/components-fixture.js +265 -0
  150. package/src/view/doc-renderers/architecture.js +304 -0
  151. package/src/view/doc-renderers/build.js +160 -0
  152. package/src/view/doc-renderers/index.js +2 -0
  153. package/src/view/doc-renderers/prd.js +35 -6
  154. package/src/view/html-page.js +700 -602
  155. package/src/view/index.js +96 -3
  156. package/src/view/live-client.js +39 -6
  157. package/src/view/needs-work.js +79 -0
  158. package/src/view/page-init.js +2193 -0
  159. package/src/view/readiness/phrasebook.js +115 -0
  160. package/src/view/readiness/po-layer.js +288 -0
  161. package/src/view/readiness/question-adapter.js +329 -0
  162. package/src/view/readiness/stage-legend.js +71 -0
  163. package/src/view/readiness.js +505 -0
  164. package/src/view/style.css +2017 -382
  165. package/src/view/test-host/embed-client.js +121 -0
  166. package/src/view/test-host/fixture.js +183 -0
  167. package/src/view/test-host/lints.js +220 -0
  168. package/src/view/view-index.js +323 -0
@@ -0,0 +1,31 @@
1
+ {
2
+ "createdAt": "2026-10-02T16:40:00Z",
3
+ "updatedAt": "2026-10-02T16:40:00Z",
4
+ "reqId": "REQ-187",
5
+ "prdId": "PRD-001",
6
+ "title": "document intake into brief statements",
7
+ "description": "A submitted document reaches the brief ledger as kinded statements with source spans through `brief add --from`, the intake scans run over the new statements against the existing ones, and every finding becomes an open question the loop asks.",
8
+ "category": "functional",
9
+ "domain": "define",
10
+ "priority": "must",
11
+ "version": "0.1.0",
12
+ "status": "draft",
13
+ "shapeClassification": {
14
+ "shapes": [
15
+ "persistence"
16
+ ],
17
+ "reason": "keyword-scan",
18
+ "classifiedAt": "2026-10-02T16:40:00Z",
19
+ "signals": [
20
+ {
21
+ "source": "description",
22
+ "shape": "persistence",
23
+ "match": "ledger"
24
+ }
25
+ ]
26
+ },
27
+ "rationale": "Spec 2026-10-01 (DEFINE step 3) section 2.1 splits intake into two halves. The harness writes a statement file beside each source and the operator corrects it. rcf-lite mints the statements, runs the intake scans over the new statements against the existing ones, and appends one openQuestion statement per finding. This is the seam decision 4 pins down: scan findings become brief statements so the loop reads one check (brief:openQuestions) and the owner answers in one place.",
28
+ "tags": [
29
+ "capability:define-intake"
30
+ ]
31
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "createdAt": "2026-10-03T00:00:00Z",
3
+ "updatedAt": "2026-10-03T00:00:00Z",
4
+ "reqId": "REQ-188",
5
+ "prdId": "PRD-001",
6
+ "title": "draft shapes for the engineer",
7
+ "description": "After intent-complete the agent pre-populates `[draft]` entities, components and interfaces as engineer entry material; `shapes:draftSettled` lists every draft home.",
8
+ "category": "functional",
9
+ "domain": "define",
10
+ "priority": "must",
11
+ "version": "0.1.0",
12
+ "status": "draft",
13
+ "shapeClassification": {
14
+ "shapes": [
15
+ "none"
16
+ ],
17
+ "reason": "operatorOverride",
18
+ "classifiedAt": "2026-10-03T00:00:00Z",
19
+ "signals": []
20
+ },
21
+ "rationale": "Spec 2026-10-01 (DEFINE step 3) section 3 and decision 8: once L1 is green, the agent populates one `[draft]` core entity per `entity` statement, one `[draft]` TAC stub per surface or external-system cluster, and `[draft]` interfaces with real kinds inside each stub, so the engineer walks into a tree whose shape is honest about what is still owed. `shapes:draftSettled` already fails `[draft]` on `TAC.interfaces[].description` in 0.29.0 (the current main; the 0.29.0 tag has not been cut). This REQ extends the marker to `TAC.purpose` and `coreEntities[].description` and extends the check to list all three id forms (`<tacId>:<name>`, `<tacId>`, `TAD.entity:<name>`). `tacHasInterface`, `kindVocabulary`, the new template-marker check and the entity join apply to drafts unchanged; nothing in the draft pass can fail a product-owner check, which is the property that keeps L1 and the draft pass independent.",
22
+ "tags": [
23
+ "capability:define-drafts"
24
+ ]
25
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "createdAt": "2026-09-24T12:00:00Z",
3
- "updatedAt": "2026-09-24T12:00:00Z",
3
+ "updatedAt": "2026-10-02T16:40:00Z",
4
4
  "tacId": "TAC-4121-define-ledgers",
5
5
  "prdId": "PRD-001",
6
6
  "tadId": "TAD-001",
@@ -15,9 +15,21 @@
15
15
  "resolveEntry flips status to resolved, stamps resolvedAt, throws LedgerError code 'usage' when the id is unknown, is idempotent-ish (resolving twice refreshes resolvedAt).",
16
16
  "parseBriefFromFile splits by non-empty lines, strips leading '- ', '* ' or '<n>. ' bullets, defaults kind to 'capability', and threads --source through to every draft.",
17
17
  "src/cli/ledger.js: `rcf define ledger <name> <verb>` argv parsing; verbs add / resolve / list; ledger-specific flags for each; renderDecisionsList formats the #241 numbered layout; list --json emits { ledger, <arrayKey>: [...] }.",
18
- "bin/rcf.js: register GROUPS.define.ledger; src/cli/help.js: import LEDGER_HELP and wire into HELP_MAP.define.ledger + DEFINE_HELP + top-level DEFINE listing so per-verb help stays single-sourced."
18
+ "bin/rcf.js: register GROUPS.define.ledger; src/cli/help.js: import LEDGER_HELP and wire into HELP_MAP.define.ledger + DEFINE_HELP + top-level DEFINE listing so per-verb help stays single-sourced.",
19
+ "`update <id>` subverb patches named fields and re-runs full-body validation; `resolvedBy` is a closed pointer grammar (REQ-nnn, TAD.entity:<name>, PRD.user:<name>, TAD.system:<name>, TAC-nnnn, omitted:<reason>) validated by D2."
19
20
  ],
20
21
  "internalStructure": "The walker never reads under rcf/define/: subdirFor in src/core/store/loader.js returns null for anything that is not a known child kind (requirements, user-stories, tacs, adrs, fbs, test-suites, code-nodes, evals), so a ledger file sitting under rcf/define/ is never enumerated for schema validation. The walker-invisibility invariant is proved by a dedicated test that seeds the four ledger files plus a bug-bait req-shaped file under rcf/define/ and asserts zero walker error paths starting with rcf/define/ and no matching id in tree.byId. Validation is JSON-schema-shaped inline (no ajv, no @stravica-ai/rcf-schemas dependency) because the sidecars ride outside the shared schema until schemas 0.7 (ADR-4121).",
21
- "interfaces": [],
22
+ "interfaces": [
23
+ {
24
+ "name": "rcf define ledger <name> update <id>",
25
+ "kind": "cliCommand",
26
+ "description": "Patch named fields on an existing ledger entry (brief, decisions, concerns or probes); full-body validation runs as `add` validates. On brief, `--resolved-by` must match the closed pointer grammar."
27
+ },
28
+ {
29
+ "name": "parseResolvedBy",
30
+ "kind": "other",
31
+ "description": "Parse a resolvedBy pointer against the tree; returns { ok, kind, target } on a resolved form (REQ-nnn, TAD.entity:<name>, PRD.user:<name>, TAD.system:<name>, TAC-nnnn, omitted:<reason>) and { ok: false, why } otherwise."
32
+ }
33
+ ],
22
34
  "notes": "The intake-scan-against-frozen-statements hook on brief add --from is a named seam this slice leaves for a later one: 'rcf discover intake' is the intended caller in slice 2's D1 gate check, and this slice ensures the CLI has one place to inject that seam without a second refactor. `list --json` is a keep-alive for slice 5's viewer, which will render the same object over http."
23
35
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "createdAt": "2026-09-24T14:30:00Z",
3
- "updatedAt": "2026-09-24T15:48:40Z",
3
+ "updatedAt": "2026-10-03T00:00:00Z",
4
4
  "tacId": "TAC-4122-define-gates",
5
5
  "prdId": "PRD-001",
6
6
  "tadId": "TAD-001",
@@ -11,15 +11,51 @@
11
11
  "responsibilities": [
12
12
  "src/query/gates.js: STAGE_ORDER = ['D1','D2','D3','D4','D5','D6','D7','D8']; STAGE_GATES map (D1 -> 'define.brief' etc.); INTERFACE_KINDS closed set matching proposal section 5.2; STAGE_SHORT_NAMES map (D1 -> 'brief' etc.).",
13
13
  "checkD1Brief: intake acknowledged (brief-ledger has at least one statement since the freeze high-water mark), every statement kinded from BRIEF_KINDS, every openQuestion statement resolved or promoted to a decisions-ledger entry, profile.md carries surface (viewer | runningApp | prDiff) and register (productOwner | engineer | unstated) markers.",
14
- "checkD2Skeleton: every 'capability' and 'constraint' statement in scope resolves to a REQ (via a REQ id in the statement's resolvedBy field OR by name-match against a REQ title, cheap check); every REQ in scope has non-TODO description, shapeClassification and domain; TAD dataStores and coreEntities non-empty when any persistence REQ exists; exactly one deploy or deferral ADR tree-wide (title prefix 'Deploy target:' or 'Deploy deferral:').",
14
+ "checkD2Skeleton: every 'capability' and 'constraint' statement in scope resolves to a REQ (via a REQ id in the statement's resolvedBy field OR by name-match against a REQ title, cheap check); every REQ in scope has non-TODO description, shapeClassification and domain; TAD.dataArchitecture.dataStores and TAD.dataArchitecture.coreEntities non-empty when any persistence REQ exists (per rcf-schemas 0.6.3 tad.schema.json; the top-level path was a slice-2 drift corrected under w-2026-09-25-dave-004); exactly one deploy or deferral ADR tree-wide (title prefix 'Deploy target:' or 'Deploy deferral:').",
15
15
  "checkD3Shapes: every TAC in scope has at least one interface; every interface's kind is in INTERFACE_KINDS. Template markers, entity join, ownerRef resolution and path resolution are seams (0.30).",
16
16
  "checkD4Stories: every REQ in scope has at least one US (fbsByAcId inversion via reqByUs is enough); every US in scope has a testable AC (ac.testable !== false and description non-empty), a failure AC or a project-scoped or req-scoped opt-out with baselineKey 'defineD4:failure', a must-not AC or an opt-out with baselineKey 'defineD4:mustNot', tacIds non-empty and every id resolving to a walker-known TAC, and no AC description containing the scaffold TODO placeholder pattern (case-sensitive uppercase 'TODO:' with a colon -- the literal shape src/core/store/init.js writes; incidental prose mentioning 'todo' does NOT trip the gate, and the validate CLI's broader whole-word notice stays separate).",
17
17
  "checkD5Crosscut: TAD.securityArchitecture non-empty when tree carries any REQ with shape 'auth' or 'httpApi'; TAD.operationalConcerns non-empty when any AC description carries the '[deployed]' marker; every concern-ledger entry on a REQ in scope has disposition applied or waived (no status 'open').",
18
18
  "checkD6Consistency: caller-supplied validateErrors is empty (tree-wide); caller-supplied probeOpenCount is zero.",
19
19
  "checkD7Decisions: no decisions-ledger entry with status 'open'.",
20
- "checkD8Freeze: prior stages passed | acknowledged at the current tree hash; every AC id owned by exactly one FBS (walker.fbsByAcId length 1); computeQueue.nextActionable is not null (queue head actionable) and the head's title is not 'TODO' or empty; validateErrors empty."
20
+ "checkD8Freeze: prior stages passed | acknowledged at the current tree hash; every AC id owned by exactly one FBS (walker.fbsByAcId length 1); computeQueue.nextActionable is not null (queue head actionable) and the head's title is not 'TODO' or empty; validateErrors empty.",
21
+ "src/setup/identity-seed.js IDENTITY_TEMPLATE seeds a '## Surface' section carrying one of 'viewer' | 'runningApp' | 'prDiff' (default 'prDiff') so a fresh 'rcf init' project satisfies checkD1Brief's profile:surface marker without the operator editing profile.md first.",
22
+ "ADR-4126 (0.29.0): own the per-check persona and question map; export `CHECK_PERSONA`, `CHECK_QUESTION`, `STAGE_FALLBACK_PERSONA`, `checkPersona(name)`, `checkQuestion(name)` and `parseInterfaceDraft(iface)`. Every entry in `stages[].checks[]` carries `persona` (productOwner | engineer) and a non-empty `question`; the `stage:<D>:scope` placeholder uses `STAGE_FALLBACK_PERSONA`.",
23
+ "ADR-4126 (0.29.0): split `skeleton:reqFields` into `skeleton:reqIntent` (productOwner: description without TODO and domain, evaluated per REQ independently) and `skeleton:reqShape` (engineer: shapeClassification.shapes). Split `decisions:allAnswered` into `decisions:wellFormed` (productOwner: non-empty question + two or more options + a default naming one of them) and `decisions:allAnswered` (engineer: zero open).",
24
+ "ADR-4126 (0.29.0): new D3 check `shapes:draftSettled` fails any interface whose `description` starts with `[draft]` (whitespace tolerated), id `<tacId>:<name>`, over delta, total = interfaces on TACs in scope; `tacHasInterface` counts a draft as present; `kindVocabulary` applies to drafts.",
25
+ "`shapes:draftSettled` reports `[draft]` on `TAC.purpose`, `coreEntities[].description` and `TAC.interfaces[].description` with ids `<tacId>`, `TAD.entity:<name>` and `<tacId>:<name>` respectively (REQ-188, 0.30.0): `parseInterfaceDraft` reads the marker on all three homes with the same whitespace-tolerant rule; the draft pass enumerates across the three homes; nothing in the draft pass can fail a product-owner check."
21
26
  ],
22
27
  "internalStructure": "Every stage function follows the same shape: 1) decide notApplicable per section 3.2 and return { state: 'notApplicable' } with a single-check summary; 2) build checks[] as a list of { name, ok, over, pass, total, failing: [{ id, why }] }; 3) fold to state: 'passed' if every ok is true; 'failing' otherwise, unless the stage is D3/D5/D6 AND freeze.gates[<gate>].state === 'acknowledged' AND freeze.gates[<gate>].at.hash === currentTreeHash, in which case state is 'acknowledged'. Blocking-vs-warn is a property of the stage, not of the state: D3/D5/D6 use the acknowledged fold, D1/D2/D4/D7/D8 do not. The caller (readiness) folds the array of stage results into freezeable and nextAction.",
23
- "interfaces": [],
28
+ "interfaces": [
29
+ {
30
+ "name": "CHECK_PERSONA",
31
+ "kind": "other",
32
+ "description": "[draft] Owning persona map (productOwner | engineer) for every stage-gate check in 0.29.0 (ADR-4126)."
33
+ },
34
+ {
35
+ "name": "CHECK_QUESTION",
36
+ "kind": "other",
37
+ "description": "[draft] Per-check heading question in the owning persona's register; the report and the Readiness tab print it as the heading of each check's findings list."
38
+ },
39
+ {
40
+ "name": "STAGE_FALLBACK_PERSONA",
41
+ "kind": "other",
42
+ "description": "[draft] Per-stage fallback persona for the `stage:<D>:scope` placeholder check that `notApplicable` envelopes carry."
43
+ },
44
+ {
45
+ "name": "checkPersona",
46
+ "kind": "other",
47
+ "description": "[draft] Look up the owning persona for a check name; returns 'productOwner' | 'engineer' | null."
48
+ },
49
+ {
50
+ "name": "checkQuestion",
51
+ "kind": "other",
52
+ "description": "[draft] Look up the question heading for a check name; returns the empty string when no question is registered."
53
+ },
54
+ {
55
+ "name": "parseInterfaceDraft",
56
+ "kind": "other",
57
+ "description": "[draft] True when an interface's `description` carries the leading `[draft]` marker (whitespace tolerated)."
58
+ }
59
+ ],
24
60
  "notes": "Slice-3 hook (rcf define freeze): reads state to refuse on any 'failing'; --ack <gate> --reason <text> writes freeze.gates[<gate>] = { state: 'acknowledged', at: { hash: <currentTreeHash>, reason: <text>, by: <id> } } and re-runs computeReadiness to check freezeable. Slice-5 hook (viewer): reads the same object end-to-end."
25
61
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "createdAt": "2026-09-24T14:30:00Z",
3
- "updatedAt": "2026-09-24T15:48:40Z",
3
+ "updatedAt": "2026-10-01T16:00:00Z",
4
4
  "tacId": "TAC-4123-define-readiness",
5
5
  "prdId": "PRD-001",
6
6
  "tadId": "TAD-001",
@@ -17,9 +17,45 @@
17
17
  "src/cli/readiness.js: text output = tree line, next-action line, chip line (D1..D8 with state color words), stage-by-stage failing items (checks with failing ids); --check exits 4 for a blocking stage that is failing, 0 with a stderr [warn] for a warn-with-ack stage that is failing but not acknowledged, 0 otherwise.",
18
18
  "src/define/ledgers.js (P3 resolution): loadAllLedgers checks each ledger file's existence with statPath and omits missing files from the returned bundle; loadLedger keeps returning an empty ledger on ENOENT so single-ledger callers retain the ergonomic default.",
19
19
  "bin/rcf.js: register GROUPS.define.readiness -> readinessMain; src/cli/help.js: import READINESS_HELP and wire into HELP_MAP.define.readiness + DEFINE_HELP + top-level DEFINE listing.",
20
- "src/query/index.js: export computeReadiness (plus its subhelpers where useful for tests) so downstream consumers (slice 3 freeze, slice 5 viewer) import through the same seam every other query verb does."
20
+ "src/query/index.js: export computeReadiness (plus its subhelpers where useful for tests) so downstream consumers (slice 3 freeze, slice 5 viewer) import through the same seam every other query verb does.",
21
+ "ADR-4126 (0.29.0): fold `levels` and `personas` from `stages[].checks[]` after the eight stage functions run. `levels.intentComplete.ok` is \"every productOwner check ok\" across every stage (stage state ignored); `levels.intentComplete.blockedBy` lists every failing PO check with ids capped at 20. `levels.readyToBuild.ok` equals freezeable; `levels.readyToBuild.blockedBy` lists every failing check in a stage whose state is `failing` (checks inside an `acknowledged` stage are not blockers). `personas.productOwner.blockers` deep-equals `levels.intentComplete.blockedBy`; `personas.engineer.blockers` is the failing engineer checks in failing stages. `levels.readyToBuild.nextAction` deep-equals the top-level `nextAction`.",
22
+ "ADR-4126 (0.29.0): `deriveNextAction(stages, persona?)` gains an optional persona argument. With no persona it behaves exactly as 0.28.4. With a persona it picks the first stage in D1..D8 order holding a failing check of that persona, then the check with the largest failing list; the command carries `--level intent` on the productOwner variant. `formatVerdictLines(result)` builds the two strings (`Intent-complete: yes/no...` and `Ready-to-build: yes/no...`) shared by the CLI (PR B) and the Readiness tab (PR C) so they cannot disagree.",
23
+ "ADR-4126 (0.29.0): the levels and personas folds are additive; every 0.28.4 top-level field (tree, delta, stages, nextAction, coverage, decisions, freezeable) is present with its 0.28.4 type. No file under rcf/ is created or modified by any `rcf define readiness` invocation. The CLI flags (`--level`, `--persona`) and the viewer tab are PR B and PR C respectively; the compute lands first (PR A).",
24
+ "ADR-4126 PR B (0.29.0): src/cli/readiness.js owns the --level intent|build and --persona productOwner|engineer dispatch. --level drives the exit code against levels.<which>.ok per spec section 3.2 (stricter than --check all on warn-with-ack failures per decision 4); --persona is a display filter that never affects the exit code or JSON content. parseLevelFlag and parsePersonaFlag share the usage-line shape with --check. Unknown values exit 2 with a usage line naming the accepted set. The CLI passes the resolved level into computeReadiness via the readiness context so _meta.level reflects the invocation (null when the flag was absent).",
25
+ "ADR-4126 PR B (0.29.0): the text report prints both verdict lines (shared via formatVerdictLines) at the top; regroups blockers into one 'Product owner: N question(s)' and one 'Engineer: N check(s) blocking' section ordered by the profile register (productOwner-first or engineer-first; unstated and absent profile default to PO first); and renders the per-check line in the per-stage detail block with the persona inside the parenthesis after `over`. --persona X collapses the opposite-persona block to one 'hidden; run without --persona' line and restricts the per-stage detail to matching-persona checks."
21
26
  ],
22
27
  "internalStructure": "readiness.js is a linear composer: 1) compute delta and its docHashes; 2) fan-out impacted / impactedFbs; 3) build stage context (freeze, delta, scope, ledgers, profile, validateErrors, probeOpenCount, coverageTree); 4) call the eight check functions; 5) fold freezeable and nextAction. The CLI parses flags, loads the tree once, loads the four ledgers and the freeze record once, computes the readiness, then routes to the text or JSON emitter. --check maps its argument to the STAGE_SHORT_NAMES map (brief -> D1 ... freeze -> D8) or accepts D1..D8 directly. The runWithAdmissibilityGate wrap sits inside main so refusals ride the same exit path as coverage's admissibility refusals.",
23
- "interfaces": [],
28
+ "interfaces": [
29
+ {
30
+ "name": "deriveLevels",
31
+ "kind": "other",
32
+ "description": "[draft] Fold the two levels (intentComplete, readyToBuild) from `stages[].checks[]` (ADR-4126, proposal section 2.1)."
33
+ },
34
+ {
35
+ "name": "derivePersonas",
36
+ "kind": "other",
37
+ "description": "[draft] Fold the `personas` block; productOwner.blockers deep-equals levels.intentComplete.blockedBy; engineer.blockers is the failing engineer checks in failing stages."
38
+ },
39
+ {
40
+ "name": "deriveNextAction",
41
+ "kind": "other",
42
+ "description": "[draft] Pick the first failing stage (optionally persona-filtered); carries `--level intent` on the productOwner variant."
43
+ },
44
+ {
45
+ "name": "formatVerdictLines",
46
+ "kind": "other",
47
+ "description": "[draft] Build the two verdict strings (Intent-complete and Ready-to-build) shared by the CLI and the Readiness tab so the two surfaces cannot disagree."
48
+ },
49
+ {
50
+ "name": "parseLevelFlag",
51
+ "kind": "other",
52
+ "description": "[draft] Validate --level against the closed value set (intent | build); return 'intent' | 'build' | null (flag absent) or throw a usage error for an unknown value (CLI maps the throw to exit 2 with a stderr usage line)."
53
+ },
54
+ {
55
+ "name": "parsePersonaFlag",
56
+ "kind": "other",
57
+ "description": "[draft] Validate --persona against the closed value set (productOwner | engineer); return the value or null (flag absent) or throw a usage error for an unknown value."
58
+ }
59
+ ],
24
60
  "notes": "Slice-3 hook (rcf define freeze) reads freezeable and stages[].state; --ack <gate> writes into freeze.gates[<gate>] and re-computes readiness. Slice-5 hook (viewer) fetches the same JSON object over the existing tab wire and renders the pill vocabulary + findings-list + diff components. Performance seam: computeImpact per id on a whole-tree delta (before freeze) may be quadratic on very large trees; the compute stays fast on the dogfood chain (~700 documents, sub-second) but the CLI notes the wall time in text mode so an operator can spot regression."
25
61
  }
@@ -0,0 +1,28 @@
1
+ {
2
+ "createdAt": "2026-09-24T16:33:15Z",
3
+ "updatedAt": "2026-09-24T16:33:15Z",
4
+ "tacId": "TAC-4124-define-freeze",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "draft",
9
+ "name": "rcf define freeze CLI verb",
10
+ "purpose": "src/cli/freeze.js is the only writer of rcf/define/freeze.json in the shipped product. It composes readiness (REQ-175) with the freeze-record writer (REQ-172): one call to computeReadiness, one conditional call to saveFreezeRecord, one section 2.5 summary line, one exit code. The verb is the visible contract slice 4's bundle --next and slice 5's viewer read against; no other verb writes the freeze record.",
11
+ "responsibilities": [
12
+ "src/cli/freeze.js: parseArgs contract with --ack (multiple), --reason (multiple), --note, --litmus-attested (comma-separated D-names), --status, --json, --help; exit codes 0 success / 2 usage / 4 refused-on-failing.",
13
+ "src/cli/freeze.js: --ack argument validation runs before any tree load and refuses unknown gates and mismatched --ack/--reason pair counts with exit 2 and a stderr message naming the accepted set (define.shapes | define.crosscut | define.consistency with their short-name aliases).",
14
+ "src/cli/freeze.js: build tree + freeze + ledgers + testPointers concurrently (Promise.all mirroring readiness), then runWithAdmissibilityGate around computeReadiness, then apply --ack acknowledgements into an in-memory freeze.gates map keyed by gate id and recompute state per WARN_WITH_ACK stage.",
15
+ "src/cli/freeze.js: on any remaining 'failing' stage after acks, print the failing-stage block on stderr and exit 4 without writing.",
16
+ "src/cli/freeze.js: on freezeable, assemble the record body: reuse delta.js currentHashes generator (recompute matching what computeDelta uses so treeHash and docHashes agree with computeReadiness's currentTreeHash), derive counts, write the record via saveFreezeRecord, and print the section 2.5 summary (or --json envelope).",
17
+ "src/cli/freeze.js: --status branch runs no gates and never writes; it loads the record and computeDelta (unfrozen path when record absent) and prints the summary block, or emits { record, delta } on --json.",
18
+ "src/define/freeze-record.js: validateFreezeRecord requires override as a nullable field (throws FreezeRecordError field 'override' when the key is missing); loadFreezeRecord defaults a missing key to null so older records load; saveFreezeRecord's own validate step catches a caller that forgets to write override.",
19
+ "src/cli/readiness.js and CHANGELOG.md: replace ' -- ' with a colon in the two spots the brief calls out.",
20
+ "bin/rcf.js and src/cli/help.js: register the freeze verb (GROUPS.define.freeze, HELP_MAP.define.freeze, DEFINE_HELP listing).",
21
+ "Version reading: rcfLite from packages/rcf-lite/package.json (read once, cached via ESM import), ruleset from src/ruleset/ruleset.json's rulesetVersion or the umbrella version depending on the ruleset shape, schemas from node_modules/@stravica-ai/rcf-schemas/package.json (fall back to the manifest.schemasVersion when the package cannot be read)."
22
+ ],
23
+ "internalStructure": "One CLI module, three top-level branches by flag: --help early exit; --status branch (readRecord, computeDelta, format); default freeze branch (loadEverything, runReadiness, applyAcks, decideExit, writeRecord, formatSummary). Ack parsing is a small helper that walks the multi-valued --ack and --reason arrays in parallel, matching each ack gate to the next reason; a mismatch is a UsageError the caller catches. Counts derivation reuses tree.kindById to bucket ids into { req, us, ac, tac, adr, fbs }; the AC bucket walks every US and every US's acceptanceCriteria[]; acByClass parses the leading '[<class>]' marker using gates.js parseAcClass; interfaces bucketing walks every TAC's interfaces[] and buckets by kind. The 'first freeze' path is decided by (loadFreezeRecord() === null) BEFORE the freeze runs; branch is not re-derived from delta shape.",
24
+ "interfaces": [
25
+ { "kind": "cliCommand", "name": "rcf define freeze", "description": "The freeze CLI verb; the single interface this TAC ships." }
26
+ ],
27
+ "notes": "Slice-4 hook (bundle --next): reads freeze.gates and freeze.frozenAt to decide the impacted-FBS printout; the impactedFbs field is not on the record yet (it is a computed shape). Slice-5 hook (viewer): renders the section-2.5 summary from the record + a fresh computeDelta call over the live tree. Litmus attestation seam (proposal section 9 decision 7): the --litmus-attested flag records the operator's per-decision attestation; the --litmus <n> real-dispatch runner is a named 0.30 seam (slice 6)."
28
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "createdAt": "2026-10-01T18:00:00Z",
3
+ "updatedAt": "2026-10-01T18:00:00Z",
4
+ "tacId": "TAC-4127-readiness-view",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "draft",
9
+ "name": "Readiness viewer tab and shared pill, findings-list and diff components",
10
+ "purpose": "Render the computeReadiness object as tab 1 of the HTML review surface so the viewer and `rcf define readiness --json` share one source of truth. Factor the three visual roles the tab needs (status pill, items list, before-after) into stable modules every tab can call. The tab is pure: it never recomputes readiness; the renderer takes the result object and the profile and emits one HTML string.",
11
+ "responsibilities": [
12
+ "src/view/readiness.js: renderReadinessPanel(result, { profile }) returns the eleven ordered blocks in spec section 5 as one HTML string: tree line (formatTreeLine), two verdict pills with formatVerdictLines strings as labels, two per-persona next-action lines, D1..D8 state chips with reason/acknowledgement text on hover, blockers grouped by persona (register orders the pair; non-register group collapses with count visible, never omitted), delta list grouped by REQ ancestor with document-level diff on changed items, per-stage check detail with persona pill and findings list when not ok, coverage tree-versus-delta with re-verify and re-execute counts, decisions outstanding in the #241 numbered format, a Freeze now control enabled iff levels.readyToBuild.ok, and the loaded freeze record.",
13
+ "src/view/readiness.js: deliberately imports nothing from src/query/ that is not already exported from src/query/index.js (formatVerdictLines, formatTreeLine, shortHash); the renderer never calls computeReadiness and never loads anything from disk.",
14
+ "src/view/components/pill.js: pill({ value, variant, title?, href? }) returns `<span class=\"rcf-pill rcf-pill--<variant> rcf-pill--<normalisedValue>\" title?>{value}</span>` or an anchor when href is set; the normalisedValue is lowercased and `/` or spaces are collapsed to `-`.",
15
+ "src/view/components/findings-list.js: findingsList({ heading, count, items }) returns `<div class=\"rcf-findings-list\"><h4>{heading} <span class=\"rcf-findings-list__count\">{count}</span></h4><ul>...<li><a href={anchor}>{id}</a>: {why}</li>...</ul></div>` with ids capped at 20 and a `... N more suppressed` line beyond.",
16
+ "src/view/components/diff.js: diff(before, after) returns a two-column rendering of two document bodies for the delta list; criterion-level before-and-after is deferred per ADR-4127.",
17
+ "src/view/html-page.js: Readiness button and tabpanel are the first entries; the former Overview button and panel are relabelled PRD; the data-tab=overview and id=tab-overview identifiers are preserved so existing hash anchors still resolve; the inline-script TABS array gains 'readiness' as the first entry (default active tab) and keeps the existing four entries in order.",
18
+ "src/view/index.js: renderModelToPage loads the freeze record, ledgers, test pointers and profile text once, invokes computeReadiness via the pure src/query/ seam, and threads the result into the model so renderPage and renderContent carry one computed object; the SSE tree-update broadcast rewalks and recomputes on every change under rcf/."
19
+ ],
20
+ "internalStructure": "readiness.js is a sequence of eleven small block builders calling the three shared components; each block consumes exactly one top-level field of the readiness result so a wire-level JSON diff ties to a visible block. The register ordering is picked once at the top via pickRegister (same helper the CLI uses) and threaded down. Hash anchors are built with the same `#<docId>` convention the existing tabs use so blocker-id links open the right tab and expand the right <details> through the existing resolveHash routine.",
21
+ "interfaces": [
22
+ {
23
+ "name": "renderReadinessPanel",
24
+ "kind": "other",
25
+ "description": "[draft] Build the Readiness tab inner HTML from the readiness result and the operator profile; pure, no I/O, no recompute."
26
+ },
27
+ {
28
+ "name": "pill",
29
+ "kind": "other",
30
+ "description": "[draft] Render one rcf-pill span (or anchor) with a vocabulary class per value."
31
+ },
32
+ {
33
+ "name": "findingsList",
34
+ "kind": "other",
35
+ "description": "[draft] Render a heading plus id-anchor list with per-id why strings; ids capped at 20."
36
+ },
37
+ {
38
+ "name": "diff",
39
+ "kind": "other",
40
+ "description": "[draft] Render two document bodies side by side; document-level only in 0.29.0."
41
+ },
42
+ {
43
+ "name": "pickRegister",
44
+ "kind": "other",
45
+ "description": "[draft] Decide persona ordering from profile.md text; same contract as the CLI helper (productOwner | engineer | unstated)."
46
+ }
47
+ ],
48
+ "notes": "The 0.29.0 scope keeps the diff component document-level only; criterion-level before-and-after is deferred to the UX review per ADR-4127. The pill component is additive and never changes the Product Map `pm-mini` or Requirements `status-chip` markup in this PR; those swaps are follow-ups once a mechanical mapping is established."
49
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "createdAt": "2026-10-02T18:00:00Z",
3
+ "updatedAt": "2026-10-03T00:00:00Z",
4
+ "tacId": "TAC-4128-define-guidance",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "draft",
9
+ "name": "DEFINE-stage guidance, prompts and managed rules",
10
+ "purpose": "serve the DEFINE-stage guidance pages, the argument-free MCP prompts and the managed-block rules that steer the harness through intake and the question loop.",
11
+ "responsibilities": [
12
+ "guidance/define-intent.md: the PO-register loop content from spec section 1.1, 1.2 and 1.4 as operator prose; wording rules for the PO register; what to print at the end.",
13
+ "guidance/elicitation-playbook.md: the L-sized restructure named in spec section 6 (sections 2 to 8 around D1 to D8 over the delta, the PO loop as the first worked example and the CSV feature as the second).",
14
+ "guidance/managed/agent-instructions-block.md: the two rules of spec section 6 last row - the PO-session rule and the prototype rule. The managed block regeneration never removes an existing rule.",
15
+ "guidance/manifest.json: register the new slug `define-intent`, the `rcf://docs/define-intent` resource, and the two new argument-free prompts `rcf_define_intent` and `rcf_define_intake`.",
16
+ "serves `rcf_define_intent` and `rcf_define_intake` prompts and the two managed-block rules of REQ-186 and REQ-187.",
17
+ "serves `rcf_define_draft_shapes` and `rcf_discover_prototype` prompts and `guidance/discovery-prototypes.md` (REQ-188, REQ-182 amended): the draft-shapes prompt walks the agent through pre-populating `[draft]` core entities, TAC stubs and interfaces after L1; the discovery-prototype prompt walks the agent through recording a prototype in DISCOVERY as evidence; `guidance/discovery-prototypes.md` carries the doctrine and the ledger conventions and is also served as `rcf://docs/discovery-prototypes`; the managed block gains one prototype rule (ADR-4132) stating that a statement sourced to `prototype:` is evidence and a document citing prototype code as its implementation is a D2 finding the engineer resolves by re-deriving."
18
+ ],
19
+ "internalStructure": "The MCP prompt and resource plumbing (src/mcp/prompts.js, src/mcp/resources.js) already serves manifest-registered entries byte-faithfully; this TAC only adds entries and markdown files. The managed block is regenerated by `rcf doctor` from the fragment in guidance/harness-template.md; the two new rules append to that fragment, and the regeneration pass preserves rules the project authored outside the managed markers.",
20
+ "interfaces": [
21
+ {
22
+ "name": "rcf_define_intent",
23
+ "kind": "other",
24
+ "description": "Argument-free MCP prompt served from guidance/define-intent.md."
25
+ },
26
+ {
27
+ "name": "rcf_define_intake",
28
+ "kind": "other",
29
+ "description": "Argument-free MCP prompt served from the intake prompt markdown."
30
+ },
31
+ {
32
+ "name": "rcf://docs/define-intent",
33
+ "kind": "other",
34
+ "description": "Resource URI backed by guidance/define-intent.md."
35
+ },
36
+ {
37
+ "name": "rcf_define_draft_shapes",
38
+ "kind": "other",
39
+ "description": "Argument-free MCP prompt served from guidance/define-draft-shapes.md: walks the agent through pre-populating [draft] core entities, TAC stubs and interfaces after L1 is green."
40
+ },
41
+ {
42
+ "name": "rcf_discover_prototype",
43
+ "kind": "other",
44
+ "description": "Argument-free MCP prompt served from guidance/discover-prototype.md: walks the agent through recording a prototype in DISCOVERY as evidence (classification, brief statements with prototype: source spans, the burn statement, the salvage-by-re-derivation path)."
45
+ },
46
+ {
47
+ "name": "rcf://docs/discovery-prototypes",
48
+ "kind": "other",
49
+ "description": "Resource URI backed by guidance/discovery-prototypes.md: the doctrine and the ledger conventions for prototypes in DISCOVERY (ADR-4132)."
50
+ }
51
+ ],
52
+ "notes": "This TAC was authored by PR 2 after the guidance slice did not land on origin/main under its reserved id ADR-4128 / TAC-4128; the shift is recorded in the PR 2 report. Later DEFINE PRs may expand it with further prompts and managed rules."
53
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "createdAt": "2026-10-02T18:00:00Z",
3
+ "updatedAt": "2026-10-02T18:00:00Z",
4
+ "tacId": "TAC-4129-define-questions",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "draft",
9
+ "name": "the questions compute and the questions verb",
10
+ "purpose": "compute and print the persona question set.",
11
+ "responsibilities": [
12
+ "src/query/questions.js exposes computeQuestions(readiness, { tree, ledgers, profileText, persona }). It reads levels.intentComplete.blockedBy for persona productOwner or personas.engineer.blockers for persona engineer, expands each blocker's failing items into one question each via a per-check template, orders them D1 then D2 then D4 then D7 then by source span, carries one writeBack entry per `when` case the template defines, groups by source span, and never creates or modifies a file under rcf/.",
13
+ "src/cli/questions.js registers `rcf define questions [--json] [--persona productOwner|engineer] [--stage <D|short>] [--limit <n>]`. Persona default comes from the profile register, else productOwner. Text mode prints groups and asks; --json emits the computeQuestions object. Exit 0 always; unknown persona exits 2 with usage naming productOwner and engineer. Writes nothing under rcf/.",
14
+ "src/mcp/tools.js adds `rcf_define_questions` (same object as the verb's --json), `rcf_define_readiness` (the readiness object), and `rcf_define_ledger` (thin wrapper over the ledger handler). All three share the envelope the existing query and write tools use."
15
+ ],
16
+ "internalStructure": "computeQuestions is a pure function: it folds the readiness blockers into questions through a per-check template table, deterministically ids each question from (stage, check, itemId), carries writeBack commands verbatim, and groups by source span (the trailing `(source: path:line-span)` marker on brief statements, else `tree`). The CLI verb runs the same walkTree + loadAllLedgers + loadFreezeRecord + readProfileText path readiness.js uses, calls computeReadiness, then computeQuestions. The MCP tools reuse the handler shape of the existing query tools (walkTree, okResult, usageErrorResult).",
17
+ "interfaces": [
18
+ {
19
+ "name": "computeQuestions",
20
+ "kind": "moduleApi",
21
+ "description": "Compute the persona question set from a readiness result, a tree, the four ledgers and the profile text. Returns an object with treeHash, persona, level, ok, remaining, groups[], questions[], optional[] and engineer (collapsed count on the PO return)."
22
+ },
23
+ {
24
+ "name": "rcf define questions",
25
+ "kind": "cliCommand",
26
+ "description": "Print or JSON-emit the persona question set. --persona defaults to the profile register. --stage narrows to one stage; --limit caps the questions array. Writes nothing under rcf/."
27
+ },
28
+ {
29
+ "name": "rcf_define_questions",
30
+ "kind": "other",
31
+ "description": "MCP tool: same structuredContent as the verb's --json output."
32
+ },
33
+ {
34
+ "name": "rcf_define_readiness",
35
+ "kind": "other",
36
+ "description": "MCP tool: the readiness object."
37
+ },
38
+ {
39
+ "name": "rcf_define_ledger",
40
+ "kind": "other",
41
+ "description": "MCP tool: thin wrapper over the ledger handler, taking { name, verb, ...flags }."
42
+ }
43
+ ],
44
+ "notes": "The viewer owns the Readiness tab PO-facing surface (viewer refresh PR 8); this TAC supplies only the data shape and the verb. Prompts and guidance land under TAC-4128."
45
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "createdAt": "2026-10-02T16:40:00Z",
3
+ "updatedAt": "2026-10-02T16:40:00Z",
4
+ "tacId": "TAC-4130-define-intake-brief",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "draft",
9
+ "name": "document intake into the brief ledger",
10
+ "purpose": "marker parser, scan wiring, `--findings` to open-question statements for `brief add --from`.",
11
+ "responsibilities": [
12
+ "src/define/ledgers.js parseBriefFromFile parses a leading `[kind]` marker and a trailing `(source: ...)` marker on each non-empty line; a line without `[kind]` takes `--kind` (default `capability`); a line without `(source: ...)` takes `--source` or the file path; `?`-, `TBC`-, `TBD`-, `Open:`- and `Question:`-leading lines kind `openQuestion` unless a marker says otherwise; an unknown `[kind]` raises LedgerError code 'usage' before any write; a 0.29.0 line-per-statement file parses unchanged.",
13
+ "src/cli/ledger.js `brief add` wires `--scan` / `--no-scan` (default on with `--from`, off with `--text`); runs the mechanical scans in src/intake/orchestrator.js over new statements against existing ones; `--findings <path>` accepts the shape `rcf discover intake --input` takes and makes one `openQuestion` statement per finding with `source: scan:<name>:<ids>`; `--dry-run` prints added + findings and writes nothing; `--json` emits `{ added: [...], findings: [...] }`; `--no-scan` together with `--findings` exits 2.",
14
+ "On a frozen tree the comparison set is every existing statement. Two statement files added in sequence scan the second against the first and the finding's `scan:` source names ids from both."
15
+ ],
16
+ "internalStructure": "parseBriefFromFile returns drafts { text, kind, source } one per non-empty line. The CLI mints statements through addEntry; when scans are on it calls runIntakeScansOnDelta(newStatements, existingStatements) over the just-minted ids and the pre-existing body, then appends one openQuestion per finding whose source names the scan and the involved ids. `--findings` reads a JSON object with a `validationFindings[]` array (the same shape `rcf discover intake --input` accepts) and converts each entry to an openQuestion statement whose text is `detail` and whose source is `scan:<kind>`. `--dry-run` composes the full plan with every finding already converted and writes nothing.",
17
+ "interfaces": [
18
+ {
19
+ "name": "parseStatementFile",
20
+ "kind": "other",
21
+ "description": "[draft] Parse a statement file into drafts { text, kind, source } one per non-empty line, honouring the leading [kind] and trailing (source: ...) markers and the kind defaults the caller supplies."
22
+ },
23
+ {
24
+ "name": "runIntakeScansOnDelta",
25
+ "kind": "other",
26
+ "description": "[draft] Run the mechanical intake scans over the newly minted statements against every existing statement and return a flat findings array."
27
+ },
28
+ {
29
+ "name": "rcf define ledger brief add --from --scan --no-scan --findings --dry-run --json",
30
+ "kind": "cliCommand",
31
+ "description": "Add brief statements from a statement file, optionally running the intake scans and converting operator-supplied findings into openQuestion statements."
32
+ }
33
+ ],
34
+ "notes": "This TAC covers the writer-side intake wiring only. The question-computation surface and the Register-prompt replacement land in PR 2 and are not covered here."
35
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "createdAt": "2026-10-02T18:00:00Z",
3
+ "updatedAt": "2026-10-02T18:00:00Z",
4
+ "tacId": "TAC-4131-build-dag-view",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "draft",
9
+ "name": "Build/DAG sub-tab: DagToolbar, DagCanvas, DagInspector and the unconnected lane",
10
+ "purpose": "Render the Build tab's DAG sub-tab as the dependency-depth layout the design doc section 7 names: a toolbar with Status / Area / Buildable-now / Critical-path / Show-unconnected filters, a scrollable canvas with HTML nodes and inline SVG edges laid by `dependsOnFbsIds` depth and `buildOrder` rows, an inspector with needs and waits-on links, and a toggled unconnected lane. The renderer is pure: it takes the built tree model, emits one HTML string, mounts no inline script and reads from no provider other than the model.",
11
+ "responsibilities": [
12
+ "src/view/doc-renderers/build.js (or an adjacent src/view/build-dag.js shared from it): renderBuildDag(model, { buildableIds, cycleIds }) returns the full DAG sub-panel HTML string - toolbar + grid + inspector shell + hidden unconnected lane - with every FBS node carrying `data-fbs-id`, `data-status`, `data-domain`, `data-depth`, `data-buildable`, `data-critical`, `data-unconnected` so the client wiring can filter and highlight without re-rendering.",
13
+ "Layout: columns are 0..maxDepth; depth is the longest chain length from `dependsOnFbsIds` to the root (0 for no dependency). Rows inside a column are `buildOrder` ascending with `fbsId` lexicographic tie-break. Nodes are fixed-width buttons; edges are cubic SVG paths with arrowheads from dependency to dependant. Cycle members are excluded from the depth layout and surfaced as a finding; non-cycle FBS still render.",
14
+ "Unconnected lane: FBS with no incoming and no outgoing dependency. Hidden by default; the toggle's count equals that set's size.",
15
+ "Critical path: longest chain by node count across the DAG (because `estimatedHours` is sparse per the gap in design doc section 7); members get `data-critical=1`.",
16
+ "src/view/page-init.js: wireBuildDag() mounts click-to-highlight (upstream outlines link, downstream outlines warning, rest dim), writes `#tab=build&sub=dag&entity=<id>` through the existing writeHash on select, reads `entity=` on load to restore the selection and auto-opens the unconnected lane when the selected id is unconnected; filter toggles mirror the toolbar state onto node data attrs and keep the current selection.",
17
+ "html-page.js: renderBuildPanel swaps the PR 5 placeholder for `renderBuildDag(model, ...)`; the Build/Specs sub-tab is unchanged, the SubTabStrip wiring from PR 5 is unchanged, the Build tab's BuildStats and page head stay in place.",
18
+ "style.css: adds `.rcf-dag-*` tokens (toolbar, grid, canvas, node, edge, inspector, unconnected lane) sized against the shared Stravica tokens so both light and dark themes work; the canvas owns its own vertical and horizontal overflow so the viewer never scrolls horizontally."
19
+ ],
20
+ "internalStructure": "Three composition seams. (1) a pure graph helper (buildDagLayout(fbsItems)) returns `{ nodes: [{ id, title, status, domain, depth, buildOrder, acCount, size, buildable, direct: { needs, waits }, closure: { upstream, downstream }, critical, unconnected }], edges: [{ from, to }], cycles: string[], unconnectedIds: string[], criticalPathIds: string[], maxDepth }`; (2) renderDagToolbar / renderDagCanvas / renderDagInspector compose one HTML string from the layout; (3) page-init.js's wireBuildDag reads from the DOM only (data attrs on nodes, aria-pressed on toggles) and never recomputes the layout. Needs and waits-on in the inspector resolve from the same closure sets the layout carries, so the two surfaces cannot disagree.",
21
+ "interfaces": [
22
+ {
23
+ "name": "renderBuildDag",
24
+ "kind": "other",
25
+ "description": "[draft] Build the Build/DAG sub-tab HTML - toolbar, canvas, inspector shell, unconnected lane - from the built tree model and the pre-computed buildable set."
26
+ },
27
+ {
28
+ "name": "buildDagLayout",
29
+ "kind": "other",
30
+ "description": "[draft] Pure projection of `fbsItems[].dependsOnFbsIds` into DAG nodes, edges, depth columns, closures, critical path and unconnected ids; cycles surface as a `cycles` list and their members are excluded from the depth layout."
31
+ },
32
+ {
33
+ "name": "wireBuildDag",
34
+ "kind": "other",
35
+ "description": "[draft] Client-side wiring for the DAG canvas: click-to-highlight, filters, `#tab=build&sub=dag&entity=` round-trip, hash-driven selection restore, Open-in-Specs navigation. Reads from the DOM; sends no outbound postMessage."
36
+ }
37
+ ],
38
+ "notes": "The v1 rcf-tools DAG is the reference: keep its tiers, click-to-highlight, filters and stats; fix its dark-only palette, missing inspector, orphan pile-up, missing deep links and two-direction scrolling. No new TS, FBS or CN entry is minted - viewer-code PRs do not grow the build-step graph. The pill component (REQ-181) renders every status pill in the inspector; the shared `findings-list` module reports the cycle finding so the toolbar carries the same vocabulary as the rest of the tabs."
39
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "createdAt": "2026-10-02T19:00:00Z",
3
+ "updatedAt": "2026-10-02T19:00:00Z",
4
+ "tacId": "TAC-4132-id-lookup",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "draft",
9
+ "name": "ID lookup: ./index.json route, LookupModal, Cmd/Ctrl+F keyboard binding",
10
+ "purpose": "Build `./index.json` from `BuiltTreeModel` on every rewalk - one row per document id plus one per AC, carrying `{ id, kind, title, snippet, parent, tab }` - and render the LookupModal that opens from the SearchButton or Cmd/Ctrl+F, ranks results per design doc section 6 and picks `#tab=<tab>&entity=<id>` through the existing writeHash so `embed` and `theme` survive. The index is a new route, fetched lazily on first open and refreshed when the SSE tree-changed event lands.",
11
+ "responsibilities": [
12
+ "src/view/view-index.js: buildViewIndex(model) returns `{ rows: Array<{ id, kind, title, snippet, parent, tab }> }` from a BuiltTreeModel. One row per PRD, REQ, US, TAD, TAC, ADR, BS, FBS, TS; one row per AC under each US; snippet is the first 110 chars of the document's description (or purpose / summary / context, the kind's prose field) with whitespace collapsed; parent is the walker-visible parent id (US for an AC, REQ for a US, PRD for a REQ, TAD for a TAC or ADR, BS for a FBS, US for a TS); tab is the viewer tab that owns the id (overview / requirements / architecture / build; TS carry the US's tab).",
13
+ "src/server/routes.js + src/server/index.js: new route `GET /index.json` returns `{ rows }` as application/json. Built once per rewalk (so the first fetch is served from an in-memory snapshot) and broadcast as part of the tree-update event version so the client knows to refetch.",
14
+ "src/view/html-page.js: a SearchButton (<button class=\"rcf-search-btn\" data-rcf-search>) renders inside the shell's `<div class=\"tools\">` right of the tabstrip, with the aria-label `Find any document or AC`. One hidden LookupModal (<div class=\"rcf-lookup\" data-rcf-lookup hidden>) renders outside #rcf-live-content (next to the toast) so the SSE innerHTML swap cannot blow it away. The modal shell carries an input, a results list, an empty state and the reload affordance; results are rendered client-side.",
15
+ "src/view/page-init.js: wireLookup() lazily fetches `./index.json` on first open (XMLHttpRequest, same relative-script policy as PR 1), caches the parsed rows, listens for `rcf-tree-update` and marks the cache dirty for the next open; binds `(ctrl|meta)+F` (but not Shift+F) on `keydown` with preventDefault; drives type-ahead ranking (id-first exact/prefix/contains; then word match on title above snippet; single character is id-only; 20 result cap; matches marked); keyboard navigation up/down/Enter/Esc; a pick calls writeHash so `?embed=` and `?theme=` are preserved per PR 1's Dex contract.",
16
+ "src/view/style.css: `.rcf-search-btn` and `.rcf-lookup*` tokens aligned with the Stravica brand palette; focus trap and visible focus ring; empty-state and reload affordance in both themes."
17
+ ],
18
+ "internalStructure": "Three composition seams. (1) `buildViewIndex(model)` is a pure projection of the model into a row list; it reads nothing beyond the model and returns a plain object. (2) The server snapshots the stringified index per rewalk alongside the content HTML; the route reads from the snapshot and never from disk. (3) `wireLookup` reads from the DOM only after the fetch resolves: the modal's input, the results list, up/down/Enter/Esc. The rank function is a pure function from (query, rows) to ordered results; the renderer paints one row per hit using the shared snippet-mark helper.",
19
+ "interfaces": [
20
+ {
21
+ "name": "GET /index.json",
22
+ "kind": "httpRoute",
23
+ "description": "[draft] Returns `{ rows: Array<{ id, kind, title, snippet, parent, tab }> }` for the current tree. Built once per rewalk; content-type application/json; relative to the viewer mount so wespa's reverse proxy sees `./index.json` under the mount point."
24
+ },
25
+ {
26
+ "name": "buildViewIndex",
27
+ "kind": "other",
28
+ "description": "[draft] Pure projection of BuiltTreeModel into the index row list used by the LookupModal; one row per document id plus one per AC."
29
+ },
30
+ {
31
+ "name": "renderLookupModal",
32
+ "kind": "other",
33
+ "description": "[draft] Emits the LookupModal shell HTML (input, results list, empty state, reload affordance) for the shell page; results are rendered client-side after the index fetch resolves."
34
+ },
35
+ {
36
+ "name": "wireLookup",
37
+ "kind": "other",
38
+ "description": "[draft] Client-side wiring for the LookupModal: Cmd/Ctrl+F keyboard binding (page-init), SearchButton open, lazy `./index.json` fetch, SSE-driven cache invalidation, type-ahead ranking per design doc section 6, up/down/Enter/Esc keyboard navigation, pick writes `#tab=<tab>&entity=<id>` through writeHash so `embed` and `theme` survive."
39
+ }
40
+ ],
41
+ "notes": "Depends on PR 1's Router (writeHash, resolveHash, parseHashParams, findByDocId, openAncestorDetails) and the relative-script policy (no leading slash, no inline script). The release notes name `./index.json` as a new route so wespa's proxy allow-list is extended before the version pin bump (Dex contract, design section 9). No outbound postMessage is added; the modal does not persist search terms. The route is the fourth or later such release-noted route (PR 1 landed `./page-init.js` as the first)."
42
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "createdAt": "2026-10-02T19:30:00Z",
3
+ "updatedAt": "2026-10-02T19:30:00Z",
4
+ "tacId": "TAC-4133-readiness-po-layer",
5
+ "prdId": "PRD-001",
6
+ "tadId": "TAD-001",
7
+ "version": "0.1.0",
8
+ "status": "draft",
9
+ "name": "Readiness tab PO layer: VerdictCard, QuestionCard, NextStep, ReqWorkTable, StageLegend, phrasebook, For-engineers DocRow",
10
+ "purpose": "Render the Readiness tab PO layer: VerdictCard, QuestionCard, NextStep, ReqWorkTable, StageLegend, phrasebook, and the For-engineers DocRow. Four blocks for the product owner (two VerdictCards at equal height with the chain term muted beside the plain label; QuestionCards grouped by source span; a what-happens-next paragraph; a Requirements that still need work table) plus one DocRow at the foot that collapses the full engineer view inside without removing anything from the DOM. The adapter seam `toQuestions(readiness)` is a pure module that reads blockers and the phrasebook today and switches to `readiness.questions` when DEFINE step 3 PR 2 ships `computeQuestions`.",
11
+ "responsibilities": [
12
+ "src/view/readiness/phrasebook.js: a plain data module keyed by check id; every row of design doc section 8's PO question table has an entry carrying `{ heading, ask, hint }` and an `optional` flag for decisions-with-a-default. The strings are the PO wording verbatim; no em-dashes. The phrasebook never duplicates the stage-gate meanings those live in STAGE_GATES (section 4 of the design).",
13
+ "src/view/readiness/question-adapter.js: `toQuestions(readiness)` is a pure function that returns `{ groups: Array<{ label, items: Array<{ heading, ask, hint, detail, checkId }> }> }`. When `readiness.questions` exists (DEFINE step 3 PR 2 feature-detected via `preferReadinessQuestions(readiness)`), it reads from there; otherwise it reads `personas.productOwner.blockers`, groups by source span (`brief:<n>` for brief checks; the REQ id for stories/skeleton checks; otherwise the stage), and fills asks from the phrasebook. Decisions-with-a-default land in an optional group that never holds up the hand-over.",
14
+ "src/view/readiness/po-layer.js: `renderReadinessPO(readiness, { persona, phrasebook })` emits the four PO blocks plus the For-engineers DocRow. Two VerdictCards share a `rcf-po-verdicts` grid so the browser lays them out at equal height; each card carries the plain label and the chain term (`intent-complete`, `ready-to-build`) muted beside it. The For-engineers DocRow is closed when `persona === 'productOwner'` (and `unstated`); open when `persona === 'engineer'`.",
15
+ "src/view/readiness/stage-legend.js: `renderStageLegend(stages)` emits the StageLegend dialog with D1..D8 by name, one-line meaning and posture, read from STAGE_GATES and the readiness stages[].state. Every D-chip and D-prefixed check name carries a hover title and opens the legend on click; wiring in page-init.js.",
16
+ "src/view/readiness.js: `renderReadinessPanel` keeps its signature but now returns the PO layer followed by the For-engineers DocRow (whose body is the former engineer rendering structurally identical). The CLI verdict strings (`formatVerdictLines`) still render inside For-engineers so parity with `rcf define readiness --json` is unchanged (AC-18002-3)."
17
+ ],
18
+ "internalStructure": "Five composition seams. (1) phrasebook.js is a static data module. (2) question-adapter.js is a pure function: `(readiness) => { groups }`; `preferReadinessQuestions(readiness)` is the feature detect that flips the source when DEFINE step 3 lands. (3) po-layer.js is a server-rendered HTML string builder; no inline script added to the page. (4) stage-legend.js is a server-rendered dialog whose client wiring lives in page-init.js (`wireStageLegend`). (5) readiness.js wraps the engineer rendering in a `<details id=\"rcf-readiness-engineer\" class=\"rcf-po-engineer\">` that opens by default when the register is engineer; the body carries the full existing engineer view unchanged.",
19
+ "interfaces": [
20
+ {
21
+ "name": "renderReadinessPO",
22
+ "kind": "other",
23
+ "description": "[draft] renderReadinessPO(readiness, { persona, phrasebook }) returns the four PO blocks plus the For-engineers DocRow as one HTML string."
24
+ },
25
+ {
26
+ "name": "toQuestions",
27
+ "kind": "other",
28
+ "description": "[draft] toQuestions(readiness) is a pure function that returns `{ groups: Array<{ label, items: Array<{ heading, ask, hint, detail, checkId }> }> }` built from the phrasebook over blockers today and from readiness.questions tomorrow (feature-detected via preferReadinessQuestions)."
29
+ },
30
+ {
31
+ "name": "renderStageLegend",
32
+ "kind": "other",
33
+ "description": "[draft] renderStageLegend(stages) returns the StageLegend dialog HTML (D1..D8 by name, one-line meaning, posture)."
34
+ }
35
+ ],
36
+ "notes": "Depends on PR 2's DocRow component, PR 1's AppShell and relative-script policy, and PR 2's SubTabStrip (not used here but shared vocabulary). Amends TAC-4127-readiness-view: the old engineer rendering is kept but is now the body of the For-engineers DocRow at the foot of the panel; nothing is removed from the engineer-level DOM (AC-18002-3). The phrasebook ships now (design doc section 11 recommendation 4); when DEFINE step 3 PR 2 lands `readiness.questions`, the adapter switches source with a one-line feature detect and the call site is unchanged. No em-dash is ever written in the phrasebook strings."
37
+ }