@stravica-ai/rcf-schemas 0.4.2 → 0.4.4

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 (36) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/docs/common.md +8 -6
  3. package/docs/id-conventions.md +34 -4
  4. package/docs/test-suite.md +3 -2
  5. package/docs/user-story.md +1 -0
  6. package/fixtures/invalid/fbs/fbs-012-uppercase-slug.json +13 -0
  7. package/fixtures/invalid/fbs/fbs-013-standard-ids-non-slug.json +16 -0
  8. package/fixtures/invalid/manifest/manifest-018-blueprint-bad-slug.json +15 -0
  9. package/fixtures/invalid/manifest/manifest-019-standards-bad-id.json +17 -0
  10. package/fixtures/invalid/req/req-005-slug-prefix-uppercase.json +13 -0
  11. package/fixtures/invalid/test-suite/ts-007-bad-tc-scope.json +20 -0
  12. package/fixtures/invalid/user-story/us-004-bad-ac-scope.json +21 -0
  13. package/fixtures/invalid/user-story/us-005-slug-prefix-trailing-hyphen.json +20 -0
  14. package/fixtures/valid/adr/adr-004-slugged-id.json +14 -0
  15. package/fixtures/valid/build-sequence/bs-002-slug-prefix.json +8 -0
  16. package/fixtures/valid/cn/cn-003-slugged-id.json +10 -0
  17. package/fixtures/valid/fbs/fbs-006-slugged-id.json +13 -0
  18. package/fixtures/valid/fbs/fbs-007-standard-ids.json +18 -0
  19. package/fixtures/valid/manifest/manifest-008-blueprints-registered.json +32 -0
  20. package/fixtures/valid/manifest/manifest-009-standards-in-repo.json +18 -0
  21. package/fixtures/valid/manifest/manifest-010-standards-copied-in.json +26 -0
  22. package/fixtures/valid/manifest/manifest-011-preblueprint-chain-frozen.json +8 -0
  23. package/fixtures/valid/prd/prd-002-slug-prefix.json +12 -0
  24. package/fixtures/valid/req/req-002-slug-prefix.json +13 -0
  25. package/fixtures/valid/tac/tac-003-slugged-id.json +18 -0
  26. package/fixtures/valid/tad/tad-002-slug-prefix.json +14 -0
  27. package/fixtures/valid/test-suite/ts-004-widened-tsId.json +27 -0
  28. package/fixtures/valid/test-suite/ts-005-slug-prefix.json +19 -0
  29. package/fixtures/valid/user-story/us-004-ac-scope-tags.json +33 -0
  30. package/fixtures/valid/user-story/us-005-slug-prefix.json +20 -0
  31. package/package.json +6 -6
  32. package/schemas/common.schema.json +37 -23
  33. package/schemas/fbs.schema.json +6 -1
  34. package/schemas/manifest.schema.json +113 -7
  35. package/schemas/test-suite.schema.json +6 -2
  36. package/schemas/user-story.schema.json +4 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,60 @@ 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.4 - 2026-08-18
