rcf-lite 0.30.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12113.json +1 -1
  3. package/fixtures/canary-manifest.json +9 -9
  4. package/guidance/define-draft-shapes.md +15 -10
  5. package/package.json +1 -1
  6. package/rcf/adrs/adr-4131-gates-bite-ack-recorded-override.json +3 -3
  7. package/rcf/adrs/adr-4137-standalone-theme-persistence.json +30 -0
  8. package/rcf/adrs/adr-4138-tree-wide-checks-before-scope-shortcircuit.json +35 -0
  9. package/rcf/code-nodes/cn-017.json +4 -2
  10. package/rcf/code-nodes/cn-771.json +15 -0
  11. package/rcf/code-nodes/cn-772.json +14 -0
  12. package/rcf/code-nodes/cn-773.json +16 -0
  13. package/rcf/code-nodes/cn-774.json +20 -0
  14. package/rcf/code-nodes/cn-775.json +15 -0
  15. package/rcf/code-nodes/cn-776.json +17 -0
  16. package/rcf/code-nodes/cn-777.json +21 -0
  17. package/rcf/code-nodes/cn-778.json +14 -0
  18. package/rcf/fbs/fbs-004.json +4 -1
  19. package/rcf/fbs/fbs-201.json +2 -1
  20. package/rcf/fbs/fbs-202.json +18 -2
  21. package/rcf/fbs/fbs-203.json +12 -4
  22. package/rcf/requirements/req-002.json +2 -2
  23. package/rcf/requirements/req-173.json +2 -2
  24. package/rcf/requirements/req-174.json +2 -2
  25. package/rcf/tacs/tac-4122-define-gates.json +7 -2
  26. package/rcf/tacs/tac-4127-readiness-view.json +3 -2
  27. package/rcf/test-suites/ts-004.json +82 -2
  28. package/rcf/test-suites/ts-007.json +1 -1
  29. package/rcf/test-suites/ts-215.json +64 -1
  30. package/rcf/test-suites/ts-216.json +18 -2
  31. package/rcf/test-suites/ts-219.json +10 -2
  32. package/rcf/test-suites/ts-220.json +18 -2
  33. package/rcf/test-suites/ts-223.json +24 -2
  34. package/rcf/test-suites/ts-225.json +10 -2
  35. package/rcf/test-suites/ts-229.json +49 -3
  36. package/rcf/test-suites/ts-230.json +26 -2
  37. package/rcf/test-suites/ts-231.json +16 -1
  38. package/rcf/test-suites/ts-236.json +44 -0
  39. package/rcf/user-stories/us-17302.json +36 -1
  40. package/rcf/user-stories/us-17401.json +11 -1
  41. package/rcf/user-stories/us-17402.json +6 -1
  42. package/rcf/user-stories/us-17403.json +11 -1
  43. package/rcf/user-stories/us-17404.json +16 -1
  44. package/rcf/user-stories/us-17405.json +7 -2
  45. package/rcf/user-stories/us-18001.json +6 -1
  46. package/rcf/user-stories/us-202.json +25 -1
  47. package/rcf/user-stories/us-205.json +6 -1
  48. package/rcf/user-stories/us-206.json +25 -4
  49. package/rcf/user-stories/us-207.json +36 -0
  50. package/releases/releases.yaml +20 -1
  51. package/src/cli/create.js +22 -2
  52. package/src/cli/freeze.js +8 -0
  53. package/src/cli/ledger.js +96 -4
  54. package/src/cli/readiness.js +17 -39
  55. package/src/core/errors/index.js +7 -1
  56. package/src/core/store/writer.js +181 -13
  57. package/src/define/interface-paths.js +47 -0
  58. package/src/define/ledgers.js +86 -7
  59. package/src/mcp/tools.js +1 -1
  60. package/src/query/gates.js +431 -163
  61. package/src/view/html-page.js +43 -13
  62. package/src/view/live-client.js +18 -10
  63. package/src/view/page-init.js +149 -8
  64. package/src/view/style.css +107 -20
  65. package/src/view/view-index.js +22 -0
