@stravica-ai/rcf-schemas 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +8 -8
  3. package/docs/README.md +1 -0
  4. package/docs/adr.md +1 -1
  5. package/docs/build-sequence.md +1 -1
  6. package/docs/cn.md +1 -1
  7. package/docs/common.md +3 -2
  8. package/docs/eval.md +105 -0
  9. package/docs/fbs.md +1 -1
  10. package/docs/manifest.md +1 -1
  11. package/docs/prd.md +1 -1
  12. package/docs/req.md +1 -1
  13. package/docs/tac.md +1 -1
  14. package/docs/tad.md +1 -1
  15. package/docs/test-suite.md +1 -1
  16. package/docs/user-story.md +4 -1
  17. package/fixtures/invalid/eval/eval-001-missing-usId.json +13 -0
  18. package/fixtures/invalid/eval/eval-002-empty-acIds.json +14 -0
  19. package/fixtures/invalid/eval/eval-003-bad-id-two-digit.json +14 -0
  20. package/fixtures/invalid/eval/eval-004-llm-judge-missing-harness.json +14 -0
  21. package/fixtures/invalid/eval/eval-005-bad-judge-type.json +14 -0
  22. package/fixtures/invalid/eval/eval-006-extra-property.json +15 -0
  23. package/fixtures/invalid/eval/eval-007-passthreshold-out-of-range.json +14 -0
  24. package/fixtures/invalid/manifest/manifest-022-library-prefix-bad-slug.json +16 -0
  25. package/fixtures/valid/eval/eval-001-minimal-rubric.json +28 -0
  26. package/fixtures/valid/eval/eval-002-llm-judge-full.json +89 -0
  27. package/fixtures/valid/eval/eval-003-scripted-metric.json +34 -0
  28. package/fixtures/valid/eval/eval-004-slug-prefix.json +18 -0
  29. package/fixtures/valid/manifest/manifest-013-blueprint-library-prefix.json +25 -0
  30. package/package.json +1 -1
  31. package/schemas/adr.schema.json +10 -10
  32. package/schemas/build-sequence.schema.json +7 -7
  33. package/schemas/cn.schema.json +8 -8
  34. package/schemas/common.schema.json +7 -2
  35. package/schemas/eval.schema.json +338 -0
  36. package/schemas/fbs.schema.json +17 -17
  37. package/schemas/manifest.schema.json +56 -52
  38. package/schemas/prd.schema.json +6 -6
  39. package/schemas/req.schema.json +9 -9
  40. package/schemas/tac.schema.json +9 -9
  41. package/schemas/tad.schema.json +7 -7
  42. package/schemas/test-suite.schema.json +9 -9
  43. package/schemas/user-story.schema.json +22 -13
