rcf-lite 0.32.0 → 0.32.2
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 +20 -0
- package/blueprints/delivery-ci-workflows/CHANGELOG.md +5 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6102.json +5 -1
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6103.json +5 -1
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6108.json +5 -1
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6111.json +10 -2
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6121.json +5 -1
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6122.json +5 -1
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6123.json +5 -1
- package/blueprints/deploy-cloudflare-workers/CHANGELOG.md +5 -0
- package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12103.json +5 -1
- package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12110.json +5 -1
- package/blueprints/email-smtp-resend/CHANGELOG.md +5 -0
- package/blueprints/email-smtp-resend/contributions/user-stories/email-smtp-resend-us-4104.json +5 -1
- package/blueprints/email-smtp-resend/contributions/user-stories/email-smtp-resend-us-4105.json +5 -1
- package/blueprints/email-smtp-resend/contributions/user-stories/email-smtp-resend-us-4106.json +5 -1
- package/blueprints/observability-probe-endpoints/CHANGELOG.md +5 -0
- package/blueprints/observability-probe-endpoints/contributions/user-stories/observability-probe-endpoints-us-14104.json +5 -1
- package/fixtures/canary-manifest.json +6 -6
- package/package.json +1 -1
- package/releases/releases.yaml +17 -1
- package/src/cli/ledger.js +15 -0
- package/src/cli/req-baseline.js +16 -2
- package/src/query/gates.js +72 -17
- package/src/view/index.js +16 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.32.2] - 2026-10-06
|
|
10
|
+
|
|
11
|
+
0.32.2 ships two Dex-lane fixes merged since 0.32.1 (merge b478a4e) as the WESPA readiness alignment release. D6 consistency:unsatisfiable now context-gates backticked identifiers in AC then-clauses so enum values, query-string parameter names, sort and filter literals, env-var names and config knobs stop being read as record-shape field names, and `rcf discover req-baseline opt-out` and `rcf define ledger concerns add` honour `--dry-run` by writing nothing. breaking: false; no behavioural changes on verbs consumers run in CI; fixes only. minAgentAction: null; the managed agent-instructions block hash did not move.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **D6 consistency:unsatisfiable stops reading backticked enum values, query-string parameter names, sort and filter literals, env-var and config-knob names as record-shape field names (issue 311, w-2026-10-06-dave-050).** `extractThenFieldTokens` in `src/query/gates.js` previously fired `consistency:unsatisfiable` on any backticked identifier in an AC `then` clause and treated every token it caught as a field some `recordShape` had to define. On WESPA (rcf-lite 0.31.0, main `df0fa47e`) 345 of 532 flagged tokens were not fields: HTTP error-envelope `code` enum values like `VALIDATION_ERROR` and `AUTH_TOKEN_INVALID_SIGNATURE`, pagination query-string keys like `pageSize`, `offset`, `cursors` and sort-order values like `asc`, `desc`, `next`, filter and match-mode literals like `query`, `exact`, `in`, env-var names like `BUILD_GIT_COMMIT_SHORT` and config knobs like `maxDiffPixelRatio`, `fullPath`, `workerAdminUser`. Fix: pattern 1 (bare backticked identifier) is now context-gated. ALL_CAPS tokens (enum values, env vars, constants) are skipped unconditionally. Other backticked tokens count only when a field-signalling word (a verb like `set`, `write`, `include`, `return`, `contain`; a predicate like `is set`, `is written`, `equals`, `holds`; or a context word like `field`, `record shape`, `response body`, `request body`, `payload`, `envelope`) sits within a short neighbourhood AND no explicit non-field context (`query parameter`, `query-string`, `sort order`, `sort direction`, `filter mode`, `match mode`, `enum value`, `env var`, `environment variable`, `config knob`, `config key`, `header name`, `cli flag`) overlaps the same neighbourhood. Pattern 4 added: `<ShapeName>.<fieldName>` dot notation is now read as a schema-style field reference. Patterns 2 (`<name> field`) and 3 (`field: <name>`) are preserved and the AC-12113-2 class is unchanged: a then-clause that names a real undefined field still fails. Regression fixtures in `test/query/gates.test.js` cover each of the five WESPA token patterns from the issue plus a dot-notation negative control and a plain-prose negative control. Expected drop on WESPA: 345 unsatisfiable findings removed; the 187 true-positive findings and all AC-12113-2 class failures remain.
|
|
16
|
+
|
|
17
|
+
- **`--dry-run` on `rcf discover req-baseline opt-out` and `rcf define ledger concerns add` writes nothing (issue 310, w-2026-10-06-dave-050).** Both verbs accepted the flag but wrote the opt-out record to `rcf/manifest.json` (`baselineAcOptOuts[]`) and created `rcf/define/concern-ledger.json` with the appended entry respectively, mutating viewer clones on rehearsal runs. Fix: `src/cli/req-baseline.js` opt-out verb and `src/cli/ledger.js` concerns handler both check `flags['dry-run']` before the write and emit a `[dry-run]` preview line (or a `{dryRun: true, ...}` JSON payload with `--json`) and exit 0. Help text updated on both verbs to document the flag. Regression tests in `test/cli/req-baseline-opt-out-dry-run.test.js` (new) and `test/cli/ledger.test.js` assert the on-disk bytes under `rcf/` are byte-equal before and after each dry-run and that the positive control (no `--dry-run`) still writes.
|
|
18
|
+
|
|
19
|
+
## [0.32.1] - 2026-10-06
|
|
20
|
+
|
|
21
|
+
0.32.1 ships two fixes merged since 0.32.0 (merge b6292f5) on top of the layout baseline chore. The viewer's Readiness tab now agrees with the CLI on D3 shapes:pathsResolve, and the D5 crosscut:operationalConcerns trigger reads the structured ac.scope field in addition to the legacy "[deployed]" marker. breaking: false; no behavioural changes on verbs consumers run in CI; fixes only. minAgentAction: null; the managed agent-instructions block hash did not move.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- **Viewer Readiness tab D3 shapes:pathsResolve now agrees with the CLI (issue 307, w-2026-10-06-dave-026).** The viewer's render path (`src/view/index.js` `renderModelToPage`) called `computeReadiness` without the `resolvedPaths` pre-walk, so D3 shapes:pathsResolve false-failed on TAC interface `path:` tokens that resolve on disk and the Readiness tab disagreed with the CLI and freeze. Hit on ai-baseline define/kickoff: CLI reported D3 4/4 passing while the viewer tab showed D3 shapes:pathsResolve failing on four TAC interface paths. Fix: thread `resolvedPaths` through the viewer compute via the same `resolveInterfacePaths` seam the CLI (PR 304, issue 302) and freeze use. The viewer compute now runs the resolve walk against `projectRoot` before `computeReadiness` and passes the resulting `resolvedPaths` set to the compute. The pure gate reads it unchanged. Additive: `renderModelToPage` also returns the computed readiness so a regression test can assert the viewer's check state without parsing HTML. Regression test in `test/view/readiness-paths-resolve-307.test.js` seeds a TAC interface whose `path:` token points at a real file and asserts the viewer compute reports D3 ok; the negative control flips the path to a missing file and asserts D3 fails with the shapes:pathsResolve finding.
|
|
26
|
+
|
|
27
|
+
- **D5 crosscut:operationalConcerns trigger reads structured `ac.scope` (issue 306, w-2026-10-06-dave-031).** `computeOperationalConcernsCheck` in `src/query/gates.js` looked only for a literal `[deployed]` marker in AC description text, so trees that carry scope as the schema-canonical field `scope: "deployed"` (like ai-baseline, with 23 deployed-scope ACs and `TAD.operationalConcerns` absent) silently passed 0/0 and the gate never fired. Fix: the trigger now also reads `ac.scope === "deployed"`; a deployed-scope AC on either form must find a non-empty `TAD.operationalConcerns`. Legacy `[deployed]` markers are unchanged. Regression fixtures in `test/query/gates.test.js` cover the ai-baseline shape (structured `ac.scope` with empty `operationalConcerns` fails) and a negative control (non-deployed-scope ACs with empty `operationalConcerns` pass).
|
|
28
|
+
|
|
9
29
|
## [0.32.0] - 2026-10-06
|
|
10
30
|
|
|
11
31
|
0.32.0 ships four readiness and viewer fixes merged since 0.31.0 (merge ec9464db) as one release. D6 consistency:orphanInterfaces now accepts the schema-canonical object-shape REQ.deliveredBy (issue 301), 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), the viewer's SSE tree-update resync preserves the active tab for every main tab including Readiness (issue 297), and the audit view header keeps the tabs visible under long project names and prefers prd.productName (issue 295). minAgentAction: null; the managed agent-instructions block hash did not move.
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- adds ownerRef on AC-6102-1 (interfaces[entry-point CLI]), AC-6103-1 (interfaces[gate-descriptor]), AC-6108-1 (interfaces[aggregate-report-schema]), AC-6111-1 (interfaces[entry-point CLI]), AC-6111-2 (interfaces[workflow-shape]), AC-6121-2 (interfaces[release-mode]), AC-6122-1 (interfaces[entry-point CLI]) and AC-6123-1 (interfaces[entry-point CLI]) so TAC-701 gate-descriptor and entry-point CLI, TAC-703 aggregate-report-schema, TAC-704 workflow-shape and entry-point CLI, TAC-705 release-mode and entry-point CLI, and TAC-706 entry-point CLI have delivering AC owners (D6 consistency:orphanInterfaces).
|
|
6
|
+
|
|
7
|
+
|
|
3
8
|
## 2.3.5 - 2026-09-11
|
|
4
9
|
|
|
5
10
|
probe-utils.writeReport no longer promotes an all-accountBoundSkipped row set to PASS via a separate override; aggregate() already returns PASS for a pure-skip set on its own and returns WARN when any WARN row is present, so an all-skipped array carrying a WARN row is no longer silently promoted. Anatomy suite tracks the strengthened shared helper.
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"then": "the aggregate report's `runner.entryPoint` field records a project-relative path to a `.js` or `.mjs` file that exists at that path in the tree",
|
|
18
18
|
"testable": true,
|
|
19
19
|
"scope": "runtime",
|
|
20
|
-
"disposition": "fixed"
|
|
20
|
+
"disposition": "fixed",
|
|
21
|
+
"ownerRef": {
|
|
22
|
+
"tacId": "TAC-701-delivery-ci-workflows-gate-runner",
|
|
23
|
+
"field": "interfaces[entry-point CLI]"
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
{
|
|
23
27
|
"id": "AC-6102-2",
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"then": "the file exists, its `gate` field is `validate`, its `command` field names the `rcf define validate` invocation, its `exitCode` field records the underlying process exit code, and its `stdout` and `stderr` fields carry the captured streams",
|
|
18
18
|
"testable": true,
|
|
19
19
|
"scope": "runtime",
|
|
20
|
-
"disposition": "template"
|
|
20
|
+
"disposition": "template",
|
|
21
|
+
"ownerRef": {
|
|
22
|
+
"tacId": "TAC-701-delivery-ci-workflows-gate-runner",
|
|
23
|
+
"field": "interfaces[gate-descriptor]"
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
{
|
|
23
27
|
"id": "AC-6103-2",
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"then": "the aggregate report's `gates[]` array contains a `coverage-strict` entry with `outcome: missing`, its `reason` field carries a non-empty string, and the aggregate `verdict` is `failed`",
|
|
18
18
|
"testable": true,
|
|
19
19
|
"scope": "runtime",
|
|
20
|
-
"disposition": "template"
|
|
20
|
+
"disposition": "template",
|
|
21
|
+
"ownerRef": {
|
|
22
|
+
"tacId": "TAC-703-delivery-ci-workflows-aggregate-report",
|
|
23
|
+
"field": "interfaces[aggregate-report-schema]"
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
{
|
|
23
27
|
"id": "AC-6108-2",
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"then": "the materialiser reads the file at the documented path and treats its `workflowShape` as authoritative",
|
|
18
18
|
"testable": true,
|
|
19
19
|
"scope": "runtime",
|
|
20
|
-
"disposition": "template"
|
|
20
|
+
"disposition": "template",
|
|
21
|
+
"ownerRef": {
|
|
22
|
+
"tacId": "TAC-704-delivery-ci-workflows-workflow-materialiser",
|
|
23
|
+
"field": "interfaces[entry-point CLI]"
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
{
|
|
23
27
|
"id": "AC-6111-2",
|
|
@@ -27,7 +31,11 @@
|
|
|
27
31
|
"then": "the materialiser processes the recognised fields (including the substitution-surface trio) and ignores the unknown fields without failing the boot-check",
|
|
28
32
|
"testable": true,
|
|
29
33
|
"scope": "runtime",
|
|
30
|
-
"disposition": "template"
|
|
34
|
+
"disposition": "template",
|
|
35
|
+
"ownerRef": {
|
|
36
|
+
"tacId": "TAC-704-delivery-ci-workflows-workflow-materialiser",
|
|
37
|
+
"field": "interfaces[workflow-shape]"
|
|
38
|
+
}
|
|
31
39
|
},
|
|
32
40
|
{
|
|
33
41
|
"id": "AC-6111-3",
|
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
"then": "the release workflow creates the release entity, runs the artefact-publish step, uploads the published artefact to the release entity, and records the just-published version identifier in the aggregate report",
|
|
28
28
|
"testable": true,
|
|
29
29
|
"scope": "runtime",
|
|
30
|
-
"disposition": "template"
|
|
30
|
+
"disposition": "template",
|
|
31
|
+
"ownerRef": {
|
|
32
|
+
"tacId": "TAC-705-delivery-ci-workflows-release-workflow",
|
|
33
|
+
"field": "interfaces[release-mode]"
|
|
34
|
+
}
|
|
31
35
|
},
|
|
32
36
|
{
|
|
33
37
|
"id": "AC-6121-3",
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"then": "the release workflow dispatches the promote workflow with `versionId` set to the just-published version identifier and waits on the promote workflow's outcome",
|
|
18
18
|
"testable": true,
|
|
19
19
|
"scope": "runtime",
|
|
20
|
-
"disposition": "template"
|
|
20
|
+
"disposition": "template",
|
|
21
|
+
"ownerRef": {
|
|
22
|
+
"tacId": "TAC-705-delivery-ci-workflows-release-workflow",
|
|
23
|
+
"field": "interfaces[entry-point CLI]"
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
{
|
|
23
27
|
"id": "AC-6122-2",
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"then": "a `scheduled-audit` workflow file lands at the provider's workflow path, its trigger set names a cron schedule, and its command line invokes the Node gate-runner entry point with the required check set writing to the distinct report path",
|
|
18
18
|
"testable": true,
|
|
19
19
|
"scope": "runtime",
|
|
20
|
-
"disposition": "template"
|
|
20
|
+
"disposition": "template",
|
|
21
|
+
"ownerRef": {
|
|
22
|
+
"tacId": "TAC-706-delivery-ci-workflows-scheduled-audit",
|
|
23
|
+
"field": "interfaces[entry-point CLI]"
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
{
|
|
23
27
|
"id": "AC-6123-2",
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- adds ownerRef on AC-12103-1 (interfaces[workerIdentity]) and AC-12110-1 (interfaces[probeUrl]) so the preview URL resolver's workerIdentity and probeUrl interfaces have delivering AC owners (D6 consistency:orphanInterfaces).
|
|
6
|
+
|
|
7
|
+
|
|
3
8
|
## 1.3.2 (register patch, 2026-09-10)
|
|
4
9
|
|
|
5
10
|
- Register: neutral wording on the ADR-1306 consequences field (closes F-1); no capability change.
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"then": "both responses carry the `versionSha` field of V's build provenance in the health-probe body, and the two responses agree on that field",
|
|
18
18
|
"testable": true,
|
|
19
19
|
"scope": "deployed",
|
|
20
|
-
"disposition": "template"
|
|
20
|
+
"disposition": "template",
|
|
21
|
+
"ownerRef": {
|
|
22
|
+
"tacId": "TAC-1303-deploy-cloudflare-workers-preview-url-resolver",
|
|
23
|
+
"field": "interfaces[workerIdentity]"
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
{
|
|
23
27
|
"id": "AC-12103-2",
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"then": "at least one HTTP request to the production URL's `/healthz` completes within the sixty-second window; the request carries no `Authorization` header that would bypass a production auth layer; the retry count for any single probe does not exceed six",
|
|
18
18
|
"testable": true,
|
|
19
19
|
"scope": "deployed",
|
|
20
|
-
"disposition": "template"
|
|
20
|
+
"disposition": "template",
|
|
21
|
+
"ownerRef": {
|
|
22
|
+
"tacId": "TAC-1303-deploy-cloudflare-workers-preview-url-resolver",
|
|
23
|
+
"field": "interfaces[probeUrl]"
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
{
|
|
23
27
|
"id": "AC-12110-2",
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- adds ownerRef on AC-4106-1 (interfaces[smtpClient]), AC-4104-2 (interfaces[auditSink]) and AC-4105-1 (interfaces[verifierClock]) so TAC-401 smtpClient and TAC-402 auditSink and verifierClock have delivering AC owners (D6 consistency:orphanInterfaces).
|
|
6
|
+
|
|
7
|
+
|
|
3
8
|
## 1.1.7 - 2026-09-11
|
|
4
9
|
|
|
5
10
|
unverified-sender-refusal AC-4102-1 and AC-4102-2 rows are de-claimed to conformanceOnly. The refusal is driven by the local catch-all SMTP fixture (fixture-as-engine) and carries no Resend-returned message id, so the identifier half of the strict-evidence contract is not satisfied here. The live real-account-resend-send row (AC-4101-2) is unchanged.
|
package/blueprints/email-smtp-resend/contributions/user-stories/email-smtp-resend-us-4104.json
CHANGED
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
"then": "the response status is 401, the response body is empty, the handler invocation counter is unchanged, and the audit sink observes exactly one `webhookVerificationFailed` event",
|
|
28
28
|
"testable": true,
|
|
29
29
|
"scope": "runtime",
|
|
30
|
-
"disposition": "fixed"
|
|
30
|
+
"disposition": "fixed",
|
|
31
|
+
"ownerRef": {
|
|
32
|
+
"tacId": "TAC-402-email-smtp-resend-webhook-verifier",
|
|
33
|
+
"field": "interfaces[auditSink]"
|
|
34
|
+
}
|
|
31
35
|
},
|
|
32
36
|
{
|
|
33
37
|
"id": "AC-4104-3",
|
package/blueprints/email-smtp-resend/contributions/user-stories/email-smtp-resend-us-4105.json
CHANGED
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"then": "the response status is 401, the response body is empty, the handler invocation counter is unchanged, and the audit sink observes exactly one `webhookReplayStale` event",
|
|
18
18
|
"testable": true,
|
|
19
19
|
"scope": "runtime",
|
|
20
|
-
"disposition": "template"
|
|
20
|
+
"disposition": "template",
|
|
21
|
+
"ownerRef": {
|
|
22
|
+
"tacId": "TAC-402-email-smtp-resend-webhook-verifier",
|
|
23
|
+
"field": "interfaces[verifierClock]"
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
{
|
|
23
27
|
"id": "AC-4105-2",
|
package/blueprints/email-smtp-resend/contributions/user-stories/email-smtp-resend-us-4106.json
CHANGED
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"then": "the scan returns exactly one module and that module is the adapter module",
|
|
18
18
|
"testable": true,
|
|
19
19
|
"scope": "runtime",
|
|
20
|
-
"disposition": "fixed"
|
|
20
|
+
"disposition": "fixed",
|
|
21
|
+
"ownerRef": {
|
|
22
|
+
"tacId": "TAC-401-email-smtp-resend-send-adapter",
|
|
23
|
+
"field": "interfaces[smtpClient]"
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
{
|
|
23
27
|
"id": "AC-4106-2",
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- adds ownerRef on AC-14104-1 (interfaces[listenerFactory]) so the listener-topology's listenerFactory interface has a delivering AC owner (D6 consistency:orphanInterfaces).
|
|
6
|
+
|
|
7
|
+
|
|
3
8
|
## 1.2.5 - 2026-09-11
|
|
4
9
|
|
|
5
10
|
Anatomy suite tracks the strengthened shared helper (WARN-only shortcut removed, log objects and content lists no longer identifiers, retained anchor existence check). No probe changes required.
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"then": "a GET against `/live` on port 9091 returns 200, a GET against `/live` on the user-traffic port returns 404 (or the project's default not-found response), and a GET against `/ready` on the user-traffic port returns 404",
|
|
18
18
|
"testable": true,
|
|
19
19
|
"scope": "runtime",
|
|
20
|
-
"disposition": "template"
|
|
20
|
+
"disposition": "template",
|
|
21
|
+
"ownerRef": {
|
|
22
|
+
"tacId": "TAC-1503-observability-probe-endpoints-listener-topology",
|
|
23
|
+
"field": "interfaces[listenerFactory]"
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
{
|
|
23
27
|
"id": "AC-14104-2",
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"registerCanary": [
|
|
3
3
|
{
|
|
4
4
|
"id": "rc-2026-10-06-001",
|
|
5
|
-
"createdAt": "2026-10-
|
|
6
|
-
"buildVersion": "0.32.
|
|
5
|
+
"createdAt": "2026-10-06T19:49:58.281Z",
|
|
6
|
+
"buildVersion": "0.32.2-mockdriver",
|
|
7
7
|
"fixturePromptId": "canary-prompt-01",
|
|
8
8
|
"responseWordCount": 55,
|
|
9
9
|
"grades": {
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"id": "rc-2026-10-06-002",
|
|
38
|
-
"createdAt": "2026-10-
|
|
39
|
-
"buildVersion": "0.32.
|
|
38
|
+
"createdAt": "2026-10-06T19:49:58.282Z",
|
|
39
|
+
"buildVersion": "0.32.2-mockdriver",
|
|
40
40
|
"fixturePromptId": "canary-prompt-02",
|
|
41
41
|
"responseWordCount": 55,
|
|
42
42
|
"grades": {
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
70
|
"id": "rc-2026-10-06-003",
|
|
71
|
-
"createdAt": "2026-10-
|
|
72
|
-
"buildVersion": "0.32.
|
|
71
|
+
"createdAt": "2026-10-06T19:49:58.282Z",
|
|
72
|
+
"buildVersion": "0.32.2-mockdriver",
|
|
73
73
|
"fixturePromptId": "canary-prompt-03",
|
|
74
74
|
"responseWordCount": 55,
|
|
75
75
|
"grades": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rcf-lite",
|
|
3
|
-
"version": "0.32.
|
|
3
|
+
"version": "0.32.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "One-install tooling for the Requirements Confidence Framework (RCF): the unified `rcf` CLI grouped into the five RCF tool groups (discover, define, build, verify, audit) plus a small core set (init, doctor, guidance, mcp), an MCP server, the live tree viewer and the fresh-context adversarial ship-gate verifier. Consumes @stravica-ai/rcf-schemas.",
|
|
6
6
|
"license": "Apache-2.0",
|
package/releases/releases.yaml
CHANGED
|
@@ -40,8 +40,24 @@
|
|
|
40
40
|
# `npm install rcf-lite`.
|
|
41
41
|
|
|
42
42
|
feedVersion: 1
|
|
43
|
-
latest: "0.32.
|
|
43
|
+
latest: "0.32.2"
|
|
44
44
|
releases:
|
|
45
|
+
- version: "0.32.2"
|
|
46
|
+
date: "2026-10-06"
|
|
47
|
+
breaking: false
|
|
48
|
+
headlines:
|
|
49
|
+
- "rcf-lite 0.32.2 ships two Dex-lane fixes on top of 0.32.1. D6 consistency:unsatisfiable now context-gates backticked identifiers in AC then-clauses so enum values, query-string parameter names, sort and filter literals, env-var names and config knobs stop being read as record-shape field names (issue 311; expected drop on WESPA 345 unsatisfiable findings, 187 true positives remain, AC-12113-2 class unchanged), and `rcf discover req-baseline opt-out` and `rcf define ledger concerns add` honour `--dry-run` by writing nothing (issue 310)."
|
|
50
|
+
minAgentAction: null
|
|
51
|
+
notesUrl: "https://stravica.ai/docs/rcf/changelog/"
|
|
52
|
+
|
|
53
|
+
- version: "0.32.1"
|
|
54
|
+
date: "2026-10-06"
|
|
55
|
+
breaking: false
|
|
56
|
+
headlines:
|
|
57
|
+
- "rcf-lite 0.32.1 fixes two readiness gates that false-paired under schema-canonical content on top of 0.32.0. The viewer's Readiness tab D3 shapes:pathsResolve now agrees with the CLI by pre-resolving TAC interface path tokens through the same resolveInterfacePaths seam (issue 307), and the D5 crosscut:operationalConcerns trigger reads the structured ac.scope field in addition to the legacy '[deployed]' marker so trees that carry scope as a schema field no longer silently pass 0/0 (issue 306)."
|
|
58
|
+
minAgentAction: null
|
|
59
|
+
notesUrl: "https://stravica.ai/docs/rcf/changelog/"
|
|
60
|
+
|
|
45
61
|
- version: "0.32.0"
|
|
46
62
|
date: "2026-10-06"
|
|
47
63
|
breaking: false
|
package/src/cli/ledger.js
CHANGED
|
@@ -112,6 +112,7 @@ concerns add flags:
|
|
|
112
112
|
--disposition applied|waived
|
|
113
113
|
Disposition (required)
|
|
114
114
|
--reason <text> Reason (mandatory when disposition=waived)
|
|
115
|
+
--dry-run Preview the minted entry; write nothing
|
|
115
116
|
|
|
116
117
|
probes add flags:
|
|
117
118
|
--req <req-id> REQ the probe finding sits on (required)
|
|
@@ -523,6 +524,20 @@ async function runAdd({ projectRoot, name, flags, stdout, stderr }) {
|
|
|
523
524
|
name: 'concerns', body, entry, now,
|
|
524
525
|
...(idOverride !== undefined ? { id: idOverride } : {}),
|
|
525
526
|
});
|
|
527
|
+
// Issue 310 (0.32.2): --dry-run on concerns add previews the
|
|
528
|
+
// minted entry without writing the ledger. Prior behaviour
|
|
529
|
+
// accepted the flag and created `rcf/define/concern-ledger.json`
|
|
530
|
+
// anyway, mutating viewer clones on rehearsal runs.
|
|
531
|
+
if (flags['dry-run']) {
|
|
532
|
+
if (flags.json) {
|
|
533
|
+
stdout.write(`${JSON.stringify({ dryRun: true, status: step.status, id: step.entry.id, entry: step.entry }, null, 2)}\n`);
|
|
534
|
+
} else if (step.status === 'unchanged') {
|
|
535
|
+
stdout.write(`[dry-run] concern-ledger: would be unchanged ${step.entry.id}\n`);
|
|
536
|
+
} else {
|
|
537
|
+
stdout.write(`[dry-run] concern-ledger: would add concern ${step.entry.id}\n`);
|
|
538
|
+
}
|
|
539
|
+
return 0;
|
|
540
|
+
}
|
|
526
541
|
if (step.status === 'unchanged') {
|
|
527
542
|
if (flags.json) {
|
|
528
543
|
stdout.write(`${JSON.stringify({ status: 'unchanged', id: step.entry.id, entry: step.entry }, null, 2)}\n`);
|
package/src/cli/req-baseline.js
CHANGED
|
@@ -60,12 +60,14 @@ Verbs:
|
|
|
60
60
|
(silence-refuses-build queue) without
|
|
61
61
|
prompting.
|
|
62
62
|
|
|
63
|
-
opt-out --req <id> --key <baselineKey> --reason "..." [--scope req|project]
|
|
63
|
+
opt-out --req <id> --key <baselineKey> --reason "..." [--scope req|project] [--dry-run]
|
|
64
64
|
Record an operator ruling that removes a
|
|
65
65
|
baseline AC from the REQ (or the whole
|
|
66
66
|
project) and inherit the ruling on future
|
|
67
67
|
sweeps. Reason is at least 20 characters
|
|
68
|
-
(schema-enforced).
|
|
68
|
+
(schema-enforced). --dry-run prints the
|
|
69
|
+
opt-out that would be written and leaves
|
|
70
|
+
the manifest untouched.
|
|
69
71
|
|
|
70
72
|
opt-out --remove --req <id> --key <baselineKey>
|
|
71
73
|
Delete an existing opt-out; the next sweep
|
|
@@ -252,6 +254,18 @@ export async function main(argv, deps = {}) {
|
|
|
252
254
|
stderr.write(`[error] usage req-baseline opt-out: --scope must be req or project (got ${scope})\n`);
|
|
253
255
|
return 2;
|
|
254
256
|
}
|
|
257
|
+
// Issue 310 (0.32.2): --dry-run on opt-out previews the opt-out
|
|
258
|
+
// record without writing to the manifest. Prior behaviour accepted
|
|
259
|
+
// the flag and wrote anyway, mutating viewer clones on rehearsal
|
|
260
|
+
// runs.
|
|
261
|
+
if (flags['dry-run']) {
|
|
262
|
+
if (flags.json) {
|
|
263
|
+
stdout.write(`${JSON.stringify({ dryRun: true, reqId: flags.req, baselineKey: flags.key, scope, reason: flags.reason }, null, 2)}\n`);
|
|
264
|
+
} else {
|
|
265
|
+
stdout.write(`[dry-run] would opt-out ${flags.req} ${flags.key} (scope=${scope})\n`);
|
|
266
|
+
}
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
255
269
|
const result = await writeOptOut({
|
|
256
270
|
projectRoot,
|
|
257
271
|
tree: walkResult.tree,
|
package/src/query/gates.js
CHANGED
|
@@ -1582,7 +1582,15 @@ function computeSecurityArchitectureCheck(ctx) {
|
|
|
1582
1582
|
function computeOperationalConcernsCheck(ctx) {
|
|
1583
1583
|
const tree = ctx.tree;
|
|
1584
1584
|
const tad = /** @type {any} */ (tree.tad ?? {});
|
|
1585
|
-
|
|
1585
|
+
// Issue 306 (2026-10-06): schemas carry AC scope as the structured
|
|
1586
|
+
// field `scope: "deployed"`; the legacy `[deployed]` description
|
|
1587
|
+
// marker (R13 2026-10-05) is kept so older trees still trigger. A
|
|
1588
|
+
// deployed-scope AC on either form must find a non-empty
|
|
1589
|
+
// TAD.operationalConcerns.
|
|
1590
|
+
const hasDeployedAc = (tree.userStories ?? []).some((us) => (us.acceptanceCriteria ?? []).some((ac) => {
|
|
1591
|
+
if (ac?.scope === 'deployed') return true;
|
|
1592
|
+
return typeof ac?.description === 'string' && ac.description.includes('[deployed]');
|
|
1593
|
+
}));
|
|
1586
1594
|
const opFail = [];
|
|
1587
1595
|
if (hasDeployedAc) {
|
|
1588
1596
|
const oc = tad.operationalConcerns;
|
|
@@ -2025,9 +2033,23 @@ function collectRecordShapeFields(tree) {
|
|
|
2025
2033
|
* that follows a verb like `set`, `write`, `include`, `return`,
|
|
2026
2034
|
* `contain`, `name` or appears as the direct object of a `then`'s
|
|
2027
2035
|
* verb phrase; a cheap approximation uses `then <subject> <verb>
|
|
2028
|
-
* <field>` plus `field <name>`, `<name> field
|
|
2029
|
-
* patterns. The scan is deliberately conservative so
|
|
2030
|
-
* sentence yields a small set.
|
|
2036
|
+
* <field>` plus `field <name>`, `<name> field`, `field: <name>` and
|
|
2037
|
+
* `<Shape>.<name>` patterns. The scan is deliberately conservative so
|
|
2038
|
+
* a plain English sentence yields a small set.
|
|
2039
|
+
*
|
|
2040
|
+
* Issue 311 (0.32.2): a bare backticked identifier (pattern 1) was
|
|
2041
|
+
* reading error-enum values, pagination query-string keys, sort and
|
|
2042
|
+
* filter-mode literals, env-var and config-knob names as record-shape
|
|
2043
|
+
* fields and false-positiving 345 of 532 ACs on WESPA. Pattern 1 is
|
|
2044
|
+
* now context-gated: ALL_CAPS tokens (enum values, env vars,
|
|
2045
|
+
* constants) are skipped unconditionally; other backticked tokens
|
|
2046
|
+
* count only when a field-signalling word sits within a short
|
|
2047
|
+
* neighbourhood AND no explicit non-field context (query parameter,
|
|
2048
|
+
* sort order, filter/match mode, env var, config knob, header name,
|
|
2049
|
+
* cli flag) overlaps the same neighbourhood. The AC-12113-2 class is
|
|
2050
|
+
* preserved: a then-clause that names a real missing field via
|
|
2051
|
+
* `<name> field`, `field: <name>` or an emit-verb context still fails
|
|
2052
|
+
* the check.
|
|
2031
2053
|
*
|
|
2032
2054
|
* @param {string} thenClause
|
|
2033
2055
|
* @returns {string[]}
|
|
@@ -2037,27 +2059,60 @@ function extractThenFieldTokens(thenClause) {
|
|
|
2037
2059
|
const text = stripQuotedSubstrings(thenClause);
|
|
2038
2060
|
/** @type {Set<string>} */
|
|
2039
2061
|
const tokens = new Set();
|
|
2040
|
-
// Pattern 1: backticked identifier (`then `balance` is set`). The
|
|
2041
|
-
// primary signal; prose that quotes a field name under backticks is
|
|
2042
|
-
// the convention this check expects.
|
|
2043
|
-
for (const m of text.matchAll(/`([A-Za-z_][A-Za-z0-9_]*)`/g)) {
|
|
2044
|
-
if (m[1]) tokens.add(m[1]);
|
|
2045
|
-
}
|
|
2046
|
-
// Pattern 2: `<name> field` (adjective-form naming). An English
|
|
2047
|
-
// "stop word" skip keeps "the field", "a field" and "its field" from
|
|
2048
|
-
// tripping the check.
|
|
2049
2062
|
const STOP = new Set(['the', 'a', 'an', 'this', 'that', 'its', 'our', 'their', 'any', 'no', 'some', 'each', 'every', 'another']);
|
|
2050
|
-
|
|
2063
|
+
|
|
2064
|
+
// Pattern 2 (preserved): `<name> field` (adjective-form naming). The
|
|
2065
|
+
// backtick around the name is optional; the stop-word skip keeps
|
|
2066
|
+
// "the field", "a field" and "its field" from tripping the check.
|
|
2067
|
+
for (const m of text.matchAll(/`?\b([A-Za-z_][A-Za-z0-9_]*)`?\s+field\b/g)) {
|
|
2051
2068
|
const token = m[1];
|
|
2052
2069
|
if (!token) continue;
|
|
2053
2070
|
if (STOP.has(token.toLowerCase())) continue;
|
|
2054
2071
|
tokens.add(token);
|
|
2055
2072
|
}
|
|
2056
|
-
// Pattern 3: `field: <name>` or `field = <name>`
|
|
2057
|
-
// notation inside a then clause).
|
|
2058
|
-
|
|
2073
|
+
// Pattern 3 (preserved): `field: <name>` or `field = <name>`
|
|
2074
|
+
// (schema-like notation inside a then clause). The name's backtick
|
|
2075
|
+
// is optional.
|
|
2076
|
+
for (const m of text.matchAll(/\bfields?\s*[:=]\s*`?([A-Za-z_][A-Za-z0-9_]*)`?/g)) {
|
|
2077
|
+
if (m[1]) tokens.add(m[1]);
|
|
2078
|
+
}
|
|
2079
|
+
// Pattern 4 (new, issue 311): `<ShapeName>.<fieldName>` dot
|
|
2080
|
+
// notation. Shape names are PascalCase; the field name after the
|
|
2081
|
+
// dot is the candidate.
|
|
2082
|
+
for (const m of text.matchAll(/\b[A-Z][A-Za-z0-9_]*\.([a-z_][A-Za-z0-9_]*)\b/g)) {
|
|
2059
2083
|
if (m[1]) tokens.add(m[1]);
|
|
2060
2084
|
}
|
|
2085
|
+
// Pattern 1 (narrowed, issue 311): backticked identifier WITH
|
|
2086
|
+
// explicit field evidence in its immediate neighbourhood AND no
|
|
2087
|
+
// overlapping non-field context. ALL_CAPS tokens (enum values, env
|
|
2088
|
+
// vars, constants) are skipped unconditionally; record-shape field
|
|
2089
|
+
// names in the WSD AI tree and the rcf-schemas canon are camelCase
|
|
2090
|
+
// or lower snake_case.
|
|
2091
|
+
const WINDOW = 36;
|
|
2092
|
+
const POS_BEFORE = /(?:\bsets?|\bsetting|\bwrites?|\bwriting|\bincludes?|\bincluding|\breturns?|\breturning|\bcontains?|\bcontaining|\bnames?|\bnaming|\bemits?|\bemitting|\bholds?|\bholding|\bcarries|\bcarrying|\badds?|\badding|\bhas|\bhave|\bhad|\bwith\s+(?:a|an|the))\s*$/i;
|
|
2093
|
+
const POS_AFTER = /^\s*(?:\bis\s+(?:set|written|included|returned|named|populated|present|absent|missing|required|optional|null|non-null|empty)|\bare\s+(?:set|written|included|returned|named|populated|present|absent|missing|required|optional)|\bequals?|\bholds?|\bcontains?|\bof\s+type|\bfields?\b)/i;
|
|
2094
|
+
const POS_CTX = /\bfields?\b|\brecord\s+shape\b|\brecord\s+body\b|\bresponse\s+body\b|\brequest\s+body\b|\bevent\s+body\b|\bpayload\b|\bevent\s+payload\b|\benvelope\b/i;
|
|
2095
|
+
const NEG_CTX = /\bquery\s+param(?:eter)?s?\b|\bquery[- ]?string\b|\bsort\s+(?:order|direction|by|key)\b|\bfilter\s+mode\b|\bmatch\s+mode\b|\benum\s+(?:value|member|variant|literal)s?\b|\benv(?:ironment)?\s*var(?:iable)?s?\b|\benvironment\s+variable\b|\bconfig(?:uration)?\s+(?:knob|key|option|value|setting|field|file)\b|\bheader\s+(?:name|value|key)\b|\b(?:cli|flag|option|argument|parameter)\s+name\b|\bconstant\b|\badmin\s+user\b|\bbuild\s+(?:info|metadata)\b|\broute\b|\bendpoint\b|\boperator\b|\bdirection\b/i;
|
|
2096
|
+
|
|
2097
|
+
for (const m of text.matchAll(/`([A-Za-z_][A-Za-z0-9_]*)`/g)) {
|
|
2098
|
+
const token = m[1];
|
|
2099
|
+
if (!token) continue;
|
|
2100
|
+
// Skip ALL_CAPS enum values, env vars, constants.
|
|
2101
|
+
if (/^[A-Z][A-Z0-9_]*$/.test(token) && token.length >= 2) continue;
|
|
2102
|
+
const start = m.index ?? 0;
|
|
2103
|
+
const end = start + m[0].length;
|
|
2104
|
+
const before = text.slice(Math.max(0, start - WINDOW), start);
|
|
2105
|
+
const after = text.slice(end, Math.min(text.length, end + WINDOW));
|
|
2106
|
+
if (NEG_CTX.test(before) || NEG_CTX.test(after)) continue;
|
|
2107
|
+
if (
|
|
2108
|
+
POS_BEFORE.test(before)
|
|
2109
|
+
|| POS_AFTER.test(after)
|
|
2110
|
+
|| POS_CTX.test(before)
|
|
2111
|
+
|| POS_CTX.test(after)
|
|
2112
|
+
) {
|
|
2113
|
+
tokens.add(token);
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2061
2116
|
return [...tokens];
|
|
2062
2117
|
}
|
|
2063
2118
|
|
package/src/view/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { resolveTestPointers, walkTree } from '#core/store';
|
|
|
12
12
|
import { computeReadiness } from '../query/readiness.js';
|
|
13
13
|
import { computeQuestions } from '../query/questions.js';
|
|
14
14
|
import { loadFreezeRecord } from '../define/freeze-record.js';
|
|
15
|
+
import { resolveInterfacePaths } from '../define/interface-paths.js';
|
|
15
16
|
import { loadAllLedgers } from '../define/ledgers.js';
|
|
16
17
|
import { renderContent, renderPage } from './html-page.js';
|
|
17
18
|
import { renderProductMapGrouping } from './product-map.js';
|
|
@@ -77,11 +78,18 @@ export async function renderModelToPage({ projectRoot }) {
|
|
|
77
78
|
// Load the readiness inputs once per rewalk; the viewer is pure
|
|
78
79
|
// (never recomputes readiness). Failures load as `null` so the tab
|
|
79
80
|
// still renders when a tree is partial.
|
|
80
|
-
const [freezeRecord, ledgers, testPointers, profileText] = await Promise.all([
|
|
81
|
+
const [freezeRecord, ledgers, testPointers, profileText, resolvedPaths] = await Promise.all([
|
|
81
82
|
loadFreezeRecord({ projectRoot }).catch(() => null),
|
|
82
83
|
loadAllLedgers({ projectRoot }).catch(() => ({})),
|
|
83
84
|
resolveTestPointers({ projectRoot, tree }).catch(() => undefined),
|
|
84
85
|
readProfileText(projectRoot),
|
|
86
|
+
// Issue 307 (2026-10-06): run the same TAC-interface path
|
|
87
|
+
// resolver the CLI and freeze run so the viewer's D3
|
|
88
|
+
// `shapes:pathsResolve` check reads the resolved set from the
|
|
89
|
+
// same seam. Before this, the viewer called computeReadiness
|
|
90
|
+
// without `resolvedPaths`, D3 false-failed on paths that resolve
|
|
91
|
+
// on disk, and the Readiness tab disagreed with the CLI.
|
|
92
|
+
resolveInterfacePaths(projectRoot, tree).catch(() => undefined),
|
|
85
93
|
]);
|
|
86
94
|
|
|
87
95
|
let readiness = null;
|
|
@@ -93,6 +101,7 @@ export async function renderModelToPage({ projectRoot }) {
|
|
|
93
101
|
profileText,
|
|
94
102
|
testPointers,
|
|
95
103
|
validateErrors: errors,
|
|
104
|
+
resolvedPaths,
|
|
96
105
|
});
|
|
97
106
|
} catch (err) {
|
|
98
107
|
// A malformed tree should not block the viewer; the Readiness tab
|
|
@@ -149,7 +158,12 @@ export async function renderModelToPage({ projectRoot }) {
|
|
|
149
158
|
// list is extended before the version pin bump). One row per doc id
|
|
150
159
|
// plus one row per AC under each US.
|
|
151
160
|
const indexJson = serialiseViewIndex(model);
|
|
152
|
-
|
|
161
|
+
// Issue 307 (2026-10-06): expose the computed readiness alongside
|
|
162
|
+
// the rendered HTML so tests can assert the viewer's D3
|
|
163
|
+
// shapes:pathsResolve agrees with the CLI without parsing HTML. The
|
|
164
|
+
// viewer HTML is still the operator-facing surface; this field is
|
|
165
|
+
// additive and consumed by test code only.
|
|
166
|
+
return { fullPageHtml, contentHtml, errors, tree, pmPartials, indexJson, readiness };
|
|
153
167
|
}
|
|
154
168
|
|
|
155
169
|
/**
|