@@ -1,7 +1,7 @@
1
1
  {
2
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.",
3
+ "updatedAt": "2026-10-05T12:30: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). 2026-10-04 (w-2026-10-04-dave-001): extends coverage to the in-viewer three-state theme control (AC-206-7..10 under ADR-4137). All TCs resolve to node tests under test/view/wespa-host-fixture.test.js and test/view/theme-control.test.js. 2026-10-05 (PR 291 landing review F1): the boot theme is applied once; a `rcfPage.init()` re-run after an SSE swap never overrides a host postMessage theme or a theme-control choice.",
5
5
  "testLevel": "unit",
6
6
  "acIds": [
7
7
  "AC-206-1",
@@ -9,7 +9,11 @@
9
9
  "AC-206-3",
10
10
  "AC-206-4",
11
11
  "AC-206-5",
12
- "AC-206-6"
12
+ "AC-206-6",
13
+ "AC-206-7",
14
+ "AC-206-8",
15
+ "AC-206-9",
16
+ "AC-206-10"
13
17
  ],
14
18
  "id": "TS-229",
15
19
  "usId": "US-206",
@@ -77,6 +81,48 @@
77
81
  "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
82
  "status": "pending",
79
83
  "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"
84
+ },
85
+ {
86
+ "id": "TC-229-theme-control-renders-non-embed",
87
+ "acId": "AC-206-7",
88
+ "description": "renderPage renders a `[data-rcf-theme-control]` group with three buttons (Light, Dark, Auto; `aria-pressed`) right of the SearchButton in `.tools` when `data-embed` is absent; a click on a theme button sets `<html data-theme>` live and flips `aria-pressed` across the three.",
89
+ "status": "pending",
90
+ "testPointer": "test/view/theme-control.test.js::AC-206-7: renderPage includes the three-state theme control in non-embed; applyThemeControl updates data-theme and aria-pressed on click"
91
+ },
92
+ {
93
+ "id": "TC-229-theme-control-persists",
94
+ "acId": "AC-206-8",
95
+ "description": "In non-embed mode, selecting a theme writes `rcf-view:v1:theme` as `light`/`dark`/`auto` and the stored value is reapplied on the next boot; a `rcf-view-theme` postMessage still applies live but does not write the stored key; the boot precedence is `?theme=` -> stored -> auto.",
96
+ "status": "pending",
97
+ "testPointer": "test/view/theme-control.test.js::AC-206-8: non-embed persistence under rcf-view:v1:theme; postMessage path writes nothing"
98
+ },
99
+ {
100
+ "id": "TC-229-theme-control-absent-embed",
101
+ "acId": "AC-206-9",
102
+ "description": "renderPage with `data-embed=\"1\"` does not emit any `[data-rcf-theme-control]` markup; wireThemeControl under embed never reads or writes `rcf-view:v1:theme` on load, on a postMessage, or on any user action; the key is zero-touched (localStorage spy sees no setItem/getItem call for the key under embed).",
103
+ "status": "pending",
104
+ "testPointer": "test/view/theme-control.test.js::AC-206-9: embed=1 omits the control markup and does not touch rcf-view:v1:theme"
105
+ },
106
+ {
107
+ "id": "TC-229-theme-control-query-wins",
108
+ "acId": "AC-206-10",
109
+ "description": "In non-embed mode with a stored value `dark`, when the URL carries `?theme=light`, boot resolves to `light` (explicit wins), the Light button is pressed and the stored value is NOT overwritten (next load without a query reuses `dark`).",
110
+ "status": "pending",
111
+ "testPointer": "test/view/theme-control.test.js::AC-206-10: explicit ?theme= beats the stored value and leaves the key as-is"
112
+ },
113
+ {
114
+ "id": "TC-229-reinit-keeps-host-theme",
115
+ "acId": "AC-206-2",
116
+ "description": "Boot `?embed=1` with no `?theme=`, post `{ type: \"rcf-view-theme\", theme: \"dark\" }`, then call `rcfPage.init()` (the live-client SSE re-run): `<html data-theme>` stays `dark` and no storage is touched.",
117
+ "status": "pending",
118
+ "testPointer": "test/view/theme-control.test.js::PR 291 F1 regression: embed=1 host theme survives an rcfPage.init() re-run"
119
+ },
120
+ {
121
+ "id": "TC-229-reinit-keeps-control-choice",
122
+ "acId": "AC-206-10",
123
+ "description": "Boot `?theme=light` in non-embed mode, click Dark, then call `rcfPage.init()`: `<html data-theme>` stays `dark` and the Dark button stays pressed.",
124
+ "status": "pending",
125
+ "testPointer": "test/view/theme-control.test.js::PR 291 F1 regression: standalone control choice survives an rcfPage.init() re-run with ?theme= in the URL"
80
126
  }
81
127
  ],
82
128
  "status": "draft"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "createdAt": "2026-10-03T10:50:00Z",
3
- "updatedAt": "2026-10-03T10:50:00Z",
3
+ "updatedAt": "2026-10-05T18:00:00Z",
4
4
  "purpose": "Cover US-17404 (D3 bites): the three D3 bite checks shapes:templateMarkers, shapes:entityJoin and shapes:pathsResolve each have pass, fail, notApplicable, id and why strings; drafts are included in templateMarkers; the readiness CLI --check shapes exits 4 on an unacknowledged D3 failure and 0 when acked at the current hash; ackable() is exactly D3, D5, D6; --ack on D1, D2, D4, D7 or D8 exits 2 unchanged.",
5
5
  "testLevel": "unit",
6
6
  "acIds": [
@@ -9,7 +9,10 @@
9
9
  "AC-17404-3",
10
10
  "AC-17404-4",
11
11
  "AC-17404-5",
12
- "AC-17404-6"
12
+ "AC-17404-6",
13
+ "AC-17404-7",
14
+ "AC-17404-8",
15
+ "AC-17404-9"
13
16
  ],
14
17
  "id": "TS-230",
15
18
  "usId": "US-17404",
@@ -70,6 +73,27 @@
70
73
  "description": "A [draft] recordShape without fields: fails shapes:templateMarkers exactly as a settled one; this completes AC-18801-4 from PR 3.",
71
74
  "status": "pending",
72
75
  "testPointer": "test/query/gates.test.js::gates (REQ-188, AC-18801-4): draft recordShape without fields fails templateMarkers (lifted by PR 5 ADR-4131)"
76
+ },
77
+ {
78
+ "id": "TC-230-r5-entityjoin-under-narrowed-scope",
79
+ "acId": "AC-17404-7",
80
+ "description": "A D3 scope narrowed so no TAC and no shaped REQ is in scope but a coreEntity has no record shape anywhere tree-wide still fails shapes:entityJoin (stage state failing, not notApplicable) via the ADR-4138 helper.",
81
+ "status": "pending",
82
+ "testPointer": "test/query/gates.test.js::gates (R5, AC-17404-7): entityJoin runs tree-wide under narrowed D3 scope"
83
+ },
84
+ {
85
+ "id": "TC-230-r6-bare-paths-whitelist",
86
+ "acId": "AC-17404-8",
87
+ "description": "extractInterfacePathTokens treats Dockerfile, Makefile, LICENSE, Procfile, Containerfile, Justfile, Rakefile, Gemfile and CODEOWNERS as candidate tokens; shapes:pathsResolve fails a path: Dockerfile when not in resolvedPaths and passes when resolved. The exported BARE_PATH_WHITELIST drives both.",
88
+ "status": "pending",
89
+ "testPointer": "test/query/gates.test.js::gates (R6, AC-17404-8): bare path whitelist is checked by pathsResolve"
90
+ },
91
+ {
92
+ "id": "TC-230-r13-helper-foldstate-delegation",
93
+ "acId": "AC-17404-9",
94
+ "description": "treeWideFailureEnvelope returns null when every tree-wide check passes under a scope-empty stage; on D3 with a failing tree-wide check and a freeze.gates ack at the currentTreeHash it folds through foldState to state 'acknowledged'; on D2 (blocking) with a failing tree-wide check it stays 'failing'.",
95
+ "status": "pending",
96
+ "testPointer": "test/query/gates.test.js::gates (PR 292 follow-up, AC-17404-9): treeWideFailureEnvelope helper semantics"
73
97
  }
74
98
  ],
75
99
  "status": "draft"