package/CHANGELOG.md CHANGED
@@ -4,6 +4,58 @@ 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.6.0 - 2026-09-04
8
+
9
+ Minor bump for commit track S1 of the ratified eval-node spec (Baz ratified 2026-09-04, "eval-node - agree spec is right shape, schema route is correct"). Adds a new `eval.schema.json` document type as the graded-output peer of a Test Suite, adds an optional `determinism` marker on the inline acceptance criterion, and adds an optional `tags` array on the User Story so a consumer authoring rule (rcf-lite blueprint-authoring checklist s6) becomes satisfiable. Every change is additive at the document layer: every 0.5.1-valid document remains valid.
10
+
11
+ Pre-1.0 semver convention (breaking-is-minor): the release is a minor because a consumer that validates strictly against the shipped bundle sees a new doc type and one new AC field. Consumers on 0.5.x keep validating their existing chains unchanged; a chain that authors any EVAL or marks any AC `nonDeterministic` declares a new floor.
12
+
13
+ ### Added
14
+
15
+ - **`eval.schema.json`** (new document type). One file per EVAL. Grades the observable output of one or more non-deterministic Acceptance Criteria on the User Story identified by `usId`. Fields: `id` (new `common.$defs.evalId`, same shape as `tsId`), `usId`, `acIds[]` (min 1; all ACs must live on the parent US), `title`, `purpose`, `criteria[]` (min 1: kebab-slug `id`, `description`, optional `critical`, optional positive `weight`), `cases[]` (min 1: kebab-slug `id`, free-shape `input`, optional `expected`, optional `criteriaIds[]`, optional `notes`), `judge` (enum `type: rubric | llmJudge | scriptedMetric`, with `harness` required when `type` is `llmJudge` and `script` required when `type` is `scriptedMetric`), `passThreshold` (required `aggregateScore` 0..1, optional `criticalMustPass`, optional non-negative-integer `minCasesPassing`), optional append-only `runRecord[]` (`runId`, `runAt`, `runner`, `aggregateScore`, `criticalFailures[]`, `verdict` enum `pass | fail | pending`, optional `perCaseScores[]`, optional `modelPinned`, optional `artefactPointer`, optional `notes`), `status` (reuses `common.authoringStatus`), `createdAt`, `updatedAt`. `additionalProperties: false` on every object.
16
+ - **`common.schema.json`**: new `$defs.evalId`, pattern `^([a-z][a-z0-9]*(?:-[a-z0-9]+)*-)?EVAL-\d{3,}$`. Three-digit minimum, unbounded above. Optional lowercase kebab-slug prefix for blueprint-contributed docs, matching the `tsId` / `usId` / `reqId` / `prdId` / `bsId` / `tadId` shape.
17
+ - **`user-story.schema.json`**: new optional `determinism` field on the inline `$defs.acceptanceCriterion`. Enum `deterministic | nonDeterministic`. Absence is treated as `deterministic` by every consumer. Governs whether an EVAL is required by `rcf audit eval coverage --strict` and whether the merge gate emits `EVAL-MISSING` / `EVAL-BELOW-THRESHOLD` per-AC verdicts. Optional at schema level; existing chains keep validating.
18
+ - **`user-story.schema.json`**: new optional `tags` at the document level, array of string. Same shape as `req.tags`. Unblocks the rcf-lite blueprint-authoring checklist rule (section 6) that requires every US a blueprint contributes to carry `blueprint:<slug>`; the checklist rule was unsatisfiable against 0.5.1 (`additionalProperties: false` on the US with no `tags` property; found by the WSD spot-walk `d-2026-09-04-012`, work item `w-2026-09-04-dave-002`).
19
+ - **`loadSchemas.js` / `SCHEMA_NAMES`**: `eval` added to the registered set. `schemas.test.js` count assertion lifted from 12 to 13.
20
+ - **Fixtures (valid)**: `fixtures/valid/eval/eval-001-minimal-rubric.json` (minimum-shape rubric EVAL), `eval-002-llm-judge-full.json` (three-criterion `llmJudge` with two cases, populated `passThreshold`, one `runRecord` entry, `criticalFailures[]`, `perCaseScores`), `eval-003-scripted-metric.json` (`scriptedMetric` variant with a Node argv), `eval-004-slug-prefix.json` (`spa-EVAL-001` blueprint-namespaced form).
21
+ - **Fixtures (invalid)**: `fixtures/invalid/eval/eval-001-missing-usId.json`, `eval-002-empty-acIds.json`, `eval-003-bad-id-two-digit.json`, `eval-004-llm-judge-missing-harness.json`, `eval-005-bad-judge-type.json`, `eval-006-extra-property.json`, `eval-007-passthreshold-out-of-range.json`.
22
+ - **Tests**: new `test/eval.test.js` (37 cases: minimal validation, missing-required rejects, id-pattern range including slug prefix, criterion / case shape rules, judge type + conditional `harness` / `script` requiredness, `judgeHarness.invoker` enum `claude | codex`, `passThreshold` range and integer floor, `runRecord` shape + verdict enum + deep `perCaseScores`, `authoringStatus` reuse, timestamp format). `test/user-story.test.js` gains 9 cases for `determinism` (optional, both enum values, unknown rejected, alongside `scope` + `provenance`) and `tags` (optional, populated, empty-array accepted, non-string rejected). Total suite grows from 275 to 323.
23
+
24
+ ### Changed
25
+
26
+ - **Canonical `$id`** URLs bumped from `https://schemas.stravica.io/rcf/v0.5.0/...` to `https://schemas.stravica.io/rcf/v0.6.0/...` on every schema, and the doc-page canonical references + `loadSchemas.js` `idMap` + `schemas.test.js` expected-URL check moved with them. Matches the precedent set at 0.3.0, 0.4.0, and 0.5.0: patch releases keep the URL pinned, minor releases bump the URL. No consumer in the Stravica org hard-codes the URL literal (last org-wide search 2026-08-28 hit only this repo's own files); `rcf-lite`, `rcf-build-lite`, `rcf-tools`, and every persona-run repo load schemas by file path and register with whatever `$id` the file declares.
27
+ - **`common.schema.json` description** now names "12 doc-type schemas" (was 11) to account for the new `eval` file.
28
+
29
+ ### Migration
30
+
31
+ - **Existing chains on 0.5.1**: bump `@stravica-ai/rcf-schemas` to `0.6.0`. Every existing document keeps validating: `determinism` and `tags` are optional additions, and no consumer authored an EVAL yet.
32
+ - **To adopt EVAL on a non-deterministic AC**: mark the AC `determinism: nonDeterministic` on its parent US, then author an `EVAL-NNN.json` file that lists the AC in `acIds[]`, declares its `criteria[]` and `cases[]`, and picks a `judge.type`. See [`docs/eval.md`](./docs/eval.md) for the field-by-field reference and [`fixtures/valid/eval/eval-002-llm-judge-full.json`](./fixtures/valid/eval/eval-002-llm-judge-full.json) for a full example.
33
+ - **Consumers to bump**: `rcf-lite` (its `packages/rcf-lite/package.json` pins `@stravica-ai/rcf-schemas` exact `0.5.1`; the L1-L4 rcf-lite train per the eval-node spec bumps this in its own minor release), and `rcf-tools` (the MCP server, which re-exports `rcf://schemas/*` and gains an `eval` resource with no code change at its next bump). No estate change lands automatically from this release; the consumer bumps are independent workstreams in the eval-node train.
34
+
35
+ ### Notes
36
+
37
+ - The `judge` object's conditional requiredness (`harness` when `type` is `llmJudge`; `script` when `type` is `scriptedMetric`) is expressed with `allOf` + `if`/`then` and redeclares the required property inside `then.properties` so Ajv strict mode accepts the constraint. Same pattern the 0.5.0 `testPointer` conditional uses.
38
+ - The `judgeHarness.invoker` enum is `claude | codex` at v1: the estate rule is subscription-only (every model call goes through the `claude` or `codex` CLI under subscription auth; no API keys, no direct HTTP to Anthropic or OpenAI), and the verify launcher already uses this pattern. An HTTP-API-backed judge is a rejected alternative in the eval-node spec (section 10); the enum can grow additively if the estate ever changes that rule.
39
+ - `back-compat-freeze.test.js` continues to pass: it pins the byte-for-byte SHA of a pre-0.4.4 manifest fixture; that fixture is unaffected by anything in this release.
40
+ - Blueprint discipline stays as before: blueprints ship no test files (`blueprint-authoring.md` section 3) and do not contribute EVAL artefacts either. A blueprint may nominate ACs whose `determinism` intent is `nonDeterministic`; the project authors the EVAL.
41
+
42
+ ## 0.5.1 - 2026-08-31
43
+
44
+ Additive patch bump for the external-blueprint-library mechanism landing in `rcf-lite`. Adds one optional field on the applied-blueprint record so a project can durably record which registered library an apply resolved through, without the ownership fact having to be re-derived by string-matching `source`. Every change is additive: every 0.5.0-valid document remains valid, and every existing id continues to validate byte-for-byte.
45
+
46
+ ### Added
47
+
48
+ - **`manifest.schema.json`**: new optional `libraryPrefix` on `$defs.appliedBlueprintRecord`, referencing `common.$defs.blueprintSlug`. Stamped when an apply resolves through a registered external blueprint library (for example, an apply of `acme:spa` stamps `libraryPrefix: acme`); absent for shelf and path applies. Once stamped, the record's `libraryPrefix` is the ownership fact: the library registry may be renamed, re-pointed, or unregistered without orphaning previously applied records. Written by the library-resolving branch of `rcf blueprint add`; read by `rcf library remove`'s ownership check in preference to `source`-prefix matching.
49
+ - Fixtures (valid): `fixtures/valid/manifest/manifest-013-blueprint-library-prefix.json` (one library-applied blueprint with `libraryPrefix` stamped, alongside a shelf-applied blueprint with no `libraryPrefix`).
50
+ - Fixtures (invalid): `fixtures/invalid/manifest/manifest-022-library-prefix-bad-slug.json` (uppercase `libraryPrefix`, violates the `blueprintSlug` shape).
51
+ - Tests: `libraryPrefix` cases added to `manifest.test.js` covering the happy path (stamped alone, coexists with `namespace` and `contributions`), the shelf/path back-compat case (record without `libraryPrefix` still validates), a mixed library-and-shelf record, the two reject cases (uppercase, leading-hyphen), and a pre-0.5.1 back-compat case that keeps a `blueprints[]` record without `libraryPrefix` valid.
52
+
53
+ ### Notes
54
+
55
+ - Additive-only: every 0.5.0-valid document remains valid. `additionalProperties: false` on the record is preserved: `libraryPrefix` is the only new permitted property.
56
+ - Canonical `$id` URLs stay at `v0.5.0`, following the patch-release precedent set at 0.2.1, 0.3.1, 0.4.1, 0.4.2, 0.4.3, 0.4.4, and 0.4.5.
57
+ - Version choice: per repo precedent (every additive change since 0.2.0 has bumped as a patch), this bumps `0.5.0 -> 0.5.1`.
58
+
7
59
  ## 0.5.0 - 2026-08-28
8
60
 
9
61
  Minor bump for the `testPointer` conditional-by-status ruling (`w-2026-08-27-dave-001`, Baz ratified 2026-08-27). `testPointer` on a Test Case is now REQUIRED when the TC's `status` is `passing` or `failing`, and OPTIONAL when the TC's `status` is `pending` or `skipped`. This closes the "a passing verdict may point at nothing" hole the 0.4.x shape allowed and moves the durable landing point for the rule out of `rcf-lite`'s local strictness overlay (`w-2026-07-28-005`) into the shared schema, where every consumer inherits it uniformly.
package/README.md CHANGED
@@ -10,9 +10,9 @@ JSON Schemas for the [Requirements Confidence Framework (RCF)](https://stravica.
10
10
 
11
11
  ## What's here
12
12
 
13
- - **12 schema files** in `schemas/`:
13
+ - **13 schema files** in `schemas/`:
14
14
  - `common.schema.json` - shared `$defs` (id patterns, status enums, version, timestamp, `docRef`). Referenced via `$ref` from every other schema.
15
- - `prd`, `req`, `user-story`, `tad`, `tac`, `adr`, `build-sequence`, `fbs`, `test-suite`, `manifest`, `cn`.
15
+ - `prd`, `req`, `user-story`, `tad`, `tac`, `adr`, `build-sequence`, `fbs`, `test-suite`, `eval`, `manifest`, `cn`.
16
16
  - **Per-schema docs** in `docs/` (one page per schema) plus cross-cutting `id-conventions.md` and `file-layout.md`.
17
17
  - **JSON test fixtures** in `fixtures/valid/` and `fixtures/invalid/` (every schema covered).
18
18
  - **CHANGELOG** in `CHANGELOG.md`.
@@ -46,7 +46,7 @@ io.stravica.rcf:rcf-schemas:0.3.0
46
46
  Each schema is fetchable by its canonical `$id`:
47
47
 
48
48
  ```
49
- https://schemas.stravica.io/rcf/v0.5.0/<name>.schema.json
49
+ https://schemas.stravica.io/rcf/v0.6.0/<name>.schema.json
50
50
  ```
51
51
 
52
52
  ## Use
@@ -66,7 +66,7 @@ const pkgRoot = require.resolve('@stravica-ai/rcf-schemas/package.json').replace
66
66
 
67
67
  const names = [
68
68
  'common', 'prd', 'req', 'user-story', 'tad', 'tac', 'adr',
69
- 'build-sequence', 'fbs', 'test-suite', 'manifest', 'cn'
69
+ 'build-sequence', 'fbs', 'test-suite', 'eval', 'manifest', 'cn'
70
70
  ];
71
71
 
72
72
  const ajv = new Ajv({ strict: true, allErrors: true });
@@ -77,7 +77,7 @@ for (const name of names) {
77
77
  ajv.addSchema(schema);
78
78
  }
79
79
 
80
- const validatePrd = ajv.getSchema('https://schemas.stravica.io/rcf/v0.5.0/prd.schema.json');
80
+ const validatePrd = ajv.getSchema('https://schemas.stravica.io/rcf/v0.6.0/prd.schema.json');
81
81
  const ok = validatePrd(myPrdDoc);
82
82
  if (!ok) console.error(validatePrd.errors);
83
83
  ```
@@ -95,7 +95,7 @@ pkg = files('stravica_rcf_schemas') / 'schemas'
95
95
 
96
96
  names = [
97
97
  'common', 'prd', 'req', 'user-story', 'tad', 'tac', 'adr',
98
- 'build-sequence', 'fbs', 'test-suite', 'manifest', 'cn'
98
+ 'build-sequence', 'fbs', 'test-suite', 'eval', 'manifest', 'cn'
99
99
  ]
100
100
 
101
101
  resources = []
@@ -115,7 +115,7 @@ Configure a `SchemaClient` (everit) or `SchemaLoader` (networknt) that resolves
115
115
 
116
116
  ### Any other language
117
117
 
118
- The schemas use JSON Schema Draft 2020-12. Any validator that supports 2020-12 + multi-file `$ref` will work. Register the 12 schemas (with `common.schema.json` first) and resolve by `$id`.
118
+ The schemas use JSON Schema Draft 2020-12. Any validator that supports 2020-12 + multi-file `$ref` will work. Register the 13 schemas (with `common.schema.json` first) and resolve by `$id`.
119
119
 
120
120
  ## Chain shape (0.3.0)
121
121
 
@@ -134,7 +134,7 @@ Per-schema docs in [`docs/`](./docs/README.md):
134
134
 
135
135
  - [common](./docs/common.md) | [prd](./docs/prd.md) | [req](./docs/req.md) | [user-story](./docs/user-story.md)
136
136
  - [tad](./docs/tad.md) | [tac](./docs/tac.md) | [adr](./docs/adr.md)
137
- - [build-sequence](./docs/build-sequence.md) | [fbs](./docs/fbs.md) | [test-suite](./docs/test-suite.md)
137
+ - [build-sequence](./docs/build-sequence.md) | [fbs](./docs/fbs.md) | [test-suite](./docs/test-suite.md) | [eval](./docs/eval.md)
138
138
  - [manifest](./docs/manifest.md) | [cn](./docs/cn.md)
139
139
 
140
140
  Cross-cutting:
package/docs/README.md CHANGED
@@ -16,6 +16,7 @@ One page per schema file under `schemas/`.
16
16
  - [build-sequence](./build-sequence.md) - ordered FBS plan.
17
17
  - [fbs](./fbs.md) - Functional Build Specification.
18
18
  - [test-suite](./test-suite.md) - Test Suite for a single AC.
19
+ - [eval](./eval.md) - EVAL, graded-output peer of a Test Suite for non-deterministic ACs.
19
20
  - [manifest](./manifest.md) - project roots-only manifest.
20
21
  - [cn](./cn.md) - Code Node, bridging the spec graph to source.
21
22
 
package/docs/adr.md CHANGED
@@ -4,7 +4,7 @@ Architectural Decision Record. One per decision, one file each. Follows the Nyga
4
4
 
5
5
  ## Canonical `$id`
6
6
 
7
- `https://schemas.stravica.io/rcf/v0.5.0/adr.schema.json`
7
+ `https://schemas.stravica.io/rcf/v0.6.0/adr.schema.json`
8
8
 
9
9
  ## Required fields
10
10
 
@@ -4,7 +4,7 @@ Build Sequence. One per project. Pure planning shell. FBS files each carry the m
4
4
 
5
5
  ## Canonical `$id`
6
6
 
7
- `https://schemas.stravica.io/rcf/v0.5.0/build-sequence.schema.json`
7
+ `https://schemas.stravica.io/rcf/v0.6.0/build-sequence.schema.json`
8
8
 
9
9
  ## Required fields
10
10
 
package/docs/cn.md CHANGED
@@ -4,7 +4,7 @@ Code Node. One file per node. Bridges the requirements graph to source code: ide
4
4
 
5
5
  ## Canonical `$id`
6
6
 
7
- `https://schemas.stravica.io/rcf/v0.5.0/cn.schema.json`
7
+ `https://schemas.stravica.io/rcf/v0.6.0/cn.schema.json`
8
8
 
9
9
  ## Required fields
10
10
 
package/docs/common.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # common.schema.json
2
2
 
3
- Shared `$defs` referenced via `$ref` from every other RCF schema. ID patterns, status enums, version string, timestamp format, and the `docRef` shape live here. This file is not itself a document schema; consumers register it alongside the other 11 schemas at validator init.
3
+ Shared `$defs` referenced via `$ref` from every other RCF schema. ID patterns, status enums, version string, timestamp format, and the `docRef` shape live here. This file is not itself a document schema; consumers register it alongside the other 12 schemas at validator init.
4
4
 
5
5
  ## Canonical `$id`
6
6
 
7
- `https://schemas.stravica.io/rcf/v0.5.0/common.schema.json`
7
+ `https://schemas.stravica.io/rcf/v0.6.0/common.schema.json`
8
8
 
9
9
  ## What's in `$defs`
10
10
 
@@ -21,6 +21,7 @@ Shared `$defs` referenced via `$ref` from every other RCF schema. ID patterns, s
21
21
  | `fbsId` | string, `^FBS-\d{3,}(-[a-z0-9]+(?:-[a-z0-9]+)*)?$` | `fbs.fbsId`, `fbs.dependsOnFbsIds[]` |
22
22
  | `tsId` | string, `^TS-\d{3,}$` | `test-suite.id` |
23
23
  | `tcId` | string, `^TC-\d{3,}-[a-z0-9-]+$` | `test-suite.testCases[].id` |
24
+ | `evalId` | string, `^([a-z][a-z0-9]*(?:-[a-z0-9]+)*-)?EVAL-\d{3,}$` | `eval.id` |
24
25
  | `cnId` | string, `^CN-\d{3,}(-[a-z0-9]+(?:-[a-z0-9]+)*)?$` | `cn.cnId`, `cn.dependencies[]` |
25
26
  | `version` | string, `^\d+\.\d+\.\d+$` | every doc-type schema |
26
27
  | `timestamp` | string, format `date-time` | every doc-type schema |
package/docs/eval.md ADDED
@@ -0,0 +1,105 @@
1
+ # eval.schema.json
2
+
3
+ EVAL document. One file per EVAL. Grades the observable output of one or more non-deterministic Acceptance Criteria on the User Story identified by `usId`. EVAL is the graded-output peer of a Test Suite: TS/TC verify deterministic behaviour, EVAL grades non-deterministic behaviour (LLM-generated text, ranked results, generated code, judgement calls).
4
+
5
+ ## Canonical `$id`
6
+
7
+ `https://schemas.stravica.io/rcf/v0.6.0/eval.schema.json`
8
+
9
+ ## When to author one
10
+
11
+ EVAL is optional in the chain. Consumers require it only for ACs marked `determinism: nonDeterministic` on the parent US (see [user-story.md](./user-story.md)). Deterministic ACs are covered by TS/TC alone; TS/TC and EVAL are peer contracts on non-deterministic ACs, neither replaces the other.
12
+
13
+ ## Required fields
14
+
15
+ | Field | Type | Notes |
16
+ |---|---|---|
17
+ | `id` | `evalId` | `EVAL-001` or slug-prefixed `spa-EVAL-001`. Same shape as `tsId`. |
18
+ | `usId` | `usId` | Parent User Story. Mandatory back-reference. |
19
+ | `acIds` | array of `acId`, min 1 | ACs this EVAL grades. All ACs must live on the parent US (cross-US coverage is not permitted at v1; the AC-to-EVAL binding is scoped by the parent US the same way TS is). |
20
+ | `title` | string, min 1 | Human-readable one-line intent. |
21
+ | `purpose` | string, min 1 | Multi-sentence rationale. What non-deterministic behaviour is being graded, and why the criteria set is the right one. |
22
+ | `criteria` | array, min 1 | Rubric criteria. See below. |
23
+ | `cases` | array, min 1 | Graded examples. See below. |
24
+ | `judge` | object | Judge configuration. See below. |
25
+ | `passThreshold` | object | Aggregate-pass rule. See below. |
26
+ | `status` | `authoringStatus` enum | Reuses the shared enum: `draft`, `review`, `needsRevision`, `approved`, `superseded`. |
27
+ | `createdAt` / `updatedAt` | ISO 8601 date-time | Lifecycle timestamps. |
28
+
29
+ ## Optional fields
30
+
31
+ | Field | Type | Purpose |
32
+ |---|---|---|
33
+ | `runRecord` | array | Append-only history of graded runs. The most recent entry is what the merge gate reads for `EVAL-BELOW-THRESHOLD`. Absent or empty means no run has landed yet (audit reports the EVAL as pending). |
34
+
35
+ ## Criterion shape
36
+
37
+ Each entry in `criteria[]`:
38
+
39
+ | Field | Type | Required | Purpose |
40
+ |---|---|---|---|
41
+ | `id` | kebab-slug | yes | Unique within the EVAL. |
42
+ | `description` | string, min 1 | yes | One-line prose describing what this criterion grades. |
43
+ | `critical` | boolean | no | When true, any case that fails this criterion fails the whole run even if the aggregate score would otherwise pass. Consumers default to false. |
44
+ | `weight` | number > 0 | no | Positive weight for aggregate scoring under `llmJudge` and `scriptedMetric`. Consumers default to 1. Unused when `judge.type` is `rubric` and every criterion is boolean. |
45
+
46
+ ## Case shape
47
+
48
+ Each entry in `cases[]`:
49
+
50
+ | Field | Type | Required | Purpose |
51
+ |---|---|---|---|
52
+ | `id` | kebab-slug | yes | Unique within the EVAL. |
53
+ | `input` | free-shape JSON | yes | The inputs the AC's system-under-test receives on this case. |
54
+ | `expected` | free-shape JSON | no | The reference output where one exists. Many non-deterministic cases have no single reference. |
55
+ | `criteriaIds` | array of kebab-slug | no | Subset of `criteria[].id` this case is graded against. Empty array or omission means all criteria. |
56
+ | `notes` | string | no | Free text; case-specific guidance for the judge to consume. |
57
+
58
+ A case is not a TC in the test-runner sense; TCs count executions of code paths, EVAL cases count graded outputs.
59
+
60
+ ## Judge shape
61
+
62
+ ```json
63
+ "judge": {
64
+ "type": "rubric" | "llmJudge" | "scriptedMetric",
65
+ "harness": { ... }, // required when type is llmJudge
66
+ "script": { ... } // required when type is scriptedMetric
67
+ }
68
+ ```
69
+
70
+ - **`rubric`**: deterministic scoring against declared boolean criteria, no external process.
71
+ - **`llmJudge`**: spawn the subscription-authenticated LLM CLI (`claude` on PATH; `codex` also permitted) with the assembled prompt on stdin, capture stdout, validate against the declared response schema. Estate rule: subscription-only. No API keys, no direct HTTP to Anthropic. The `harness` object carries `invoker` (`claude` | `codex`), optional `modelHint`, optional `systemPromptPath`, and required `responseSchemaPath`.
72
+ - **`scriptedMetric`**: run a project-owned executable whose stdout is a scored JSON envelope. The `script` object carries `command` (argv, min 1 entry) and `responseSchemaPath`.
73
+
74
+ ## passThreshold shape
75
+
76
+ | Field | Type | Required | Purpose |
77
+ |---|---|---|---|
78
+ | `aggregateScore` | number, 0..1 | yes | Minimum weighted aggregate for a run to be counted a pass. |
79
+ | `criticalMustPass` | boolean | no | When true, any critical criterion failing on any case fails the run even if `aggregateScore` is met. Consumers default to true. |
80
+ | `minCasesPassing` | integer >= 0 | no | Optional floor on how many cases must fully pass (all criteria met, no critical failure). Absent means no case-count floor is enforced. |
81
+
82
+ ## runRecord shape
83
+
84
+ Append-only list. Each entry:
85
+
86
+ | Field | Type | Required | Purpose |
87
+ |---|---|---|---|
88
+ | `runId` | string, min 1 | yes | Unique identifier for this run. Free-form; runners commonly use an ISO timestamp plus a short suffix. |
89
+ | `runAt` | ISO 8601 date-time | yes | When the run completed. |
90
+ | `runner` | string, min 1 | yes | Free-form identifier for what produced the record (for example, `claude-cli:0.1.28`, `rcf-lite:0.17.0`, `hand-authored`). |
91
+ | `modelPinned` | string, min 1 | no | Model pin captured at run time (relevant for `llmJudge` runs). |
92
+ | `aggregateScore` | number, 0..1 | yes | Weighted aggregate for this run. |
93
+ | `criticalFailures` | array of kebab-slug | yes | Criterion ids that failed a critical check on at least one case. Empty means no critical failures. |
94
+ | `perCaseScores` | array | no | Optional per-case detail. Absent for compact records; present for full grading detail. |
95
+ | `verdict` | enum: `pass`, `fail`, `pending` | yes | Overall verdict. `pending` is used when a run is recorded but grading is not complete; the coverage audit treats `pending` as absent for resolving-EVAL purposes. |
96
+ | `artefactPointer` | string, min 1 | no | Path relative to project root of a raw-judge-output artefact. Consumers do not read the pointed file at coverage time; it is retained for post-hoc review. |
97
+ | `notes` | string | no | Free text; runners record fallbacks (for example, `modelHint` not honoured by the invoker) here. |
98
+
99
+ ## Why not fold EVAL into TS
100
+
101
+ TS is a test-runner artefact (per-AC coverage via TC pointers, resolved against the working tree). EVAL is a graded-output artefact with its own case set and judge. Merging them would force one runner to speak both surfaces or force a fake pointer-shape on EVAL cases. The peer document is honest.
102
+
103
+ ## Example
104
+
105
+ See `fixtures/valid/eval/eval-002-llm-judge-full.json` for a full `llmJudge` EVAL with three criteria (two of them critical), two cases, a populated `passThreshold`, and one `runRecord` entry.
package/docs/fbs.md CHANGED
@@ -6,7 +6,7 @@ Structurally, each FBS carries the mandatory `bsId` back-reference plus `buildOr
6
6
 
7
7
  ## Canonical `$id`
8
8
 
9
- `https://schemas.stravica.io/rcf/v0.5.0/fbs.schema.json`
9
+ `https://schemas.stravica.io/rcf/v0.6.0/fbs.schema.json`
10
10
 
11
11
  ## Required fields
12
12
 
package/docs/manifest.md CHANGED
@@ -4,7 +4,7 @@ Project manifest. Roots-only shape: declares `prd`, `tad`, `bs`. Children are ow
4
4
 
5
5
  ## Canonical `$id`
6
6
 
7
- `https://schemas.stravica.io/rcf/v0.5.0/manifest.schema.json`
7
+ `https://schemas.stravica.io/rcf/v0.6.0/manifest.schema.json`
8
8
 
9
9
  ## Required fields
10
10
 
package/docs/prd.md CHANGED
@@ -4,7 +4,7 @@ Product Requirements Document. One per project. Pure shell document; REQ bodies
4
4
 
5
5
  ## Canonical `$id`
6
6
 
7
- `https://schemas.stravica.io/rcf/v0.5.0/prd.schema.json`
7
+ `https://schemas.stravica.io/rcf/v0.6.0/prd.schema.json`
8
8
 
9
9
  ## Required fields
10
10
 
package/docs/req.md CHANGED
@@ -4,7 +4,7 @@ Individual Requirement within a PRD. One file per REQ. Recommended path: `rcf/re
4
4
 
5
5
  ## Canonical `$id`
6
6
 
7
- `https://schemas.stravica.io/rcf/v0.5.0/req.schema.json`
7
+ `https://schemas.stravica.io/rcf/v0.6.0/req.schema.json`
8
8
 
9
9
  ## Required fields
10
10
 
package/docs/tac.md CHANGED
@@ -4,7 +4,7 @@ Technical Architecture Component. One file per architectural component. Verbose
4
4
 
5
5
  ## Canonical `$id`
6
6
 
7
- `https://schemas.stravica.io/rcf/v0.5.0/tac.schema.json`
7
+ `https://schemas.stravica.io/rcf/v0.6.0/tac.schema.json`
8
8
 
9
9
  ## Required fields
10
10
 
package/docs/tad.md CHANGED
@@ -4,7 +4,7 @@ Master Technical Architecture Document. One per project. Pure shell for cross-cu
4
4
 
5
5
  ## Canonical `$id`
6
6
 
7
- `https://schemas.stravica.io/rcf/v0.5.0/tad.schema.json`
7
+ `https://schemas.stravica.io/rcf/v0.6.0/tad.schema.json`
8
8
 
9
9
  ## Required fields
10
10
 
@@ -4,7 +4,7 @@ Test Suite. One file per Test Suite. The JSON specification of what Test Cases e
4
4
 
5
5
  ## Canonical `$id`
6
6
 
7
- `https://schemas.stravica.io/rcf/v0.5.0/test-suite.schema.json`
7
+ `https://schemas.stravica.io/rcf/v0.6.0/test-suite.schema.json`
8
8
 
9
9
  ## TS-as-JSON: what this schema is for
10
10
 
@@ -4,7 +4,7 @@ User Story document. One file per US. Contains Acceptance Criteria inline as `ac
4
4
 
5
5
  ## Canonical `$id`
6
6
 
7
- `https://schemas.stravica.io/rcf/v0.5.0/user-story.schema.json`
7
+ `https://schemas.stravica.io/rcf/v0.6.0/user-story.schema.json`
8
8
 
9
9
  ## Required fields
10
10
 
@@ -27,6 +27,8 @@ User Story document. One file per US. Contains Acceptance Criteria inline as `ac
27
27
  | Field | Type | Purpose |
28
28
  |---|---|---|
29
29
  | `description` | string | Additional narrative beyond the asA / iWant / soThat triple. |
30
+ | `tacIds` | array of `tacId` | Cross-link to TAC components this story exercises. |
31
+ | `tags` | array of string | Free-form tag list (0.6.0). Same shape as `req.tags`. Consumers may impose local conventions (for example, the rcf-lite blueprint-authoring checklist requires `blueprint:<slug>` on every US a blueprint contributes). Optional at schema; absence is not an error. |
30
32
 
31
33
  ## Acceptance criterion shape
32
34
 
@@ -41,6 +43,7 @@ Each AC inside `acceptanceCriteria[]`:
41
43
  | `then` | string | no | Observable outcome. |
42
44
  | `testable` | boolean | yes | Author declares the AC is testable. |
43
45
  | `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. |
46
+ | `determinism` | enum: `deterministic`, `nonDeterministic` | no | 0.6.0. Author's declaration of whether the AC's observable output is deterministic or non-deterministic (LLM-generated text, ranked results, generated code, judgement calls). Optional at schema level; absence is treated as `deterministic` by every consumer. Governs whether an EVAL is required by `rcf audit eval coverage --strict` and whether the merge gate emits `EVAL-MISSING` / `EVAL-BELOW-THRESHOLD` per-AC verdicts. |
44
47
 
45
48
  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.
46
49
 
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": "EVAL-001",
3
+ "acIds": ["AC-101-1"],
4
+ "title": "Missing usId",
5
+ "purpose": "No parent US declared.",
6
+ "criteria": [{ "id": "on-topic", "description": "x" }],
7
+ "cases": [{ "id": "case-a", "input": {} }],
8
+ "judge": { "type": "rubric" },
9
+ "passThreshold": { "aggregateScore": 0.8 },
10
+ "status": "draft",
11
+ "createdAt": "2026-09-04T00:00:00Z",
12
+ "updatedAt": "2026-09-04T00:00:00Z"
13
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "EVAL-001",
3
+ "usId": "US-101",
4
+ "acIds": [],
5
+ "title": "Empty acIds",
6
+ "purpose": "acIds[] must contain at least one AC id.",
7
+ "criteria": [{ "id": "on-topic", "description": "x" }],
8
+ "cases": [{ "id": "case-a", "input": {} }],
9
+ "judge": { "type": "rubric" },
10
+ "passThreshold": { "aggregateScore": 0.8 },
11
+ "status": "draft",
12
+ "createdAt": "2026-09-04T00:00:00Z",
13
+ "updatedAt": "2026-09-04T00:00:00Z"
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "EVAL-42",
3
+ "usId": "US-101",
4
+ "acIds": ["AC-101-1"],
5
+ "title": "Bad id",
6
+ "purpose": "Two-digit EVAL id violates the three-digit-minimum rule.",
7
+ "criteria": [{ "id": "on-topic", "description": "x" }],
8
+ "cases": [{ "id": "case-a", "input": {} }],
9
+ "judge": { "type": "rubric" },
10
+ "passThreshold": { "aggregateScore": 0.8 },
11
+ "status": "draft",
12
+ "createdAt": "2026-09-04T00:00:00Z",
13
+ "updatedAt": "2026-09-04T00:00:00Z"
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "EVAL-001",
3
+ "usId": "US-101",
4
+ "acIds": ["AC-101-1"],
5
+ "title": "llmJudge without harness",
6
+ "purpose": "judge.type llmJudge requires judge.harness; this fixture omits it.",
7
+ "criteria": [{ "id": "on-topic", "description": "x" }],
8
+ "cases": [{ "id": "case-a", "input": {} }],
9
+ "judge": { "type": "llmJudge" },
10
+ "passThreshold": { "aggregateScore": 0.8 },
11
+ "status": "draft",
12
+ "createdAt": "2026-09-04T00:00:00Z",
13
+ "updatedAt": "2026-09-04T00:00:00Z"
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "EVAL-001",
3
+ "usId": "US-101",
4
+ "acIds": ["AC-101-1"],
5
+ "title": "Bad judge type",
6
+ "purpose": "judge.type enum rejects unknown values.",
7
+ "criteria": [{ "id": "on-topic", "description": "x" }],
8
+ "cases": [{ "id": "case-a", "input": {} }],
9
+ "judge": { "type": "vibes" },
10
+ "passThreshold": { "aggregateScore": 0.8 },
11
+ "status": "draft",
12
+ "createdAt": "2026-09-04T00:00:00Z",
13
+ "updatedAt": "2026-09-04T00:00:00Z"
14
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "EVAL-001",
3
+ "usId": "US-101",
4
+ "acIds": ["AC-101-1"],
5
+ "title": "Extra property",
6
+ "purpose": "additionalProperties false on the EVAL doc; unknown top-level fields are refused.",
7
+ "criteria": [{ "id": "on-topic", "description": "x" }],
8
+ "cases": [{ "id": "case-a", "input": {} }],
9
+ "judge": { "type": "rubric" },
10
+ "passThreshold": { "aggregateScore": 0.8 },
11
+ "status": "draft",
12
+ "notARealField": true,
13
+ "createdAt": "2026-09-04T00:00:00Z",
14
+ "updatedAt": "2026-09-04T00:00:00Z"
15
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "EVAL-001",
3
+ "usId": "US-101",
4
+ "acIds": ["AC-101-1"],
5
+ "title": "passThreshold out of range",
6
+ "purpose": "aggregateScore must be within 0..1.",
7
+ "criteria": [{ "id": "on-topic", "description": "x" }],
8
+ "cases": [{ "id": "case-a", "input": {} }],
9
+ "judge": { "type": "rubric" },
10
+ "passThreshold": { "aggregateScore": 1.5 },
11
+ "status": "draft",
12
+ "createdAt": "2026-09-04T00:00:00Z",
13
+ "updatedAt": "2026-09-04T00:00:00Z"
14
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "projectName": "Invalid libraryPrefix (uppercase)",
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-31T10:00:00Z",
12
+ "source": "acme:spa",
13
+ "libraryPrefix": "ACME"
14
+ }
15
+ ]
16
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "id": "EVAL-001",
3
+ "usId": "US-101",
4
+ "acIds": ["AC-101-1"],
5
+ "title": "Answer relevance rubric",
6
+ "purpose": "Grade the assistant's answer for topical relevance against a small hand-authored case set.",
7
+ "criteria": [
8
+ {
9
+ "id": "on-topic",
10
+ "description": "Answer addresses the user's stated question."
11
+ }
12
+ ],
13
+ "cases": [
14
+ {
15
+ "id": "case-hello",
16
+ "input": { "question": "hello?" }
17
+ }
18
+ ],
19
+ "judge": {
20
+ "type": "rubric"
21
+ },
22
+ "passThreshold": {
23
+ "aggregateScore": 0.8
24
+ },
25
+ "status": "draft",
26
+ "createdAt": "2026-09-04T10:00:00Z",
27
+ "updatedAt": "2026-09-04T10:00:00Z"
28
+ }