8
+
9
+ Additive patch bump for the blueprint-library mechanism landing in `rcf-build-lite`. Unblocks Phase 1 of the blueprint programme (`w-2026-08-18-016`) by adding the schema seams the mechanism keys to. Every change is additive: every 0.4.3-valid document remains valid, and every existing id continues to validate byte-for-byte.
10
+
11
+ ### Added
12
+
13
+ - **`common.schema.json`**: new `$defs.blueprintSlug` and `$defs.standardSlug`, both `^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$`. Shared shape for blueprint and standards-pack slugs; referenced from `manifest.blueprints[]`, `manifest.standards[]`, and `fbs.contextRequirements.standardIds`.
14
+ - **`manifest.schema.json`**: new optional `blueprints[]` at top level, items `$defs.appliedBlueprintRecord` (slug, version, appliedAt, source, optional namespace, optional contributions[] listing every doc id + path the blueprint contributed). Written by the `rcf blueprint` verb family (`add` on clean apply, `upgrade` on re-apply, `remove` deletes the entry). Composition (multiple applied blueprints) is expressed by multiple entries.
15
+ - **`manifest.schema.json`**: new optional `standards[]` at top level, items `$defs.standardsPackRecord` (id `^std-<slug>$`, slug, sourcePath, optional copyPath, tags[], optional summary, `testsProvidedBy` = `standard | agent | none`, `provenance` = `personal | corporate`). Records standards-pack registrations from `rcf init` and `rcf standards add`. `copyPath` is present only when the source lived outside the project root at registration time.
16
+ - **`fbs.schema.json`**: new optional `contextRequirements.standardIds[]`, items reference `common.standardSlug`. Host for the bundle assembler's agentic selective-retrieval step; an operator-authored value overrides the agentic selection.
17
+ - Fixtures (valid): `fixtures/valid/manifest/manifest-008-blueprints-registered.json` (two applied blueprints, one with contributions[]), `fixtures/valid/manifest/manifest-009-standards-in-repo.json` (in-repo standards pack, no copyPath), `fixtures/valid/manifest/manifest-010-standards-copied-in.json` (out-of-root pack with copyPath), `fixtures/valid/fbs/fbs-007-standard-ids.json` (FBS with agentically-populated standardIds), `fixtures/valid/req/req-002-slug-prefix.json`, `fixtures/valid/user-story/us-005-slug-prefix.json`, `fixtures/valid/prd/prd-002-slug-prefix.json`, `fixtures/valid/build-sequence/bs-002-slug-prefix.json`, `fixtures/valid/tad/tad-002-slug-prefix.json`, `fixtures/valid/test-suite/ts-005-slug-prefix.json`, plus `fixtures/valid/manifest/manifest-011-preblueprint-chain-frozen.json` (frozen pre-0.4.3 chain snapshot used by the byte-for-byte back-compat test).
18
+ - Fixtures (invalid): `fixtures/invalid/manifest/manifest-018-blueprint-bad-slug.json`, `fixtures/invalid/manifest/manifest-019-standards-bad-id.json`, `fixtures/invalid/fbs/fbs-013-standard-ids-non-slug.json`, `fixtures/invalid/req/req-003-slug-prefix-uppercase.json`, `fixtures/invalid/user-story/us-005-slug-prefix-trailing-hyphen.json`.
19
+ - Tests: prefix-id cases added to `req.test.js`, `user-story.test.js`, `prd.test.js`, `build-sequence.test.js`, `tad.test.js`, `test-suite.test.js`; blueprints[] and standards[] cases added to `manifest.test.js`; contextRequirements.standardIds cases added to `fbs.test.js`; new `back-compat-freeze.test.js` asserts the frozen pre-0.4.3 chain snapshot validates byte-for-byte (sha256 pin).
20
+
21
+ ### Changed
22
+
23
+ - **`common.schema.json`**: `prdId`, `reqId`, `usId`, `bsId`, `tadId`, `tsId` patterns widened to accept an optional lowercase kebab-slug prefix, `^([a-z][a-z0-9]*(?:-[a-z0-9]+)*-)?<PREFIX>-\d{3,}$`. Numeric-only ids continue to validate unchanged. Slug prefixes are well-formed kebab (leading letter, lowercase alnum segments joined by single hyphens; leading, trailing, and double hyphens are rejected).
24
+
25
+ ### Notes
26
+
27
+ - Additive-only: every 0.4.3-valid document remains valid byte-for-byte. `back-compat-freeze.test.js` proves this with a hash-pinned fixture read as raw bytes.
28
+ - Id grammar is now mixed across types: **prefix** for REQ/US/PRD/BS/TAD/TS (this bump), **suffix** for ADR/TAC/FBS/CN (already the shape from 0.4.3). This is a minimal-delta choice tonight to unblock the blueprint mechanism without churning the four types whose suffix affordance already namespaces them. Unifying the grammar is a schemas follow-up; the mixed grammar is documented and the working tools accept both.
29
+ - The `acId` and `tcId` patterns are intentionally left unchanged. AC/TC ids are anchored to their parent US/TS chain, whose ids now carry the slug prefix; adding a redundant prefix on ACs and TCs was out of scope for tonight and adds no expressiveness.
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, 0.4.2, and 0.4.3.
31
+ - Version choice: per repo precedent (every prior additive change since 0.2.0 has bumped as a **patch**: 0.2.1, 0.3.1, 0.4.1, 0.4.2, 0.4.3), this bumps `0.4.3 -> 0.4.4`. Dave's Path A brief allowed for a minor bump if the repo convention read pattern-widening as minor; the repo convention does not: the 0.4.3 pattern-widening bump was itself a patch. Precedent wins.
32
+
33
+ ## 0.4.3 - 2026-08-12
34
+
35
+ 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.
36
+
37
+ ### Added
38
+
39
+ - **`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.
40
+ - **`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.
41
+ - **`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.
42
+ - 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`.
43
+ - 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`).
44
+
45
+ ### Changed
46
+
47
+ - **`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.
48
+ - **`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).
49
+ - **`test-suite.schema.json`**: inline `testCase.id` pattern widened in lockstep with `common.tcId` (`^TC-\d{3,}-[a-z0-9-]+$`).
50
+ - **`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.
51
+ - **`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.
52
+
53
+ ### Notes
54
+
55
+ - 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.
56
+ - 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.
57
+ - 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.
58
+ - Existing documents are NOT retro-slugged. The design is preferred-with-numeric-fallback; adopting slugs is opt-in per document at authoring time.
59
+ - 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.
60
+
7
61
  ## 0.4.2 - 2026-07-31
8
62
 
9
63
  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.
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,37 @@ 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.
28
+
29
+ ## Optional slug prefix (0.4.4) — blueprint namespacing
30
+
31
+ Six families accept an optional kebab-case slug **prefix**: `PRD`, `REQ`, `US`, `BS`, `TAD`, `TS`. The full pattern is:
32
+
33
+ ```
34
+ ^([a-z][a-z0-9]*(?:-[a-z0-9]+)*-)?<PREFIX>-\d{3,}$
35
+ ```
36
+
37
+ The prefix is a blueprint's slug (or any well-formed kebab slug) followed by a hyphen. Examples: `spa-REQ-001`, `spa-US-101`, `rest-BS-001`, `my-blueprint-TAD-002`. Existing numeric-only ids continue to validate; the prefix is purely opt-in.
38
+
39
+ This unblocks the blueprint mechanism landing in `rcf-build-lite`: blueprint-contributed docs carry the blueprint's slug as a namespace so multiple applied blueprints cannot collide on ids and every trace query names the provenance visibly.
40
+
41
+ **Mixed grammar (intentional, minimal-delta):** ADR/TAC/FBS/CN carry the slug in the **suffix** (0.4.3); PRD/REQ/US/BS/TAD/TS carry it in the **prefix** (0.4.4). ADR/TAC/FBS/CN were left untouched at 0.4.4 because their suffix affordance already namespaces blueprint contributions (`TAC-002-spa-tokens`, `ADR-005-rest-versioning`). Unifying the grammar is a schemas follow-up; the mixed grammar is documented and working tools accept both.
42
+
43
+ **Not extended:** `AC` and `TC` ids keep the numeric-only + AC-hierarchical / TC-slug forms. AC/TC ids are anchored to their parent US/TS chain, whose ids now carry the slug prefix; adding a redundant prefix on ACs and TCs adds no expressiveness.
14
44
 
15
45
  ## Prefix table
16
46
 
@@ -42,11 +72,11 @@ Pick per project; the schema does not enforce either form. The published `Stravi
42
72
 