@@ -9,7 +9,8 @@
9
9
  "AC-17405-3",
10
10
  "AC-17405-4",
11
11
  "AC-17405-5",
12
- "AC-17405-6"
12
+ "AC-17405-6",
13
+ "AC-17405-9"
13
14
  ],
14
15
  "id": "TS-231",
15
16
  "usId": "US-17405",
@@ -98,6 +99,20 @@
98
99
  "description": "R11 refuses dotted grammar; rcf define validate reports the finding naming the dotted field.",
99
100
  "status": "passing",
100
101
  "testPointer": "test/define/validate-findings.test.js::validate findings (PR 9, R11): ownerRef uses the widened grammar; deliveredBy object form is checked"
102
+ },
103
+ {
104
+ "id": "TC-231-r8-standards-applied-waived-or-cited",
105
+ "acId": "AC-17405-5",
106
+ "description": "A concern-ledger entry concern 'standards:<pack>' with disposition 'applied' satisfies skeleton:standardsCited (R8 2026-10-05); 'waived' also satisfies it; neither nor cited, the pack fails standards:<pack>.",
107
+ "status": "pending",
108
+ "testPointer": "test/query/gates.test.js::gates (R8, AC-17405-5): applied disposition satisfies standardsCited alongside waived and citation"
109
+ },
110
+ {
111
+ "id": "TC-231-r7-standards-under-narrowed-d2-scope",
112
+ "acId": "AC-17405-9",
113
+ "description": "A D2 scope narrowed so no REQ, PRD, TAD or brief statement is in scope but a registered standards pack is neither cited nor waived tree-wide still fails skeleton:standardsCited (stage state failing, not notApplicable) via the ADR-4138 helper.",
114
+ "status": "pending",
115
+ "testPointer": "test/query/gates.test.js::gates (R7, AC-17405-9): standardsCited runs tree-wide under narrowed D2 scope"
101
116
  }
102
117
  ],
103
118
  "status": "draft"
@@ -0,0 +1,44 @@
1
+ {
2
+ "createdAt": "2026-10-04T14:00:00Z",
3
+ "updatedAt": "2026-10-05T12:30:00Z",
4
+ "purpose": "Cover US-207 (every viewer text and control colour pair meets WCAG 2.1 AA in both themes): a static CSS-contrast audit parses `src/view/style.css`, resolves the `--sv-*` tokens for the light (`:root`) and dark (`:root[data-theme=\"dark\"]`) blocks, enumerates every rule that sets `color` paired with a reachable background, walks state variants (default, :hover, :focus, :focus-visible, :active, [aria-pressed=true], [aria-selected=true], .is-active, :disabled) and asserts the WCAG 2.1 AA thresholds (>= 4.5:1 for text, >= 3:1 for large text and non-text UI boundaries); the test exits non-zero on any failure, naming selector, state, theme, foreground, background, computed ratio and threshold. Points at test/view/contrast-aa.test.js and the BEFORE/AFTER report at ./output/contrast-report.md captured during the lane work (w-2026-10-04-dave-001). 2026-10-05 (PR 291 landing review F2, F3): state rules are merged over their base selector before pairing, border and outline colours are enumerated against the adjacent surface at 3:1, and the SearchButton and theme-control states are required to exist and pass in both themes.",
5
+ "testLevel": "unit",
6
+ "acIds": [
7
+ "AC-207-1",
8
+ "AC-207-2"
9
+ ],
10
+ "id": "TS-236",
11
+ "usId": "US-207",
12
+ "title": "WCAG 2.1 AA contrast audit across the viewer stylesheet, both themes, every state",
13
+ "testCases": [
14
+ {
15
+ "id": "TC-236-aa-enumerates-all-pairs",
16
+ "acId": "AC-207-1",
17
+ "description": "The audit parses `src/view/style.css`, enumerates every selector that sets `color` with every reachable background (own rule, ancestor surface token, page background `--sv-canvas`) in each state (default, :hover, :focus, :focus-visible, :active, [aria-pressed=true], [aria-selected=true], .is-active, :disabled) in both the light and dark token sets, computes the WCAG relative-luminance ratio and asserts >= 4.5:1 for normal text and >= 3:1 for large text and non-text UI boundaries (WCAG 1.4.3 and 1.4.11).",
18
+ "status": "pending",
19
+ "testPointer": "test/view/contrast-aa.test.js::enumerates every color/background pair and asserts WCAG 2.1 AA in both themes (AC-207-1)"
20
+ },
21
+ {
22
+ "id": "TC-236-aa-failure-message",
23
+ "acId": "AC-207-2",
24
+ "description": "When a contrast pair is below the threshold, the test failure message names the selector, state, theme, foreground colour, background colour, computed ratio and applied threshold; the test exits non-zero so the suite blocks a merge that drops below AA.",
25
+ "status": "pending",
26
+ "testPointer": "test/view/contrast-aa.test.js::failure message names selector/state/theme/fg/bg/ratio/threshold and exits non-zero (AC-207-2)"
27
+ },
28
+ {
29
+ "id": "TC-236-state-merge-and-boundaries",
30
+ "acId": "AC-207-1",
31
+ "description": "A state rule that changes only the background (the original `.rcf-search-btn:hover` defect) is merged over its base selector and paired with the inherited colour; `border` / `border-color` and `outline` / `outline-color` are enumerated against the adjacent surface at the 3:1 non-text threshold; run against the pre-amendment stylesheet the helper reports the dark `.rcf-search-btn:hover` failure.",
32
+ "status": "pending",
33
+ "testPointer": "test/view/contrast-aa.test.js::state rules merge over their base; borders and outlines are enumerated as 3:1 pairs (AC-207-1)"
34
+ },
35
+ {
36
+ "id": "TC-236-theme-btn-states",
37
+ "acId": "AC-207-1",
38
+ "description": "`.rcf-theme-btn` hover, focus-visible and active pairs exist and pass in both themes, and its focus ring clears 3:1 against the surface it sits on.",
39
+ "status": "pending",
40
+ "testPointer": "test/view/contrast-aa.test.js::.rcf-theme-btn passes WCAG 2.1 AA in both themes across hover, focus-visible and active (AC-207-1)"
41
+ }
42
+ ],
43
+ "status": "draft"
44
+ }
@@ -39,6 +39,41 @@
39
39
  "id": "AC-17302-6",
