@stravica-ai/rcf-schemas 0.8.0 → 0.10.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.
- package/CHANGELOG.md +77 -0
- package/docs/user-story.md +6 -0
- package/fixtures/invalid/user-story/us-019-ac-superseded-by-missing-note.json +29 -0
- package/fixtures/invalid/user-story/us-020-ac-superseded-by-missing-date.json +29 -0
- package/fixtures/invalid/user-story/us-021-ac-supersession-note-without-superseded-by.json +28 -0
- package/fixtures/invalid/user-story/us-022-ac-superseded-by-not-an-ac-id.json +30 -0
- package/fixtures/invalid/user-story/us-023-ac-superseded-on-bad-date.json +30 -0
- package/fixtures/invalid/user-story/us-024-ac-supersession-note-whitespace.json +30 -0
- package/fixtures/invalid/user-story/us-025-ac-probe-path-only.json +24 -0
- package/fixtures/invalid/user-story/us-026-ac-verification-bad-value.json +21 -0
- package/fixtures/valid/user-story/us-007-ac-probe-minimal.json +5 -4
- package/fixtures/valid/user-story/us-014-ac-superseded.json +31 -0
- package/fixtures/valid/user-story/us-015-ac-verification.json +28 -0
- package/package.json +1 -1
- package/schemas/user-story.schema.json +34 -4
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,83 @@ 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.10.0 - 2026-10-25
|
|
8
|
+
|
|
9
|
+
Minor release for rcf-lite FBS-232, the rcf-schemas slice of rcf-lite's verification flag (rcf-lite ADR-4152). rcf-lite gives every acceptance criterion a verification flag, `external` or `testOnly`, set at DEFINE and read by review, coverage and verify: `external` when an operator can observe the behaviour by running the packed artefact, installed cold, against a fixture project, and `testOnly` when the behaviour lives only in the repository's own tree, CI configuration or chain state. Under 0.9.0 the closed `$defs.acceptanceCriterion` refuses the field, so rcf-lite records the flag as an interim user-story tag until its pin moves. This release gives the flag a validated place on the criterion itself.
|
|
10
|
+
|
|
11
|
+
Semver: URLs pinned at `v0.6.0`. The change is additive: one optional property on the closed `$defs.acceptanceCriterion`, and every 0.9.0-valid document keeps validating. Under `CONTRIBUTING.md` an additive change is a patch bump; the release-holder set this release at `0.10.0`.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`user-story.schema.json`**: new optional `verification` on `$defs.acceptanceCriterion`, `"enum": ["external", "testOnly"]` with a `description` and no `type` or `default` (the `determinism` shape). Any other value, including another spelling or case, `null`, a boolean, a number, an array or an object, fails on one `enum` error at the criterion's `verification`. `required` stays `["id", "description", "testable"]` and `additionalProperties` stays `false`, so an undeclared property beside the flag is still rejected. As with `disposition` and `scope`, the description leaves to the consumer whether the flag is required and what each value means for a criterion's end state.
|
|
16
|
+
- **Fixtures (valid)**: `fixtures/valid/user-story/us-015-ac-verification.json` (two criteria, one `external` and one `testOnly`).
|
|
17
|
+
- **Fixtures (invalid)**: `fixtures/invalid/user-story/us-026-ac-verification-bad-value.json` (`"verification": "manual"`, one `enum` error). It is also invalid under 0.9.0 (unknown property), so its case pins the new error shape.
|
|
18
|
+
- **Tests**: `test/user-story.test.js` gains 10 cases: each of `external` and `testOnly` validates; a criterion without the field validates (rcf-lite AC-19501-4); thirteen values outside the enum (`External`, `TESTONLY`, `testonly`, `test-only`, `internal`, `manual`, `deployed`, the empty string, `null`, `true`, `1`, `["external"]` and `{}`) are each rejected on `enum` alone; an undeclared `verificationMode` is rejected on `additionalProperties`, alone and beside a valid `verification`; the flag alongside every other optional criterion field, supersession included, validates; the schema file read directly declares the enum as exactly `["external", "testOnly"]`, keeps `required` unchanged and keeps `additionalProperties: false` (rcf-lite AC-19501-6); and one case per new fixture. Total suite grows from 465 to 475.
|
|
19
|
+
- **`docs/user-story.md`**: the AC field table gains the `verification` row.
|
|
20
|
+
|
|
21
|
+
### Migration
|
|
22
|
+
|
|
23
|
+
- **Existing chains on 0.9.0**: nothing changes. The field is optional, and no existing document gains or loses validity (see Notes). A criterion may now carry `"verification": "external"` or `"verification": "testOnly"`.
|
|
24
|
+
- **Consumers to bump**: `rcf-lite` (pins the schemas package exact; bump the pin to `0.10.0`). Requiring the flag on new or amended criteria, and what each value means for a criterion's end state, are rcf-lite rules this repo does not touch. Once a criterion carries the field, a consumer still pinned to 0.9.0 rejects the document on `additionalProperties`.
|
|
25
|
+
|
|
26
|
+
### Notes
|
|
27
|
+
|
|
28
|
+
- Scripted drift report at authoring time: every fixture at main `906b668` (0.9.0: 73 valid + 107 invalid = 180), in its original bytes, validated against the 0.10.0 schemas keeps its outcome and its exact ajv error list: zero drift.
|
|
29
|
+
- Mutation check at authoring time: the 0.10.0 tests and fixtures run against the 0.9.0 `user-story.schema.json` fail 9 of 475: both value cases, the out-of-enum case (each value fails on `additionalProperties` instead of `enum`), the undeclared property beside a valid `verification` (two `additionalProperties` errors instead of one), the alongside case, the schema-file case, both fixture cases, and the existing `valid fixtures: user-story` runner, which fails only because `us-015` is invalid there. The absent-field case and the undeclared-property case pass on both.
|
|
30
|
+
- `back-compat-freeze.test.js` continues to pass.
|
|
31
|
+
|
|
32
|
+
## 0.9.0 - 2026-10-19
|
|
33
|
+
|
|
34
|
+
Minor release for the rcf-lite FBS-220 supersession round and the FBS-218 gate 4 schema findings. It folds in the 0.8.1 draft, which was never published, and adopts decision 1 from that draft. A retired acceptance criterion can now carry a durable, validating record of what replaced it: `supersededBy` (an AC id), `supersededOn` (a date) and `supersessionNote` (why), which travel together. In the FBS-220 round `rcf define update AC-16505-2 --set supersededBy=...` failed validate because the criterion had no such field and `disposition` accepts only `fixed | template`. A probe must now carry at least one response assertion: a probe with only `path` (optionally `method` or `parityEnv`) asserts nothing, rcf-lite already refuses one at runtime (finding BH-1 on FBS-218), and the schema now states the same contract for every consumer. Three further findings were analysed in the same pass and are recorded under Decisions; none of them changes a schema.
|
|
35
|
+
|
|
36
|
+
Semver: URLs pinned at `v0.6.0`. The supersession fields are additive (three optional properties on the closed `$defs.acceptanceCriterion`, bound to each other by `dependentRequired`) and on their own were drafted as the patch `0.8.1`. The probe rule is a narrowing: a user story whose probe carries no assertion validated under 0.8.0 and is rejected under 0.9.0. Under this repo's rule (`CONTRIBUTING.md` and the `README.md` Versioning section) a narrowing is a minor bump, as 0.8.0 did for the non-whitespace rule and 0.5.0 for the `testPointer` tightening, so the combined release ships as `0.9.0`. No `0.8.1` was tagged or published; its content ships here.
|
|
37
|
+
|
|
38
|
+
### Breaking
|
|
39
|
+
|
|
40
|
+
- **`user-story.schema.json`**: `$defs.acProbe` gains an `anyOf` of four arms, `{ "properties": { "<field>": true }, "required": ["<field>"] }` for `expectStatus`, `expectHeader`, `expectBody` and `selector`. A probe with none of the four fails with one `required` error per arm and one `anyOf` error, five errors at the probe's instance path. `path` (the request target), `method` (the verb) and `parityEnv` (the environment scope) are not assertions. Each arm declares its property locally because the test loader runs ajv `strictRequired`. The rule counts presence only: an assertion that is present but malformed (for example `expectBody: {}`) fails on its own constraint, not on `anyOf`.
|
|
41
|
+
- **Fixture and test results that changed.** These are the only changes the scripted drift report finds (see Notes):
|
|
42
|
+
- `fixtures/valid/user-story/us-007-ac-probe-minimal.json`: its 0.8.0 bytes go from valid to invalid (five errors). It was a path-only probe, exactly the vacuous probe the rule exists to reject, so the flip is the intended effect, not drift to avoid. Rewritten in place as the smallest probe that still asserts (`path` plus `expectStatus: 200`), valid. The path-only shape moves to the new invalid fixture `us-025`.
|
|
43
|
+
- `fixtures/invalid/user-story/us-009-ac-probe-unknown-property.json`: still invalid, but on six errors instead of one (the five `anyOf` errors plus the original `additionalProperties` on `timeoutMs`), because its probe is also path-only. Bytes unchanged; the 0.7.1 note that each probe fixture fails on exactly one ajv error no longer holds for it, and its six-error list is now pinned by a test.
|
|
44
|
+
- Test `user-story: AC with a minimal probe (path only) validates` (0.7.1) encoded path-only as valid. It is now `user-story: AC with a path-only probe is rejected on anyOf (a probe requires at least one assertion)`, pinning the five errors, next to a new `user-story: AC with a minimal probe (path and expectStatus) validates`.
|
|
45
|
+
- Test `user-story: probe accepts every method in the enum` (0.7.1) paired each method with a path-only probe and would now fail. Each case now carries `expectStatus: 200`, so the loop still tests the method enum alone.
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- **`user-story.schema.json`**: new optional `supersededBy` on `$defs.acceptanceCriterion` (`$ref` to the shared `acId`, so `AC-101-1` and `AC-201` both validate and a REQ or lowercase id does not). Presence marks the criterion as retired: it stays in the record as history and consumers stop treating it as live.
|
|
50
|
+
- **`user-story.schema.json`**: new optional `supersededOn` (string, `format: date`, `YYYY-MM-DD`, the `vendorCitation.verifiedOn` shape) and `supersessionNote` (min-length-1 string with `"pattern": "\\S"`, the 0.8.0 non-whitespace rule) on the same def.
|
|
51
|
+
- **`user-story.schema.json`**: `dependentRequired` on `$defs.acceptanceCriterion`: `supersededBy` requires `supersededOn` and `supersessionNote`, and each of those requires `supersededBy`. All three or none; a partial set fails with one `dependentRequired` error per missing field, naming it in `params.missingProperty`.
|
|
52
|
+
- **Fixtures (valid)**: `fixtures/valid/user-story/us-014-ac-superseded.json` (a `fixed`-disposition criterion superseded by its live sibling, which keeps its `disposition`).
|
|
53
|
+
- **Fixtures (invalid)**: `fixtures/invalid/user-story/us-019-ac-superseded-by-missing-note.json`, `us-020-ac-superseded-by-missing-date.json`, `us-021-ac-supersession-note-without-superseded-by.json` (each one `dependentRequired` error), `us-022-ac-superseded-by-not-an-ac-id.json` (`REQ-303`, one `pattern` error), `us-023-ac-superseded-on-bad-date.json` (`17/10/2026`, one `format` error), `us-024-ac-supersession-note-whitespace.json` (two spaces and a newline, one `pattern` error). Each is also invalid under 0.8.0 (unknown property), so the cases pin the new error shape.
|
|
54
|
+
- **Tests**: `test/user-story.test.js` gains 21 cases: back-compat without the fields; the full record; the flat AC id form; the record alongside every other optional AC field; missing note, missing date, and the FBS-220 regression (`supersededBy` alone, both missing fields named); date or note without `supersededBy`; five non-AC ids rejected on `pattern`; four non-dates rejected on `format`; ten whitespace-only notes rejected on `pattern` and an empty note on `minLength`; `disposition: "superseded"` still rejected on `enum`; a `status` field on a criterion still rejected on `additionalProperties`; self-supersession validating at schema level (a consumer rule, pinned so the limit is visible); and one case per new fixture.
|
|
55
|
+
- **`docs/user-story.md`**: the AC field table gains the three fields and a paragraph on why supersession is not a `disposition` value.
|
|
56
|
+
- **Fixtures (invalid)**: `fixtures/invalid/user-story/us-025-ac-probe-path-only.json`, the path-only probe that `us-007` carried from 0.7.1 to 0.8.0, with a `title` naming the rule. It fails on the `anyOf` rule alone (five errors, one reason) and validates under 0.8.0, so it exercises the new constraint alone.
|
|
57
|
+
- **Tests**: `test/user-story.test.js` gains 11 cases for decision 1: a minimal probe (`path` plus `expectStatus`) validates; `path` with each of the four assertion fields alone validates; `path` with `method` and `parityEnv` but no assertion is rejected with the five-error shape; a path-only probe is rejected for every method in the enum; a present but malformed `expectBody` fails on `minProperties`, not on `anyOf`; and one case each for `valid/us-007` (rewritten), `invalid/us-025` and `invalid/us-009` (its six-error list). Total suite grows from 433 to 465: 21 supersession cases and 11 decision 1 cases.
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- **`user-story.schema.json`**: the `description` of `$defs.acProbe`, `path` and `parityEnv` now says a probe needs an assertion and that `path` and `parityEnv` are not assertions (`path` was described as "The only required field").
|
|
62
|
+
- **Tests hardened, results unchanged**: four 0.7.1 rejection cases used a path-only probe, so under 0.9.0 each would also fail on `anyOf` and no longer prove its own constraint. Each now carries `expectStatus: 200` and pins its single error: lowercase `method` (`enum`), empty `path` (`minLength`), unknown `timeoutMs` (`additionalProperties`) and empty `parityEnv` (`minLength`). They pass against both 0.8.0 and 0.9.0.
|
|
63
|
+
|
|
64
|
+
### Decisions
|
|
65
|
+
|
|
66
|
+
1. **Probe requires at least one response assertion: adopted.** The rule is right: a probe with only `path` asserts nothing, and rcf-lite already refuses one at runtime (`PROBE_ASSERTION_FIELDS` in `packages/rcf-lite/src/verify/engine/catalog.js`, gate finding BH-1, which resolves it to a fail with `missing assertion`), so the schema states the same contract for every consumer instead of leaving it to one resolver. The 0.8.1 draft held the rule because it cannot ship with zero fixture drift. The release-holder adopted it on 2026-10-19: the drift is the point, since `us-007` is an example of exactly the vacuous probe the rule rejects and `us-009` is the same shape with an unknown property added. Keeping the runtime check as the only enforcement was rejected. Shape and the named drifts are under Breaking above.
|
|
67
|
+
2. **Match-anything regexes: runtime, not schema.** Nothing is added to the schema. Whether a pattern matches every body is a property of the regex, not of its spelling: a denylist of `.*`, `^.*$`, `[\s\S]*`, `.+`, `^`, `$` and `(?:)` is evaded by `.*?`, `(.*)`, `.{0,}`, `\S*`, `x?`, `a|`, `[^]*` and `.`, so a schema `pattern` or `not.enum` would advertise a guarantee it cannot give. It also could not apply to `expectHeader.match`, which rcf-lite resolves as a substring (`value.includes(match)`), not a regex, so `.*` there is a literal and not vacuous. **Rule for rcf-lite's verify engine:** before resolving `expectBody.matches`, compile the source and test it against two fixed canary strings from disjoint character classes, one of word characters only (for example `qzvxjk0729`) and one of non-word, non-space punctuation only (for example `~!@#%^&*()`). A pattern that matches both asserts nothing about the body: resolve the criterion as a fail with `failedExpectation: "expectBody.matches"` and an expected value naming the vacuity, never a pass; a source that does not compile is also a fail. Every spelling above, named or evasive, matches both canaries; `^\{"notes":\[`, `<h1>\s*Notes\s*</h1>`, `Hello, \w+`, `\w+`, `[0-9a-f]{8}`, `^$` and plain text match neither or one and pass the check. Fixed canaries keep the deterministic mode deterministic. If `expectHeader.match` ever becomes a regex, the same rule applies to it.
|
|
68
|
+
3. **Criterion-level supersession: `supersededBy` plus a required dated note (option a).** Extending `disposition` with `superseded` (option b) was rejected because `disposition` records who owns the criterion (blueprint or applying agent), a separate axis from whether it is live: a `fixed` criterion that is superseded would lose its owner marker. Both (option c) would give two sources of truth that can disagree. `supersededBy` matches the field the FBS-220 round already reached for and the ADR record's own `supersededBy`. A single successor id is carried; a criterion split in two names its primary successor and the note names the other, and widening to a list later is additive.
|
|
69
|
+
4. **Packed-artefact profile on VerifyReport v1: not in this repo.** The VerifyReport v1 shape (`schemaVersion: '1'`, `profile`) is defined in rcf-lite (`packages/rcf-lite/src/verify/report/index.js` and `src/verify/profile/index.js`); rcf-schemas carries no verify-report schema. Nothing is added here; the rcf-lite suite-as-probe DEFINE picks up the `packagedArtefact` profile and the `artefact` object.
|
|
70
|
+
|
|
71
|
+
### Migration
|
|
72
|
+
|
|
73
|
+
- **Existing chains on 0.8.0**: bump `@stravica-ai/rcf-schemas` to `0.9.0`. A document is rejected only when a probe carries none of `expectStatus`, `expectHeader`, `expectBody` or `selector`: add the assertion the criterion actually makes (for a health check, `expectStatus: 200`), or remove the probe so the criterion stays critique-only. The three supersession fields are optional and unknown to pre-0.9.0 callers.
|
|
74
|
+
- **To retire a criterion**: set all three in one write, for example `rcf define update AC-16505-2 --set supersededBy=AC-16505-4 --set supersededOn=2026-10-17 --set supersessionNote="..."`. Setting `supersededBy` alone fails validate by design and names both missing fields.
|
|
75
|
+
- **Consumers to bump**: `rcf-lite` (pins the schemas package exact; bump the pin to `0.9.0`). For decision 1, the runtime refusal in `catalog.js` is now duplicated by the schema, so a path-only probe is rejected wherever rcf-lite validates a user story against `$defs.acProbe`; `catalog.js` may keep its check as belt and braces for any probe that reaches verify without passing schema validation. Any rcf-lite fixture or test that carries a path-only probe flips with the pin and needs an assertion. What rcf-lite must also do, none of which this repo touches: (1) `rcf audit coverage --strict` treats a criterion carrying `supersededBy` as retired: it requires no bound test and leaves the coverage denominator, and a test still bound to it is reported, not counted; (2) the cross-record rules the schema cannot express: the successor exists in the same chain and is not the criterion itself, and following `supersededBy` from any retired criterion ends at a live one (no cycle); (3) deterministic verify skips a retired criterion's probe rather than resolving it; (4) the decision 2 canary rule in the verify engine.
|
|
76
|
+
|
|
77
|
+
### Notes
|
|
78
|
+
|
|
79
|
+
- Scripted drift report at authoring time: every fixture at main `2341f24` (0.8.0: 72 valid + 100 invalid = 172) and every fixture at the 0.8.1 draft head `973b8fb` (73 valid + 106 invalid = 179), in their original bytes, validated against the 0.9.0 schemas. Exactly two change outcome or error list: `valid/user-story/us-007` (valid to invalid) and `invalid/user-story/us-009` (one error to six). Every other fixture keeps its outcome and its exact ajv error list. The supersession fields alone produced zero drift on the 172.
|
|
80
|
+
- Mutation check at authoring time: the 0.9.0 tests and fixtures run against the 0.8.1 draft schemas (`973b8fb`) fail 6 of 465: the five decision 1 rejection cases (path-only, path-only per method, path with `method` and `parityEnv`, and the `us-025` and `us-009` fixture pins) and the existing `invalid fixtures: user-story` runner, which fails only because the new `us-025` validates there. Run against the 0.8.0 schemas they fail 27 of 465: those 6, the 20 supersession cases that depend on the new fields, and the existing `valid fixtures: user-story` runner on `us-014`. Every acceptance case, every hardened case and every other pre-existing case passes on both.
|
|
81
|
+
- `docs/user-story.md` labels the three supersession rows 0.9.0 and still does not describe `probe` (deferred at 0.7.1).
|
|
82
|
+
- `back-compat-freeze.test.js` continues to pass.
|
|
83
|
+
|
|
7
84
|
## 0.8.0 - 2026-10-16
|
|
8
85
|
|
|
9
86
|
Minor release for rcf-lite FBS-218 (per-AC probe catalog, rcf-lite PR 370), review gate 3. A probe `{ "path": "/", "expectBody": { "contains": " " } }` validated under 0.7.1, because `minLength: 1` counts a single space, and a deterministic verify run resolved it to a vacuous pass. Every assertion string on the probe must now contain at least one non-whitespace character: `"pattern": "\\S"` alongside the existing `minLength: 1`.
|
package/docs/user-story.md
CHANGED
|
@@ -47,6 +47,12 @@ Each AC inside `acceptanceCriteria[]`:
|
|
|
47
47
|
| `ownerRef` | object: `{tacId?, adrId?, field?}` | no | 0.6.2. Back-reference naming the owning TAC or ADR the criterion observes rather than restating. `tacId` and `adrId` re-use the shared id patterns; `field` is an optional dotted path into the owning record (for example, `interfaces.principal.fields` or `decision.retryPolicy`) pinpointing the surface. All sub-fields optional at schema level so the record composes additively; consumers may require at least one of `tacId` or `adrId` when the criterion references an owning artefact. `additionalProperties: false`. |
|
|
48
48
|
| `disposition` | enum: `fixed`, `template` | no | 0.6.2. Marks whether the criterion is the blueprint's to fix (mechanism-invariant, inherited unchanged by every applying project) or the applying agent's to set (project-parameterised, shape-given and value-open). Optional at schema level; consumer rulesets that gate blueprint contributions may require it on every AC of a shipped blueprint. Absence carries no default: a consumer that requires the marker treats an unmarked AC as a defect, and a consumer that does not require it ignores the absence. |
|
|
49
49
|
| `vendorCitation` | object: `{url, verifiedOn}` | no | 0.6.2. Vendor citation for a criterion whose truth rests on a third-party platform fact. Both sub-fields required when the object is present: `url` is the vendor documentation URL (JSON Schema `format: uri`); `verifiedOn` is the ISO-8601 date (`format: date`, `YYYY-MM-DD`) the fact was verified against that URL. Consumers may fetch the URL at gate time so a dead link is a finding, and may compare `verifiedOn` against a tolerance window. `additionalProperties: false`. |
|
|
50
|
+
| `supersededBy` | `acId` | no | 0.9.0. Id of the criterion that replaces this one. Presence marks the criterion as retired: it stays in the record as history, and consumers stop treating it as live (a coverage audit does not require a bound test for it). Requires `supersededOn` and `supersessionNote`. That the successor exists in the chain, is not this criterion and forms no cycle are cross-record rules for the consumer. |
|
|
51
|
+
| `supersededOn` | string, `format: date` | with `supersededBy` | 0.9.0. ISO-8601 date (`YYYY-MM-DD`) the criterion was superseded. Valid only alongside `supersededBy`. |
|
|
52
|
+
| `supersessionNote` | string, min 1, at least one non-whitespace character | with `supersededBy` | 0.9.0. Why the criterion was retired and how its successor covers it (for example the decision id that ruled the change). Valid only alongside `supersededBy`. |
|
|
53
|
+
| `verification` | enum: `external`, `testOnly` | no | 0.10.0. How the criterion is verified: `external` when an operator can observe the behaviour by running the packed artefact, installed cold, against a fixture project; `testOnly` when the behaviour lives only in the repository's own tree, CI configuration or chain state. Optional at schema level. Absence carries no default at schema level: whether a consumer requires the flag, and what each value means for a criterion's end state, are the consumer's rules. Written by rcf-lite, which sets it at DEFINE (rcf-lite ADR-4152). |
|
|
54
|
+
|
|
55
|
+
The three supersession fields travel together (`dependentRequired`): all three or none, and a partial set is rejected. Supersession is deliberately not a `disposition` value, because `disposition` records who owns the criterion (blueprint or applying agent), a separate axis from whether it is still live; a blueprint-fixed criterion can be superseded and keeps its `disposition`.
|
|
50
56
|
|
|
51
57
|
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.
|
|
52
58
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-303",
|
|
3
|
+
"prdId": "PRD-100",
|
|
4
|
+
"reqId": "REQ-303",
|
|
5
|
+
"version": "1.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Supersession without a note is rejected",
|
|
8
|
+
"asA": "author retiring a criterion",
|
|
9
|
+
"iWant": "a supersession record that validates",
|
|
10
|
+
"soThat": "a retired criterion cannot lose its successor, date or reason",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-303-1",
|
|
14
|
+
"description": "supersededBy and supersededOn without supersessionNote.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "deployed",
|
|
17
|
+
"supersededBy": "AC-303-2",
|
|
18
|
+
"supersededOn": "2026-10-17"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "AC-303-2",
|
|
22
|
+
"description": "The notes list renders the 25 most recent notes.",
|
|
23
|
+
"testable": true,
|
|
24
|
+
"scope": "deployed"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"createdAt": "2026-01-01T00:00:00Z",
|
|
28
|
+
"updatedAt": "2026-10-19T00:00:00Z"
|
|
29
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-303",
|
|
3
|
+
"prdId": "PRD-100",
|
|
4
|
+
"reqId": "REQ-303",
|
|
5
|
+
"version": "1.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Supersession without a date is rejected",
|
|
8
|
+
"asA": "author retiring a criterion",
|
|
9
|
+
"iWant": "a supersession record that validates",
|
|
10
|
+
"soThat": "a retired criterion cannot lose its successor, date or reason",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-303-1",
|
|
14
|
+
"description": "supersededBy and supersessionNote without supersededOn.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "deployed",
|
|
17
|
+
"supersededBy": "AC-303-2",
|
|
18
|
+
"supersessionNote": "A pagination ruling caps the list at 25; AC-303-2 carries the bound."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "AC-303-2",
|
|
22
|
+
"description": "The notes list renders the 25 most recent notes.",
|
|
23
|
+
"testable": true,
|
|
24
|
+
"scope": "deployed"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"createdAt": "2026-01-01T00:00:00Z",
|
|
28
|
+
"updatedAt": "2026-10-19T00:00:00Z"
|
|
29
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-303",
|
|
3
|
+
"prdId": "PRD-100",
|
|
4
|
+
"reqId": "REQ-303",
|
|
5
|
+
"version": "1.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "A supersession note without a successor is rejected",
|
|
8
|
+
"asA": "author retiring a criterion",
|
|
9
|
+
"iWant": "a supersession record that validates",
|
|
10
|
+
"soThat": "a retired criterion cannot lose its successor, date or reason",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-303-1",
|
|
14
|
+
"description": "supersessionNote without supersededBy.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "deployed",
|
|
17
|
+
"supersessionNote": "A pagination ruling caps the list at 25; AC-303-2 carries the bound."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "AC-303-2",
|
|
21
|
+
"description": "The notes list renders the 25 most recent notes.",
|
|
22
|
+
"testable": true,
|
|
23
|
+
"scope": "deployed"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"createdAt": "2026-01-01T00:00:00Z",
|
|
27
|
+
"updatedAt": "2026-10-19T00:00:00Z"
|
|
28
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-303",
|
|
3
|
+
"prdId": "PRD-100",
|
|
4
|
+
"reqId": "REQ-303",
|
|
5
|
+
"version": "1.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Supersession pointing at a non-AC id is rejected",
|
|
8
|
+
"asA": "author retiring a criterion",
|
|
9
|
+
"iWant": "a supersession record that validates",
|
|
10
|
+
"soThat": "a retired criterion cannot lose its successor, date or reason",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-303-1",
|
|
14
|
+
"description": "supersededBy names a requirement, not a criterion.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "deployed",
|
|
17
|
+
"supersededBy": "REQ-303",
|
|
18
|
+
"supersededOn": "2026-10-17",
|
|
19
|
+
"supersessionNote": "A pagination ruling caps the list at 25; AC-303-2 carries the bound."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "AC-303-2",
|
|
23
|
+
"description": "The notes list renders the 25 most recent notes.",
|
|
24
|
+
"testable": true,
|
|
25
|
+
"scope": "deployed"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"createdAt": "2026-01-01T00:00:00Z",
|
|
29
|
+
"updatedAt": "2026-10-19T00:00:00Z"
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-303",
|
|
3
|
+
"prdId": "PRD-100",
|
|
4
|
+
"reqId": "REQ-303",
|
|
5
|
+
"version": "1.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Supersession with a malformed date is rejected",
|
|
8
|
+
"asA": "author retiring a criterion",
|
|
9
|
+
"iWant": "a supersession record that validates",
|
|
10
|
+
"soThat": "a retired criterion cannot lose its successor, date or reason",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-303-1",
|
|
14
|
+
"description": "supersededOn is not an ISO-8601 date.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "deployed",
|
|
17
|
+
"supersededBy": "AC-303-2",
|
|
18
|
+
"supersededOn": "17/10/2026",
|
|
19
|
+
"supersessionNote": "A pagination ruling caps the list at 25; AC-303-2 carries the bound."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "AC-303-2",
|
|
23
|
+
"description": "The notes list renders the 25 most recent notes.",
|
|
24
|
+
"testable": true,
|
|
25
|
+
"scope": "deployed"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"createdAt": "2026-01-01T00:00:00Z",
|
|
29
|
+
"updatedAt": "2026-10-19T00:00:00Z"
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-303",
|
|
3
|
+
"prdId": "PRD-100",
|
|
4
|
+
"reqId": "REQ-303",
|
|
5
|
+
"version": "1.1.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Supersession with a whitespace-only note is rejected",
|
|
8
|
+
"asA": "author retiring a criterion",
|
|
9
|
+
"iWant": "a supersession record that validates",
|
|
10
|
+
"soThat": "a retired criterion cannot lose its successor, date or reason",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-303-1",
|
|
14
|
+
"description": "supersessionNote is two spaces and a newline.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "deployed",
|
|
17
|
+
"supersededBy": "AC-303-2",
|
|
18
|
+
"supersededOn": "2026-10-17",
|
|
19
|
+
"supersessionNote": " \n"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "AC-303-2",
|
|
23
|
+
"description": "The notes list renders the 25 most recent notes.",
|
|
24
|
+
"testable": true,
|
|
25
|
+
"scope": "deployed"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"createdAt": "2026-01-01T00:00:00Z",
|
|
29
|
+
"updatedAt": "2026-10-19T00:00:00Z"
|
|
30
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-301",
|
|
3
|
+
"prdId": "PRD-100",
|
|
4
|
+
"reqId": "REQ-301",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Path-only probe is rejected: a probe requires at least one assertion (acProbe anyOf, 0.9.0)",
|
|
8
|
+
"asA": "operator running deterministic verify",
|
|
9
|
+
"iWant": "a probe to carry at least one of expectStatus, expectHeader, expectBody or selector",
|
|
10
|
+
"soThat": "a probe that only names a path cannot resolve to a vacuous pass",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-301-1",
|
|
14
|
+
"description": "Probe names a path and asserts nothing about the response (the 0.7.1 to 0.8.0 us-007 shape, rcf-lite FBS-218 finding BH-1).",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "deployed",
|
|
17
|
+
"probe": {
|
|
18
|
+
"path": "/healthz"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"createdAt": "2026-01-01T00:00:00Z",
|
|
23
|
+
"updatedAt": "2026-10-19T00:00:00Z"
|
|
24
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-202",
|
|
3
|
+
"prdId": "PRD-100",
|
|
4
|
+
"reqId": "REQ-202",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"title": "Verification outside the enum is rejected",
|
|
8
|
+
"asA": "author",
|
|
9
|
+
"iWant": "verification to be one of external or testOnly",
|
|
10
|
+
"soThat": "the flag is unambiguous for a consumer that gates on it",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-202-1",
|
|
14
|
+
"description": "Verification is neither external nor testOnly.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"verification": "manual"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"createdAt": "2026-01-01T00:00:00Z",
|
|
20
|
+
"updatedAt": "2026-10-25T00:00:00Z"
|
|
21
|
+
}
|
|
@@ -6,19 +6,20 @@
|
|
|
6
6
|
"status": "approved",
|
|
7
7
|
"title": "Health endpoint answers on the deployed URL",
|
|
8
8
|
"asA": "operator running deterministic verify",
|
|
9
|
-
"iWant": "an acceptance criterion to carry the smallest
|
|
9
|
+
"iWant": "an acceptance criterion to carry the smallest probe that still asserts something",
|
|
10
10
|
"soThat": "rcf verify run resolves it without a critique pass",
|
|
11
11
|
"acceptanceCriteria": [
|
|
12
12
|
{
|
|
13
13
|
"id": "AC-301-1",
|
|
14
|
-
"description": "The health endpoint answers on the deployed URL.",
|
|
14
|
+
"description": "The health endpoint answers 200 on the deployed URL.",
|
|
15
15
|
"testable": true,
|
|
16
16
|
"scope": "deployed",
|
|
17
17
|
"probe": {
|
|
18
|
-
"path": "/healthz"
|
|
18
|
+
"path": "/healthz",
|
|
19
|
+
"expectStatus": 200
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
],
|
|
22
23
|
"createdAt": "2026-01-01T00:00:00Z",
|
|
23
|
-
"updatedAt": "2026-10-
|
|
24
|
+
"updatedAt": "2026-10-19T00:00:00Z"
|
|
24
25
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-303",
|
|
3
|
+
"prdId": "PRD-100",
|
|
4
|
+
"reqId": "REQ-303",
|
|
5
|
+
"version": "1.1.0",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"title": "Notes list paginates",
|
|
8
|
+
"asA": "author retiring a criterion",
|
|
9
|
+
"iWant": "a retired criterion to keep a durable record of its successor",
|
|
10
|
+
"soThat": "coverage stops asking for a test on it and the history stays auditable",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-303-1",
|
|
14
|
+
"description": "The notes list renders every note.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "deployed",
|
|
17
|
+
"disposition": "fixed",
|
|
18
|
+
"supersededBy": "AC-303-2",
|
|
19
|
+
"supersededOn": "2026-10-17",
|
|
20
|
+
"supersessionNote": "A pagination ruling caps the list at 25; AC-303-2 carries the bound."
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "AC-303-2",
|
|
24
|
+
"description": "The notes list renders the 25 most recent notes.",
|
|
25
|
+
"testable": true,
|
|
26
|
+
"scope": "deployed"
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"createdAt": "2026-01-01T00:00:00Z",
|
|
30
|
+
"updatedAt": "2026-10-19T00:00:00Z"
|
|
31
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "US-304",
|
|
3
|
+
"prdId": "PRD-100",
|
|
4
|
+
"reqId": "REQ-304",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"title": "Criteria carry their verification flag",
|
|
8
|
+
"asA": "test-author writing criteria at DEFINE",
|
|
9
|
+
"iWant": "each criterion to say whether it is verified from the packed artefact or by the repository's own tests",
|
|
10
|
+
"soThat": "review, coverage and verify read a validated field, not a convention in prose",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-304-1",
|
|
14
|
+
"description": "The notes list renders the 25 most recent notes.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "deployed",
|
|
17
|
+
"verification": "external"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "AC-304-2",
|
|
21
|
+
"description": "The notes repository's CI runs the full suite on every pull request.",
|
|
22
|
+
"testable": true,
|
|
23
|
+
"verification": "testOnly"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"createdAt": "2026-01-01T00:00:00Z",
|
|
27
|
+
"updatedAt": "2026-10-25T00:00:00Z"
|
|
28
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stravica-ai/rcf-schemas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
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",
|
|
@@ -72,7 +72,31 @@
|
|
|
72
72
|
"disposition": { "$ref": "#/$defs/acDisposition" },
|
|
73
73
|
"vendorCitation": { "$ref": "#/$defs/acVendorCitation" },
|
|
74
74
|
"probe": { "$ref": "#/$defs/acProbe" },
|
|
75
|
-
"provenance": { "$ref": "#/$defs/acProvenance" }
|
|
75
|
+
"provenance": { "$ref": "#/$defs/acProvenance" },
|
|
76
|
+
"supersededBy": {
|
|
77
|
+
"$ref": "https://schemas.stravica.io/rcf/v0.6.0/common.schema.json#/$defs/acId",
|
|
78
|
+
"description": "Id of the acceptance criterion that replaces this one. Presence marks the criterion as retired: it stays in the record as history and consumers stop treating it as live (a coverage audit does not require a bound test for it). Requires supersededOn and supersessionNote. Optional at schema level; that the successor exists, is not this criterion, and forms no cycle are cross-record rules for the consumer."
|
|
79
|
+
},
|
|
80
|
+
"supersededOn": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"format": "date",
|
|
83
|
+
"description": "ISO-8601 date (YYYY-MM-DD) the criterion was superseded. Required with supersededBy, and valid only alongside it."
|
|
84
|
+
},
|
|
85
|
+
"supersessionNote": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"minLength": 1,
|
|
88
|
+
"pattern": "\\S",
|
|
89
|
+
"description": "Why the criterion was retired and how its successor covers it (for example the decision id that ruled the change). Required with supersededBy, and valid only alongside it. At least one non-whitespace character is required."
|
|
90
|
+
},
|
|
91
|
+
"verification": {
|
|
92
|
+
"enum": ["external", "testOnly"],
|
|
93
|
+
"description": "How the acceptance criterion is verified. external: an operator can observe the behaviour by running the packed artefact, installed cold, against a fixture project. testOnly: the behaviour lives only in the repository's own tree, CI configuration or chain state. Optional at schema level. Absence carries no default at schema level: whether a consumer requires the flag, and what each value means for a criterion's end state, are the consumer's rules. Written by rcf-lite, which sets it at DEFINE (rcf-lite ADR-4152)."
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"dependentRequired": {
|
|
97
|
+
"supersededBy": ["supersededOn", "supersessionNote"],
|
|
98
|
+
"supersededOn": ["supersededBy"],
|
|
99
|
+
"supersessionNote": ["supersededBy"]
|
|
76
100
|
}
|
|
77
101
|
},
|
|
78
102
|
"acOwnerRef": {
|
|
@@ -125,7 +149,7 @@
|
|
|
125
149
|
"path": {
|
|
126
150
|
"type": "string",
|
|
127
151
|
"minLength": 1,
|
|
128
|
-
"description": "URL path the probe requests, relative to the run's --url (for example /healthz or /api/notes?limit=1). The
|
|
152
|
+
"description": "URL path the probe requests, relative to the run's --url (for example /healthz or /api/notes?limit=1). Required. The request target, not an assertion: a probe also needs at least one of expectStatus, expectHeader, expectBody or selector."
|
|
129
153
|
},
|
|
130
154
|
"expectStatus": {
|
|
131
155
|
"type": "integer",
|
|
@@ -144,10 +168,16 @@
|
|
|
144
168
|
"parityEnv": {
|
|
145
169
|
"type": "string",
|
|
146
170
|
"minLength": 1,
|
|
147
|
-
"description": "Target environment the probe is scoped to; the consuming resolver pairs it with rcf verify run --parity-env."
|
|
171
|
+
"description": "Target environment the probe is scoped to; the consuming resolver pairs it with rcf verify run --parity-env. Scopes the probe and is not an assertion."
|
|
148
172
|
}
|
|
149
173
|
},
|
|
150
|
-
"
|
|
174
|
+
"anyOf": [
|
|
175
|
+
{ "properties": { "expectStatus": true }, "required": ["expectStatus"] },
|
|
176
|
+
{ "properties": { "expectHeader": true }, "required": ["expectHeader"] },
|
|
177
|
+
{ "properties": { "expectBody": true }, "required": ["expectBody"] },
|
|
178
|
+
{ "properties": { "selector": true }, "required": ["selector"] }
|
|
179
|
+
],
|
|
180
|
+
"description": "Deterministic per-AC probe resolved by rcf verify run (rcf-lite issue 349). Absent means the criterion stays critique-only. Shape follows the Probe record in rcf-lite TAC-4150; the schema constrains shape only and the resolution semantics belong to the consumer. From 0.9.0 a probe carries at least one response assertion (anyOf: expectStatus, expectHeader, expectBody or selector): a probe with only path, method or parityEnv asserts nothing and is rejected. Each anyOf arm declares its property locally so ajv strictRequired accepts it."
|
|
151
181
|
},
|
|
152
182
|
"acProbeExpectHeader": {
|
|
153
183
|
"type": "object",
|