rcf-lite 0.28.2 → 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.
- package/CHANGELOG.md +317 -0
- package/bin/rcf.js +8 -0
- package/fixtures/canary-manifest.json +9 -9
- package/guidance/define-draft-shapes.md +89 -0
- package/guidance/define-intake.md +97 -0
- package/guidance/define-intent.md +107 -0
- package/guidance/discover-prototype.md +67 -0
- package/guidance/discovery-prototypes.md +69 -0
- package/guidance/harness-template.md +34 -0
- package/guidance/managed/agent-instructions-block.hash +1 -1
- package/guidance/managed/agent-instructions-block.md +34 -0
- package/guidance/manifest.json +9 -2
- package/package.json +1 -1
- package/rcf/adrs/adr-4120-canonical-json-hashing.json +30 -0
- package/rcf/adrs/adr-4121-define-sidecars.json +30 -0
- package/rcf/adrs/adr-4122-define-gates-blocking-and-optout.json +30 -0
- package/rcf/adrs/adr-4123-readiness-single-object.json +30 -0
- package/rcf/adrs/adr-4124-define-freeze-verb.json +30 -0
- package/rcf/adrs/adr-4126-persona-tagged-blockers-two-levels.json +40 -0
- package/rcf/adrs/adr-4127-readiness-tab-order-and-register.json +40 -0
- package/rcf/adrs/adr-4129-questions-harness-driven-loop.json +25 -0
- package/rcf/adrs/adr-4130-intake-harness-split-tool-scanned.json +20 -0
- package/rcf/adrs/adr-4132-prototypes-in-discovery-as-evidence.json +20 -0
- package/rcf/adrs/adr-4133-dag-dependency-depth-layout.json +40 -0
- package/rcf/adrs/adr-4134-index-json-route.json +35 -0
- package/rcf/adrs/adr-4135-readiness-po-register-and-adapter.json +35 -0
- package/rcf/adrs/adr-4136-embedding-host-fixture.json +35 -0
- package/rcf/code-nodes/cn-700.json +23 -0
- package/rcf/code-nodes/cn-701.json +14 -0
- package/rcf/code-nodes/cn-702.json +14 -0
- package/rcf/code-nodes/cn-703.json +16 -0
- package/rcf/code-nodes/cn-720.json +15 -0
- package/rcf/code-nodes/cn-721.json +15 -0
- package/rcf/code-nodes/cn-722.json +15 -0
- package/rcf/code-nodes/cn-723.json +18 -0
- package/rcf/code-nodes/cn-724.json +14 -0
- package/rcf/code-nodes/cn-725.json +37 -0
- package/rcf/code-nodes/cn-726.json +25 -0
- package/rcf/code-nodes/cn-727.json +23 -0
- package/rcf/code-nodes/cn-728.json +37 -0
- package/rcf/code-nodes/cn-729.json +26 -0
- package/rcf/code-nodes/cn-730.json +24 -0
- package/rcf/code-nodes/cn-731.json +20 -0
- package/rcf/code-nodes/cn-732.json +14 -0
- package/rcf/code-nodes/cn-733.json +14 -0
- package/rcf/code-nodes/cn-734.json +14 -0
- package/rcf/code-nodes/cn-735.json +20 -0
- package/rcf/code-nodes/cn-736.json +14 -0
- package/rcf/code-nodes/cn-737.json +14 -0
- package/rcf/code-nodes/cn-738.json +14 -0
- package/rcf/code-nodes/cn-739.json +18 -0
- package/rcf/code-nodes/cn-740.json +19 -0
- package/rcf/code-nodes/cn-741.json +16 -0
- package/rcf/code-nodes/cn-742.json +16 -0
- package/rcf/code-nodes/cn-743.json +15 -0
- package/rcf/code-nodes/cn-744.json +22 -0
- package/rcf/code-nodes/cn-745.json +16 -0
- package/rcf/code-nodes/cn-746.json +14 -0
- package/rcf/code-nodes/cn-747.json +18 -0
- package/rcf/code-nodes/cn-748.json +16 -0
- package/rcf/code-nodes/cn-749.json +16 -0
- package/rcf/code-nodes/cn-750.json +15 -0
- package/rcf/code-nodes/cn-751.json +18 -0
- package/rcf/code-nodes/cn-752.json +14 -0
- package/rcf/fbs/fbs-190.json +37 -0
- package/rcf/fbs/fbs-191.json +34 -0
- package/rcf/fbs/fbs-192.json +17 -0
- package/rcf/fbs/fbs-193.json +37 -0
- package/rcf/fbs/fbs-194.json +35 -0
- package/rcf/fbs/fbs-195.json +37 -0
- package/rcf/fbs/fbs-196.json +45 -0
- package/rcf/fbs/fbs-197.json +41 -0
- package/rcf/fbs/fbs-198.json +42 -0
- package/rcf/fbs/fbs-199.json +59 -0
- package/rcf/fbs/fbs-200.json +46 -0
- package/rcf/fbs/fbs-201.json +49 -0
- package/rcf/fbs/fbs-202.json +110 -0
- package/rcf/fbs/fbs-203.json +100 -0
- package/rcf/requirements/req-001.json +2 -1
- package/rcf/requirements/req-002.json +3 -2
- package/rcf/requirements/req-003.json +2 -1
- package/rcf/requirements/req-004.json +2 -1
- package/rcf/requirements/req-005.json +2 -1
- package/rcf/requirements/req-006.json +2 -1
- package/rcf/requirements/req-007.json +2 -1
- package/rcf/requirements/req-008.json +2 -1
- package/rcf/requirements/req-009.json +2 -1
- package/rcf/requirements/req-010.json +5 -2
- package/rcf/requirements/req-011.json +2 -1
- package/rcf/requirements/req-012.json +2 -1
- package/rcf/requirements/req-013.json +2 -1
- package/rcf/requirements/req-014.json +2 -1
- package/rcf/requirements/req-015.json +2 -1
- package/rcf/requirements/req-016.json +5 -2
- package/rcf/requirements/req-017.json +6 -2
- package/rcf/requirements/req-018.json +5 -2
- package/rcf/requirements/req-019.json +5 -2
- package/rcf/requirements/req-020.json +5 -2
- package/rcf/requirements/req-021.json +5 -2
- package/rcf/requirements/req-022.json +5 -2
- package/rcf/requirements/req-023.json +6 -4
- package/rcf/requirements/req-024.json +5 -2
- package/rcf/requirements/req-025.json +5 -2
- package/rcf/requirements/req-026.json +5 -2
- package/rcf/requirements/req-027.json +5 -2
- package/rcf/requirements/req-028.json +5 -2
- package/rcf/requirements/req-029.json +5 -2
- package/rcf/requirements/req-030.json +5 -2
- package/rcf/requirements/req-040.json +5 -2
- package/rcf/requirements/req-041.json +5 -2
- package/rcf/requirements/req-042.json +5 -2
- package/rcf/requirements/req-043.json +5 -2
- package/rcf/requirements/req-050.json +5 -2
- package/rcf/requirements/req-051.json +5 -2
- package/rcf/requirements/req-052.json +5 -2
- package/rcf/requirements/req-053.json +5 -2
- package/rcf/requirements/req-054.json +5 -2
- package/rcf/requirements/req-060.json +5 -2
- package/rcf/requirements/req-061.json +5 -2
- package/rcf/requirements/req-062.json +5 -2
- package/rcf/requirements/req-063.json +5 -2
- package/rcf/requirements/req-070.json +5 -2
- package/rcf/requirements/req-071.json +5 -2
- package/rcf/requirements/req-072.json +5 -2
- package/rcf/requirements/req-073.json +5 -2
- package/rcf/requirements/req-074.json +5 -2
- package/rcf/requirements/req-075.json +5 -2
- package/rcf/requirements/req-076.json +5 -2
- package/rcf/requirements/req-077.json +5 -2
- package/rcf/requirements/req-080.json +5 -2
- package/rcf/requirements/req-081.json +5 -2
- package/rcf/requirements/req-082.json +5 -2
- package/rcf/requirements/req-083.json +5 -2
- package/rcf/requirements/req-084.json +5 -2
- package/rcf/requirements/req-085.json +5 -2
- package/rcf/requirements/req-086.json +5 -2
- package/rcf/requirements/req-090.json +5 -2
- package/rcf/requirements/req-091.json +5 -2
- package/rcf/requirements/req-092.json +5 -2
- package/rcf/requirements/req-093.json +5 -2
- package/rcf/requirements/req-094.json +5 -2
- package/rcf/requirements/req-100.json +5 -2
- package/rcf/requirements/req-101.json +5 -2
- package/rcf/requirements/req-102.json +5 -2
- package/rcf/requirements/req-103.json +5 -2
- package/rcf/requirements/req-104.json +5 -2
- package/rcf/requirements/req-110.json +5 -2
- package/rcf/requirements/req-111.json +5 -2
- package/rcf/requirements/req-112.json +5 -2
- package/rcf/requirements/req-113.json +5 -2
- package/rcf/requirements/req-114.json +5 -2
- package/rcf/requirements/req-115.json +5 -2
- package/rcf/requirements/req-120.json +5 -2
- package/rcf/requirements/req-121.json +5 -2
- package/rcf/requirements/req-122.json +5 -2
- package/rcf/requirements/req-123.json +5 -2
- package/rcf/requirements/req-124.json +5 -2
- package/rcf/requirements/req-125.json +5 -2
- package/rcf/requirements/req-130.json +6 -2
- package/rcf/requirements/req-131.json +6 -2
- package/rcf/requirements/req-132.json +6 -2
- package/rcf/requirements/req-133.json +6 -2
- package/rcf/requirements/req-134.json +6 -2
- package/rcf/requirements/req-140.json +5 -2
- package/rcf/requirements/req-145.json +5 -2
- package/rcf/requirements/req-150.json +6 -2
- package/rcf/requirements/req-151.json +6 -2
- package/rcf/requirements/req-152.json +6 -2
- package/rcf/requirements/req-153.json +6 -2
- package/rcf/requirements/req-154.json +5 -2
- package/rcf/requirements/req-155.json +4 -1
- package/rcf/requirements/req-156.json +4 -1
- package/rcf/requirements/req-157.json +4 -1
- package/rcf/requirements/req-158.json +4 -1
- package/rcf/requirements/req-159.json +4 -1
- package/rcf/requirements/req-160.json +4 -1
- package/rcf/requirements/req-161.json +4 -1
- package/rcf/requirements/req-162.json +4 -1
- package/rcf/requirements/req-163.json +5 -2
- package/rcf/requirements/req-164.json +5 -2
- package/rcf/requirements/req-165.json +5 -2
- package/rcf/requirements/req-170.json +33 -0
- package/rcf/requirements/req-171.json +44 -0
- package/rcf/requirements/req-172.json +25 -0
- package/rcf/requirements/req-173.json +25 -0
- package/rcf/requirements/req-174.json +31 -0
- package/rcf/requirements/req-175.json +31 -0
- package/rcf/requirements/req-176.json +25 -0
- package/rcf/requirements/req-180.json +31 -0
- package/rcf/requirements/req-181.json +31 -0
- package/rcf/requirements/req-182.json +25 -0
- package/rcf/requirements/req-186.json +25 -0
- package/rcf/requirements/req-187.json +31 -0
- package/rcf/requirements/req-188.json +25 -0
- package/rcf/tacs/tac-4120-define-delta-and-freeze.json +21 -0
- package/rcf/tacs/tac-4121-define-ledgers.json +35 -0
- package/rcf/tacs/tac-4122-define-gates.json +61 -0
- package/rcf/tacs/tac-4123-define-readiness.json +61 -0
- package/rcf/tacs/tac-4124-define-freeze.json +28 -0
- package/rcf/tacs/tac-4127-readiness-view.json +49 -0
- package/rcf/tacs/tac-4128-define-guidance.json +53 -0
- package/rcf/tacs/tac-4129-define-questions.json +45 -0
- package/rcf/tacs/tac-4130-define-intake-brief.json +35 -0
- package/rcf/tacs/tac-4131-build-dag-view.json +39 -0
- package/rcf/tacs/tac-4132-id-lookup.json +42 -0
- package/rcf/tacs/tac-4133-readiness-po-layer.json +37 -0
- package/rcf/tacs/tac-4134-wespa-host-fixture.json +54 -0
- package/rcf/test-suites/ts-016.json +3 -3
- package/rcf/test-suites/ts-204.json +30 -0
- package/rcf/test-suites/ts-205.json +22 -0
- package/rcf/test-suites/ts-206.json +22 -0
- package/rcf/test-suites/ts-207.json +22 -0
- package/rcf/test-suites/ts-208.json +22 -0
- package/rcf/test-suites/ts-209.json +22 -0
- package/rcf/test-suites/ts-210.json +22 -0
- package/rcf/test-suites/ts-211.json +22 -0
- package/rcf/test-suites/ts-212.json +22 -0
- package/rcf/test-suites/ts-213.json +50 -0
- package/rcf/test-suites/ts-214.json +74 -0
- package/rcf/test-suites/ts-215.json +211 -0
- package/rcf/test-suites/ts-216.json +94 -0
- package/rcf/test-suites/ts-217.json +62 -0
- package/rcf/test-suites/ts-218.json +78 -0
- package/rcf/test-suites/ts-219.json +70 -0
- package/rcf/test-suites/ts-220.json +78 -0
- package/rcf/test-suites/ts-221.json +78 -0
- package/rcf/test-suites/ts-222.json +100 -0
- package/rcf/test-suites/ts-223.json +94 -0
- package/rcf/test-suites/ts-224.json +70 -0
- package/rcf/test-suites/ts-225.json +78 -0
- package/rcf/test-suites/ts-226.json +158 -0
- package/rcf/test-suites/ts-227.json +38 -0
- package/rcf/test-suites/ts-228.json +86 -0
- package/rcf/test-suites/ts-229.json +83 -0
- package/rcf/user-stories/us-15601.json +4 -4
- package/rcf/user-stories/us-17001.json +27 -0
- package/rcf/user-stories/us-17002.json +22 -0
- package/rcf/user-stories/us-17003.json +22 -0
- package/rcf/user-stories/us-17004.json +22 -0
- package/rcf/user-stories/us-17005.json +22 -0
- package/rcf/user-stories/us-17006.json +21 -0
- package/rcf/user-stories/us-17007.json +21 -0
- package/rcf/user-stories/us-17008.json +22 -0
- package/rcf/user-stories/us-17101.json +22 -0
- package/rcf/user-stories/us-17102.json +22 -0
- package/rcf/user-stories/us-17201.json +38 -0
- package/rcf/user-stories/us-17301.json +38 -0
- package/rcf/user-stories/us-17302.json +50 -0
- package/rcf/user-stories/us-17401.json +70 -0
- package/rcf/user-stories/us-17402.json +55 -0
- package/rcf/user-stories/us-17403.json +60 -0
- package/rcf/user-stories/us-17501.json +48 -0
- package/rcf/user-stories/us-17502.json +60 -0
- package/rcf/user-stories/us-17503.json +65 -0
- package/rcf/user-stories/us-17601.json +58 -0
- package/rcf/user-stories/us-18001.json +55 -0
- package/rcf/user-stories/us-18002.json +35 -0
- package/rcf/user-stories/us-18101.json +35 -0
- package/rcf/user-stories/us-18202.json +40 -0
- package/rcf/user-stories/us-18601.json +55 -0
- package/rcf/user-stories/us-18602.json +50 -0
- package/rcf/user-stories/us-18603.json +45 -0
- package/rcf/user-stories/us-18701.json +50 -0
- package/rcf/user-stories/us-18702.json +50 -0
- package/rcf/user-stories/us-18801.json +45 -0
- package/rcf/user-stories/us-204.json +59 -0
- package/rcf/user-stories/us-205.json +64 -0
- package/rcf/user-stories/us-206.json +54 -0
- package/releases/releases.yaml +31 -1
- package/src/cli/feedback.js +126 -10
- package/src/cli/freeze.js +730 -0
- package/src/cli/help.js +23 -0
- package/src/cli/ledger.js +615 -0
- package/src/cli/questions.js +288 -0
- package/src/cli/readiness.js +498 -0
- package/src/cli/view.js +29 -0
- package/src/core/store/writer.js +86 -6
- package/src/define/freeze-record.js +247 -0
- package/src/define/ledgers.js +712 -0
- package/src/feedback/redact.js +105 -19
- package/src/intake/index.js +1 -1
- package/src/intake/orchestrator.js +117 -0
- package/src/mcp/tools.js +354 -0
- package/src/query/delta.js +282 -0
- package/src/query/gates.js +1107 -0
- package/src/query/index.js +50 -0
- package/src/query/questions.js +438 -0
- package/src/query/readiness.js +574 -0
- package/src/server/index.js +60 -1
- package/src/server/routes.js +133 -4
- package/src/setup/identity-seed.js +17 -0
- package/src/view/build-dag.js +563 -0
- package/src/view/components/badge.js +41 -0
- package/src/view/components/diff.js +72 -0
- package/src/view/components/doc-row.js +62 -0
- package/src/view/components/empty-state.js +35 -0
- package/src/view/components/entity-selector.js +137 -0
- package/src/view/components/filter-bar.js +97 -0
- package/src/view/components/findings-list.js +49 -0
- package/src/view/components/index.js +12 -0
- package/src/view/components/pill.js +46 -0
- package/src/view/components/sub-tab-strip.js +43 -0
- package/src/view/components/toast.js +25 -0
- package/src/view/components-fixture.js +265 -0
- package/src/view/doc-renderers/architecture.js +304 -0
- package/src/view/doc-renderers/build.js +160 -0
- package/src/view/doc-renderers/helpers.js +2 -2
- package/src/view/doc-renderers/index.js +2 -0
- package/src/view/doc-renderers/prd.js +35 -6
- package/src/view/doc-renderers/req.js +10 -2
- package/src/view/doc-renderers/user-story.js +10 -5
- package/src/view/html-page.js +717 -300
- package/src/view/index.js +109 -3
- package/src/view/live-client.js +61 -8
- package/src/view/needs-work.js +79 -0
- package/src/view/page-init.js +2193 -0
- package/src/view/product-map.js +751 -0
- package/src/view/readiness/phrasebook.js +115 -0
- package/src/view/readiness/po-layer.js +288 -0
- package/src/view/readiness/question-adapter.js +329 -0
- package/src/view/readiness/stage-legend.js +71 -0
- package/src/view/readiness.js +505 -0
- package/src/view/style.css +2279 -266
- package/src/view/test-host/embed-client.js +121 -0
- package/src/view/test-host/fixture.js +183 -0
- package/src/view/test-host/lints.js +220 -0
- package/src/view/view-index.js +323 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"createdAt": "2026-09-24T14:30:00Z",
|
|
3
|
+
"updatedAt": "2026-10-01T16:00:00Z",
|
|
4
|
+
"tacId": "TAC-4123-define-readiness",
|
|
5
|
+
"prdId": "PRD-001",
|
|
6
|
+
"tadId": "TAD-001",
|
|
7
|
+
"version": "0.1.0",
|
|
8
|
+
"status": "draft",
|
|
9
|
+
"name": "computeReadiness compute and rcf define readiness CLI",
|
|
10
|
+
"purpose": "src/query/readiness.js and src/cli/readiness.js land as a matched pair. The pure module composes computeDelta, computeImpact, the eight gate check functions from TAC-4122, computeCoverage, and the decisions ledger into the single-object contract section 2.4 pins; the CLI is its twin. Text output for readable status; --json for machine consumers and the viewer slice; --check <stage>|all for targeted inspection with the same 0-green / 4-red exit shape rcf audit coverage uses.",
|
|
11
|
+
"responsibilities": [
|
|
12
|
+
"src/query/readiness.js: computeReadiness(tree, { freeze, ledgers, profile, testPointers }) returns { tree, delta, stages, nextAction, coverage, decisions, freezeable } exactly as section 2.4 specifies; internal computeScope collects changed + added + impacted ids into a Set; internal composeStages calls each check function in D1..D8 order; internal computeNextAction picks the first failing stage and check; internal deriveTreeSummary shortens the tree hash to eight characters for the tree line.",
|
|
13
|
+
"src/query/readiness.js: computeImpact is called once per id in changed union added; impacted is the deduplicated union of every impact node id excluding the pivots; impactedFbs is the subset with kind 'fbs' AND actionNeeded === 're-execute'.",
|
|
14
|
+
"src/query/readiness.js: computeCoverage is called once tree-wide (strict per-AC, testPointers threaded through) for coverage.tree; coverage.delta is one CoverageResult per unique REQ ancestor of the delta ids (via tree.parentByChild walk to the reqId, or the doc's own reqId field).",
|
|
15
|
+
"src/query/readiness.js: freezeable is true when every stage state in ['passed', 'acknowledged', 'notApplicable'] and false otherwise; nextAction is null when freezeable is true.",
|
|
16
|
+
"src/cli/readiness.js: parseArgs on --json, --check, --help; findProjectRoot; walkTree; loadFreezeRecord; loadAllLedgers; resolveTestPointers; runWithAdmissibilityGate wraps a producer that invokes computeReadiness; on refuse the CLI prints an informational '[warn] readiness: chain admissibility refused (informational; the readiness compute still ran).' on stderr and STILL runs the compute so the diagnostic reports which stage blocks (readiness is a diagnostic, not a gate that hides its answer); the top-level exit stays 0 in that case and --check <stage> follows its normal per-stage exit rule; on ok the CLI renders text (default) or JSON.",
|
|
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
|
+
"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
|
+
"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.",
|
|
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."
|
|
26
|
+
],
|
|
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.",
|
|
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
|
+
],
|
|
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."
|
|
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
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"createdAt": "2026-10-02T20:00:00Z",
|
|
3
|
+
"updatedAt": "2026-10-02T20:00:00Z",
|
|
4
|
+
"tacId": "TAC-4134-wespa-host-fixture",
|
|
5
|
+
"prdId": "PRD-001",
|
|
6
|
+
"tadId": "TAD-001",
|
|
7
|
+
"version": "0.1.0",
|
|
8
|
+
"status": "draft",
|
|
9
|
+
"name": "Wespa host fixture: /test-host.html and the three embedding lints",
|
|
10
|
+
"purpose": "Serve a same-origin test-host fixture page that mimics the wespa shell (a 56px header, a resizable 64/200-400px sidebar, a content pane carrying `<iframe src=\"./?embed=1&theme=light\">`) and ship three CI lints (framing headers, `target=\"_top\"` grep, localStorage namespace grep) that keep wespa's embedding contract honest over time. The fixture is off the default route surface so wespa's reverse-proxy allow-list is unaffected; it is gated on a `--test-host` CLI flag or the `RCF_VIEW_TEST_HOST=1` env var and never served under production use.",
|
|
11
|
+
"responsibilities": [
|
|
12
|
+
"src/view/test-host/fixture.js: `renderTestHostPage()` returns the HTML for the fixture (56px header with Light/Dark/Auto theme toggle buttons, a resizable sidebar with a drag handle and a Collapse/Expand control, a content pane containing the single `<iframe id=\"rcf-test-host-iframe\" src=\"./?embed=1&theme=light\">` sized to 100% of its box). No inline script; the page references `./test-host.js` as a relative script.",
|
|
13
|
+
"src/view/test-host/embed-client.js: classic (non-module) browser script that wires the theme toggle buttons to `iframe.contentWindow.postMessage({ type: \"rcf-view-theme\", theme }, window.location.origin)`, the sidebar drag handle to a live width update (clamped to 64-400px) and the Collapse/Expand button to a 64px <-> 200px snap. The script posts nothing beyond `rcf-view-theme`, does not read event.origin itself (the viewer does), and writes nothing to localStorage so a passing run proves the iframe honours the message.",
|
|
14
|
+
"src/view/test-host/lints.js: `assertFramingHeaders(response)` scans a GET / response for `X-Frame-Options`, CSP `frame-ancestors` and `X-Content-Type-Options` oddities (any present or any `frame-ancestors` directive forbidding `self` fails); `assertNoTargetTop(renderedHtml)` scans the HTML for any `target=\"_top\"` or `target='_top'` occurrence and refuses on a non-zero count; `assertLocalStorageNamespace(sourceTree)` scans every `.js` file under `src/view/**` (not vendored, not `.min.js`) for `localStorage.(setItem|getItem|removeItem)(` and `storage.(setItem|getItem|removeItem)(` call sites and refuses on any key literal that does not start with `rcf-view:v1:`.",
|
|
15
|
+
"src/server/routes.js: when `deps.testHost === true`, register `GET /test-host.html` returning `renderTestHostPage()` with `content-type: text/html` and no framing-blocking header; register `GET /test-host.js` returning the embed-client script as `application/javascript` with no framing-blocking header. Off the default route surface: both routes 404 unless `testHost` is on.",
|
|
16
|
+
"src/server/index.js: plumb an optional `testHost: boolean` option through `startServer(args)` into the router; default false. Wire the embed-client asset at startup via `loadStaticAsset`, same discipline as `./live-client.js`.",
|
|
17
|
+
"src/cli/view.js: parse `--test-host` into `opts.testHost`; precedence is flag beats `RCF_VIEW_TEST_HOST=1` env beats default (false). Pass `testHost` through to `startServer`. Help text names the flag and marks it a test-suite fixture, not a shipped UI.",
|
|
18
|
+
"test/view/wespa-host-fixture.test.js: end-to-end node-level checks that boot the router with `testHost: true` over `http.createServer`, GET /, GET /test-host.html, GET /test-host.js, assert the three lints green, parse the iframe `src` from the fixture HTML and assert `./?embed=1&theme=light` exactly, assert the fixture carries no inline `<script>` block, assert the viewer's rendered HTML carries `<html data-embed=\"1\">` wiring (via page-init.js) and that the fixture page does not persist theme to localStorage."
|
|
19
|
+
],
|
|
20
|
+
"internalStructure": "Three composition seams. (1) `renderTestHostPage()` is a pure function (no reads, no I/O) that emits the host HTML and references the relative embed-client script; swap the host markup in one place. (2) The embed-client script is served as a snapshotted asset at startup; it is the only client-side JS the fixture carries. (3) `src/view/test-host/lints.js` exports three pure assertions keyed on their inputs (response object, HTML string, source tree root) so each lint composes into any test that needs it and the test file is the sole caller.",
|
|
21
|
+
"interfaces": [
|
|
22
|
+
{
|
|
23
|
+
"name": "GET /test-host.html",
|
|
24
|
+
"kind": "httpRoute",
|
|
25
|
+
"description": "[draft] Returns the host fixture HTML (`renderTestHostPage()`). Served only when `startServer({ testHost: true })` is on (CLI `--test-host` or env `RCF_VIEW_TEST_HOST=1`); 404 otherwise. No `X-Frame-Options`, no CSP `frame-ancestors` directive. Relative script `./test-host.js` is the only client-side JS. Not under the production root; the viewer's `/` surface is untouched."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "GET /test-host.js",
|
|
29
|
+
"kind": "httpRoute",
|
|
30
|
+
"description": "[draft] Returns the embed-client script that wires the theme toggle, sidebar drag and Collapse/Expand to postMessage and sidebar width updates. `application/javascript`; snapshotted at startup. Same `testHost` gate as the HTML route."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "assertFramingHeaders",
|
|
34
|
+
"kind": "moduleApi",
|
|
35
|
+
"description": "[draft] `assertFramingHeaders(response)` on a `GET /` response. Refuses if `X-Frame-Options` is present, if a CSP `frame-ancestors` directive forbids `self`, or if `X-Content-Type-Options: nosniff` is set in a way that breaks the fixture (today it does not; the lint still records absence). Returns `{ ok: true }` or throws with the first failure."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "assertNoTargetTop",
|
|
39
|
+
"kind": "moduleApi",
|
|
40
|
+
"description": "[draft] `assertNoTargetTop(renderedHtml)` greps the server-rendered HTML for `target=\"_top\"` and `target='_top'` and refuses on any match. Returns `{ ok: true, count: 0 }` or `{ ok: false, count: n, matches: [...] }`."
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "assertLocalStorageNamespace",
|
|
44
|
+
"kind": "moduleApi",
|
|
45
|
+
"description": "[draft] `assertLocalStorageNamespace(sourceTreeRoot)` walks every `.js` file under `src/view/**` (excluding vendored and `.min.js`), finds every `localStorage.(setItem|getItem|removeItem)(KEY)` and `storage.(setItem|getItem|removeItem)(KEY)` call site, and asserts that the key literal starts with `rcf-view:v1:` (or references `STORAGE_NS` which equals `rcf-view:v1:`). Returns `{ ok: true, scanned: n }` or `{ ok: false, violations: [...] }`."
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "renderTestHostPage",
|
|
49
|
+
"kind": "moduleApi",
|
|
50
|
+
"description": "[draft] Pure function that emits the fixture HTML (56px header, resizable 64/200-400px sidebar, iframe content pane). No inline script; references `./test-host.js` as the single client-side JS."
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"notes": "Depends on PR 1's `embed` and `theme` contract (page-init.js stamping `<html data-embed=\"1\">` and `<html data-theme>`, origin-checked `rcf-view-theme` listener, hash navigation preserving the host query string), on PR 1's relative-script policy (no inline script, no leading-slash references) and on live-client.js's existing `STORAGE_NS = 'rcf-view:v1:'` namespace. The test-host route surface is behind `--test-host` / `RCF_VIEW_TEST_HOST=1`; the release notes mark it as a test-suite fixture, not a shipped UI, so wespa's proxy allow-list is unaffected. The three lints are called from the viewer's test suite; wespa smoke on the live wespa shell is Dex's side."
|
|
54
|
+
}
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
{
|
|
15
15
|
"id": "TC-016-tools-list-structured-schemas",
|
|
16
16
|
"acId": "AC-601-1",
|
|
17
|
-
"description": "an MCP client listing tools sees all
|
|
17
|
+
"description": "an MCP client listing tools sees all fourteen rcf tools with input and output schemas",
|
|
18
18
|
"status": "pending",
|
|
19
|
-
"testPointer": "test/mcp/stdio.e2e.test.js::e2e: tools/list returns all
|
|
19
|
+
"testPointer": "test/mcp/stdio.e2e.test.js::e2e: tools/list returns all fourteen tools with schemas"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"id": "TC-016-mcp-coverage-cli-parity",
|
|
@@ -34,4 +34,4 @@
|
|
|
34
34
|
}
|
|
35
35
|
],
|
|
36
36
|
"status": "draft"
|
|
37
|
-
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "TS-204",
|
|
3
|
+
"usId": "US-17001",
|
|
4
|
+
"title": "Product Map shape grouping",
|
|
5
|
+
"purpose": "Cover the shape-grouping renderer and its drill-down for AC-17001-1 and AC-17001-2.",
|
|
6
|
+
"testLevel": "integration",
|
|
7
|
+
"acIds": [
|
|
8
|
+
"AC-17001-1",
|
|
9
|
+
"AC-17001-2"
|
|
10
|
+
],
|
|
11
|
+
"testCases": [
|
|
12
|
+
{
|
|
13
|
+
"id": "TC-204-shape-buckets-present",
|
|
14
|
+
"acId": "AC-17001-1",
|
|
15
|
+
"description": "Rendering the tree emits a product-map tab with shape buckets covering every classified shape plus Unclassified and Business rules.",
|
|
16
|
+
"status": "passing",
|
|
17
|
+
"testPointer": "test/view/product-map.test.js::product-map shape grouping renders one bucket per shape plus Unclassified and Business rules"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "TC-204-shape-drill-req-us-ac",
|
|
21
|
+
"acId": "AC-17001-2",
|
|
22
|
+
"description": "The shape grouping drills bucket -> REQ (status pill) -> US -> AC using the shared detailsWrap helper; every id inside the Product Map tab is bucket-scoped so the same REQ can appear under multiple buckets without duplicating ids.",
|
|
23
|
+
"status": "passing",
|
|
24
|
+
"testPointer": "test/view/product-map.test.js::product-map shape grouping drills bucket to REQ to US to AC with status pill, ids scoped to the bucket (P2-1)"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"status": "approved",
|
|
28
|
+
"createdAt": "2026-09-21T17:00:00.000Z",
|
|
29
|
+
"updatedAt": "2026-09-21T18:02:10.540Z"
|
|
30
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "TS-205",
|
|
3
|
+
"usId": "US-17002",
|
|
4
|
+
"title": "Product Map status filter",
|
|
5
|
+
"purpose": "Cover AC-17002-1: status filter control and empty-bucket collapse.",
|
|
6
|
+
"testLevel": "integration",
|
|
7
|
+
"acIds": [
|
|
8
|
+
"AC-17002-1"
|
|
9
|
+
],
|
|
10
|
+
"testCases": [
|
|
11
|
+
{
|
|
12
|
+
"id": "TC-205-status-filter-control",
|
|
13
|
+
"acId": "AC-17002-1",
|
|
14
|
+
"description": "The tab renders a status filter with six options; server-rendered outer pm-req counts per status match a model-side prediction across every grouping (the same DOM the client-side applyPmStatusFilter reads).",
|
|
15
|
+
"status": "passing",
|
|
16
|
+
"testPointer": "test/view/product-map.test.js::product-map status filter markup + server-rendered per-bucket status counts match the model (P2-3 real assertion)"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"status": "approved",
|
|
20
|
+
"createdAt": "2026-09-21T17:00:00.000Z",
|
|
21
|
+
"updatedAt": "2026-09-21T18:02:10.757Z"
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "TS-206",
|
|
3
|
+
"usId": "US-17003",
|
|
4
|
+
"title": "Product Map component grouping",
|
|
5
|
+
"purpose": "Cover AC-17003-1: TAD-rooted TAC hierarchy with Unmapped bucket.",
|
|
6
|
+
"testLevel": "integration",
|
|
7
|
+
"acIds": [
|
|
8
|
+
"AC-17003-1"
|
|
9
|
+
],
|
|
10
|
+
"testCases": [
|
|
11
|
+
{
|
|
12
|
+
"id": "TC-206-component-tad-root",
|
|
13
|
+
"acId": "AC-17003-1",
|
|
14
|
+
"description": "Component grouping shows TAD -> TAC (with dependencies) -> US -> REQ -> AC, with an Unmapped bucket for REQs no component reaches.",
|
|
15
|
+
"status": "passing",
|
|
16
|
+
"testPointer": "test/view/product-map.test.js::product-map component grouping roots at TAD and lists TAC to US to REQ to AC with an Unmapped bucket"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"status": "approved",
|
|
20
|
+
"createdAt": "2026-09-21T17:00:00.000Z",
|
|
21
|
+
"updatedAt": "2026-09-21T17:00:00.000Z"
|
|
22
|
+
}
|