40
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
41
  "testable": true
42
+ },
43
+ {
44
+ "id": "AC-17302-7",
45
+ "description": "[happy] (R1 2026-10-05) given rcf define ledger brief add --id 3 --text \"foo\" --kind capability on a ledger whose entry 3 already carries kind capability and text foo, when the verb runs, then it exits 0 and stdout reads 'brief-ledger: unchanged 3' (with --json, {\"status\":\"unchanged\",\"id\":3}); the file is not rewritten.",
46
+ "testable": true
47
+ },
48
+ {
49
+ "id": "AC-17302-8",
50
+ "description": "[failure] (R1 2026-10-05) given rcf define ledger brief add --id 3 --text \"bar\" --kind constraint on a ledger whose entry 3 carries kind capability and text foo, when the verb runs, then it exits 3 and stderr names the id and the differing fields (text, kind); the file is not rewritten.",
51
+ "testable": true
52
+ },
53
+ {
54
+ "id": "AC-17302-9",
55
+ "description": "[happy] (R1 2026-10-05) given rcf define ledger brief add --text \"foo\" with no --id on an empty ledger, when the verb runs, then it mints id 1 as before (idempotency opt-in is id-carried; the harness threads --id back on replay).",
56
+ "testable": true
57
+ },
58
+ {
59
+ "id": "AC-17302-10",
60
+ "description": "[happy] (R1 create extension 2026-10-05, PR 292 follow-up; amended on the PR 293 landing review) given rcf define create ac --parent US-1 --id AC-1-1 --description \"foo\" on a tree where US-1 already carries AC-1-1 with description foo and testable true and no other fields, when the verb runs, then it exits 0 and stdout reads 'AC-1-1: unchanged (rcf/user-stories/us-1.json)'; the parent US file is not rewritten; and given the same verb with --description \"bar\" instead, it exits 3, stderr names the id and the differing field (description), and the file is not rewritten. rcf define create has no --json flag; the stdout line is the whole output (suppressed by --quiet).",
61
+ "testable": true
62
+ },
63
+ {
64
+ "id": "AC-17302-11",
65
+ "description": "[happy] (R1 create extension, PR 293 landing review) given rcf define create tc --parent TS-1 --id TC-1-foo --ac AC-1-1 --description \"foo\" --test-pointer t.test.js::foo on a tree where TS-1 already carries TC-1-foo with the same acId, description, testPointer and status pending, when the verb runs, then it exits 0 and stdout reads 'TC-1-foo: unchanged (rcf/test-suites/ts-1.json)' and the file is not rewritten; given the same verb with --description \"bar\", it exits 3 naming the id and the differing field (description) and the file is not rewritten. Idempotency is id-carried: without --id, a derived-slug collision still refuses with exit 2 and the 'supply --slug explicitly' message.",
66
+ "testable": true
67
+ },
68
+ {
69
+ "id": "AC-17302-12",
70
+ "description": "[happy] (R1 create extension, PR 293 landing review) given rcf define create cn --id CN-1 --path src/a.js --acs AC-1-1 on a tree where CN-1 already carries path src/a.js, implementsAcIds [AC-1-1], dependencies [], version 0.1.0 and status draft, when the verb runs, then it exits 0 and stdout reads 'CN-1: unchanged (rcf/code-nodes/cn-1.json)' and the file is not rewritten; given the same verb with --path src/b.js, it exits 3 naming the id and the differing field (path) and the file is not rewritten.",
71
+ "testable": true
72
+ },
73
+ {
74
+ "id": "AC-17302-13",
75
+ "description": "[happy] (R1 create extension, PR 293 landing review) given rcf define create req --parent PRD-001 --id REQ-050 --title T --description D whose first run persisted a classifier-owned shapeClassification block, when the identical command is replayed, then it exits 0 and stdout reads 'REQ-050: unchanged (rcf/requirements/req-050.json)' and the file is not rewritten: the replay compare ignores every writer- and classifier-owned field (createdAt, updatedAt, shapeClassification; one exported REPLAY_IGNORE_FIELDS list in src/core/store/writer.js); given the same verb with a different --description, it exits 3 naming description and the file is not rewritten.",
76
+ "testable": true
42
77
  }
43
78
  ],
44
79
  "tacIds": [
@@ -46,5 +81,5 @@
46
81
  ],
47
82
  "tags": [],
48
83
  "createdAt": "2026-10-02T16:40:00Z",
49
- "updatedAt": "2026-10-02T16:40:00Z"
84
+ "updatedAt": "2026-10-05T19:00:00Z"
50
85
  }
@@ -59,6 +59,16 @@
59
59
  "id": "AC-17401-10",
60
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
61
  "testable": true
62
+ },
63
+ {
64
+ "id": "AC-17401-11",
65
+ "description": "[happy] (R13 2026-10-05) given a D5 scope narrowed so no REQ is in scope but the tree has an auth or httpApi REQ and TAD.securityArchitecture is empty, when D5 runs, then crosscut:securityArchitecture still fails TAD.securityArchitecture and the stage state is failing (not notApplicable) through the shared tree-wide-before-scope helper (ADR-4138 extended).",
66
+ "testable": true
67
+ },
68
+ {
69
+ "id": "AC-17401-12",
70
+ "description": "[happy] (R13 2026-10-05) given a D5 scope narrowed so no REQ is in scope but the tree carries a US with an AC description that contains the '[deployed]' marker and TAD.operationalConcerns is empty, when D5 runs, then crosscut:operationalConcerns still fails TAD.operationalConcerns and the stage state is failing (not notApplicable) through the shared tree-wide-before-scope helper (ADR-4138 extended).",
71
+ "testable": true
62
72
  }
63
73
  ],
64
74
  "tacIds": [
@@ -66,5 +76,5 @@
66
76
  ],
67
77
  "tags": [],
68
78
  "createdAt": "2026-09-24T14:30:00Z",
69
- "updatedAt": "2026-10-01T13:03:38Z"
79
+ "updatedAt": "2026-10-05T18:00:00Z"
70
80
  }
@@ -44,6 +44,11 @@
44
44
  "id": "AC-17402-7",
