@stravica-ai/rcf-schemas 0.4.1 → 0.4.3

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 CHANGED
@@ -4,6 +4,55 @@ All notable changes to `@stravica-ai/rcf-schemas` are documented in this file.
4
4
 
5
5
  The format roughly follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Pre-1.0 breaking changes are signalled by a minor bump per semver 0.x convention.
6
6
 
7
+ ## 0.4.3 - 2026-08-12
8
+
9
+ Additive patch bump: car 1 of the `rcf-lite` 0.8.0 slug-train. Widens four id patterns to accept an optional kebab-case slug tail (per the slug design ratified on `w-2026-07-28-012`), widens the TS and TC id patterns to drop the 999 cap, adds a shared scope-tag vocabulary in `common.schema.json`, and exposes an optional `scope` field on ACs and TCs. Every change is additive; every existing id and every existing document continues to validate.
10
+
11
+ ### Added
12
+
13
+ - **`common.schema.json`**: new `$defs.scopeTag` enum (`library | runtime | deployed | unclassified`), shared between `user-story.acceptanceCriteria[].scope` and `test-suite.testCases[].scope`. `unclassified` is the migration state; the schema does not distinguish gate-passing values, ruleset-enforcing consumers do.
14
+ - **`user-story.schema.json`**: new optional `scope` on `$defs.acceptanceCriterion`, referencing `common.scopeTag`. Names the scope at which the AC is observable and governs which test scopes count as coverage.
15
+ - **`test-suite.schema.json`**: new optional `scope` on `$defs.testCase`, referencing `common.scopeTag`. Ruleset-enforcing consumers may require the TC scope to be equal to or wider than the covered AC's scope.
16
+ - Fixtures: `fixtures/valid/fbs/fbs-006-slugged-id.json`, `fixtures/valid/cn/cn-003-slugged-id.json`, `fixtures/valid/adr/adr-004-slugged-id.json`, `fixtures/valid/tac/tac-003-slugged-id.json`, `fixtures/valid/test-suite/ts-004-widened-tsId.json` (four-digit TS with two scope-tagged TCs), `fixtures/valid/user-story/us-004-ac-scope-tags.json` (three ACs, one per scope value). Invalid coverage: `fixtures/invalid/fbs/fbs-012-uppercase-slug.json`, `fixtures/invalid/test-suite/ts-007-bad-tc-scope.json`, `fixtures/invalid/user-story/us-004-bad-ac-scope.json`.
17
+ - Tests: slug-id cases added to `fbs.test.js`, `cn.test.js`, `adr.test.js`, `tac.test.js`; widened TS/TC id cases and TC scope cases added to `test-suite.test.js`; AC scope cases added to `user-story.test.js`; composite FBS-embedded id cases added to `manifest.test.js` (`reviewAudit.id`, `browserVerification.id`, `shipWithoutVerified.id`).
18
+
19
+ ### Changed
20
+
21
+ - **`common.schema.json`**: `fbsId`, `cnId`, `adrId`, `tacId` patterns widened to accept an optional kebab-case slug tail, `^<PREFIX>-\d{3,}(-[a-z0-9]+(?:-[a-z0-9]+)*)?$`. Numeric-only ids continue to validate unchanged. Slug segments must be lowercase alphanumeric joined by single hyphens; leading, trailing, and double hyphens are rejected. Kebab is enforced (well-formed) rather than the looser `[a-z0-9-]+` shape so downstream tools can round-trip slugs without normalisation.
22
+ - **`common.schema.json`**: `tsId` widened from `^TS-\d{3}$` to `^TS-\d{3,}$`; `tcId` widened from `^TC-\d{3}-[a-z0-9-]+$` to `^TC-\d{3,}-[a-z0-9-]+$`. Removes the 999 cap on both, and closes the silent-skip trap in downstream walkers that were coded against the three-digit-exact pattern. TC's slug portion keeps the looser `[a-z0-9-]+` shape (legacy compatibility).
23
+ - **`test-suite.schema.json`**: inline `testCase.id` pattern widened in lockstep with `common.tcId` (`^TC-\d{3,}-[a-z0-9-]+$`).
24
+ - **`manifest.schema.json`**: the three FBS-embedded composite id patterns widened to accept both numeric-only and slugged FBS ids: `reviewAudit.id` (`^ra-FBS-\d{3,}(?:-[a-z0-9]+(?:-[a-z0-9]+)*)?-\d+$`), `browserVerification.id` (`^bv-FBS-\d{3,}(?:-[a-z0-9]+(?:-[a-z0-9]+)*)?-\d+$`), and `shipWithoutVerified.id` (`^swv-FBS-\d{3,}(?:-[a-z0-9]+(?:-[a-z0-9]+)*)?-\d+$`). The trailing `-<n>` counter still parses cleanly against slugged FBS ids because the counter is required and anchored.
25
+ - **`docs/id-conventions.md`**, **`docs/common.md`**, **`docs/test-suite.md`**, **`docs/user-story.md`**: updated to reflect the widened patterns, the optional slug suffix, and the `scopeTag` vocabulary.
26
+
27
+ ### Notes
28
+
29
+ - Additive-only: every 0.4.0-, 0.4.1-, and 0.4.2-valid document remains valid. Every numeric-only id continues to validate; every existing AC and TC without a `scope` field continues to validate.
30
+ - Canonical `$id` URLs stay at `v0.4.0`, following the patch-release precedent set at 0.2.1, 0.3.1, 0.4.1, and 0.4.2. Per the ratified umbrella exact-pin doctrine, the 0.8.0 slug-train's second and third cars (`rcf-lite-core`, `rcf-build-lite`) pin `@stravica-ai/rcf-schemas` to exactly `0.4.3` (no caret, no range) on merge; the pin review is recorded in the umbrella's release notes.
31
+ - Slug scope (FBS, CN, ADR, TAC) is the ratified set: BS is out of scope by design (Baz ruling 2026-07-28, `w-2026-07-28-012`), and PRD, REQ, US, TAD are per-project singletons where number collisions are not the problem the slug solves.
32
+ - Existing documents are NOT retro-slugged. The design is preferred-with-numeric-fallback; adopting slugs is opt-in per document at authoring time.
33
+ - Downstream landmines called out on `w-2026-07-28-012` (walker.js toUpperCase stem, writer.js nextFlatId parser, hardcoded `\d{3}` in walker.js:696-699, deriveSlug 'tc' leak) live in `rcf-lite` consumers, not in this repo. They are addressed by that repo's train cars, not here.
34
+
35
+ ## 0.4.2 - 2026-07-31
36
+
37
+ Additive patch bump: closes the Track B N-5 review finding on the 0.7.0 train. The `testTheatreFinding` shape now expresses "not every finding kind anchors on a test suite" honestly instead of forcing a UI-drift finding to smuggle an FBS id through the `tsId` slot.
38
+
39
+ ### Added
40
+
41
+ - **`manifest.schema.json`**: new optional `anchorId` (string, `minLength: 1`) on `$defs.testTheatreFinding`, for kinds whose anchor is not a test suite (for example, `uiBaselineDrift`, which anchors on the FBS id or a file path).
42
+ - Fixture: `fixtures/valid/manifest/manifest-007-ui-baseline-drift-anchor.json` proves the new shape (a `uiBaselineDrift` finding without `tsId`, carrying `anchorId`) validates alongside a legacy test-theatre finding on the same record.
43
+ - Tests: `manifest.test.js` covers the new `anchorId` field, the per-kind `tsId` requiredness rule (drift may omit; test-theatre kinds still must supply), the back-compat guarantee (a pre-0.4.2 emitter's `uiBaselineDrift` with `tsId` still validates), the `anchorId` `minLength: 1` guard, and the coexistence of `anchorId` + `tsId` on one finding.
44
+
45
+ ### Changed
46
+
47
+ - **`$defs.testTheatreFinding`**: top-level `required` list drops `tsId` (now `["kind", "detail", "severity"]`); a new `allOf` clause requires `tsId` for every kind other than `uiBaselineDrift`. Test-theatre kinds (`mockOnlyIntegrationClaim`, `testPointerBroken`, `assertionStrengthWeak`, `acIdsCoverageDrift`, `otherDeclared`) still cannot omit `tsId`.
48
+ - `manifest.schema.json` description names the 0.4.2 addition and restates the back-compat guarantee.
49
+
50
+ ### Notes
51
+
52
+ - Additive-only: every 0.4.0- or 0.4.1-valid manifest remains valid. The change relaxes a required field on one specific kind and adds an optional field; nothing that previously validated stops validating.
53
+ - Canonical `$id` URLs stay at `v0.4.0`, following the patch-release precedent set at 0.2.1, 0.3.1, and 0.4.1.
54
+ - Consumed by `rcf-build-lite`'s Track B fix pass (PR `Stravica/rcf-lite#75`), which will bump its `@stravica-ai/rcf-schemas` dep to `^0.4.2` and emit `uiBaselineDrift` findings with `anchorId` instead of the legacy `tsId: fbs.fbsId` smuggle.
55
+
7
56
  ## 0.4.1 - 2026-07-31
8
57
 
9
58
  Additive patch bump: closes the verification-integrity B-1 finding on the 0.7.0 train by giving `rcf finalise --ship-without-verified` a durable, greppable manifest record.
package/docs/common.md CHANGED
@@ -15,19 +15,21 @@ Shared `$defs` referenced via `$ref` from every other RCF schema. ID patterns, s
15
15
  | `usId` | string, `^US-\d{3,}$` | `user-story`, `test-suite` |
16
16
  | `acId` | string, `^AC-\d{3,}(-\d+)?$` | `user-story.acceptanceCriteria[].id`, `fbs.acIds[]`, `test-suite.acId` |
17
17
  | `tadId` | string, `^TAD-\d{3,}$` | `tad`, `tac`, `adr` |
18
- | `tacId` | string, `^TAC-\d{3,}$` | `tac`, `fbs.contextRequirements.tacIds[]` |
19
- | `adrId` | string, `^ADR-\d{3,}$` | `adr`, `fbs.contextRequirements.adrIds[]` |
18
+ | `tacId` | string, `^TAC-\d{3,}(-[a-z0-9]+(?:-[a-z0-9]+)*)?$` | `tac`, `fbs.contextRequirements.tacIds[]` |
19
+ | `adrId` | string, `^ADR-\d{3,}(-[a-z0-9]+(?:-[a-z0-9]+)*)?$` | `adr`, `fbs.contextRequirements.adrIds[]` |
20
20
  | `bsId` | string, `^BS-\d{3,}$` | `build-sequence`, `fbs.bsId` |
21
- | `fbsId` | string, `^FBS-\d{3,}$` | `fbs.fbsId`, `fbs.dependsOnFbsIds[]` |
22
- | `tsId` | string, `^TS-\d{3}$` | `test-suite.id` |
23
- | `tcId` | string, `^TC-\d{3}-[a-z0-9-]+$` | `test-suite.testCases[].id` |
24
- | `cnId` | string, `^CN-\d{3,}$` | `cn.cnId`, `cn.dependencies[]` |
21
+ | `fbsId` | string, `^FBS-\d{3,}(-[a-z0-9]+(?:-[a-z0-9]+)*)?$` | `fbs.fbsId`, `fbs.dependsOnFbsIds[]` |
22
+ | `tsId` | string, `^TS-\d{3,}$` | `test-suite.id` |
23
+ | `tcId` | string, `^TC-\d{3,}-[a-z0-9-]+$` | `test-suite.testCases[].id` |
24
+ | `cnId` | string, `^CN-\d{3,}(-[a-z0-9]+(?:-[a-z0-9]+)*)?$` | `cn.cnId`, `cn.dependencies[]` |
25
25
  | `version` | string, `^\d+\.\d+\.\d+$` | every doc-type schema |
26
26
  | `timestamp` | string, format `date-time` | every doc-type schema |
27
27
  | `authoringStatus` | enum: `draft`, `review`, `needsRevision`, `approved`, `superseded` | PRD, REQ, US, TAD, TAC, BS |
28
28
  | `adrStatus` | enum: `draft`, `proposed`, `accepted`, `superseded`, `deprecated` | ADR |
29
29
  | `executionStatus` | enum: `notStarted`, `inProgress`, `complete`, `verified` | FBS doc + BS slot |
30
30
  | `cnStatus` | enum: `draft`, `approved`, `deprecated` | CN |
31
+ | `attestationMode` | enum: `live`, `sandboxed`, `mocked`, `declaredMockOnly`, `notShipped` | `fbs.dependsOnServices[]`, manifest `preFlightConfig.servicesInScope[]` |
32
+ | `scopeTag` | enum: `library`, `runtime`, `deployed`, `unclassified` | `user-story.acceptanceCriteria[].scope`, `test-suite.testCases[].scope` |
31
33
  | `docRef` | object `{ id, path }` | manifest `prd`, `tad`, `bs` |
32
34
 
33
35
  ## Why these are shared
@@ -1,6 +1,6 @@
1
1
  # ID conventions
2
2
 
3
- The pattern, the prefix table, the AC sub-id form, and the file/nested asymmetry.
3
+ The pattern, the prefix table, the AC sub-id form, the optional slug suffix, and the file/nested asymmetry.
4
4
 
5
5
  ## The pattern
6
6
 
@@ -10,7 +10,21 @@ The pattern, the prefix table, the AC sub-id form, and the file/nested asymmetry
10
10
 
11
11
  Three-digit minimum, unbounded above. `PRD-001` through `PRD-999` use the clean visual; `PRD-1000+` works without a schema migration. A single digit (`PRD-1`) is rejected.
12
12
 
13
- Two families deviate from the family default and are documented alongside their prefix below: `TS-\d{3}` (exactly three digits) and `TC-\d{3}-<slug>` (three-digit TS suffix plus a lowercase alphanumeric slug).
13
+ One family layers the same three-digit-minimum tail onto a slug: `TC-\d{3,}-<slug>` (TS-suffix, three-digit minimum, unbounded above, joined to a lowercase alphanumeric slug).
14
+
15
+ ## Optional slug suffix (0.4.3)
16
+
17
+ Four families accept an optional kebab-case slug tail: `FBS`, `CN`, `ADR`, `TAC`. The full pattern is:
18
+
19
+ ```
20
+ ^<PREFIX>-\d{3,}(-[a-z0-9]+(?:-[a-z0-9]+)*)?$
21
+ ```
22
+
23
+ The slug is derived from the document title (kebab-case, lowercase alphanumeric segments joined by single hyphens). Leading hyphens, trailing hyphens, double hyphens, uppercase letters, and underscores are rejected.
24
+
25
+ Both forms remain valid: `FBS-004` and `FBS-004-user-login` both validate. Existing numeric-only ids continue to validate; there is no forcing function. Consumers that address documents by id treat the slug as part of the id string, not a separately parseable field.
26
+
27
+ Rationale: parallel authoring on branches can allocate the same number for different titles. The slug makes those distinct addresses at the filesystem and id layer, converting an add/add merge conflict into a clean co-existence. See the `rcf-lite` slug-train design for the full argument.
14
28
 
15
29
  ## Prefix table
16
30
 
@@ -42,11 +56,11 @@ Pick per project; the schema does not enforce either form. The published `Stravi
42
56
 
43
57
  ## TS ids
44
58
 
45
- `^TS-\d{3}$`. Sequential, not parent-grouped: a US may have multiple TSs across levels (unit, integration, e2e, ...) and a parent-grouped id would collide. Recommended path: `rcf/test-suites/ts-001.json`.
59
+ `^TS-\d{3,}$`. Sequential, not parent-grouped: a US may have multiple TSs across levels (unit, integration, e2e, ...) and a parent-grouped id would collide. Recommended path: `rcf/test-suites/ts-001.json`.
46
60
 
47
61
  ## TC ids
48
62
 
49
- `^TC-\d{3}-[a-z0-9-]+$`. TCs are nested in their parent TS's `testCases[]`. The pattern is `TC-<TS-suffix>-<slug>` where slug is a lowercase alphanumeric identifier chosen by the author (hyphens allowed). Examples: `TC-001-happy-path`, `TC-001-rejects-empty`, `TC-042-integration-boundary`. Slug uniqueness is within-TS, not global.
63
+ `^TC-\d{3,}-[a-z0-9-]+$`. TCs are nested in their parent TS's `testCases[]`. The pattern is `TC-<TS-suffix>-<slug>` where slug is a lowercase alphanumeric identifier chosen by the author (hyphens allowed). Examples: `TC-001-happy-path`, `TC-001-rejects-empty`, `TC-042-integration-boundary`. Slug uniqueness is within-TS, not global.
50
64
 
51
65
  Rationale: sequential numbering for TCs is brittle if you renumber; slug-based ids give readable pointers in test-suite bodies and traceability queries.
52
66
 
@@ -20,7 +20,7 @@ Recommended path under the RCF tree: `rcf/test-suites/ts-001.json`. The schema v
20
20
 
21
21
  | Field | Type | Notes |
22
22
  |---|---|---|
23
- | `id` | `^TS-\d{3}$` | Test Suite identifier. Sequential, not parent-grouped (a US can have multiple TSs across levels). |
23
+ | `id` | `^TS-\d{3,}$` | Test Suite identifier. Sequential, not parent-grouped (a US can have multiple TSs across levels). Three-digit minimum, unbounded above (widened in 0.4.3). |
24
24
  | `usId` | `^US-\d{3,}$` | Parent User Story. Mandatory back-reference. |
25
25
  | `title` | string, min 1 | Human-readable suite title. |
26
26
  | `purpose` | string, min 1 | One-paragraph "what this suite is testing and why". |
@@ -36,11 +36,12 @@ Each entry in `testCases[]`:
36
36
 
37
37
  | Field | Type | Required | Purpose |
38
38
  |---|---|---|---|
39
- | `id` | `^TC-\d{3}-[a-z0-9-]+$` | yes | TC identifier. `TC-<TS-suffix>-<slug>`; slug is a lowercase alphanumeric identifier (hyphens allowed). Slug uniqueness is within-TS, not global. |
39
+ | `id` | `^TC-\d{3,}-[a-z0-9-]+$` | yes | TC identifier. `TC-<TS-suffix>-<slug>`; slug is a lowercase alphanumeric identifier (hyphens allowed). Slug uniqueness is within-TS, not global. TS-suffix widened to three-digit minimum, unbounded above (0.4.3). |
40
40
  | `acId` | `^AC-\d{3,}(-\d+)?$` | yes | Which AC on the parent US this TC verifies. |
41
41
  | `description` | string, min 1 | yes | What the TC covers. |
42
42
  | `testPointer` | string | no | Pointer to the executable test, `filePath::testName` format. |
43
43
  | `status` | enum | yes | `pending`, `passing`, `failing`, `skipped`. Authored in 0.2.0; derived from real test runs in Phase 6+. |
44
+ | `scope` | `scopeTag` enum | no | `library`, `runtime`, `deployed`, `unclassified`. Optional at schema level (0.4.3). Ruleset-enforcing consumers may require the TC scope to be equal to or wider than the covered AC's scope. |
44
45
 
45
46
  ## What changed in 0.2.0
46
47
 
@@ -40,6 +40,7 @@ Each AC inside `acceptanceCriteria[]`:
40
40
  | `when` | string | no | Trigger. |
41
41
  | `then` | string | no | Observable outcome. |
42
42
  | `testable` | boolean | yes | Author declares the AC is testable. |
43
+ | `scope` | `scopeTag` enum | no | `library`, `runtime`, `deployed`, `unclassified`. Optional at schema level (0.4.3). Names the scope at which the AC is observable; governs which test scopes count as coverage. |
43
44
 
44
45
  The Given/When/Then triple is optional at the schema level so non-Gherkin teams aren't forced into it; the `testable` boolean is required to make authors stop and confirm the AC can actually be tested.
45
46
 
@@ -0,0 +1,13 @@
1
+ {
2
+ "fbsId": "FBS-004-UserLogin",
3
+ "prdId": "PRD-001",
4
+ "bsId": "BS-001",
5
+ "buildOrder": 4,
6
+ "executionStatus": "notStarted",
7
+ "title": "x",
8
+ "summary": "y",
9
+ "acIds": ["AC-001"],
10
+ "dependsOnFbsIds": [],
11
+ "createdAt": "2026-08-12T09:00:00Z",
12
+ "updatedAt": "2026-08-12T09:00:00Z"
13
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "id": "TS-001",
3
+ "usId": "US-001",
4
+ "title": "x",
5
+ "purpose": "y",
6
+ "testLevel": "unit",
7
+ "acIds": ["AC-001"],
8
+ "testCases": [
9
+ {
10
+ "id": "TC-001-a",
11
+ "acId": "AC-001",
12
+ "description": "TC scope is not one of the ratified vocabulary values.",
13
+ "status": "pending",
14
+ "scope": "production"
15
+ }
16
+ ],
17
+ "status": "draft",
18
+ "createdAt": "2026-01-01T00:00:00Z",
19
+ "updatedAt": "2026-01-01T00:00:00Z"
20
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "usId": "US-001",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-001",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "x",
8
+ "asA": "y",
9
+ "iWant": "z",
10
+ "soThat": "w",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-001",
14
+ "description": "AC scope is not one of the ratified vocabulary values.",
15
+ "testable": true,
16
+ "scope": "production"
17
+ }
18
+ ],
19
+ "createdAt": "2026-01-01T00:00:00Z",
20
+ "updatedAt": "2026-01-01T00:00:00Z"
21
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "adrId": "ADR-012-choose-postgres",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "0.1.0",
6
+ "status": "accepted",
7
+ "title": "Choose PostgreSQL for the primary datastore",
8
+ "context": "The service needs a relational store with strong consistency and mature JSON support.",
9
+ "decision": "Adopt PostgreSQL 16 as the primary datastore.",
10
+ "consequences": "Introduces PostgreSQL as a runtime dependency; simplifies query semantics.",
11
+ "relatedAdrs": ["ADR-005-alignment-policy"],
12
+ "createdAt": "2026-08-12T09:00:00Z",
13
+ "updatedAt": "2026-08-12T09:00:00Z"
14
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "cnId": "CN-042-store-validator",
3
+ "path": "src/store/validator.js#getAjv",
4
+ "implementsAcIds": ["AC-102-1"],
5
+ "dependencies": ["CN-001", "CN-002-user-notifier"],
6
+ "version": "0.1.0",
7
+ "status": "draft",
8
+ "createdAt": "2026-08-12T09:00:00Z",
9
+ "updatedAt": "2026-08-12T09:00:00Z"
10
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "fbsId": "FBS-004-user-login",
3
+ "prdId": "PRD-001",
4
+ "bsId": "BS-001",
5
+ "buildOrder": 4,
6
+ "executionStatus": "notStarted",
7
+ "title": "User login",
8
+ "summary": "HTML login page and session cookie on submit.",
9
+ "acIds": ["AC-004-1"],
10
+ "dependsOnFbsIds": ["FBS-001", "FBS-002-persist-notes"],
11
+ "createdAt": "2026-08-12T09:00:00Z",
12
+ "updatedAt": "2026-08-12T09:00:00Z"
13
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "projectName": "uiBaselineDrift anchor coverage",
4
+ "prd": { "id": "PRD-001", "path": "rcf/prd.json" },
5
+ "tad": { "id": "TAD-001", "path": "rcf/tad.json" },
6
+ "bs": { "id": "BS-001", "path": "rcf/build-sequence.json" },
7
+ "reviewAudit": [
8
+ {
9
+ "id": "ra-FBS-016-1",
10
+ "fbsId": "FBS-016",
11
+ "createdAt": "2026-07-31T15:10:00Z",
12
+ "testTheatreFindings": [
13
+ {
14
+ "anchorId": "FBS-016",
15
+ "kind": "uiBaselineDrift",
16
+ "detail": "hex literal #ff0000 detected in src/ui/dashboard.ts (baseline defaults.noHexInViewFiles: true).",
17
+ "severity": "block"
18
+ },
19
+ {
20
+ "tsId": "TS-016",
21
+ "kind": "mockOnlyIntegrationClaim",
22
+ "detail": "testLevel=integration but runtimeProvenance.profile=mock",
23
+ "severity": "block"
24
+ }
25
+ ],
26
+ "verdict": "block"
27
+ }
28
+ ]
29
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "tacId": "TAC-004-payment-gateway",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "name": "Payment gateway",
8
+ "purpose": "Server-side integration with the third-party payment provider.",
9
+ "responsibilities": [
10
+ "Issue authorization requests",
11
+ "Reconcile capture events"
12
+ ],
13
+ "dependencies": [
14
+ { "name": "Notes store", "kind": "tac", "tacId": "TAC-002-notes-store" }
15
+ ],
16
+ "createdAt": "2026-08-12T09:00:00Z",
17
+ "updatedAt": "2026-08-12T09:00:00Z"
18
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "id": "TS-1000",
3
+ "usId": "US-1001",
4
+ "title": "Boot integration smoke",
5
+ "purpose": "Cover the boot-integration observable for AC-1001-1 on a real host.",
6
+ "testLevel": "integration",
7
+ "acIds": ["AC-1001-1"],
8
+ "testCases": [
9
+ {
10
+ "id": "TC-1000-startup-loads-config",
11
+ "acId": "AC-1001-1",
12
+ "description": "On boot, the config loader wires and dispatches the first tick.",
13
+ "status": "passing",
14
+ "scope": "runtime"
15
+ },
16
+ {
17
+ "id": "TC-1000-deployed-smoke",
18
+ "acId": "AC-1001-1",
19
+ "description": "On the deployed instance, the first probe fires against a real target.",
20
+ "status": "passing",
21
+ "scope": "deployed"
22
+ }
23
+ ],
24
+ "status": "approved",
25
+ "createdAt": "2026-08-12T09:00:00Z",
26
+ "updatedAt": "2026-08-12T09:00:00Z"
27
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "usId": "US-1001",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-010",
5
+ "version": "0.1.0",
6
+ "status": "approved",
7
+ "title": "Signed-in owner sees the dashboard on boot",
8
+ "asA": "signed-in owner",
9
+ "iWant": "to land on the dashboard after logging in",
10
+ "soThat": "I can see every monitor's status at a glance",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-1001-1",
14
+ "description": "on startup, the loader wires the config file and the first probe fires",
15
+ "testable": true,
16
+ "scope": "runtime"
17
+ },
18
+ {
19
+ "id": "AC-1001-2",
20
+ "description": "on the deployed instance, the first probe hits the real target within 60s of boot",
21
+ "testable": true,
22
+ "scope": "deployed"
23
+ },
24
+ {
25
+ "id": "AC-1001-3",
26
+ "description": "parseConfig rejects an empty configuration document",
27
+ "testable": true,
28
+ "scope": "library"
29
+ }
30
+ ],
31
+ "createdAt": "2026-08-12T09:00:00Z",
32
+ "updatedAt": "2026-08-12T09:00:00Z"
33
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stravica-ai/rcf-schemas",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "type": "module",
5
5
  "description": "JSON Schemas for the Requirements Confidence Framework (RCF). Language-neutral. The contract every RCF tool keys to.",
6
6
  "license": "Apache-2.0",
@@ -31,13 +31,13 @@
31
31
  },
32
32
  "tacId": {
33
33
  "type": "string",
34
- "pattern": "^TAC-\\d{3,}$",
35
- "description": "Technical Architecture Component identifier."
34
+ "pattern": "^TAC-\\d{3,}(-[a-z0-9]+(?:-[a-z0-9]+)*)?$",
35
+ "description": "Technical Architecture Component identifier. Three-digit minimum, unbounded above. An optional kebab-case slug suffix (for example, TAC-004-payment-gateway) is permitted; existing numeric-only ids remain valid."
36
36
  },
37
37
  "adrId": {
38
38
  "type": "string",
39
- "pattern": "^ADR-\\d{3,}$",
40
- "description": "Architectural Decision Record identifier."
39
+ "pattern": "^ADR-\\d{3,}(-[a-z0-9]+(?:-[a-z0-9]+)*)?$",
40
+ "description": "Architectural Decision Record identifier. Three-digit minimum, unbounded above. An optional kebab-case slug suffix (for example, ADR-012-choose-postgres) is permitted; existing numeric-only ids remain valid."
41
41
  },
42
42
  "bsId": {
43
43
  "type": "string",
@@ -46,23 +46,23 @@
46
46
  },
47
47
  "fbsId": {
48
48
  "type": "string",
49
- "pattern": "^FBS-\\d{3,}$",
50
- "description": "Functional Build Specification identifier."
49
+ "pattern": "^FBS-\\d{3,}(-[a-z0-9]+(?:-[a-z0-9]+)*)?$",
50
+ "description": "Functional Build Specification identifier. Three-digit minimum, unbounded above. An optional kebab-case slug suffix (for example, FBS-004-user-login) is permitted; existing numeric-only ids remain valid."
51
51
  },
52
52
  "tsId": {
53
53
  "type": "string",
54
- "pattern": "^TS-\\d{3}$",
55
- "description": "Test Suite identifier."
54
+ "pattern": "^TS-\\d{3,}$",
55
+ "description": "Test Suite identifier. Three-digit minimum, unbounded above."
56
56
  },
57
57
  "tcId": {
58
58
  "type": "string",
59
- "pattern": "^TC-\\d{3}-[a-z0-9-]+$",
60
- "description": "Test Case identifier. TS suffix (three digits) joined to a lowercase alphanumeric slug."
59
+ "pattern": "^TC-\\d{3,}-[a-z0-9-]+$",
60
+ "description": "Test Case identifier. TS suffix (three-digit minimum, unbounded above) joined to a lowercase alphanumeric slug."
61
61
  },
62
62
  "cnId": {
63
63
  "type": "string",
64
- "pattern": "^CN-\\d{3,}$",
65
- "description": "Code Node identifier. Three-digit minimum, unbounded above."
64
+ "pattern": "^CN-\\d{3,}(-[a-z0-9]+(?:-[a-z0-9]+)*)?$",
65
+ "description": "Code Node identifier. Three-digit minimum, unbounded above. An optional kebab-case slug suffix (for example, CN-042-store-validator) is permitted; existing numeric-only ids remain valid."
66
66
  },
67
67
  "version": {
68
68
  "type": "string",
@@ -94,6 +94,10 @@
94
94
  "enum": ["live", "sandboxed", "mocked", "declaredMockOnly", "notShipped"],
95
95
  "description": "How a third-party service dependency is attested by tests. Shared between fbs.dependsOnServices[] and manifest.preFlightConfig.servicesInScope[]. live: tests hit the live provider with real credentials. sandboxed: tests hit a provider-run sandbox. mocked: local fixtures or stubs; not ship-authoritative on its own. declaredMockOnly: operator has explicitly declared the FBS ships mock-only for this service. notShipped: the service is used only in local development."
96
96
  },
97
+ "scopeTag": {
98
+ "enum": ["library", "runtime", "deployed", "unclassified"],
99
+ "description": "Scope tag classifying where a behaviour is observable. Shared between user-story.acceptanceCriteria[].scope and test-suite.testCases[].scope. library: observable in an isolated library or fixture. runtime: observable in the running product against local integration. deployed: observable on a booted artefact against real config and real network. unclassified: migration state for chains authored before the scope-tag vocabulary shipped; ruleset-enforcing consumers decide whether unclassified is currently accepted."
100
+ },
97
101
  "docRef": {
98
102
  "type": "object",
99
103
  "additionalProperties": false,
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://schemas.stravica.io/rcf/v0.4.0/manifest.schema.json",
4
4
  "title": "RCF Project Manifest",
5
- "description": "Project manifest. Roots-only shape: declares prd, tad, bs. Children are owned by their parents and discovered by walking from the roots. From 0.4.0 the manifest also carries optional records for pre-flight configuration, review audits, UI baselines, browser verification, baseline-AC opt-outs, intake classification, register-regression canaries, and the review-surface state. From 0.4.1 the manifest additionally carries optional shipWithoutVerified acknowledgements written by rcf finalise --ship-without-verified. Every added field is optional; pre-0.4.0 manifests remain valid.",
5
+ "description": "Project manifest. Roots-only shape: declares prd, tad, bs. Children are owned by their parents and discovered by walking from the roots. From 0.4.0 the manifest also carries optional records for pre-flight configuration, review audits, UI baselines, browser verification, baseline-AC opt-outs, intake classification, register-regression canaries, and the review-surface state. From 0.4.1 the manifest additionally carries optional shipWithoutVerified acknowledgements written by rcf finalise --ship-without-verified. From 0.4.2 the testTheatreFinding shape carries an optional anchorId and drops tsId from its required list when kind is uiBaselineDrift (the drift finding is not anchored on a test suite); every other kind still requires tsId, and any 0.4.0- or 0.4.1-valid record remains valid. Every added field is optional; pre-0.4.0 manifests remain valid.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": ["version", "projectName", "prd", "tad", "bs"],
@@ -154,8 +154,8 @@
154
154
  "properties": {
155
155
  "id": {
156
156
  "type": "string",
157
- "pattern": "^ra-FBS-\\d{3,}-\\d+$",
158
- "description": "Review-audit record id. Format: ra-<fbsId>-<n>; the trailing integer increments per re-audit of the same FBS."
157
+ "pattern": "^ra-FBS-\\d{3,}(?:-[a-z0-9]+(?:-[a-z0-9]+)*)?-\\d+$",
158
+ "description": "Review-audit record id. Format: ra-<fbsId>-<n>; the trailing integer increments per re-audit of the same FBS. Accepts both numeric-only and kebab-slug FBS ids."
159
159
  },
160
160
  "fbsId": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/fbsId" },
161
161
  "createdAt": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/timestamp" },
@@ -178,10 +178,15 @@
178
178
  "testTheatreFinding": {
179
179
  "type": "object",
180
180
  "additionalProperties": false,
181
- "required": ["tsId", "kind", "detail", "severity"],
181
+ "required": ["kind", "detail", "severity"],
182
182
  "properties": {
183
183
  "tsId": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/tsId" },
184
184
  "tcId": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/tcId" },
185
+ "anchorId": {
186
+ "type": "string",
187
+ "minLength": 1,
188
+ "description": "Free-form anchor for findings whose kind does not map to a test suite (for example, uiBaselineDrift, which anchors on the FBS id or a file path). Optional; test-theatre kinds keep tsId as the anchor."
189
+ },
185
190
  "kind": {
186
191
  "enum": [
187
192
  "mockOnlyIntegrationClaim",
@@ -207,7 +212,19 @@
207
212
  "enum": ["advisory", "warn", "block"],
208
213
  "description": "Severity of the finding. Only block severities force a block verdict on the audit."
209
214
  }
210
- }
215
+ },
216
+ "allOf": [
217
+ {
218
+ "if": { "properties": { "kind": { "const": "uiBaselineDrift" } }, "required": ["kind"] },
219
+ "then": {},
220
+ "else": {
221
+ "properties": {
222
+ "tsId": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/tsId" }
223
+ },
224
+ "required": ["tsId"]
225
+ }
226
+ }
227
+ ]
211
228
  },
212
229
  "mutationSamplingRecord": {
213
230
  "type": "object",
@@ -372,8 +389,8 @@
372
389
  "properties": {
373
390
  "id": {
374
391
  "type": "string",
375
- "pattern": "^bv-FBS-\\d{3,}-\\d+$",
376
- "description": "Browser-verification record id. Format: bv-<fbsId>-<n>."
392
+ "pattern": "^bv-FBS-\\d{3,}(?:-[a-z0-9]+(?:-[a-z0-9]+)*)?-\\d+$",
393
+ "description": "Browser-verification record id. Format: bv-<fbsId>-<n>. Accepts both numeric-only and kebab-slug FBS ids."
377
394
  },
378
395
  "fbsId": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/fbsId" },
379
396
  "createdAt": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/timestamp" },
@@ -713,8 +730,8 @@
713
730
  "properties": {
714
731
  "id": {
715
732
  "type": "string",
716
- "pattern": "^swv-FBS-\\d{3,}-\\d+$",
717
- "description": "Ship-without-verified acknowledgement id. Format: swv-<fbsId>-<n>; the trailing integer increments per acknowledgement of the same FBS."
733
+ "pattern": "^swv-FBS-\\d{3,}(?:-[a-z0-9]+(?:-[a-z0-9]+)*)?-\\d+$",
734
+ "description": "Ship-without-verified acknowledgement id. Format: swv-<fbsId>-<n>; the trailing integer increments per acknowledgement of the same FBS. Accepts both numeric-only and kebab-slug FBS ids."
718
735
  },
719
736
  "fbsId": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/fbsId" },
720
737
  "ackedAt": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/timestamp" },
@@ -45,8 +45,8 @@
45
45
  "properties": {
46
46
  "id": {
47
47
  "type": "string",
48
- "pattern": "^TC-\\d{3}-[a-z0-9-]+$",
49
- "description": "Test Case identifier. TS suffix (three digits) joined to a lowercase alphanumeric slug (hyphens allowed)."
48
+ "pattern": "^TC-\\d{3,}-[a-z0-9-]+$",
49
+ "description": "Test Case identifier. TS suffix (three-digit minimum, unbounded above) joined to a lowercase alphanumeric slug (hyphens allowed)."
50
50
  },
51
51
  "acId": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/acId" },
52
52
  "description": { "type": "string", "minLength": 1 },
@@ -55,6 +55,10 @@
55
55
  "description": "Optional pointer to the executable test, format: filePath::testName."
56
56
  },
57
57
  "status": { "enum": ["pending", "passing", "failing", "skipped"] },
58
+ "scope": {
59
+ "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/scopeTag",
60
+ "description": "Scope this test case exercises (library, runtime, deployed, unclassified). Optional at schema level; ruleset-enforcing consumers may require the TC scope to be equal to or wider than the covered AC's scope."
61
+ },
58
62
  "runtimeProvenance": { "$ref": "#/$defs/runtimeProvenance" }
59
63
  }
60
64
  },
@@ -55,6 +55,10 @@
55
55
  "when": { "type": "string" },
56
56
  "then": { "type": "string" },
57
57
  "testable": { "type": "boolean" },
58
+ "scope": {
59
+ "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/scopeTag",
60
+ "description": "Scope at which this acceptance criterion is observable (library, runtime, deployed, unclassified). Optional at schema level; ruleset-enforcing consumers may require it. Governs which test scopes count as coverage for this AC."
61
+ },
58
62
  "provenance": { "$ref": "#/$defs/acProvenance" }
59
63
  }
60
64
  },