43
73
  ## TS ids
44
74
 
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`.
75
+ `^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
76
 
47
77
  ## TC ids
48
78
 
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.
79
+ `^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
80
 
51
81
  Rationale: sequential numbering for TCs is brittle if you renumber; slug-based ids give readable pointers in test-suite bodies and traceability queries.
52
82
 
@@ -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,16 @@
1
+ {
2
+ "fbsId": "FBS-014",
3
+ "prdId": "PRD-001",
4
+ "bsId": "BS-001",
5
+ "buildOrder": 14,
6
+ "executionStatus": "notStarted",
7
+ "title": "Bad standardIds shape",
8
+ "summary": "standardIds carries an uppercase entry that is not a valid slug.",
9
+ "acIds": ["AC-001"],
10
+ "dependsOnFbsIds": [],
11
+ "contextRequirements": {
12
+ "standardIds": ["WSD-Naming"]
13
+ },
14
+ "createdAt": "2026-08-18T00:00:00Z",
15
+ "updatedAt": "2026-08-18T00:00:00Z"
16
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "projectName": "Invalid blueprint slug",
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
+ "blueprints": [
8
+ {
9
+ "slug": "SPA",
10
+ "version": "1.0.0",
11
+ "appliedAt": "2026-08-18T10:00:00Z",
12
+ "source": "@stravica-ai/rcf-blueprint-spa@1.0.0"
13
+ }
14
+ ]
15
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "projectName": "Invalid standards id",
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
+ "standards": [
8
+ {
9
+ "id": "wsd-naming",
10
+ "slug": "wsd-naming",
11
+ "sourcePath": "docs/standards/wsd-naming",
12
+ "tags": ["naming"],
13
+ "testsProvidedBy": "agent",
14
+ "provenance": "corporate"
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "reqId": "SPA-REQ-001",
3
+ "prdId": "PRD-001",
4
+ "title": "Uppercase slug prefix must be rejected",
5
+ "description": "Slug prefixes are lowercase kebab; upper-case must fail.",
6
+ "category": "functional",
7
+ "domain": "ui",
8
+ "priority": "must",
9
+ "version": "0.1.0",
10
+ "status": "draft",
11
+ "createdAt": "2026-08-18T00:00:00Z",
12
+ "updatedAt": "2026-08-18T00: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,20 @@
1
+ {
2
+ "usId": "spa--US-101",
3
+ "prdId": "PRD-001",
4
+ "reqId": "REQ-001",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "Double-hyphen slug prefix must be rejected",
8
+ "asA": "user",
9
+ "iWant": "to see this fail",
10
+ "soThat": "the slug grammar rejects double hyphens",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-001",
14
+ "description": "n/a",
15
+ "testable": true
16
+ }
17
+ ],
18
+ "createdAt": "2026-08-18T00:00:00Z",
19
+ "updatedAt": "2026-08-18T00:00:00Z"
20
+ }
@@ -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,8 @@
1
+ {
2
+ "bsId": "spa-BS-001",
3
+ "prdId": "PRD-001",
4
+ "version": "0.1.0",
5
+ "status": "draft",
6
+ "createdAt": "2026-08-18T00:00:00Z",
7
+ "updatedAt": "2026-08-18T00:00:00Z"
8
+ }
@@ -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,18 @@
1
+ {
2
+ "fbsId": "FBS-014",
3
+ "prdId": "PRD-001",
4
+ "bsId": "BS-001",
5
+ "buildOrder": 14,
6
+ "executionStatus": "notStarted",
7
+ "title": "Wire error envelope on the POST /users endpoint",
8
+ "summary": "Introduce the RFC 7807 problem-details envelope on the POST /users error paths.",
9
+ "acIds": ["AC-014-1", "AC-014-2"],
10
+ "dependsOnFbsIds": [],
11
+ "contextRequirements": {
12
+ "tacIds": ["TAC-002-error-envelope"],
13
+ "adrIds": ["ADR-004-rfc7807"],
14
+ "standardIds": ["wsd-naming", "security-baseline"]
15
+ },
16
+ "createdAt": "2026-08-18T00:00:00Z",
17
+ "updatedAt": "2026-08-18T00:00:00Z"
18
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "projectName": "Composed SPA + REST project",
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
+ "blueprints": [
8
+ {
9
+ "slug": "spa",
10
+ "version": "1.0.0",
11
+ "appliedAt": "2026-08-18T10:00:00Z",
12
+ "source": "@stravica-ai/rcf-blueprint-spa@1.0.0",
13
+ "contributions": [
14
+ { "id": "spa-REQ-001", "path": "rcf/requirements/spa-req-001.json", "kind": "req" },
15
+ { "id": "spa-US-101", "path": "rcf/user-stories/spa-us-101.json", "kind": "us" },
16
+ { "id": "TAC-002-spa-tokens", "path": "rcf/tacs/tac-002-spa-tokens.json", "kind": "tac" },
17
+ { "id": "ADR-004-spa-theme", "path": "rcf/adrs/adr-004-spa-theme.json", "kind": "adr" }
18
+ ]
19
+ },
20
+ {
21
+ "slug": "rest",
22
+ "version": "1.0.0",
23
+ "appliedAt": "2026-08-18T10:00:05Z",
24
+ "source": "@stravica-ai/rcf-blueprint-rest@1.0.0",
25
+ "namespace": "rest",
26
+ "contributions": [
27
+ { "id": "rest-REQ-001", "path": "rcf/requirements/rest-req-001.json", "kind": "req" },
28
+ { "id": "ADR-005-rest-versioning", "path": "rcf/adrs/adr-005-rest-versioning.json", "kind": "adr" }
29
+ ]
30
+ }
31
+ ]
32
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "projectName": "In-repo standards project",
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
+ "standards": [
8
+ {
9
+ "id": "std-wsd-naming",
10
+ "slug": "wsd-naming",
11
+ "sourcePath": "docs/standards/wsd-naming",
12
+ "tags": ["naming", "conventions"],
13
+ "summary": "Naming conventions for symbols, files and endpoints across WSD services.",
14
+ "testsProvidedBy": "agent",
15
+ "provenance": "corporate"
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "projectName": "Out-of-root standards project",
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
+ "standards": [
8
+ {
9
+ "id": "std-security-baseline",
10
+ "slug": "security-baseline",
11
+ "sourcePath": "/Users/thefoot/personal/patterns/security-baseline",
12
+ "copyPath": "rcf/standards/security-baseline",
13
+ "tags": ["security", "logging"],
14
+ "testsProvidedBy": "standard",
15
+ "provenance": "personal"
16
+ },
17
+ {
18
+ "id": "std-http-errors",
19
+ "slug": "http-errors",
20
+ "sourcePath": "docs/patterns/http-errors",
21
+ "tags": ["api", "errors"],
22
+ "testsProvidedBy": "none",
23
+ "provenance": "corporate"
24
+ }
25
+ ]
26
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "projectName": "Frozen pre-0.4.3 chain manifest",
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
+ "testCommand": "pnpm -r test"
8
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "prdId": "spa-PRD-001",
3
+ "productName": "Blueprint-owned PRD fragment",
4
+ "version": "0.1.0",
5
+ "status": "draft",
6
+ "problemStatement": "Blueprint-contributed PRD fragment attached to a wider project PRD.",
7
+ "objectives": [
8
+ "Cover the SPA-specific scope the blueprint owns."
9
+ ],
10
+ "createdAt": "2026-08-18T00:00:00Z",
11
+ "updatedAt": "2026-08-18T00:00:00Z"
12
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "reqId": "spa-REQ-001",
3
+ "prdId": "PRD-001",
4
+ "title": "Every route ships loading, empty, error and success states",
5
+ "description": "Blueprint-contributed requirement: every declared route in the SPA carries all four state visuals.",
6
+ "category": "functional",
7
+ "domain": "ui",
8
+ "priority": "must",
9
+ "version": "0.1.0",
10
+ "status": "draft",
11
+ "createdAt": "2026-08-18T00:00:00Z",
12
+ "updatedAt": "2026-08-18T00:00:00Z"
13
+ }
@@ -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,14 @@
1
+ {
2
+ "tadId": "spa-TAD-001",
3
+ "prdId": "PRD-001",
4
+ "version": "0.1.0",
5
+ "status": "draft",
6
+ "systemOverview": {
7
+ "executiveSummary": "Blueprint-contributed TAD fragment covering the SPA shell.",
8
+ "systemPurpose": "Frame the design tokens, nav shell and theme parity the SPA blueprint owns.",
9
+ "architecturalApproach": "Single-page application delivered light and dark by default.",
10
+ "keyCapabilities": ["Theme toggle", "Skeleton loaders", "Responsive layout"]
11
+ },
12
+ "createdAt": "2026-08-18T00:00:00Z",
13
+ "updatedAt": "2026-08-18T00:00:00Z"
14
+ }
@@ -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,19 @@
1
+ {
2
+ "id": "spa-TS-001",
3
+ "usId": "spa-US-101",
4
+ "title": "Loading-state coverage on every fetched route",
5
+ "purpose": "Assert every declared SPA route with a fetch shows a skeleton loader within 100ms.",
6
+ "testLevel": "unit",
7
+ "acIds": ["AC-001"],
8
+ "testCases": [
9
+ {
10
+ "id": "TC-001-happy-path",
11
+ "acId": "AC-001",
12
+ "description": "Skeleton renders synchronously for a route with a pending fetch.",
13
+ "status": "pending"
14
+ }
15
+ ],
16
+ "status": "draft",
17
+ "createdAt": "2026-08-18T00:00:00Z",
18
+ "updatedAt": "2026-08-18T00:00:00Z"
19
+ }
@@ -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
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "usId": "spa-US-101",
3
+ "prdId": "PRD-001",
4
+ "reqId": "spa-REQ-001",
5
+ "version": "0.1.0",
6
+ "status": "draft",
7
+ "title": "As a visitor I see a loading state on every fetch",
8
+ "asA": "first-time visitor",
9
+ "iWant": "to see a skeleton or spinner while data is loading",
10
+ "soThat": "I know the app is doing something instead of guessing whether it is broken",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-001",
14
+ "description": "Every route with a fetch shows a skeleton within 100ms of navigation",
15
+ "testable": true
16
+ }
17
+ ],
18
+ "createdAt": "2026-08-18T00:00:00Z",
19
+ "updatedAt": "2026-08-18T00:00:00Z"
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stravica-ai/rcf-schemas",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
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",
@@ -19,6 +19,9 @@
19
19
  "LICENSE",
20
20
  "CHANGELOG.md"
21
21
  ],
22
+ "scripts": {
23
+ "test": "node --test test/*.test.js"
24
+ },
22
25
  "devDependencies": {
23
26
  "ajv": "^8.17.1",
24
27
  "ajv-formats": "^3.0.1"
@@ -37,8 +40,5 @@
37
40
  "json-schema",
38
41
  "specifications",
39
42
  "traceability"
40
- ],
41
- "scripts": {
42
- "test": "node --test test/*.test.js"
43
- }
44
- }
43
+ ]
44
+ }
@@ -2,22 +2,22 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json",
4
4
  "title": "RCF Shared Definitions",
5
- "description": "Shared $defs referenced via $ref from every other RCF schema. ID patterns, status enums, version string, timestamp format, and the docRef shape. This file is not a document schema; consumers register it alongside the 11 doc-type schemas at validator init.",
5
+ "description": "Shared $defs referenced via $ref from every other RCF schema. ID patterns, status enums, version string, timestamp format, and the docRef shape. This file is not a document schema; consumers register it alongside the 11 doc-type schemas at validator init. From 0.4.4 the reqId/usId/prdId/bsId/tadId/tsId patterns accept an optional lowercase kebab-slug prefix (for example, spa-REQ-001) so blueprint mechanisms can contribute namespaced doc ids without colliding with project-authored ids. Existing numeric-only ids remain valid. The adrId/tacId/fbsId/cnId patterns already carry an optional kebab-slug SUFFIX (0.4.3) and are intentionally left untouched: id grammar is mixed across types for tonight (prefix for REQ/US/PRD/BS/TAD/TS; suffix for ADR/TAC/FBS/CN); unifying is a schemas follow-up.",
6
6
  "$defs": {
7
7
  "prdId": {
8
8
  "type": "string",
9
- "pattern": "^PRD-\\d{3,}$",
10
- "description": "PRD identifier. Three-digit minimum, unbounded above."
9
+ "pattern": "^([a-z][a-z0-9]*(?:-[a-z0-9]+)*-)?PRD-\\d{3,}$",
10
+ "description": "PRD identifier. Three-digit minimum, unbounded above. An optional lowercase kebab-slug prefix (for example, spa-PRD-001) is permitted for blueprint-contributed docs; existing numeric-only ids remain valid."
11
11
  },
12
12
  "reqId": {
13
13
  "type": "string",
14
- "pattern": "^REQ-\\d{3,}$",
15
- "description": "Requirement identifier."
14
+ "pattern": "^([a-z][a-z0-9]*(?:-[a-z0-9]+)*-)?REQ-\\d{3,}$",
15
+ "description": "Requirement identifier. An optional lowercase kebab-slug prefix (for example, spa-REQ-001) is permitted for blueprint-contributed docs; existing numeric-only ids remain valid."
16
16
  },
17
17
  "usId": {
18
18
  "type": "string",
19
- "pattern": "^US-\\d{3,}$",
20
- "description": "User Story identifier."
19
+ "pattern": "^([a-z][a-z0-9]*(?:-[a-z0-9]+)*-)?US-\\d{3,}$",
20
+ "description": "User Story identifier. An optional lowercase kebab-slug prefix (for example, spa-US-101) is permitted for blueprint-contributed docs; existing numeric-only ids remain valid."
21
21
  },
22
22
  "acId": {
23
23
  "type": "string",
@@ -26,43 +26,43 @@
26
26
  },
27
27
  "tadId": {
28
28
  "type": "string",
29
- "pattern": "^TAD-\\d{3,}$",
30
- "description": "Technical Architecture Document identifier."
29
+ "pattern": "^([a-z][a-z0-9]*(?:-[a-z0-9]+)*-)?TAD-\\d{3,}$",
30
+ "description": "Technical Architecture Document identifier. An optional lowercase kebab-slug prefix (for example, spa-TAD-001) is permitted for blueprint-contributed docs; existing numeric-only ids remain valid."
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",
44
- "pattern": "^BS-\\d{3,}$",
45
- "description": "Build Sequence identifier."
44
+ "pattern": "^([a-z][a-z0-9]*(?:-[a-z0-9]+)*-)?BS-\\d{3,}$",
45
+ "description": "Build Sequence identifier. An optional lowercase kebab-slug prefix (for example, spa-BS-001) is permitted for blueprint-contributed docs; existing numeric-only ids remain valid."
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": "^([a-z][a-z0-9]*(?:-[a-z0-9]+)*-)?TS-\\d{3,}$",
55
+ "description": "Test Suite identifier. Three-digit minimum, unbounded above. An optional lowercase kebab-slug prefix (for example, spa-TS-001) is permitted for blueprint-contributed docs; existing numeric-only ids remain valid."
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,20 @@
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
+ },
101
+ "blueprintSlug": {
102
+ "type": "string",
103
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
104
+ "description": "Blueprint slug in lowercase kebab (for example, spa, rest, msg-consumer). Same shape as the id-prefix slug that namespaces blueprint-contributed docs."
105
+ },
106
+ "standardSlug": {
107
+ "type": "string",
108
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
109
+ "description": "Standards-pack slug in lowercase kebab (for example, wsd-naming, security-baseline). Used by manifest.standards[] and referenced by fbs.contextRequirements.standardIds."
110
+ },
97
111
  "docRef": {
98
112
  "type": "object",
99
113
  "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/fbs.schema.json",
4
4
  "title": "RCF Functional Build Spec",
5
- "description": "Functional Build Specification. One per build session. Carries the mandatory bsId parent reference plus buildOrder and executionStatus. Owns its acIds[] scope (Acceptance Criteria delivered by this FBS) and its dependsOnFbsIds[] graph edge (other FBSs this one depends on).",
5
+ "description": "Functional Build Specification. One per build session. Carries the mandatory bsId parent reference plus buildOrder and executionStatus. Owns its acIds[] scope (Acceptance Criteria delivered by this FBS) and its dependsOnFbsIds[] graph edge (other FBSs this one depends on). From 0.4.4 the contextRequirements block carries an optional standardIds[] array. It is the host for the bundle assembler's selective-retrieval step: the assembler analyses the FBS work text against the manifest.standards[] tag vocabulary and populates standardIds for that bundle only, or honours an operator override authored on the FBS. Every added field is optional; pre-0.4.4 FBSes remain valid.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": [
@@ -57,6 +57,11 @@
57
57
  "type": "array",
58
58
  "items": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/adrId" }
59
59
  },
60
+ "standardIds": {
61
+ "type": "array",
62
+ "items": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/standardSlug" },
63
+ "description": "Standards-pack slugs (from manifest.standards[]) that apply to this FBS. Populated by the bundle assembler's agentic selective-retrieval step at bundle-assembly time; an operator-authored value overrides the agentic selection for that FBS. Empty or absent means no standards content is included in the bundle; the assembler never blocks on an empty selection."
64
+ },
60
65
  "existingModules": {
61
66
  "type": "array",
62
67
  "items": { "type": "string" }
@@ -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. 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.",
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. From 0.4.4 the manifest carries two optional additive sections that host the blueprint-library mechanism: blueprints[] (applied-blueprints registry written by rcf blueprint add / remove / upgrade) and standards[] (standards-pack registry written at rcf init and rcf standards add). 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"],
@@ -55,6 +55,16 @@
55
55
  "type": "array",
56
56
  "items": { "$ref": "#/$defs/shipWithoutVerifiedRecord" },
57
57
  "description": "Operator acknowledgements recorded when rcf finalise --ship-without-verified acknowledges a MOCK-ONLY-DECLARED or BLOCKED-BY-DECLARATION verdict on the verify report and ships the FBS at complete without promoting to verified. One entry per acknowledgement (verification-integrity-cluster-spec section 5.2). The ack is durable and greppable at ship time."
58
+ },
59
+ "blueprints": {
60
+ "type": "array",
61
+ "items": { "$ref": "#/$defs/appliedBlueprintRecord" },
62
+ "description": "Applied-blueprints registry. One entry per rcf blueprint add / upgrade invocation on the project. Records the blueprint's slug, applied version, source, namespace and the ids and paths the blueprint contributed to the tree. Written by the rcf blueprint verb family; read by rcf blueprint list / remove / upgrade. Optional; projects that never call rcf blueprint have no entries. Composition (multiple applied blueprints) is expressed by multiple entries with distinct slugs."
63
+ },
64
+ "standards": {
65
+ "type": "array",
66
+ "items": { "$ref": "#/$defs/standardsPackRecord" },
67
+ "description": "Standards-pack registry. One entry per standards folder registered at rcf init (or later via rcf standards add). Records the pack's id, slug, source path, optional copy path (present only when the source lived outside the project root and was copied into rcf/standards/<slug>/ for portability), tag vocabulary, testsProvidedBy classification, and provenance. Read by the bundle assembler's selective-retrieval step when populating fbs.contextRequirements.standardIds. Optional; projects that register no standards have no entries."
58
68
  }
59
69
  },
60
70
  "$defs": {
@@ -154,8 +164,8 @@
154
164
  "properties": {
155
165
  "id": {
156
166
  "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."
167
+ "pattern": "^ra-FBS-\\d{3,}(?:-[a-z0-9]+(?:-[a-z0-9]+)*)?-\\d+$",
168
+ "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
169
  },
160
170
  "fbsId": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/fbsId" },
161
171
  "createdAt": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/timestamp" },
@@ -389,8 +399,8 @@
389
399
  "properties": {
390
400
  "id": {
391
401
  "type": "string",
392
- "pattern": "^bv-FBS-\\d{3,}-\\d+$",
393
- "description": "Browser-verification record id. Format: bv-<fbsId>-<n>."
402
+ "pattern": "^bv-FBS-\\d{3,}(?:-[a-z0-9]+(?:-[a-z0-9]+)*)?-\\d+$",
403
+ "description": "Browser-verification record id. Format: bv-<fbsId>-<n>. Accepts both numeric-only and kebab-slug FBS ids."
394
404
  },
395
405
  "fbsId": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/fbsId" },
396
406
  "createdAt": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/timestamp" },
@@ -730,8 +740,8 @@
730
740
  "properties": {
731
741
  "id": {
732
742
  "type": "string",
733
- "pattern": "^swv-FBS-\\d{3,}-\\d+$",
734
- "description": "Ship-without-verified acknowledgement id. Format: swv-<fbsId>-<n>; the trailing integer increments per acknowledgement of the same FBS."
743
+ "pattern": "^swv-FBS-\\d{3,}(?:-[a-z0-9]+(?:-[a-z0-9]+)*)?-\\d+$",
744
+ "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."
735
745
  },
736
746
  "fbsId": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/fbsId" },
737
747
  "ackedAt": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/timestamp" },
@@ -764,6 +774,102 @@
764
774
  "description": "Optional verify-supplied reason string carried through from the report entry."
765
775
  }
766
776
  }
777
+ },
778
+ "appliedBlueprintRecord": {
779
+ "type": "object",
780
+ "additionalProperties": false,
781
+ "required": ["slug", "version", "appliedAt", "source"],
782
+ "properties": {
783
+ "slug": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/blueprintSlug" },
784
+ "version": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/version" },
785
+ "appliedAt": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/timestamp" },
786
+ "source": {
787
+ "type": "string",
788
+ "minLength": 1,
789
+ "description": "Where the blueprint was resolved from at apply time (for example, a registry path, a git ref, or a local file path). Provenance for rcf blueprint upgrade."
790
+ },
791
+ "namespace": {
792
+ "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/blueprintSlug",
793
+ "description": "Doc-id namespace used for this blueprint's contributions. Defaults to slug when omitted; the field exists so an operator can rename the namespace at add-time to avoid a project-authored slug collision."
794
+ },
795
+ "contributions": {
796
+ "type": "array",
797
+ "items": { "$ref": "#/$defs/appliedBlueprintContribution" },
798
+ "description": "Every doc id and file path the blueprint contributed to the tree at apply time. Makes rcf blueprint remove and rcf blueprint upgrade mechanical: remove deletes exactly these paths and refuses when any project-authored doc references any of these ids; upgrade diffs the new contribution set against this one."
799
+ }
800
+ },
801
+ "description": "One applied-blueprint record. Written by rcf blueprint add on clean apply and rewritten by rcf blueprint upgrade; removed by rcf blueprint remove."
802
+ },
803
+ "appliedBlueprintContribution": {
804
+ "type": "object",
805
+ "additionalProperties": false,
806
+ "required": ["id", "path"],
807
+ "properties": {
808
+ "id": {
809
+ "type": "string",
810
+ "minLength": 1,
811
+ "description": "Contributed doc id (for example, spa-REQ-001, or TAC-002-spa-logging when the doc kind namespaces via kebab-suffix). Schema-wise a minLength: 1 string because the union of all doc-id patterns is enforced at the owning doc's schema."
812
+ },
813
+ "path": {
814
+ "type": "string",
815
+ "minLength": 1,
816
+ "description": "Project-relative path the contribution was written to (for example, rcf/requirements/spa-req-001.json)."
817
+ },
818
+ "kind": {
819
+ "enum": ["prd", "req", "us", "tad", "tac", "adr", "bs", "fbs", "ts", "cn"],
820
+ "description": "Optional doc-kind classifier. Purely informational; the id + path pair is authoritative."
821
+ },
822
+ "scope": {
823
+ "enum": ["global"],
824
+ "description": "ADR contributions only: marks a whole-project decision (auth model, error envelope, versioning strategy). Two applied blueprints each contributing a scope:global ADR on the same topic is a hard conflict, surfaced by rcf blueprint add. Absent for non-global contributions."
825
+ },
826
+ "topic": {
827
+ "type": "string",
828
+ "minLength": 1,
829
+ "description": "ADR contributions only, required in practice when scope=global: the topic key the conflict detector matches on (for example, 'versioning', 'auth', 'errorEnvelope'). Absent for non-global contributions."
830
+ }
831
+ }
832
+ },
833
+ "standardsPackRecord": {
834
+ "type": "object",
835
+ "additionalProperties": false,
836
+ "required": ["id", "slug", "sourcePath", "tags", "testsProvidedBy", "provenance"],
837
+ "properties": {
838
+ "id": {
839
+ "type": "string",
840
+ "pattern": "^std-[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
841
+ "description": "Standards-pack record id. Format: std-<slug>. Slug is repeated in the id so the record id is grep-friendly across the manifest."
842
+ },
843
+ "slug": { "$ref": "https://schemas.stravica.io/rcf/v0.4.0/common.schema.json#/$defs/standardSlug" },
844
+ "sourcePath": {
845
+ "type": "string",
846
+ "minLength": 1,
847
+ "description": "Path to the standards content. When the source lives inside the project root the pack is referenced in place (no copy is written); when outside, the pack is copied into rcf/standards/<slug>/ and copyPath records the copy destination."
848
+ },
849
+ "copyPath": {
850
+ "type": "string",
851
+ "minLength": 1,
852
+ "description": "Present only when sourcePath was outside the project root at registration time. Points at the in-tree mirror under rcf/standards/<slug>/. Absent for in-repo sources (reference-by-default)."
853
+ },
854
+ "tags": {
855
+ "type": "array",
856
+ "items": { "type": "string", "minLength": 1 },
857
+ "description": "Ordered tag vocabulary for this pack (for example, naming, security, logging, ui, api). Read by the bundle assembler's selective-retrieval step alongside the pack summary when scoring relevance to an FBS's work text."
858
+ },
859
+ "summary": {
860
+ "type": "string",
861
+ "description": "Short human-readable summary of the pack's scope. Read by the selective-retrieval step; omitted for guidance-only packs where the tags carry enough signal."
862
+ },
863
+ "testsProvidedBy": {
864
+ "enum": ["standard", "agent", "none"],
865
+ "description": "How adherence to this pack is expressed. standard: the pack ships adherence checks that convert into ACs in the tree at registration or standards add time. agent: the working agent derives adherence from the pack's prose during build. none: guidance-only; the pack contributes prose that lands in the bundle but no adherence tests."
866
+ },
867
+ "provenance": {
868
+ "enum": ["personal", "corporate"],
869
+ "description": "Records whether the pack is a personal pattern collection or a corporate standards library. Reads at bundle-time as informational; init records the distinction so a project-level report can name where each pack came from."
870
+ }
871
+ },
872
+ "description": "One standards-pack registration. Written by rcf init (during standards prompting) and rcf standards add; read by the bundle assembler's selective-retrieval step."
767
873
  }
768
874
  }
769
875
  }
@@ -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
  },