45
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
46
  "testable": true
47
+ },
48
+ {
49
+ "id": "AC-17402-8",
50
+ "description": "[happy] (R13 2026-10-05) given a D2 scope narrowed so no REQ, PRD, TAD or resolving brief statement is in scope but the tree has zero Deploy target or Deploy deferral ADRs (or two or more), when D2 runs, then skeleton:deployAdr still fails ADR:deploy and the stage state is failing (not notApplicable) through the shared tree-wide-before-scope helper (ADR-4138 extended).",
51
+ "testable": true
47
52
  }
48
53
  ],
49
54
  "tacIds": [
@@ -51,5 +56,5 @@
51
56
  ],
52
57
  "tags": [],
53
58
  "createdAt": "2026-10-01T14:30:00Z",
54
- "updatedAt": "2026-10-01T14:30:00Z"
59
+ "updatedAt": "2026-10-05T18:00:00Z"
55
60
  }
@@ -49,6 +49,16 @@
49
49
  "id": "AC-17403-8",
50
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
51
  "testable": true
52
+ },
53
+ {
54
+ "id": "AC-17403-9",
55
+ "description": "[happy] (R2 2026-10-05) given a D3 scope that excludes every TAC and shaped REQ (no TAC, no httpApi/persistence/auth REQ in scope) but a [draft] marker exists on a TAC OUTSIDE the narrowed scope, when D3 runs, then shapes:draftSettled still fails the <tacId>:<name> finding and the stage state is failing (not notApplicable) through the shared tree-wide-before-scope helper (ADR-4138); the same test asserted against pre-R2 gates.js (origin/main d8adedff^) returns notApplicable instead.",
56
+ "testable": true
57
+ },
58
+ {
59
+ "id": "AC-17403-10",
60
+ "description": "[happy] (PR 292 follow-up 2026-10-05) given a scope-empty D3 with a failing shapes:draftSettled tree-wide, when the failing envelope the ADR-4138 helper returns is rendered by src/cli/readiness.js, then the printed D3 line reads 'D3 (define.shapes): failing' without the pre-R13 '(no TAC ... in scope)' notApplicable reason trailer; the envelope omits the reason field (foldState delegation inside the helper carries no reason text).",
61
+ "testable": true
52
62
  }
53
63
  ],
54
64
  "tacIds": [
@@ -56,5 +66,5 @@
56
66
  ],
57
67
  "tags": [],
58
68
  "createdAt": "2026-10-01T14:30:00Z",
59
- "updatedAt": "2026-10-01T14:30:00Z"
69
+ "updatedAt": "2026-10-05T18:00:00Z"
60
70
  }
@@ -39,6 +39,21 @@
39
39
  "id": "AC-17404-6",
40
40
  "description": "[must-not] given --ack on D1, D2, D4, D7 or D8, when freeze runs, then it exits 2 unchanged from 0.29.0.",
41
41
  "testable": true
42
+ },
43
+ {
44
+ "id": "AC-17404-7",
45
+ "description": "[happy] (R5 2026-10-05) given a D3 scope narrowed so no recordShape is in scope but a coreEntity has no record shape anywhere tree-wide, when D3 runs, then shapes:entityJoin fails TAD.entity:<name> and the stage state is failing (not notApplicable); the shared tree-wide-before-scope helper (ADR-4138) governs the ordering.",
46
+ "testable": true
47
+ },
48
+ {
49
+ "id": "AC-17404-8",
50
+ "description": "[happy] (R6 2026-10-05) given an interface description with path: Dockerfile and no entry for Dockerfile in resolvedPaths, when D3 runs, then shapes:pathsResolve fails it; given resolvedPaths contains Dockerfile, then it passes. The whitelist exported from gates.js names Dockerfile, Makefile, LICENSE, Procfile, Containerfile, Justfile, Rakefile, Gemfile and CODEOWNERS; a bare word outside the list (foo) still skips as before.",
51
+ "testable": true
52
+ },
53
+ {
54
+ "id": "AC-17404-9",
55
+ "description": "[happy] (PR 292 follow-up 2026-10-05) given treeWideFailureEnvelope invoked on a scope-empty stage with every tree-wide check passing, when the helper runs, then it returns null (the stage proceeds to its own notApplicable envelope); given at least one failing tree-wide check on an ackable stage (D3) with a freeze.gates acknowledgement whose at.hash equals the currentTreeHash, then the helper returns an envelope with state 'acknowledged' through foldState delegation (the helper does not re-implement ack-at-current-hash); given the same shape on D2 (blocking), then the helper never folds to acknowledged.",
56
+ "testable": true
42
57
  }
43
58
  ],
44
59
  "tacIds": [
@@ -47,5 +62,5 @@
47
62
  ],
48
63
  "tags": [],
49
64
  "createdAt": "2026-10-03T10:50:00Z",
50
- "updatedAt": "2026-10-03T10:50:00Z"
65
+ "updatedAt": "2026-10-05T18:00:00Z"
51
66
  }
@@ -32,7 +32,7 @@
32
32
  },
33
33
  {
34
34
  "id": "AC-17405-5",
35
- "description": "[happy] given a registered standards pack cited in an ADR, when D2 runs, then skeleton:standardsCited passes; uncited and unwaived, it fails standards:<pack>.",
35
+ "description": "[happy] (R8 2026-10-05) given a registered standards pack cited in an ADR, when D2 runs, then skeleton:standardsCited passes; uncited and unwaived, it fails standards:<pack>; given a concern-ledger entry concern 'standards:<pack>' with disposition 'applied' or 'waived', it passes (the check question reads 'cited, applied or waived').",
36
36
  "testable": true
37
37
  },
38
38
  {
@@ -49,6 +49,11 @@
49
49
  "id": "AC-17405-8",
50
50
  "description": "[must-not] (PR 9 R11) given an ownerRef whose field uses dotted grammar (interfaces.loan, responsibilities.audit), when D4 or rcf define validate runs, then it fails with the finding naming the dotted field so the author can rewrite to bracket grammar.",
51
51
  "testable": true
52
+ },
53
+ {
54
+ "id": "AC-17405-9",
55
+ "description": "[happy] (R7 2026-10-05) given a D2 scope narrowed so no REQ, PRD, TAD or brief statement is in scope but a registered standards pack is neither cited nor waived tree-wide, when D2 runs, then skeleton:standardsCited fails standards:<pack> and the stage state is failing (not notApplicable); the shared tree-wide-before-scope helper (ADR-4138) governs the ordering.",
56
+ "testable": true
52
57
  }
