testguard-cli 0.19.0 → 0.20.1
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 +24 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/spec/GATE-SEMANTICS.md +18 -0
- package/spec/lib/origins.mjs +19 -0
- package/spec/schemas/claims.schema.json +1 -15
- package/spec/schemas/common.schema.json +16 -0
- package/spec/schemas/evidence.schema.json +1 -14
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.20.1] - 2026-10-12
|
|
11
|
+
|
|
12
|
+
Automated weekly release — everything merged since `v0.20.0`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- chore(homebrew): sha256 for v0.20.0 (#216)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## [0.20.0] - 2026-10-11
|
|
20
|
+
|
|
21
|
+
Automated weekly release — everything merged since `v0.19.0`.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- feat(spec): structured requirement references in source.ref (#214)
|
|
26
|
+
- docs(jules): note the Inactive-schedule quirk and the shallow VM clone (#213)
|
|
27
|
+
- chore(homebrew): sha256 for v0.19.0 (#212)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- Structured requirement references: a claim's `source.ref` may name the requirement it defends as `req:<spec-id>#<requirement-id>` (kind `spec`). A malformed `req:` reference, or one under another kind, now fails validation; every other reference stays opaque. The claim source is now defined once in `common.schema.json` (`$defs/claimSource`), and `requirementRef()` in `spec/lib/origins.mjs` parses the same pattern. Older readers accept the form as an opaque string. This is the format DocGuard's requirement anchors read to report the tests that cite a changed requirement as re-verified.
|
|
33
|
+
|
|
10
34
|
## [0.19.0] - 2026-10-11
|
|
11
35
|
|
|
12
36
|
Automated weekly release — everything merged since `v0.18.3`.
|
package/README.md
CHANGED
|
@@ -129,9 +129,9 @@ raises coverage. TestGuard admits it because it fails when the claim is false.
|
|
|
129
129
|
| npm | `npm i -D testguard-cli` then `npx testguard probe` |
|
|
130
130
|
| pip | `pip install testguard-cli` then `testguard probe` (needs Node ≥ 20) |
|
|
131
131
|
| Homebrew | `brew tap raccioly/tap && brew install testguard` |
|
|
132
|
-
| GitHub Action | `uses: raccioly/testguard@v0.
|
|
132
|
+
| GitHub Action | `uses: raccioly/testguard@v0.20.1` |
|
|
133
133
|
| pre-commit | `repo: https://github.com/raccioly/testguard`, hooks `testguard-claims`, `testguard-gate`, `testguard-probe` |
|
|
134
|
-
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.
|
|
134
|
+
| GitLab CI | `include: - remote: https://raw.githubusercontent.com/raccioly/testguard/v0.20.1/packaging/gitlab/testguard.gitlab-ci.yml` |
|
|
135
135
|
|
|
136
136
|
Requirements, offline installs, `ENOVERSIONS` and every CI option are in the
|
|
137
137
|
[installation guide](https://github.com/raccioly/testguard/blob/main/docs/installation.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testguard-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1",
|
|
4
4
|
"description": "Breaks your code on purpose and reports every promise your tests did not notice breaking. Mutation testing bound to the claims a project makes — vitest, jest, Playwright, pytest and unittest.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/spec/GATE-SEMANTICS.md
CHANGED
|
@@ -16,6 +16,24 @@ These are declarations, not authentication of independence, chronology,
|
|
|
16
16
|
production occurrence or correctness. The optional reference remains opaque
|
|
17
17
|
metadata (at most 512 characters); verification does not fetch it. Producer
|
|
18
18
|
provenance and measured test independence are separate signals.
|
|
19
|
+
|
|
20
|
+
One prefix is reserved. A `ref` that starts with `req:` is a structured
|
|
21
|
+
requirement reference, `req:<spec-id>#<requirement-id>`, with `<spec-id>`
|
|
22
|
+
matching `[A-Za-z0-9][A-Za-z0-9._-]{0,127}` and `<requirement-id>` matching
|
|
23
|
+
`[A-Za-z][A-Za-z0-9._-]{0,63}`. It must be declared with kind `spec`.
|
|
24
|
+
Validation rejects a malformed one at `/source/ref` and one under another kind
|
|
25
|
+
at `/source/kind`; it is never degraded to opaque, because a typo would
|
|
26
|
+
otherwise read as "no evidence for this requirement". Every other `ref` stays
|
|
27
|
+
opaque. A structured reference is still a declaration. TestGuard does not
|
|
28
|
+
resolve it, fetch the spec or check that the requirement exists. A consumer
|
|
29
|
+
that keeps a requirement index, such as DocGuard, compares
|
|
30
|
+
`<spec-id>#<requirement-id>` exactly against its own keys. `requirementRef()`
|
|
31
|
+
in `spec/lib/origins.mjs` parses the same pattern the shared
|
|
32
|
+
`$defs/claimSource` declares. Older readers accept the form as an opaque string,
|
|
33
|
+
so adopting it needs no upgrade; only readers with this rule catch a malformed
|
|
34
|
+
one. The reference is claim metadata: editing it makes saved evidence for that
|
|
35
|
+
claim stale, as any source edit does. Fingerprints, verdicts and gates do not
|
|
36
|
+
change.
|
|
19
37
|
Adding kinds does not change verdicts, baseline/fingerprint identity or default
|
|
20
38
|
gates. Existing ranking weights stay unchanged; new kinds use the existing 0.9
|
|
21
39
|
fallback, not an invented strength hierarchy. Existing documents remain valid
|
package/spec/lib/origins.mjs
CHANGED
|
@@ -5,6 +5,25 @@ import { isDeepStrictEqual } from 'node:util';
|
|
|
5
5
|
const common = JSON.parse(readFileSync(new URL('../schemas/common.schema.json', import.meta.url), 'utf8'));
|
|
6
6
|
export const CLAIM_SOURCE_KINDS = Object.freeze([...common.$defs.claimSourceKind.enum]);
|
|
7
7
|
const kindSet = new Set(CLAIM_SOURCE_KINDS);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A structured requirement reference, `req:<spec-id>#<requirement-id>`, read
|
|
11
|
+
* from the one pattern the shared schema declares (`$defs/claimSource`). It is
|
|
12
|
+
* a declaration: nothing here resolves the spec or checks the requirement
|
|
13
|
+
* exists. Consumers compare `specId#requirementId` exactly.
|
|
14
|
+
*/
|
|
15
|
+
const sourceRef = common.$defs.claimSource.properties.ref.pattern;
|
|
16
|
+
// The schema's second alternative is the structured form; the first keeps every other ref opaque.
|
|
17
|
+
export const REQUIREMENT_REF_PATTERN = new RegExp(`^${sourceRef.slice(sourceRef.indexOf('|^') + 2)}`, 'u');
|
|
18
|
+
const REQUIREMENT_REF_PARTS = /^req:([^#]+)#(.+)$/u;
|
|
19
|
+
|
|
20
|
+
/** `{ specId, requirementId }` for a valid structured `spec` reference; `null` for anything else. */
|
|
21
|
+
export function requirementRef(source) {
|
|
22
|
+
if (!source || typeof source !== 'object' || source.kind !== 'spec' || typeof source.ref !== 'string') return null;
|
|
23
|
+
if (!REQUIREMENT_REF_PATTERN.test(source.ref)) return null;
|
|
24
|
+
const [, specId, requirementId] = REQUIREMENT_REF_PARTS.exec(source.ref);
|
|
25
|
+
return { specId, requirementId };
|
|
26
|
+
}
|
|
8
27
|
const counts = () => Object.fromEntries(CLAIM_SOURCE_KINDS.map((kind) => [kind, 0]));
|
|
9
28
|
|
|
10
29
|
// Consumers validate their full document first. These narrow guards prevent
|
|
@@ -46,21 +46,7 @@
|
|
|
46
46
|
"maxLength": 2000
|
|
47
47
|
},
|
|
48
48
|
"source": {
|
|
49
|
-
"
|
|
50
|
-
"type": "object",
|
|
51
|
-
"required": [
|
|
52
|
-
"kind"
|
|
53
|
-
],
|
|
54
|
-
"properties": {
|
|
55
|
-
"kind": {
|
|
56
|
-
"$ref": "urn:claimspec:v1:common#/$defs/claimSourceKind"
|
|
57
|
-
},
|
|
58
|
-
"ref": {
|
|
59
|
-
"type": "string",
|
|
60
|
-
"maxLength": 512
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
"additionalProperties": false
|
|
49
|
+
"$ref": "urn:claimspec:v1:common#/$defs/claimSource"
|
|
64
50
|
},
|
|
65
51
|
"severity": {
|
|
66
52
|
"$ref": "urn:claimspec:v1:common#/$defs/severity"
|
|
@@ -77,6 +77,22 @@
|
|
|
77
77
|
"description": "Declared origin of claim intent, not authenticated independence or an evidence-strength order. References remain opaque; producer provenance and measured test independence are separate.",
|
|
78
78
|
"enum": ["spec", "adr", "annotation", "comment", "manual", "doc", "bug", "incident", "review", "inferred"]
|
|
79
79
|
},
|
|
80
|
+
"claimSource": {
|
|
81
|
+
"description": "Declared origin of claim intent. A source label or reference does not authenticate independence, chronology or correctness. Producer provenance and measured test independence are separate. `ref` is opaque, except that the `req:` prefix is reserved for a structured requirement reference, `req:<spec-id>#<requirement-id>`, which must be declared with kind `spec`. Neither form is ever resolved or fetched.",
|
|
82
|
+
"type": "object",
|
|
83
|
+
"required": ["kind"],
|
|
84
|
+
"properties": {
|
|
85
|
+
"kind": { "$ref": "#/$defs/claimSourceKind" },
|
|
86
|
+
"ref": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"maxLength": 512,
|
|
89
|
+
"pattern": "^(?!req:)[\\s\\S]*$|^req:[A-Za-z0-9][A-Za-z0-9._-]{0,127}#[A-Za-z][A-Za-z0-9._-]{0,63}$"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"additionalProperties": false,
|
|
93
|
+
"if": { "type": "object", "required": ["ref"], "properties": { "ref": { "type": "string", "pattern": "^req:" } } },
|
|
94
|
+
"then": { "properties": { "kind": { "const": "spec" } } }
|
|
95
|
+
},
|
|
80
96
|
"nestedProject": {
|
|
81
97
|
"description": "Changed files delegated to a separately validated descendant TestGuard project. This is not coverage or evidence; run that project's gate separately.",
|
|
82
98
|
"type": "object",
|
|
@@ -339,20 +339,7 @@
|
|
|
339
339
|
"$ref": "urn:claimspec:v1:common#/$defs/severity"
|
|
340
340
|
},
|
|
341
341
|
"source": {
|
|
342
|
-
"
|
|
343
|
-
"required": [
|
|
344
|
-
"kind"
|
|
345
|
-
],
|
|
346
|
-
"properties": {
|
|
347
|
-
"kind": {
|
|
348
|
-
"$ref": "urn:claimspec:v1:common#/$defs/claimSourceKind"
|
|
349
|
-
},
|
|
350
|
-
"ref": {
|
|
351
|
-
"type": "string",
|
|
352
|
-
"maxLength": 512
|
|
353
|
-
}
|
|
354
|
-
},
|
|
355
|
-
"additionalProperties": false
|
|
342
|
+
"$ref": "urn:claimspec:v1:common#/$defs/claimSource"
|
|
356
343
|
},
|
|
357
344
|
"producedBy": {
|
|
358
345
|
"$ref": "urn:claimspec:v1:common#/$defs/provenance"
|