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.
- package/CHANGELOG.md +107 -1
- package/bin/rcf.js +4 -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-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-725.json +18 -2
- package/rcf/code-nodes/cn-726.json +10 -2
- package/rcf/code-nodes/cn-727.json +13 -4
- package/rcf/code-nodes/cn-728.json +18 -2
- package/rcf/code-nodes/cn-729.json +10 -2
- package/rcf/code-nodes/cn-730.json +13 -3
- 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-196.json +13 -5
- 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-002.json +2 -2
- package/rcf/requirements/req-173.json +2 -2
- package/rcf/requirements/req-174.json +10 -4
- package/rcf/requirements/req-175.json +10 -4
- 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-4121-define-ledgers.json +15 -3
- package/rcf/tacs/tac-4122-define-gates.json +40 -4
- package/rcf/tacs/tac-4123-define-readiness.json +39 -3
- 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-215.json +148 -4
- package/rcf/test-suites/ts-216.json +19 -3
- 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-17302.json +50 -0
- package/rcf/user-stories/us-17401.json +14 -2
- package/rcf/user-stories/us-17402.json +55 -0
- package/rcf/user-stories/us-17403.json +60 -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 +11 -1
- package/src/cli/freeze.js +730 -0
- package/src/cli/help.js +12 -0
- package/src/cli/ledger.js +228 -12
- package/src/cli/questions.js +288 -0
- package/src/cli/readiness.js +267 -49
- package/src/cli/view.js +29 -0
- package/src/define/freeze-record.js +30 -15
- package/src/define/ledgers.js +210 -11
- 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 +6 -0
- package/src/query/gates.js +314 -39
- package/src/query/index.js +16 -2
- package/src/query/questions.js +438 -0
- package/src/query/readiness.js +248 -7
- package/src/server/index.js +24 -2
- package/src/server/routes.js +68 -1
- 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/index.js +2 -0
- package/src/view/doc-renderers/prd.js +35 -6
- package/src/view/html-page.js +700 -602
- package/src/view/index.js +96 -3
- package/src/view/live-client.js +39 -6
- package/src/view/needs-work.js +79 -0
- package/src/view/page-init.js +2193 -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 +2017 -382
- 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,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"createdAt": "2026-10-03T09:55:00Z",
|
|
3
|
+
"updatedAt": "2026-10-03T10:00:00Z",
|
|
4
|
+
"purpose": "Cover US-206 (wespa's embedding contract is proved by a CI host fixture): the fixture renders a 56px header + resizable sidebar around an iframe pointed at ./?embed=1&theme=light with no inline script (AC-206-1), theme flips via postMessage without a reload (AC-206-2, source invariants + Playwright live proof in the lane evidence), embed and theme survive a hash-only in-iframe navigation (AC-206-3, source invariants + Playwright live proof), cross-origin messages are ignored by the viewer listener (AC-206-4), the three lints keep framing headers, target=_top and localStorage namespace in place (AC-206-5), and no new outbound postMessage shape is introduced by the viewer or the fixture (AC-206-6). All TCs resolve to node tests under test/view/wespa-host-fixture.test.js.",
|
|
5
|
+
"testLevel": "unit",
|
|
6
|
+
"acIds": [
|
|
7
|
+
"AC-206-1",
|
|
8
|
+
"AC-206-2",
|
|
9
|
+
"AC-206-3",
|
|
10
|
+
"AC-206-4",
|
|
11
|
+
"AC-206-5",
|
|
12
|
+
"AC-206-6"
|
|
13
|
+
],
|
|
14
|
+
"id": "TS-229",
|
|
15
|
+
"usId": "US-206",
|
|
16
|
+
"title": "Wespa host fixture: iframe shell, theme postMessage, hash-nav embed preservation, cross-origin refusal, three lints, no new outbound postMessage shape",
|
|
17
|
+
"testCases": [
|
|
18
|
+
{
|
|
19
|
+
"id": "TC-229-fixture-shell",
|
|
20
|
+
"acId": "AC-206-1",
|
|
21
|
+
"description": "renderTestHostPage returns an HTML5 document with a 56px wespa-header, data-rcf-host-sidebar + data-rcf-host-sidebar-drag markers, data-rcf-host-theme light/dark toggles, an iframe whose src is literally './?embed=1&theme=light' (HTML-entity encoded), a '<script src=./test-host.js defer>' line and zero inline <script> blocks.",
|
|
22
|
+
"status": "pending",
|
|
23
|
+
"testPointer": "test/view/wespa-host-fixture.test.js::renderTestHostPage: HTML5 document, no inline script, iframe src is ./?embed=1&theme=light"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "TC-229-test-host-http-roundtrip",
|
|
27
|
+
"acId": "AC-206-1",
|
|
28
|
+
"description": "The router serves /test-host.html with the fixture body (iframe src + script tag) and /test-host.js with application/javascript when testHost: true; both routes return 404 by default so wespa's reverse-proxy allow-list is unaffected.",
|
|
29
|
+
"status": "pending",
|
|
30
|
+
"testPointer": "test/view/wespa-host-fixture.test.js::/test-host.html is served when testHost: true; iframe src and no inline script round-trip over HTTP"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "TC-229-theme-postmessage-source",
|
|
34
|
+
"acId": "AC-206-2",
|
|
35
|
+
"description": "src/view/test-host/embed-client.js posts { type: 'rcf-view-theme', theme } to iframe.contentWindow targeting window.location.origin, and its code (comments stripped) contains no localStorage/sessionStorage write (ADR-4136 does-not-persist invariant). The live postMessage round-trip is captured in Playwright evidence (agents/dave/remote-lane/inbox/r-20261002-161325-e2a2/output/evidence/test-host-dark-iframe-flipped.png).",
|
|
36
|
+
"status": "pending",
|
|
37
|
+
"testPointer": "test/view/wespa-host-fixture.test.js::AC-206-2: embed-client posts { type: rcf-view-theme, theme } to the iframe targeting window.location.origin"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "TC-229-hash-nav-embed-preserved",
|
|
41
|
+
"acId": "AC-206-3",
|
|
42
|
+
"description": "src/view/page-init.js exposes writeHash (reassigns location.hash only, preserving the search string by construction) and stamps data-embed + data-theme on load. Live hash-nav proof lives in Playwright evidence (hash-nav-proof.txt + test-host-iframe-hash-nav-embed-preserved.png).",
|
|
43
|
+
"status": "pending",
|
|
44
|
+
"testPointer": "test/view/wespa-host-fixture.test.js::AC-206-3: page-init preserves embed=1 and theme= across hash writes (urlWithHash + writeHash)"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "TC-229-cross-origin-ignored",
|
|
48
|
+
"acId": "AC-206-4",
|
|
49
|
+
"description": "src/view/page-init.js registers an addEventListener('message', ...) whose first guard returns early when ev.origin !== window.location.origin; cross-origin messages never reach the applyTheme path and <html data-theme> is unchanged.",
|
|
50
|
+
"status": "pending",
|
|
51
|
+
"testPointer": "test/view/wespa-host-fixture.test.js::AC-206-4: page-init theme message listener rejects messages whose origin differs from window.location.origin"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "TC-229-lint-framing",
|
|
55
|
+
"acId": "AC-206-5",
|
|
56
|
+
"description": "assertFramingHeaders over GET / returns ok: true (no X-Frame-Options; no CSP frame-ancestors forbidding same-origin). Positive + negative unit cases also pass (refuses on X-Frame-Options present, refuses on frame-ancestors 'none', accepts frame-ancestors 'self').",
|
|
57
|
+
"status": "pending",
|
|
58
|
+
"testPointer": "test/view/wespa-host-fixture.test.js::lint 1 (framing): GET / carries no X-Frame-Options and no CSP frame-ancestors that forbids same-origin"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "TC-229-lint-no-target-top",
|
|
62
|
+
"acId": "AC-206-5",
|
|
63
|
+
"description": "assertNoTargetTop returns count: 0 across every rendered page (renderPage shell, renderComponentsFixturePage, renderTestHostPage). Also lints the populated dogfood tree render (renderModelToPage) so per-document conditional output cannot smuggle a target=\"_top\" past the test. Positive + negative unit cases cover target=\"_top\" / target='_top' / target=_top.",
|
|
64
|
+
"status": "pending",
|
|
65
|
+
"testPointer": "test/view/wespa-host-fixture.test.js::lint 2 (target=\"_top\"): zero occurrences across every rendered page (shell, components fixture, test-host fixture, populated dogfood tree)"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "TC-229-lint-localstorage-namespace",
|
|
69
|
+
"acId": "AC-206-5",
|
|
70
|
+
"description": "assertLocalStorageNamespace over the src/view tree returns ok: true; every localStorage/sessionStorage call site resolves to a key starting with 'rcf-view:v1:'.",
|
|
71
|
+
"status": "pending",
|
|
72
|
+
"testPointer": "test/view/wespa-host-fixture.test.js::lint 3 (localStorage namespace): every storage call site under src/view uses an rcf-view:v1: key"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"id": "TC-229-no-new-outbound-postmessage-shape",
|
|
76
|
+
"acId": "AC-206-6",
|
|
77
|
+
"description": "A grep over src/view/** (excluding test-host/embed-client.js, the sanctioned emitter) finds zero .postMessage( call sites; the embed-client itself posts exactly one shape, 'rcf-view-theme'.",
|
|
78
|
+
"status": "pending",
|
|
79
|
+
"testPointer": "test/view/wespa-host-fixture.test.js::AC-206-6: no outbound postMessage shape beyond rcf-view-theme is introduced by the viewer or the fixture"
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
"status": "draft"
|
|
83
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-17302",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-173",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "update and the pointer grammar",
|
|
8
|
+
"description": "User story under REQ-173. Every ledger supports `update <id>` with field patches validated as `add` validates; `resolvedBy` on a brief statement is a closed pointer grammar validated by D2.",
|
|
9
|
+
"asA": "harness",
|
|
10
|
+
"iWant": "to patch a statement's kind, text or resolution",
|
|
11
|
+
"soThat": "a correction does not mint a duplicate",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-17302-1",
|
|
15
|
+
"description": "[happy] given statement 7, when rcf define ledger brief update 7 --kind constraint --resolved-by REQ-003 runs, then the statement carries kind constraint and resolvedBy REQ-003 and its id and addedAt are unchanged.",
|
|
16
|
+
"testable": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "AC-17302-2",
|
|
20
|
+
"description": "[happy] given decision 2 with one option, when rcf define ledger decisions update 2 --option a:x --option b:y --default a runs, then decisions:wellFormed passes it.",
|
|
21
|
+
"testable": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "AC-17302-3",
|
|
25
|
+
"description": "[happy] given each pointer form REQ-nnn, TAD.entity:<name>, PRD.user:<name>, TAD.system:<name>, TAC-nnnn and omitted:<reason> resolving on the fixture, when D2 runs, then skeleton:resolvedBy passes the statement.",
|
|
26
|
+
"testable": true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "AC-17302-4",
|
|
30
|
+
"description": "[failure] given --resolved-by \"loan thing\", when update runs, then it exits 2 naming the grammar and writes nothing.",
|
|
31
|
+
"testable": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "AC-17302-5",
|
|
35
|
+
"description": "[failure] given --resolved-by REQ-999 on a tree with no REQ-999, when D2 runs, then skeleton:resolvedBy fails the statement with \"pointer does not resolve\".",
|
|
36
|
+
"testable": true
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "AC-17302-6",
|
|
40
|
+
"description": "[must-not] given a statement whose text contains an existing REQ title and no resolvedBy, when D2 runs in 0.30.0, then skeleton:resolvedBy does not pass it on the title hit.",
|
|
41
|
+
"testable": true
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"tacIds": [
|
|
45
|
+
"TAC-4121-define-ledgers"
|
|
46
|
+
],
|
|
47
|
+
"tags": [],
|
|
48
|
+
"createdAt": "2026-10-02T16:40:00Z",
|
|
49
|
+
"updatedAt": "2026-10-02T16:40:00Z"
|
|
50
|
+
}
|
|
@@ -49,10 +49,22 @@
|
|
|
49
49
|
"id": "AC-17401-8",
|
|
50
50
|
"description": "checkD8Freeze enforces the tree-wide gate: every prior stage state (D1..D7 as computeReadiness composes them) is passed or acknowledged at the current hash; every AC id in the tree is bound by exactly one FBS (walker's fbsByAcId map has length 1); the queue head (computeQueue.nextActionable) is not null; validateErrors is empty. The stage is never notApplicable.",
|
|
51
51
|
"testable": true
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "AC-17401-9",
|
|
55
|
+
"description": "checkD2Skeleton reads tad.dataArchitecture.dataStores and tad.dataArchitecture.coreEntities (not top-level) when evaluating skeleton:tadPersistence; a tree whose TAD places both arrays under dataArchitecture per rcf-schemas 0.6.3 passes the check and a tree with the arrays missing or empty under dataArchitecture fails with { id: 'TAD.dataArchitecture.dataStores' } / { id: 'TAD.dataArchitecture.coreEntities' }.",
|
|
56
|
+
"testable": true
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": "AC-17401-10",
|
|
60
|
+
"description": "src/setup/identity-seed.js IDENTITY_TEMPLATE seeds a '## Surface' section containing one line that is one of 'viewer' | 'runningApp' | 'prDiff' (default 'prDiff' with explanatory text matching the Register section's style) so that a project immediately after 'rcf init' passes checkD1Brief's profile:surface check without further operator action. The seed stays idempotent (an existing profile.md is left byte-identical, per the slice-1 'kept' contract).",
|
|
61
|
+
"testable": true
|
|
52
62
|
}
|
|
53
63
|
],
|
|
54
|
-
"tacIds": [
|
|
64
|
+
"tacIds": [
|
|
65
|
+
"TAC-4122-define-gates"
|
|
66
|
+
],
|
|
55
67
|
"tags": [],
|
|
56
68
|
"createdAt": "2026-09-24T14:30:00Z",
|
|
57
|
-
"updatedAt": "2026-
|
|
69
|
+
"updatedAt": "2026-10-01T13:03:38Z"
|
|
58
70
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-17402",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-174",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Persona tag on every gate check",
|
|
8
|
+
"description": "User story under REQ-174. Every entry in stages[].checks[] carries an owning persona (productOwner | engineer) declared in code next to the check, so readiness consumers (the CLI, the viewer tab, delivery-stream artefacts) can route a failing check to whoever is actually blocked.",
|
|
9
|
+
"asA": "an engineer reading a readiness report",
|
|
10
|
+
"iWant": "every failing check to say whose question it is",
|
|
11
|
+
"soThat": "product-owner questions reach the product owner and engineering gaps stay with me",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-17402-1",
|
|
15
|
+
"description": "[happy] Given any tree, when readiness is computed, then every entry in stages[].checks[] carries persona equal to 'productOwner' or 'engineer' and a non-empty question string.",
|
|
16
|
+
"testable": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "AC-17402-2",
|
|
20
|
+
"description": "[happy] Given the D1 checks, when readiness is computed, then brief:sinceFreeze, brief:kinds, brief:openQuestions and brief:profile carry persona 'productOwner'.",
|
|
21
|
+
"testable": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "AC-17402-3",
|
|
25
|
+
"description": "[happy] Given the D2 checks, when readiness is computed, then skeleton:resolvedBy and skeleton:reqIntent carry persona 'productOwner' and skeleton:reqShape, skeleton:tadPersistence and skeleton:deployAdr carry persona 'engineer'.",
|
|
26
|
+
"testable": true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "AC-17402-4",
|
|
30
|
+
"description": "[happy] Given the D4 and D7 checks, when readiness is computed, then stories:reqHasUs and decisions:wellFormed carry persona 'productOwner' and stories:usFloors and decisions:allAnswered carry persona 'engineer'.",
|
|
31
|
+
"testable": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "AC-17402-5",
|
|
35
|
+
"description": "[happy] Given the D3, D5, D6 and D8 checks, when readiness is computed, then every check carries persona 'engineer'.",
|
|
36
|
+
"testable": true
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "AC-17402-6",
|
|
40
|
+
"description": "[must-not] Given any check, when readiness is computed, then no check carries a persona value other than 'productOwner' or 'engineer', and no failing item carries its own persona.",
|
|
41
|
+
"testable": true
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "AC-17402-7",
|
|
45
|
+
"description": "[failure] Given a notApplicable stage, when readiness is computed, then its stage:<D>:scope placeholder check carries the stage's STAGE_FALLBACK_PERSONA and ok true.",
|
|
46
|
+
"testable": true
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"tacIds": [
|
|
50
|
+
"TAC-4122-define-gates"
|
|
51
|
+
],
|
|
52
|
+
"tags": [],
|
|
53
|
+
"createdAt": "2026-10-01T14:30:00Z",
|
|
54
|
+
"updatedAt": "2026-10-01T14:30:00Z"
|
|
55
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-17403",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-174",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Split heterogeneous checks and detect the D3 draft marker",
|
|
8
|
+
"description": "User story under REQ-174. skeleton:reqFields and decisions:allAnswered split into homogeneous checks (per persona), and a new D3 shapes:draftSettled flags any interface whose description still carries the leading [draft] marker. L1 pre-populated shapes stay distinguishable from engineer-settled ones.",
|
|
9
|
+
"asA": "an engineer",
|
|
10
|
+
"iWant": "the mixed checks split and a draft interface distinguishable from a settled one",
|
|
11
|
+
"soThat": "a shape the L1 agent pre-populated never reads as my finished work",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-17403-1",
|
|
15
|
+
"description": "[happy] Given a REQ in scope with a TODO description and no shapeClassification, when D2 runs, then skeleton:reqIntent fails the REQ for the description and skeleton:reqShape fails the same REQ for the missing shapes.",
|
|
16
|
+
"testable": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "AC-17403-2",
|
|
20
|
+
"description": "[happy] Given a REQ in scope with a description and shapes but no domain, when D2 runs, then skeleton:reqIntent fails the REQ with 'domain missing' and skeleton:reqShape passes it.",
|
|
21
|
+
"testable": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "AC-17403-3",
|
|
25
|
+
"description": "[happy] Given a TAC in scope whose only interface description starts with [draft], when D3 runs, then shapes:tacHasInterface passes the TAC and shapes:draftSettled fails <tacId>:<name> with 'draft interface pre-populated at L1, not yet settled'.",
|
|
26
|
+
"testable": true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "AC-17403-4",
|
|
30
|
+
"description": "[happy] Given the same TAC after the [draft] marker is removed, when D3 runs, then shapes:draftSettled passes it.",
|
|
31
|
+
"testable": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "AC-17403-5",
|
|
35
|
+
"description": "[failure] Given a [draft] interface whose kind is not in INTERFACE_KINDS, when D3 runs, then shapes:kindVocabulary fails it exactly as it would a settled interface.",
|
|
36
|
+
"testable": true
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "AC-17403-6",
|
|
40
|
+
"description": "[happy] Given a decisions ledger with one open entry that has a question, two options and a default, when D7 runs, then decisions:wellFormed passes and decisions:allAnswered fails the entry.",
|
|
41
|
+
"testable": true
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "AC-17403-7",
|
|
45
|
+
"description": "[failure] Given a decisions ledger entry with one option and no default, when D7 runs, then decisions:wellFormed fails it with 'decision is not enumerated (question, two or more options, a default)'.",
|
|
46
|
+
"testable": true
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "AC-17403-8",
|
|
50
|
+
"description": "[must-not] Given any tree, when D2 or D7 runs, then no check named skeleton:reqFields is emitted and the stage envelope shape (stage, gate, state, checks) is unchanged.",
|
|
51
|
+
"testable": true
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"tacIds": [
|
|
55
|
+
"TAC-4122-define-gates"
|
|
56
|
+
],
|
|
57
|
+
"tags": [],
|
|
58
|
+
"createdAt": "2026-10-01T14:30:00Z",
|
|
59
|
+
"updatedAt": "2026-10-01T14:30:00Z"
|
|
60
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-17502",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-175",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Two levels folded on one readiness object",
|
|
8
|
+
"description": "User story under REQ-175. computeReadiness folds levels (intentComplete, readyToBuild) and personas (productOwner, engineer) from stages[].checks[] on every read; nothing new is written. The `rcf define readiness --level intent --json` output at a tree hash is the artefact a delivery stream picks up.",
|
|
9
|
+
"asA": "a delivery stream",
|
|
10
|
+
"iWant": "`rcf define readiness --level intent --json` at a tree hash to be the artefact that says the product owner's part is done",
|
|
11
|
+
"soThat": "I can pick a tree up without a hand-over meeting",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-17502-1",
|
|
15
|
+
"description": "[happy] Given a tree where every productOwner check is ok and at least one engineer check fails, when readiness is computed, then levels.intentComplete.ok is true and levels.readyToBuild.ok is false.",
|
|
16
|
+
"testable": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "AC-17502-2",
|
|
20
|
+
"description": "[happy] Given a tree where freezeable is true, when readiness is computed, then levels.readyToBuild.ok is true and levels.intentComplete.ok is true.",
|
|
21
|
+
"testable": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "AC-17502-3",
|
|
25
|
+
"description": "[failure] Given a tree where brief:profile fails, when readiness is computed, then levels.intentComplete.ok is false and levels.intentComplete.blockedBy holds one blocker for D1 brief:profile with persona productOwner and ids profile:surface and profile:register.",
|
|
26
|
+
"testable": true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "AC-17502-4",
|
|
30
|
+
"description": "[happy] Given a D3 stage acknowledged at the current hash with a failing engineer check, when readiness is computed, then that check is absent from levels.readyToBuild.blockedBy.",
|
|
31
|
+
"testable": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "AC-17502-5",
|
|
35
|
+
"description": "[happy] Given any tree, when readiness is computed, then personas.productOwner.blockers deep-equals levels.intentComplete.blockedBy and levels.readyToBuild.nextAction deep-equals nextAction.",
|
|
36
|
+
"testable": true
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "AC-17502-6",
|
|
40
|
+
"description": "[happy] Given failing engineer checks in D2 and D4 and a failing productOwner check in D4 only, when readiness is computed, then personas.productOwner.nextAction names D4 and personas.engineer.nextAction names D2.",
|
|
41
|
+
"testable": true
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "AC-17502-7",
|
|
45
|
+
"description": "[must-not] Given any invocation of `rcf define readiness`, when it completes, then no file under rcf/ has been created or modified.",
|
|
46
|
+
"testable": true
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "AC-17502-8",
|
|
50
|
+
"description": "[must-not] Given the 0.28.4 JSON consumer fields (tree, delta, stages, nextAction, coverage, decisions, freezeable), when readiness is emitted under 0.29.0, then every one of them is present with its 0.28.4 type.",
|
|
51
|
+
"testable": true
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"tacIds": [
|
|
55
|
+
"TAC-4123-define-readiness"
|
|
56
|
+
],
|
|
57
|
+
"tags": [],
|
|
58
|
+
"createdAt": "2026-10-01T14:30:00Z",
|
|
59
|
+
"updatedAt": "2026-10-01T14:30:00Z"
|
|
60
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-17503",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-175",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Level and persona flags on rcf define readiness",
|
|
8
|
+
"description": "User story under REQ-175. The verb exposes the two levels folded in US-17502 through --level intent|build and groups the text report by persona via --persona productOwner|engineer. --level drives the exit code against levels.<which>.ok (stricter than --check all on warn-with-ack failures per ADR-4126 and decision 4 of the step 2 spec); --persona is a display filter and never affects the exit code or JSON content. The JSON gains a _meta.level marker when --level is set; both verdict lines always print at the top of the text report. See the DEFINE step 2 persona-levels spec dated 2026-10-01 (section 3) for the full flag table and exit matrix.",
|
|
9
|
+
"asA": "an operator",
|
|
10
|
+
"iWant": "--level intent|build and --persona on rcf define readiness",
|
|
11
|
+
"soThat": "a script exits on the verdict it cares about and a person sees their own questions first",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-17503-1",
|
|
15
|
+
"description": "[happy] Given no --level and no --check, when the verb runs on a failing tree, then it prints both verdict lines and exits 0.",
|
|
16
|
+
"testable": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "AC-17503-2",
|
|
20
|
+
"description": "[happy] Given --level intent on a tree where only engineer checks fail, when the verb runs, then it prints 'Intent-complete: yes.' and exits 0.",
|
|
21
|
+
"testable": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "AC-17503-3",
|
|
25
|
+
"description": "[failure] Given --level intent on a tree where a productOwner check fails, when the verb runs, then it exits 4.",
|
|
26
|
+
"testable": true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "AC-17503-4",
|
|
30
|
+
"description": "[failure] Given --level build on a tree with an unacknowledged D3 failure and every blocking stage passed, when the verb runs, then it prints the [warn] line and exits 4.",
|
|
31
|
+
"testable": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "AC-17503-5",
|
|
35
|
+
"description": "[happy] Given --check all on the same tree, when the verb runs, then it exits 0, unchanged from 0.28.4.",
|
|
36
|
+
"testable": true
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "AC-17503-6",
|
|
40
|
+
"description": "[happy] Given --persona productOwner, when the verb runs in text mode, then the engineer block is one line with its check count and the exit code is the same as without the flag.",
|
|
41
|
+
"testable": true
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "AC-17503-7",
|
|
45
|
+
"description": "[failure] Given --level ship or --persona architect, when the verb runs, then it exits 2 with a usage line naming the accepted values.",
|
|
46
|
+
"testable": true
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "AC-17503-8",
|
|
50
|
+
"description": "[happy] Given --json --level intent, when the verb runs, then _meta.level is 'intent' and the object carries levels and personas.",
|
|
51
|
+
"testable": true
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "AC-17503-9",
|
|
55
|
+
"description": "[happy] Given the text report on a failing tree, when it renders, then the product-owner block precedes the engineer block and each failing check line carries its persona inside the parenthesis after over.",
|
|
56
|
+
"testable": true
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"tacIds": [
|
|
60
|
+
"TAC-4123-define-readiness"
|
|
61
|
+
],
|
|
62
|
+
"tags": [],
|
|
63
|
+
"createdAt": "2026-10-01T16:00:00Z",
|
|
64
|
+
"updatedAt": "2026-10-01T16:00:00Z"
|
|
65
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-17601",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-176",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "rcf define freeze runs readiness, refuses on failing, writes freeze.json and prints the section 2.5 summary",
|
|
8
|
+
"description": "User story under REQ-176. The freeze verb is the only close verb: no change-object is minted, nothing else changes state. It reads the live tree via computeReadiness, refuses on any failing stage (accepting acks only on the three warn-with-ack gates), and writes the freeze record slice 4's bundle --next and slice 5's viewer will read.",
|
|
9
|
+
"asA": "an operator wrapping up a change on the tree",
|
|
10
|
+
"iWant": "one command that says 'yes freezeable and I have written the record' or 'no here is the gate that blocks and its first failing ids', accepts --ack on the three warn-first gates with a mandatory reason, and prints a summary line I can paste into a commit message",
|
|
11
|
+
"soThat": "the freeze record is authoritative for slice 4's bundle --next and slice 5's viewer, and the same rules that gate readiness also gate the freeze",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-17601-1",
|
|
15
|
+
"description": "[happy] rcf define freeze runs computeReadiness once over walkTree(projectRoot) plus loadFreezeRecord and loadAllLedgers. When result.freezeable is true, it writes rcf/define/freeze.json via saveFreezeRecord with these fields: frozenAt (ISO UTC now), treeHash (result.tree.currentTreeHash), docHashes (delta.js currentHashes over the live tree plus 'ledger:<name>' for each ledger present), briefStatements (the count of statements in the brief ledger, ledgers.brief.statements.length or 0), gates (one entry per stage-gate with { state, at: { hash, at, reason?, by? } | null, failing: [{ id, why }] } from the live readiness plus any --ack entries), counts { req, us, ac, acByClass: { happy, edge, failure, mustNot, nonFunctional, unclassified }, tac, interfaces: { <kind>: n, ... }, adr, fbs }, litmus { attestedAt, readers: 0 }, versions { rcfLite, ruleset, schemas } read from package.json, src/ruleset/ruleset.json and node_modules/@stravica-ai/rcf-schemas/package.json, note (--note argument or null), override (always null on a fresh freeze). The verb exits 0. Immediately after the write, calling computeDelta over the live tree + the new freeze reports every list empty (changed / added / removed / briefSince).",
|
|
16
|
+
"testable": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "AC-17601-2",
|
|
20
|
+
"description": "[failure] When result.freezeable is false and no --ack argument covers the failing stage, the verb exits 4 without writing the freeze record. It prints the failing gates (one per line: '<stage> (<gate>): failing (<first check name>); failing ids: <id1>, <id2>, ...' capped at 20 ids per gate) to stderr, followed by the exact rcf define readiness --check <shortName> command that will re-print the same block for each failing stage. The file at rcf/define/freeze.json is not created or modified (a pre-existing file is byte-identical after the refusal).",
|
|
21
|
+
"testable": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "AC-17601-3",
|
|
25
|
+
"description": "[failure] --ack <gate> --reason <text> is accepted only for the three warn-with-ack gates: define.shapes / D3 / shapes, define.crosscut / D5 / crosscut, define.consistency / D6 / consistency (the same STAGE_ALIASES readiness accepts). --ack on any other gate name is a usage refusal (exit 2) with a stderr message naming the accepted set. --ack without a matching --reason argument is a usage refusal (exit 2). Multiple --ack / --reason pairs may be passed together (parseArgs multiple: true) as long as the count matches; a mismatch is a usage refusal. An acked gate lands in the written freeze.gates entry as { state: 'acknowledged', at: { hash: <currentTreeHash>, at: <frozenAt>, reason: <text>, by: <freezerId or null> }, failing: [<id>, ...] } and its acknowledgement carries into the next computeReadiness run (which reports the stage state as 'acknowledged').",
|
|
26
|
+
"testable": true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "AC-17601-4",
|
|
30
|
+
"description": "[happy] The stdout summary derived from the delta BEFORE the freeze matches proposal section 2.5: 'frozen at <shortHash>: <n> REQ, <n> stories, <n> criteria, <n> interfaces added; <ids> amended; <impactedFbs> re-execute.' Counts of added come from delta.added grouped by tree.kindById; amended is delta.changed (US ids in the amended list per the proposal's example wording); impactedFbs is delta.impactedFbs. On the first freeze (freeze record absent before the run), the summary is 'first freeze at <shortHash>: <req> REQ, <us> stories, <ac> criteria, <interfaces> interfaces, <tac> TAC, <adr> ADR, <fbs> FBS.' --json emits { record, summary, delta } where record is the just-written freeze record body and delta is the pre-freeze DeltaResult.",
|
|
31
|
+
"testable": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "AC-17601-5",
|
|
35
|
+
"description": "[happy] --status prints the current record and the live delta summary, exit 0. Text: 'Frozen at <short> on <frozenAt>. Note: <note|(none)>. Counts: <req> REQ / <us> US / <ac> AC / <tac> TAC / <adr> ADR / <fbs> FBS. Acknowledged: <gate1> (\"<reason>\"); <gate2> ...(none). Litmus: attestedAt [<D3>, <D6>], readers <n>. Versions: rcfLite <v> / ruleset <v> / schemas <v>. Override: <null|<reason by at>>.' followed by the live delta summary line: '<changed> changed, <added> added, <removed> removed, <impacted> impacted, <impactedFbs> FBS re-execute.' --status --json emits { record: <freeze body>, delta: <computeDelta output> }. --status when no record exists prints 'never frozen' and exits 0 (--json: { record: null, delta: <computeDelta output over unfrozen tree> }). --status never runs the gates and never writes anything.",
|
|
36
|
+
"testable": true
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "AC-17601-6",
|
|
40
|
+
"description": "[happy] The freeze producer is wrapped with runWithAdmissibilityGate the same way readiness is (ADR-4124 carrying ADR-4123): the wrap runs first so the ruleset toolScope guard is honoured, and on a refused-admissibility verdict the CLI prints the refusal envelope to stderr as a [warn] line and the freeze compute still runs and writes if freezeable. The verb never exits 4 solely because of a refused-admissibility verdict.",
|
|
41
|
+
"testable": true
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "AC-17601-7",
|
|
45
|
+
"description": "[must-not] src/define/freeze-record.js is updated so validateFreezeRecord REQUIRES the override key (nullable): a body without override throws FreezeRecordError with field 'override'. loadFreezeRecord tolerates older records that lack the key by defaulting to null (never fails on ENOKEY). saveFreezeRecord validates first and refuses to write a body that omits override. Every existing test fixture that omits override still loads (either the loader default fills it in, or a targeted fixture update writes override: null; no legacy fixture becomes unreadable).",
|
|
46
|
+
"testable": true
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "AC-17601-8",
|
|
50
|
+
"description": "[non-functional] The src/cli/readiness.js stage header line no longer contains a double-dash: it uses a colon between the stage id and the state label ('D1 (define.brief): failing'). The CHANGELOG.md line 'The regex is now `/\\bTODO:/` -- case-sensitive uppercase `TODO`' is likewise rewritten to a colon. Any golden fixture that captured the old header text is updated. `grep -R ' -- ' src/ test/ CHANGELOG.md` returns no matches under text this slice added or edited (the grep is scoped to added lines; unrelated pre-existing occurrences elsewhere in the tree stay out of scope of the AC).",
|
|
51
|
+
"testable": true
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"tacIds": ["TAC-4124-define-freeze"],
|
|
55
|
+
"tags": [],
|
|
56
|
+
"createdAt": "2026-09-24T16:33:15Z",
|
|
57
|
+
"updatedAt": "2026-09-24T16:33:15Z"
|
|
58
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-18001",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-180",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Readiness as the first tab in the viewer",
|
|
8
|
+
"description": "User story under REQ-180. Opening the viewer puts Readiness first (default active tab), the former Overview tab is renamed PRD with its content preserved, the two level verdict pills carry the formatVerdictLines strings as labels, blockers group by persona ordered by the profile.md register (the non-register group collapses with its count visible, never omitted), and the Freeze now control is enabled iff levels.readyToBuild.ok. See the DEFINE step 2 spec dated 2026-10-01 (section 5) for the layout.",
|
|
9
|
+
"asA": "an operator opening the viewer",
|
|
10
|
+
"iWant": "Readiness as the first tab with the two verdict pills and persona-grouped blockers",
|
|
11
|
+
"soThat": "the first thing I see is whether I can build and whose questions stand in the way",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-18001-1",
|
|
15
|
+
"description": "[happy] Given any project, when the viewer renders, then the first tab is Readiness and the second is PRD with the former Overview content unchanged.",
|
|
16
|
+
"testable": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "AC-18001-2",
|
|
20
|
+
"description": "[happy] Given a failing tree, when the Readiness tab renders, then it shows two verdict pills whose labels equal the formatVerdictLines strings for the same tree.",
|
|
21
|
+
"testable": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "AC-18001-3",
|
|
25
|
+
"description": "[happy] Given personas.productOwner.blockers non-empty, when the tab renders, then each blocker is a findings list headed by its question with its ids as anchor links into the document tabs.",
|
|
26
|
+
"testable": true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "AC-18001-4",
|
|
30
|
+
"description": "[happy] Given a profile register of productOwner, when the tab renders, then the product-owner group is expanded first and the engineer group is collapsed with its count visible.",
|
|
31
|
+
"testable": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "AC-18001-5",
|
|
35
|
+
"description": "[must-not] Given any profile register, when the tab renders, then no blocker id of either persona is omitted from the DOM.",
|
|
36
|
+
"testable": true
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "AC-18001-6",
|
|
40
|
+
"description": "[failure] Given a tree where levels.readyToBuild.ok is false, when the tab renders, then the Freeze now control is disabled and names the failing gates from levels.readyToBuild.blockedBy.",
|
|
41
|
+
"testable": true
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "AC-18001-7",
|
|
45
|
+
"description": "[happy] Given the tab and the CLI --json output for the same tree hash, when compared, then every id rendered on the tab appears in the JSON for that hash.",
|
|
46
|
+
"testable": true
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"tacIds": [
|
|
50
|
+
"TAC-4127-readiness-view"
|
|
51
|
+
],
|
|
52
|
+
"tags": [],
|
|
53
|
+
"createdAt": "2026-10-01T18:00:00Z",
|
|
54
|
+
"updatedAt": "2026-10-01T18:00:00Z"
|
|
55
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-18002",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-180",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "the Readiness tab renders question cards grouped by source span",
|
|
8
|
+
"description": "User story under REQ-180, reserved by DEFINE step 3 spec section 11.8 and authored here by the viewer UI refresh PR 8. The Readiness tab leads with the product owner's three questions in plain words: two VerdictCards from `levels` (Ready for engineers, Ready to build, each with its chain term `intent-complete` or `ready-to-build` muted beside the plain label; both cards rendered at equal height), QuestionCards grouped by source span (one group per blocker; one card per failing item, phrasebook-sourced today, swapping to `readiness.questions[]` when DEFINE step 3 PR 2 ships `computeQuestions`), a what-happens-next paragraph, and a Requirements that still need work table. Every engineer-level finding stays on the page one level down in one collapsed `For engineers` DocRow (closed in the productOwner register, open in engineer; never removed from the DOM). The adapter seam `toQuestions(readiness)` is a pure module reading blockers today and feature-detecting `readiness.questions` tomorrow.",
|
|
9
|
+
"asA": "a product owner opening the Readiness tab",
|
|
10
|
+
"iWant": "the questions waiting on me in plain words, grouped by their source span, without ids or stage codes",
|
|
11
|
+
"soThat": "I can answer them in my own session and see the hand-over state update as I go",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-18002-1",
|
|
15
|
+
"description": "[happy] given questions[] non-empty, when the tab renders, then the block lists each group with its asks and every id is in the JSON.",
|
|
16
|
+
"testable": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "AC-18002-2",
|
|
20
|
+
"description": "[failure] given questions[] empty, when the tab renders, then the block shows the intent-complete verdict line.",
|
|
21
|
+
"testable": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "AC-18002-3",
|
|
25
|
+
"description": "[must-not] given any register, when the tab renders, then the engineer blockers are not removed from the DOM.",
|
|
26
|
+
"testable": true
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"tacIds": [
|
|
30
|
+
"TAC-4133-readiness-po-layer"
|
|
31
|
+
],
|
|
32
|
+
"tags": [],
|
|
33
|
+
"createdAt": "2026-10-02T19:30:00Z",
|
|
34
|
+
"updatedAt": "2026-10-02T19:30:00Z"
|
|
35
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-18101",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "REQ-181",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Shared pill, findings-list and diff components",
|
|
8
|
+
"description": "User story under REQ-181. Three view modules land under src/view/components/: pill, findings-list, diff. Readiness is their first consumer; existing Product Map and Requirements rendering keeps its markup where the swap is not mechanical, and no existing test expectation changes in that case. See the DEFINE step 2 spec dated 2026-10-01 (section 5) for the component contract.",
|
|
9
|
+
"asA": "a maintainer of the viewer",
|
|
10
|
+
"iWant": "one pill, one findings list and one diff component",
|
|
11
|
+
"soThat": "gate state, persona, level verdict and AC class read the same on every tab",
|
|
12
|
+
"acceptanceCriteria": [
|
|
13
|
+
{
|
|
14
|
+
"id": "AC-18101-1",
|
|
15
|
+
"description": "[happy] Given the pill component, when rendered with a gate state, a level verdict, a persona or an AC class, then it emits the same markup shape with a vocabulary class per value.",
|
|
16
|
+
"testable": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "AC-18101-2",
|
|
20
|
+
"description": "[happy] Given the findings-list component, when called with the same inputs from the Readiness and Product Map callers, then the two call sites produce byte-identical markup.",
|
|
21
|
+
"testable": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "AC-18101-3",
|
|
25
|
+
"description": "[must-not] Given the shared components, when introduced, then no existing Product Map or Requirements test changes its expected output except where this PR explicitly re-points that tab.",
|
|
26
|
+
"testable": true
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"tacIds": [
|
|
30
|
+
"TAC-4127-readiness-view"
|
|
31
|
+
],
|
|
32
|
+
"tags": [],
|
|
33
|
+
"createdAt": "2026-10-01T18:00:00Z",
|
|
34
|
+
"updatedAt": "2026-10-01T18:00:00Z"
|
|
35
|
+
}
|