53
58
  ],
54
59
  "tacIds": [
@@ -57,5 +62,5 @@
57
62
  ],
58
63
  "tags": [],
59
64
  "createdAt": "2026-10-03T13:30:00Z",
60
- "updatedAt": "2026-10-03T20:30:00Z"
65
+ "updatedAt": "2026-10-05T12:00:00Z"
61
66
  }
@@ -44,6 +44,11 @@
44
44
  "id": "AC-18001-7",
45
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
46
  "testable": true
47
+ },
48
+ {
49
+ "id": "AC-18001-8",
50
+ "description": "[must-not] Given any main tab (readiness, overview, requirements, architecture, build, product-map) is the active tab, when an SSE tree-update swaps #rcf-live-content innerHTML and resyncTabsAfterSwap runs, then exactly one tabpanel is visible and it is the active tab's panel. The live-client derives the tab list from the server-rendered nav.tabs rather than carrying a parallel array that can drift from page-init: every nav.tabs [role=tab][data-tab] present at swap time is honoured. Readiness is covered as issue 297's regression.",
51
+ "testable": true
47
52
  }
48
53
  ],
49
54
  "tacIds": [
@@ -51,5 +56,5 @@
51
56
  ],
52
57
  "tags": [],
53
58
  "createdAt": "2026-10-01T18:00:00Z",
54
- "updatedAt": "2026-10-01T18:00:00Z"
59
+ "updatedAt": "2026-10-06T12:00:00Z"
55
60
  }
@@ -33,8 +33,32 @@
33
33
  "when": "a document links to another",
34
34
  "then": "the link navigates to that document's section",
35
35
  "testable": true
36
+ },
37
+ {
38
+ "id": "AC-202-4",
39
+ "description": "[happy] The audit view header keeps every main tab visible and the .tools group right of them at any viewport the chrome supports: the tablist and .tools cannot shrink below their intrinsic width (flex-shrink: 0) and .product carries the shrink budget via min-width: 0 (both on the flex item and on .product .name) so the project name ellipsizes first.",
40
+ "given": "the viewer rendered in the standalone (non-embed) shell",
41
+ "when": "any viewport >= 721px the chrome supports (above the responsive @media that re-rows the tabs under 720px)",
42
+ "then": "the six main tabs are fully visible and the .tools group sits right of them; .product .name ellipsizes when the project name is longer than its slot and nothing in .tools overlaps a clipped tab",
43
+ "testable": true
44
+ },
45
+ {
46
+ "id": "AC-202-5",
47
+ "description": "[happy] The .product .name span carries a title=\"<full name>\" attribute so hovering reveals the whole name when the ellipsis has clipped it; the title is HTML-escaped on the same path as the inner text, and when the product block collapses (no project name on the tree) no title attribute is emitted.",
48
+ "given": "a project tree with a projectName",
49
+ "when": "the viewer renders the header",
50
+ "then": "the .name span has title=\"<the full projectName>\" with the same HTML-escaping as the inner text",
51
+ "testable": true
52
+ },
53
+ {
54
+ "id": "AC-202-6",
55
+ "description": "[happy] The header display name prefers prd.productName, then manifest.projectName, then prd.productTitle. A rename via `rcf define update PRD-001 --set productName=...` therefore reaches the audit chrome without touching manifest.projectName (which the chain keeps for ids and paths).",
56
+ "given": "a tree where both manifest.projectName and prd.productName are set",
57
+ "when": "resolveProductHeader runs",
58
+ "then": "the header projectName is prd.productName; when the PRD carries no productName the fallback is manifest.projectName, then prd.productTitle",
59
+ "testable": true
36
60
  }
37
61
  ],
38
62
  "createdAt": "2026-06-25T00:00:00Z",
39
- "updatedAt": "2026-07-29T13:34:38.914Z"
63
+ "updatedAt": "2026-10-06T10:12:07.288Z"
40
64
  }
@@ -49,6 +49,11 @@
49
49
  "id": "AC-205-8",
50
50
  "description": "[must-not] Given the LookupModal, when the user types a query, picks a result or closes the modal, then no `postMessage` is sent to the parent window, no outbound network request beyond the `./index.json` fetch is made, and no search term is written to `localStorage` / `sessionStorage` / cookies.",
51
51
  "testable": true
52
+ },
53
+ {
54
+ "id": "AC-205-9",
55
+ "description": "[happy] Given the SearchButton (`.rcf-search-btn`) in the shell's `.tools` group, when it is rendered in either theme and in each of its states (default, `:hover`, `:focus-visible`, `:active`), then the computed foreground / background contrast ratio for the button text, the icon stroke against the background and the focus-ring colour against the adjacent surface meets WCAG 2.1 AA (>= 4.5:1 for the text, >= 3:1 for the icon and the focus-ring boundary) in both the light (`:root`) and dark (`:root[data-theme=\"dark\"]`) token sets.",
56
+ "testable": true
52
57
  }
53
58
  ],
54
59
  "tacIds": [
@@ -60,5 +65,5 @@
60
65
  "navigation"
61
66
  ],
62
67
  "createdAt": "2026-10-02T19:00:00Z",
63
- "updatedAt": "2026-10-02T19:00:00Z"
68
+ "updatedAt": "2026-10-04T14:00:00Z"
64
69
  }
@@ -5,10 +5,10 @@
5
5
  "version": "0.1.0",
6
6
  "status": "draft",
7
7
  "title": "wespa's embedding contract is proved by a CI host fixture",
8
- "description": "A same-origin test-host fixture served by the viewer's dev server (route `/test-host.html` behind a `--test-host` flag or an env guard; off by default so wespa's proxy allow-list is unaffected) mimics the wespa shell: a 56px header, a resizable sidebar (64px collapsed, 200-400px expanded, user-resizable live via a drag handle), a content pane carrying an `<iframe src=\"./?embed=1&theme=light\">`. The host posts `{ type: \"rcf-view-theme\", theme: \"light\"|\"dark\" }` on a toggle control; it stores no theme itself so a passing run proves the iframe reads the message. The viewer sends no outbound postMessage. Three lints run in the test suite: (1) framing lint (`GET /` returns no `X-Frame-Options`, no CSP `frame-ancestors` that forbids same-origin, and the fixture loads the iframe without a browser error); (2) target lint (no `target=\"_top\"` string in the server-rendered HTML); (3) localStorage namespace lint (every `localStorage.{setItem,getItem,removeItem}` and shared `storage.*` call site in `src/view/**` uses a key starting with `rcf-view:v1:`). See viewer UI refresh design doc section 9 and Dex's wespa requirements of 2026-10-02.",
8
+ "description": "A same-origin test-host fixture served by the viewer's dev server (route `/test-host.html` behind a `--test-host` flag or an env guard; off by default so wespa's proxy allow-list is unaffected) mimics the wespa shell: a 56px header, a resizable sidebar (64px collapsed, 200-400px expanded, user-resizable live via a drag handle), a content pane carrying an `<iframe src=\"./?embed=1&theme=light\">`. The host posts `{ type: \"rcf-view-theme\", theme: \"light\"|\"dark\" }` on a toggle control; it stores no theme itself so a passing run proves the iframe reads the message. The viewer sends no outbound postMessage. Three lints run in the test suite: (1) framing lint (`GET /` returns no `X-Frame-Options`, no CSP `frame-ancestors` that forbids same-origin, and the fixture loads the iframe without a browser error); (2) target lint (no `target=\"_top\"` string in the server-rendered HTML); (3) localStorage namespace lint (every `localStorage.{setItem,getItem,removeItem}` and shared `storage.*` call site in `src/view/**` uses a key starting with `rcf-view:v1:`). Amended 2026-10-04 (w-2026-10-04-dave-001): the viewer's own three-state theme control (Light|Dark|Auto) sits right of the SearchButton in non-embed mode, is hidden entirely under `embed=1` and in non-embed mode persists its setting under `rcf-view:v1:theme` per ADR-4137 (Dave's storage ruling 2026-10-04). See viewer UI refresh design doc section 9 and section 11 recommendation 6, and Dex's wespa requirements of 2026-10-02.",
9
9
  "asA": "Dex integrating the rcf-lite viewer behind wespa's reverse proxy",
10
- "iWant": "a CI host fixture that proves the viewer honours the embedding contract and three lints that keep framing, target='_top' and localStorage namespace in place over time",
11
- "soThat": "regressions in the embedding contract are caught in the viewer's own suite before they reach wespa",
10
+ "iWant": "a CI host fixture that proves the viewer honours the embedding contract and three lints that keep framing, target='_top' and localStorage namespace in place over time; and a visible three-state theme control in the standalone viewer that is hidden under embed=1",
11
+ "soThat": "regressions in the embedding contract are caught in the viewer's own suite before they reach wespa, and standalone viewer users can switch theme without a URL edit",
12
12
  "acceptanceCriteria": [
13
13
  {
14
14
  "id": "AC-206-1",
@@ -39,6 +39,26 @@
39
39
  "id": "AC-206-6",
40
40
  "description": "[must-not] Given the test-host fixture and the viewer page, when either is loaded, then neither posts any outbound message beyond the host's `rcf-view-theme`; the viewer sends no `postMessage` of any shape, and the test-host adds no new postMessage shape.",
41
41
  "testable": true
42
+ },
43
+ {
44
+ "id": "AC-206-7",
45
+ "description": "[happy] Given the standalone viewer loaded without `embed=1`, when the shell paints, then a three-state theme control (`role=\"group\"`, three buttons Light|Dark|Auto with `aria-pressed`) renders in the header's `.tools` group right of the SearchButton; when a button is pressed, `<html data-theme>` is set to `light` or `dark` (or the attribute is removed for Auto so the `prefers-color-scheme` media query decides) live without a reload, and the pressed button's `aria-pressed` becomes `true` while the others become `false`.",
46
+ "testable": true
47
+ },
48
+ {
49
+ "id": "AC-206-8",
50
+ "description": "[happy] Given the standalone (non-embed) viewer, when a theme button is pressed, then the chosen value (`light`, `dark` or `auto`) is written to `localStorage['rcf-view:v1:theme']`; on the next page load the stored value is reapplied; the boot precedence is `?theme=` wins first, then the stored value, then the default (`auto`); and the postMessage `rcf-view-theme` listener still applies live but writes nothing to the stored key.",
51
+ "testable": true
52
+ },
53
+ {
54
+ "id": "AC-206-9",
55
+ "description": "[must-not] Given the viewer loaded with `?embed=1`, when the shell paints, then the three-state theme control is absent from the DOM entirely (no `[data-rcf-theme-control]` element rendered, not merely hidden by CSS), and no read or write of `localStorage['rcf-view:v1:theme']` happens on load, on a postMessage theme change, or on any user action inside the viewer.",
56
+ "testable": true
57
+ },
58
+ {
59
+ "id": "AC-206-10",
60
+ "description": "[happy] Given the standalone viewer with a stored value `rcf-view:v1:theme = dark`, when the URL carries `?theme=light`, then on load `<html data-theme>` is `light` (the explicit query wins) and the Light button is pressed; the stored value is left as `dark` and is reused on the next load without a `?theme=` query.",
61
+ "testable": true
42
62
  }
43
63
  ],
44
64
  "tacIds": [
@@ -47,8 +67,9 @@
47
67
  "tags": [
48
68
  "view",
49
69
  "embedding",
70
+ "theme",
50
71
  "lint"
51
72
  ],
52
73
  "createdAt": "2026-10-02T20:00:00Z",
53
- "updatedAt": "2026-10-02T20:00:00Z"
74
+ "updatedAt": "2026-10-04T14:00:00Z"
54
75
  }
@@ -0,0 +1,36 @@
1
+ {
2
+ "usId": "US-207",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-002",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "Every viewer text and control colour pair meets WCAG 2.1 AA in both themes",
8
+ "description": "A static CSS-contrast audit test parses `packages/rcf-lite/src/view/style.css`, resolves the `--sv-*` custom properties for the light (`:root`) and dark (`:root[data-theme=\"dark\"]`) token blocks, and enumerates every rule that sets `color` together with a reachable `background` / `background-color` (own rule, then the nearest ancestor surface token, default `--sv-canvas` page background). Each pair is enumerated in both themes and across every reachable state variant: default, `:hover`, `:focus`, `:focus-visible`, `:active`, `[aria-pressed=\"true\"]`, `[aria-selected=\"true\"]`, `.is-active`, `:disabled`. For each pair the WCAG relative-luminance contrast ratio is computed and asserted against the WCAG 2.1 AA thresholds (>= 4.5:1 for normal text, >= 3:1 for large text and non-text UI boundaries per WCAG 1.4.3 and 1.4.11). Pairs that cannot be resolved statically (gradients, rgba over an unknown background) are computed against the two most likely surfaces (`--sv-canvas` and the ancestor surface token) and recorded as approximate rather than skipped. The test emits a BEFORE/AFTER table under `./output/contrast-report.md` and refuses the suite on any failing pair. Scope covers the whole viewer shell, every tab, components, SearchButton and LookupModal, Readiness, Requirements, Architecture, Build (Specs and DAG), ID lookup; Mermaid and Product Map themed palettes are audited on a best-effort basis and anything bound to a third-party default is listed as a known gap with its ratio. See viewer UI refresh design doc section 9 (theme) and Baz's 2026-10-04 review (`w-2026-10-04-dave-001`).",
9
+ "asA": "an engineer or reviewer looking at the review surface in either theme",
10
+ "iWant": "every text and control colour pair in the viewer to meet WCAG 2.1 AA in both themes, pinned by a test that fails the suite when a new rule breaks the floor",
11
+ "soThat": "the review surface stays readable in both themes and nobody has to re-do the audit by hand on every CSS change",
12
+ "acceptanceCriteria": [
13
+ {
14
+ "id": "AC-207-1",
15
+ "description": "[happy] Given the viewer stylesheet `src/view/style.css` and the two token blocks (`:root` and `:root[data-theme=\"dark\"]`), when the contrast test runs, then it enumerates every CSS rule that sets `color` (and every state rule merged over its base selector, so a state that changes only the background is paired with the inherited colour) paired with every reachable background (own rule, nearest ancestor surface token, page background `--sv-canvas`) and every state variant (default, `:hover`, `:focus`, `:focus-visible`, `:active`, `[aria-pressed=\"true\"]`, `[aria-selected=\"true\"]`, `.is-active`, `:disabled`), computes the WCAG relative-luminance contrast ratio for each, and asserts >= 4.5:1 for text, >= 3:1 for large text (>= 18.66px bold or >= 24px) and for non-text UI boundaries (control borders and outlines, focus rings against the adjacent colour, each enumerated as its own pair) per WCAG 1.4.3 and 1.4.11 in both the light and dark token sets.",
16
+ "testable": true
17
+ },
18
+ {
19
+ "id": "AC-207-2",
20
+ "description": "[failure] Given a contrast pair that falls below the applicable threshold, when the test runs, then the failure message names the selector, state, theme, foreground colour, background colour, computed ratio and the threshold applied, and the test exits non-zero so the suite blocks a merge that drops a pair below AA.",
21
+ "testable": true
22
+ }
23
+ ],
24
+ "tacIds": [
25
+ "TAC-4134-wespa-host-fixture"
26
+ ],
27
+ "tags": [
28
+ "view",
29
+ "contrast",
30
+ "accessibility",
31
+ "a11y",
32
+ "theme"
33
+ ],
34
+ "createdAt": "2026-10-04T14:00:00Z",
35
+ "updatedAt": "2026-10-05T12:30:00Z"
36
+ }
@@ -40,8 +40,27 @@
40
40
  # `npm install rcf-lite`.
41
41
 
42
42
  feedVersion: 1
43
- latest: "0.30.0"
43
+ latest: "0.32.0"
44
44
  releases:
45
+ - version: "0.32.0"
46
+ date: "2026-10-06"
47
+ breaking: false
48
+ headlines:
49
+ - "rcf-lite 0.32.0 fixes two readiness and freeze gates that false-tripped under schema-canonical content: D6 consistency:orphanInterfaces now accepts the object-shape REQ.deliveredBy (issue 301), and D3 shapes:pathsResolve inside rcf define freeze pre-resolves TAC interface path tokens the way readiness does (issue 302). The deploy-cloudflare-workers blueprint reworded AC-12113-2 so D6 consistency:unsatisfiable stops reading 'absent' as a field name (PR 300)."
50
+ - "Viewer fixes: SSE tree-update resync preserves the active tab for every main tab including Readiness (issue 297), and the audit view header keeps the six tabs visible under long project names and prefers prd.productName (issue 295)."
51
+ minAgentAction: null
52
+ notesUrl: "https://stravica.ai/docs/rcf/changelog/"
53
+
54
+ - version: "0.31.0"
55
+ date: "2026-10-05"
56
+ breaking: true
57
+ headlines:
58
+ - "rcf-lite 0.31.0 gives the viewer a Light, Dark and Auto theme control in standalone mode and brings its text and control colours up to WCAG AA contrast in both themes, with a test that blocks any regression. Under embed=1 the control is absent, nothing is stored and the rcf-view-theme message contract is unchanged."
59
+ - "DEFINE step 3 rulings R1 to R13 land: tree-wide readiness checks now run before any scope short-circuit, so a narrowed scope still reports a draft marker, an unjoined core entity, an uncited standards pack, a missing Deploy ADR or an empty security or operational section anywhere in the tree. A stage whose scope is empty but whose tree-wide check fails now reads failing rather than not applicable, and the draft-settled check is tree-wide."
60
+ - "Write-backs that carry an id are idempotent: rcf define create <kind> --id and rcf define ledger <name> add --id exit 0 and write nothing when the same id is replayed with the same content, and exit 3 naming the differing fields when the content differs."
61
+ minAgentAction: null
62
+ notesUrl: "https://stravica.ai/docs/rcf/changelog/"
63
+
45
64
  - version: "0.30.0"
46
65
  date: "2026-10-03"
47
66
  breaking: true