@vizejs/marquette 0.299.1 → 0.303.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/LICENSE +21 -0
- package/README.md +24 -3
- package/dist/application-contract.schema.json +167 -0
- package/dist/test-run-admission.d.mts +58 -2
- package/dist/test-run-admission.d.mts.map +1 -1
- package/dist/test-run-admission.mjs +100 -2
- package/dist/test-run-admission.mjs.map +1 -1
- package/dist/test-run-admission.schema.json +84 -0
- package/dist/test-run-canonical-CB-KzZxk.d.mts +35 -0
- package/dist/test-run-canonical-CB-KzZxk.d.mts.map +1 -0
- package/dist/test-run-canonical.d.mts +2 -35
- package/dist/test-run-check.d.mts +80 -0
- package/dist/test-run-check.d.mts.map +1 -0
- package/dist/test-run-check.mjs +116 -0
- package/dist/test-run-check.mjs.map +1 -0
- package/dist/test-run-check.schema.json +82 -0
- package/dist/test-run-evidence.schema.json +266 -0
- package/dist/test-run-transition.d.mts +146 -0
- package/dist/test-run-transition.d.mts.map +1 -0
- package/dist/test-run-transition.mjs +188 -0
- package/dist/test-run-transition.mjs.map +1 -0
- package/dist/test-run-transition.schema.json +148 -0
- package/dist/{test-run-validate-XF0vyoWI.mjs → test-run-validate-C_KR031E.mjs} +5 -5
- package/dist/test-run-validate-C_KR031E.mjs.map +1 -0
- package/dist/test-run-validate.mjs +1 -1
- package/package.json +27 -14
- package/dist/test-run-canonical.d.mts.map +0 -1
- package/dist/test-run-validate-XF0vyoWI.mjs.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ubugeeei
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -99,18 +99,39 @@ deployment tooling can bind a `tests` check to retained, immutable facts:
|
|
|
99
99
|
import { defineTestRunEvidence } from "@vizejs/marquette/test-run";
|
|
100
100
|
import { validateTestRunEvidence } from "@vizejs/marquette/test-run/validate";
|
|
101
101
|
import { testRunAdmissionId } from "@vizejs/marquette/test-run/canonical";
|
|
102
|
-
import { admitTestRun } from "@vizejs/marquette/test-run/admission";
|
|
102
|
+
import { admitTestRun, decideTestRunAdmission } from "@vizejs/marquette/test-run/admission";
|
|
103
|
+
import { verifyTestRunCheck } from "@vizejs/marquette/test-run/check";
|
|
104
|
+
import { verifyTestRunTransition } from "@vizejs/marquette/test-run/transition";
|
|
103
105
|
|
|
104
106
|
const diagnostics = validateTestRunEvidence(evidence);
|
|
105
107
|
const admissionId = await testRunAdmissionId(evidence); // test-run:<sha256>
|
|
106
108
|
const rejections = await admitTestRun(evidence, candidate, admissionId, now);
|
|
109
|
+
const decision = await decideTestRunAdmission(evidence, candidate, admissionId, now);
|
|
110
|
+
if (!decision.allowed) {
|
|
111
|
+
console.error(decision.denialCodes); // e.g. ["record-expired"]
|
|
112
|
+
}
|
|
113
|
+
const release = await verifyTestRunCheck(retainedCheck, candidate, evidence, now);
|
|
114
|
+
const journal = await verifyTestRunTransition(nextTransition, chainTip);
|
|
107
115
|
```
|
|
108
116
|
|
|
109
117
|
Validation shares its `VIZE_MARQUETTE_1xx` codes, paths, and ordering with the
|
|
110
118
|
native implementation, and the canonical entry produces byte-identical
|
|
111
119
|
serialization and SHA-256 fingerprints, proven by the shared fixtures in
|
|
112
|
-
`tests/fixtures/test-run-evidence/`.
|
|
113
|
-
|
|
120
|
+
`tests/fixtures/test-run-evidence/`. Admission decisions carry the stable,
|
|
121
|
+
append-only denial-code vocabulary shared by every backend family; the same
|
|
122
|
+
fixtures pin every decision so a JavaScript, Rust, Go, or JVM gate denies for
|
|
123
|
+
identical machine-readable causes. The check entry replaces every generic
|
|
124
|
+
test-result reference in release evidence: a retained `vize.test-run.check`
|
|
125
|
+
record may only name the exact `test-run:<sha256>` admission id, bind the six
|
|
126
|
+
candidate facts, and record an observer independent from the runner. The
|
|
127
|
+
transition entry makes each release decision and the complete accepted
|
|
128
|
+
anti-replay state one durable atomic record, chained by canonical SHA-256
|
|
129
|
+
fingerprints; hosts persist it with a write-then-atomic-rename and verify the
|
|
130
|
+
recovered tip before deciding anything new. The published record schema is
|
|
131
|
+
available from `@vizejs/marquette/test-run/schema`, the decision contract from
|
|
132
|
+
`@vizejs/marquette/test-run/admission/schema`, the tests-check contract from
|
|
133
|
+
`@vizejs/marquette/test-run/check/schema`, and the transition contract from
|
|
134
|
+
`@vizejs/marquette/test-run/transition/schema`.
|
|
114
135
|
|
|
115
136
|
## Guarantees
|
|
116
137
|
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://vizejs.dev/schemas/marquette/application-contract.schema.json",
|
|
4
|
+
"title": "Vize Application Marquette",
|
|
5
|
+
"description": "Versioned language-neutral application target, environment, backend, protocol, and route graph.",
|
|
6
|
+
"$ref": "#/$defs/contract",
|
|
7
|
+
"$defs": {
|
|
8
|
+
"identifier": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"pattern": "^[a-z0-9][a-z0-9._-]*$"
|
|
11
|
+
},
|
|
12
|
+
"target": {
|
|
13
|
+
"enum": ["web", "native", "desktop", "terminal"]
|
|
14
|
+
},
|
|
15
|
+
"runtime": {
|
|
16
|
+
"enum": [
|
|
17
|
+
"browser",
|
|
18
|
+
"javascript",
|
|
19
|
+
"rust",
|
|
20
|
+
"go",
|
|
21
|
+
"jvm",
|
|
22
|
+
"native",
|
|
23
|
+
"desktop",
|
|
24
|
+
"terminal",
|
|
25
|
+
"external"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"capabilityDefinition": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"additionalProperties": false,
|
|
31
|
+
"required": ["id", "description"],
|
|
32
|
+
"properties": {
|
|
33
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
34
|
+
"description": { "type": "string", "minLength": 1 },
|
|
35
|
+
"version": { "type": "integer", "minimum": 1, "default": 1 }
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"environment": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"additionalProperties": false,
|
|
41
|
+
"required": ["id", "target", "consumer", "runtime"],
|
|
42
|
+
"properties": {
|
|
43
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
44
|
+
"target": { "$ref": "#/$defs/target" },
|
|
45
|
+
"consumer": { "enum": ["client", "server"] },
|
|
46
|
+
"runtime": { "$ref": "#/$defs/runtime" },
|
|
47
|
+
"entry": { "type": "string", "minLength": 1 },
|
|
48
|
+
"dependsOn": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"items": { "$ref": "#/$defs/identifier" },
|
|
51
|
+
"uniqueItems": true,
|
|
52
|
+
"default": []
|
|
53
|
+
},
|
|
54
|
+
"capabilities": {
|
|
55
|
+
"type": "array",
|
|
56
|
+
"items": { "$ref": "#/$defs/identifier" },
|
|
57
|
+
"uniqueItems": true,
|
|
58
|
+
"default": []
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"backend": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"additionalProperties": false,
|
|
65
|
+
"required": ["id", "family"],
|
|
66
|
+
"properties": {
|
|
67
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
68
|
+
"family": { "enum": ["javascript", "rust", "go", "jvm", "external"] },
|
|
69
|
+
"environment": { "$ref": "#/$defs/identifier" },
|
|
70
|
+
"capabilities": {
|
|
71
|
+
"type": "array",
|
|
72
|
+
"items": { "$ref": "#/$defs/identifier" },
|
|
73
|
+
"uniqueItems": true,
|
|
74
|
+
"default": []
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"protocol": {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"additionalProperties": false,
|
|
81
|
+
"required": ["id", "family", "backend"],
|
|
82
|
+
"properties": {
|
|
83
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
84
|
+
"family": { "enum": ["schema-query", "typed-rpc", "binary-rpc"] },
|
|
85
|
+
"backend": { "$ref": "#/$defs/identifier" },
|
|
86
|
+
"capabilities": {
|
|
87
|
+
"type": "array",
|
|
88
|
+
"items": { "$ref": "#/$defs/identifier" },
|
|
89
|
+
"uniqueItems": true,
|
|
90
|
+
"default": []
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"route": {
|
|
95
|
+
"type": "object",
|
|
96
|
+
"additionalProperties": false,
|
|
97
|
+
"required": ["id", "path", "environment", "rendering"],
|
|
98
|
+
"properties": {
|
|
99
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
100
|
+
"path": { "type": "string", "pattern": "^/" },
|
|
101
|
+
"environment": { "$ref": "#/$defs/identifier" },
|
|
102
|
+
"rendering": {
|
|
103
|
+
"enum": [
|
|
104
|
+
"client",
|
|
105
|
+
"static",
|
|
106
|
+
"server",
|
|
107
|
+
"stream",
|
|
108
|
+
"partial",
|
|
109
|
+
"hybrid",
|
|
110
|
+
"native",
|
|
111
|
+
"desktop",
|
|
112
|
+
"terminal"
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
"backend": { "$ref": "#/$defs/identifier" },
|
|
116
|
+
"protocol": { "$ref": "#/$defs/identifier" },
|
|
117
|
+
"capabilities": {
|
|
118
|
+
"type": "array",
|
|
119
|
+
"items": { "$ref": "#/$defs/identifier" },
|
|
120
|
+
"uniqueItems": true,
|
|
121
|
+
"default": []
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"contract": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"additionalProperties": false,
|
|
128
|
+
"required": ["application"],
|
|
129
|
+
"properties": {
|
|
130
|
+
"formatVersion": { "const": 1, "default": 1 },
|
|
131
|
+
"application": { "$ref": "#/$defs/identifier" },
|
|
132
|
+
"targets": {
|
|
133
|
+
"type": "array",
|
|
134
|
+
"items": { "$ref": "#/$defs/target" },
|
|
135
|
+
"uniqueItems": true,
|
|
136
|
+
"default": []
|
|
137
|
+
},
|
|
138
|
+
"capabilities": {
|
|
139
|
+
"type": "object",
|
|
140
|
+
"propertyNames": { "$ref": "#/$defs/identifier" },
|
|
141
|
+
"additionalProperties": { "$ref": "#/$defs/capabilityDefinition" },
|
|
142
|
+
"default": {}
|
|
143
|
+
},
|
|
144
|
+
"environments": {
|
|
145
|
+
"type": "array",
|
|
146
|
+
"items": { "$ref": "#/$defs/environment" },
|
|
147
|
+
"default": []
|
|
148
|
+
},
|
|
149
|
+
"backends": {
|
|
150
|
+
"type": "array",
|
|
151
|
+
"items": { "$ref": "#/$defs/backend" },
|
|
152
|
+
"default": []
|
|
153
|
+
},
|
|
154
|
+
"protocols": {
|
|
155
|
+
"type": "array",
|
|
156
|
+
"items": { "$ref": "#/$defs/protocol" },
|
|
157
|
+
"default": []
|
|
158
|
+
},
|
|
159
|
+
"routes": {
|
|
160
|
+
"type": "array",
|
|
161
|
+
"items": { "$ref": "#/$defs/route" },
|
|
162
|
+
"default": []
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as TestRunEvidence } from "./test-run-model-DkllmEsY.mjs";
|
|
2
2
|
import { MarquetteDiagnostic } from "./validate.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { r as parseTestRunAdmissionId, t as TEST_RUN_ADMISSION_PREFIX } from "./test-run-canonical-CB-KzZxk.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/test-run-admission.d.ts
|
|
6
6
|
/**
|
|
@@ -43,6 +43,62 @@ interface TestRunCandidate {
|
|
|
43
43
|
* hand the parsed record here.
|
|
44
44
|
*/
|
|
45
45
|
declare function admitTestRun(evidence: TestRunEvidence, candidate: TestRunCandidate, admissionId: string, now: string): Promise<MarquetteDiagnostic[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Every denial code, in the stable lexicographic decision order.
|
|
48
|
+
*
|
|
49
|
+
* The vocabulary is shared by every backend family: a JavaScript, Rust, Go,
|
|
50
|
+
* or JVM host must derive the same codes from the same diagnostics, as
|
|
51
|
+
* pinned by the shared `tests/fixtures/test-run-evidence` decision fixtures.
|
|
52
|
+
* Codes are append-only: they are never renamed, renumbered, reused, or
|
|
53
|
+
* removed, and a new rejection cause always ships with a new code.
|
|
54
|
+
*/
|
|
55
|
+
declare const TEST_RUN_DENIAL_CODES: readonly ["admission-id-malformed", "admission-id-mismatch", "admission-time-malformed", "candidate-application-mismatch", "candidate-artifact-fingerprint-mismatch", "candidate-contract-fingerprint-mismatch", "candidate-environment-mismatch", "candidate-release-mismatch", "candidate-source-revision-mismatch", "check-candidate-mismatch", "check-invalid", "check-observer-not-independent", "record-expired", "record-invalid", "skipped-tests-recorded", "transition-chain-broken", "transition-invalid", "transition-replayed", "transition-state-mismatch", "verification-not-accepted"];
|
|
56
|
+
/** Stable machine-readable cause class of one admission denial. */
|
|
57
|
+
type TestRunDenialCode = (typeof TEST_RUN_DENIAL_CODES)[number];
|
|
58
|
+
/**
|
|
59
|
+
* Structured allow-or-deny admission decision for one exact candidate.
|
|
60
|
+
*
|
|
61
|
+
* The decision carries the machine-readable cause classes next to the exact
|
|
62
|
+
* diagnostics, so a deployment gate in any language can act on one bounded
|
|
63
|
+
* vocabulary while operators keep the full explanation. Serialization
|
|
64
|
+
* follows the shared `test-run-admission` schema; decisions are outputs, so
|
|
65
|
+
* a gate must never trust a decision it did not compute itself.
|
|
66
|
+
*/
|
|
67
|
+
interface TestRunAdmissionDecision {
|
|
68
|
+
/** Whether the record admits the candidate; true only with no diagnostics. */
|
|
69
|
+
readonly allowed: boolean;
|
|
70
|
+
/** Deduplicated denial causes sorted lexicographically; empty when allowed. */
|
|
71
|
+
readonly denialCodes: readonly TestRunDenialCode[];
|
|
72
|
+
/** Complete diagnostics in the stable path, code, message order. */
|
|
73
|
+
readonly diagnostics: readonly MarquetteDiagnostic[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Returns the stable denial code one admission diagnostic maps to.
|
|
77
|
+
*
|
|
78
|
+
* The mapping is total and identical in every host family: admission codes
|
|
79
|
+
* `VIZE_MARQUETTE_141` through `VIZE_MARQUETTE_148` map to their exact
|
|
80
|
+
* cause, `VIZE_MARQUETTE_144` distinguishes the mismatched candidate binding
|
|
81
|
+
* by its diagnostic path, `VIZE_MARQUETTE_149` through `VIZE_MARQUETTE_151`
|
|
82
|
+
* map to their tests-check cause, `VIZE_MARQUETTE_156` through
|
|
83
|
+
* `VIZE_MARQUETTE_159` map to their transition cause, every other
|
|
84
|
+
* diagnostic at a `check.` path is a `check-invalid` tests-check validation
|
|
85
|
+
* failure, every other diagnostic at a `transition.` path is a
|
|
86
|
+
* `transition-invalid` transition validation failure, and every remaining
|
|
87
|
+
* diagnostic is a `record-invalid` record-validation failure.
|
|
88
|
+
*/
|
|
89
|
+
declare function testRunDenialCode(diagnostic: MarquetteDiagnostic): TestRunDenialCode;
|
|
90
|
+
/**
|
|
91
|
+
* Decides one candidate and returns the structured admission decision.
|
|
92
|
+
*
|
|
93
|
+
* The decision wraps {@link admitTestRun}: `diagnostics` is exactly its
|
|
94
|
+
* result, `denialCodes` maps every diagnostic through
|
|
95
|
+
* {@link testRunDenialCode} and then deduplicates and sorts the codes
|
|
96
|
+
* lexicographically, and `allowed` is true only when both are empty. Codes,
|
|
97
|
+
* ordering, and diagnostics are identical to the native implementation, as
|
|
98
|
+
* pinned by the shared decision fixtures. Inputs carry the same obligations
|
|
99
|
+
* as {@link admitTestRun}.
|
|
100
|
+
*/
|
|
101
|
+
declare function decideTestRunAdmission(evidence: TestRunEvidence, candidate: TestRunCandidate, admissionId: string, now: string): Promise<TestRunAdmissionDecision>;
|
|
46
102
|
//#endregion
|
|
47
|
-
export { TEST_RUN_ADMISSION_PREFIX, TestRunCandidate, admitTestRun, parseTestRunAdmissionId };
|
|
103
|
+
export { TEST_RUN_ADMISSION_PREFIX, TEST_RUN_DENIAL_CODES, TestRunAdmissionDecision, TestRunCandidate, TestRunDenialCode, admitTestRun, decideTestRunAdmission, parseTestRunAdmissionId, testRunDenialCode };
|
|
48
104
|
//# sourceMappingURL=test-run-admission.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-admission.d.mts","names":[],"sources":["../src/test-run-admission.ts"],"mappings":";;;;;;;AAcA;;;;UAAiB,gBAAA;EAIN;EAAA,SAFA,WAAA;EAMA;EAAA,SAJA,WAAA;EAQA;EAAA,SANA,mBAAA;EAMmB;EAAA,SAJnB,cAAA;EA0BuB;EAAA,SAxBvB,OAAA;EAyBC;EAAA,SAvBD,mBAAA;AAAA
|
|
1
|
+
{"version":3,"file":"test-run-admission.d.mts","names":[],"sources":["../src/test-run-admission.ts"],"mappings":";;;;;;;AAcA;;;;UAAiB,gBAAA;EAIN;EAAA,SAFA,WAAA;EAMA;EAAA,SAJA,WAAA;EAQA;EAAA,SANA,mBAAA;EAMmB;EAAA,SAJnB,cAAA;EA0BuB;EAAA,SAxBvB,OAAA;EAyBC;EAAA,SAvBD,mBAAA;AAAA;;;;;;;;;;;;;;AA+HX;;;;;AAwBA;iBAjIsB,YAAA,CACpB,QAAA,EAAU,eAAA,EACV,SAAA,EAAW,gBAAA,EACX,WAAA,UACA,GAAA,WACC,OAAA,CAAQ,mBAAA;;;;AAuIX;;;;;;cAnCa,qBAAA;;KAwBD,iBAAA,WAA4B,qBAAA;;;AA2CxC;;;;;;;UAhCiB,wBAAA;EAgCoE;EAAA,SA9B1E,OAAA;EAqFiC;EAAA,SAnFjC,WAAA,WAAsB,iBAAA;EAoFrB;EAAA,SAlFD,WAAA,WAAsB,mBAAA;AAAA;;;;;;;;;;;;;;;iBA0BjB,iBAAA,CAAkB,UAAA,EAAY,mBAAA,GAAsB,iBAAA;;;;;;;;;;;;iBAuD9C,sBAAA,CACpB,QAAA,EAAU,eAAA,EACV,SAAA,EAAW,gBAAA,EACX,WAAA,UACA,GAAA,WACC,OAAA,CAAQ,wBAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as isStrictTimestamp, t as validateTestRunEvidence, u as error } from "./test-run-validate-C_KR031E.mjs";
|
|
2
2
|
import { TEST_RUN_ADMISSION_PREFIX, parseTestRunAdmissionId, testRunFingerprint } from "./test-run-canonical.mjs";
|
|
3
3
|
//#region src/test-run-admission.ts
|
|
4
4
|
/**
|
|
@@ -66,7 +66,105 @@ async function admitTestRun(evidence, candidate, admissionId, now) {
|
|
|
66
66
|
diagnostics.sort((left, right) => left.path !== right.path ? left.path < right.path ? -1 : 1 : left.code !== right.code ? left.code < right.code ? -1 : 1 : left.message < right.message ? -1 : left.message > right.message ? 1 : 0);
|
|
67
67
|
return diagnostics;
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Every denial code, in the stable lexicographic decision order.
|
|
71
|
+
*
|
|
72
|
+
* The vocabulary is shared by every backend family: a JavaScript, Rust, Go,
|
|
73
|
+
* or JVM host must derive the same codes from the same diagnostics, as
|
|
74
|
+
* pinned by the shared `tests/fixtures/test-run-evidence` decision fixtures.
|
|
75
|
+
* Codes are append-only: they are never renamed, renumbered, reused, or
|
|
76
|
+
* removed, and a new rejection cause always ships with a new code.
|
|
77
|
+
*/
|
|
78
|
+
const TEST_RUN_DENIAL_CODES = [
|
|
79
|
+
"admission-id-malformed",
|
|
80
|
+
"admission-id-mismatch",
|
|
81
|
+
"admission-time-malformed",
|
|
82
|
+
"candidate-application-mismatch",
|
|
83
|
+
"candidate-artifact-fingerprint-mismatch",
|
|
84
|
+
"candidate-contract-fingerprint-mismatch",
|
|
85
|
+
"candidate-environment-mismatch",
|
|
86
|
+
"candidate-release-mismatch",
|
|
87
|
+
"candidate-source-revision-mismatch",
|
|
88
|
+
"check-candidate-mismatch",
|
|
89
|
+
"check-invalid",
|
|
90
|
+
"check-observer-not-independent",
|
|
91
|
+
"record-expired",
|
|
92
|
+
"record-invalid",
|
|
93
|
+
"skipped-tests-recorded",
|
|
94
|
+
"transition-chain-broken",
|
|
95
|
+
"transition-invalid",
|
|
96
|
+
"transition-replayed",
|
|
97
|
+
"transition-state-mismatch",
|
|
98
|
+
"verification-not-accepted"
|
|
99
|
+
];
|
|
100
|
+
const CANDIDATE_MISMATCH_CODES = new Map([
|
|
101
|
+
["application", "candidate-application-mismatch"],
|
|
102
|
+
["artifact.fingerprint", "candidate-artifact-fingerprint-mismatch"],
|
|
103
|
+
["contractFingerprint", "candidate-contract-fingerprint-mismatch"],
|
|
104
|
+
["environment", "candidate-environment-mismatch"],
|
|
105
|
+
["release", "candidate-release-mismatch"],
|
|
106
|
+
["sourceRevision", "candidate-source-revision-mismatch"]
|
|
107
|
+
]);
|
|
108
|
+
/**
|
|
109
|
+
* Returns the stable denial code one admission diagnostic maps to.
|
|
110
|
+
*
|
|
111
|
+
* The mapping is total and identical in every host family: admission codes
|
|
112
|
+
* `VIZE_MARQUETTE_141` through `VIZE_MARQUETTE_148` map to their exact
|
|
113
|
+
* cause, `VIZE_MARQUETTE_144` distinguishes the mismatched candidate binding
|
|
114
|
+
* by its diagnostic path, `VIZE_MARQUETTE_149` through `VIZE_MARQUETTE_151`
|
|
115
|
+
* map to their tests-check cause, `VIZE_MARQUETTE_156` through
|
|
116
|
+
* `VIZE_MARQUETTE_159` map to their transition cause, every other
|
|
117
|
+
* diagnostic at a `check.` path is a `check-invalid` tests-check validation
|
|
118
|
+
* failure, every other diagnostic at a `transition.` path is a
|
|
119
|
+
* `transition-invalid` transition validation failure, and every remaining
|
|
120
|
+
* diagnostic is a `record-invalid` record-validation failure.
|
|
121
|
+
*/
|
|
122
|
+
function testRunDenialCode(diagnostic) {
|
|
123
|
+
switch (diagnostic.code) {
|
|
124
|
+
case "VIZE_MARQUETTE_141": return "admission-id-malformed";
|
|
125
|
+
case "VIZE_MARQUETTE_142": return "admission-id-mismatch";
|
|
126
|
+
case "VIZE_MARQUETTE_144": {
|
|
127
|
+
const mismatch = CANDIDATE_MISMATCH_CODES.get(diagnostic.path);
|
|
128
|
+
if (mismatch !== void 0) return mismatch;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
case "VIZE_MARQUETTE_145": return "record-expired";
|
|
132
|
+
case "VIZE_MARQUETTE_146": return "verification-not-accepted";
|
|
133
|
+
case "VIZE_MARQUETTE_147": return "skipped-tests-recorded";
|
|
134
|
+
case "VIZE_MARQUETTE_148": return "admission-time-malformed";
|
|
135
|
+
case "VIZE_MARQUETTE_149": return "check-candidate-mismatch";
|
|
136
|
+
case "VIZE_MARQUETTE_150": return "check-invalid";
|
|
137
|
+
case "VIZE_MARQUETTE_151": return "check-observer-not-independent";
|
|
138
|
+
case "VIZE_MARQUETTE_156": return "transition-state-mismatch";
|
|
139
|
+
case "VIZE_MARQUETTE_157": return "transition-chain-broken";
|
|
140
|
+
case "VIZE_MARQUETTE_158": return "transition-replayed";
|
|
141
|
+
case "VIZE_MARQUETTE_159": return "transition-state-mismatch";
|
|
142
|
+
default: break;
|
|
143
|
+
}
|
|
144
|
+
if (diagnostic.path.startsWith("check.")) return "check-invalid";
|
|
145
|
+
return diagnostic.path.startsWith("transition.") ? "transition-invalid" : "record-invalid";
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Decides one candidate and returns the structured admission decision.
|
|
149
|
+
*
|
|
150
|
+
* The decision wraps {@link admitTestRun}: `diagnostics` is exactly its
|
|
151
|
+
* result, `denialCodes` maps every diagnostic through
|
|
152
|
+
* {@link testRunDenialCode} and then deduplicates and sorts the codes
|
|
153
|
+
* lexicographically, and `allowed` is true only when both are empty. Codes,
|
|
154
|
+
* ordering, and diagnostics are identical to the native implementation, as
|
|
155
|
+
* pinned by the shared decision fixtures. Inputs carry the same obligations
|
|
156
|
+
* as {@link admitTestRun}.
|
|
157
|
+
*/
|
|
158
|
+
async function decideTestRunAdmission(evidence, candidate, admissionId, now) {
|
|
159
|
+
const diagnostics = await admitTestRun(evidence, candidate, admissionId, now);
|
|
160
|
+
const denialCodes = [...new Set(diagnostics.map(testRunDenialCode))].sort();
|
|
161
|
+
return {
|
|
162
|
+
allowed: diagnostics.length === 0,
|
|
163
|
+
denialCodes,
|
|
164
|
+
diagnostics
|
|
165
|
+
};
|
|
166
|
+
}
|
|
69
167
|
//#endregion
|
|
70
|
-
export { TEST_RUN_ADMISSION_PREFIX, admitTestRun, parseTestRunAdmissionId };
|
|
168
|
+
export { TEST_RUN_ADMISSION_PREFIX, TEST_RUN_DENIAL_CODES, admitTestRun, decideTestRunAdmission, parseTestRunAdmissionId, testRunDenialCode };
|
|
71
169
|
|
|
72
170
|
//# sourceMappingURL=test-run-admission.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-admission.mjs","names":[],"sources":["../src/test-run-admission.ts"],"sourcesContent":["import type { TestRunEvidence } from \"./test-run-model.js\";\nimport type { MarquetteDiagnostic } from \"./validate.js\";\nimport { parseTestRunAdmissionId, testRunFingerprint } from \"./test-run-canonical.js\";\nimport { validateTestRunEvidence } from \"./test-run-validate.js\";\nimport { error, isStrictTimestamp } from \"./test-run-validate-rules.js\";\n\nexport { TEST_RUN_ADMISSION_PREFIX, parseTestRunAdmissionId } from \"./test-run-canonical.js\";\n\n/**\n * Exact release candidate a deployment gate wants evidence for.\n *\n * Every field must match the record exactly; admission never falls back to a\n * newer, older, or partially matching record.\n */\nexport interface TestRunCandidate {\n /** Application the gate is deploying. */\n readonly application: string;\n /** Deployment environment the gate is promoting into. */\n readonly environment: string;\n /** Lowercase SHA-256 fingerprint of the application contract. */\n readonly contractFingerprint: string;\n /** Exact source revision of the candidate. */\n readonly sourceRevision: string;\n /** Release the candidate belongs to. */\n readonly release: string;\n /** Lowercase SHA-256 fingerprint of the exact artifact being promoted. */\n readonly artifactFingerprint: string;\n}\n\n/**\n * Decides whether one record admits one exact candidate at one instant.\n *\n * An empty result admits the deployment. Any diagnostic rejects it: the\n * record must validate cleanly, its canonical fingerprint must be the one\n * named by `admissionId`, every candidate binding must match exactly, the\n * record must not be expired at `now`, the independent verification must\n * have accepted the run, and no skipped test may remain unaccounted for.\n *\n * Codes, paths, messages, and ordering are identical to the native\n * implementation, so both host families reach the same decision for the\n * same record.\n *\n * `now` must be a millisecond-precision UTC timestamp such as\n * `2026-01-01T00:00:00.000Z`; the fixed-width format keeps the expiry\n * comparison exact. Callers own retrieval: fetch the canonical bytes from an\n * immutable store within their own deadline, refuse oversized content, and\n * hand the parsed record here.\n */\nexport async function admitTestRun(\n evidence: TestRunEvidence,\n candidate: TestRunCandidate,\n admissionId: string,\n now: string,\n): Promise<MarquetteDiagnostic[]> {\n const diagnostics = validateTestRunEvidence(evidence);\n\n const nowIsExact = isStrictTimestamp(now);\n if (!nowIsExact) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_148\",\n \"admission.now\",\n \"admission time must be a millisecond-precision UTC instant\",\n ),\n );\n }\n\n const expected = parseTestRunAdmissionId(admissionId);\n if (expected === undefined) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_141\",\n \"admission.id\",\n \"admission id must be test-run: followed by 64 lowercase hexadecimal characters\",\n ),\n );\n } else if ((await testRunFingerprint(evidence)) !== expected) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_142\",\n \"admission.id\",\n \"admission id does not name this record's canonical fingerprint\",\n ),\n );\n }\n\n const bindings = [\n [evidence.application, candidate.application, \"application\"],\n [evidence.environment, candidate.environment, \"environment\"],\n [evidence.contractFingerprint, candidate.contractFingerprint, \"contractFingerprint\"],\n [evidence.sourceRevision, candidate.sourceRevision, \"sourceRevision\"],\n [evidence.release, candidate.release, \"release\"],\n [evidence.artifact.fingerprint, candidate.artifactFingerprint, \"artifact.fingerprint\"],\n ] as const;\n for (const [recorded, wanted, field] of bindings) {\n if (recorded !== wanted) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_144\", field, `record does not bind the candidate ${field}`),\n );\n }\n }\n\n if (nowIsExact && evidence.validUntil <= now) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_145\", \"validUntil\", \"record is expired at the admission time\"),\n );\n }\n if (evidence.verification.outcome !== \"accepted\") {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_146\",\n \"verification.outcome\",\n \"only an accepted verification can admit a deployment\",\n ),\n );\n }\n if (evidence.verification.skipped > 0) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_147\",\n \"verification.skipped\",\n \"skipped tests are not approved for deployment admission\",\n ),\n );\n }\n\n diagnostics.sort((left, right) =>\n left.path !== right.path\n ? left.path < right.path\n ? -1\n : 1\n : left.code !== right.code\n ? left.code < right.code\n ? -1\n : 1\n : left.message < right.message\n ? -1\n : left.message > right.message\n ? 1\n : 0,\n );\n return diagnostics;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgDA,eAAsB,aACpB,UACA,WACA,aACA,KACgC;CAChC,MAAM,cAAc,wBAAwB,SAAS;CAErD,MAAM,aAAa,kBAAkB,IAAI;CACzC,IAAI,CAAC,YACH,YAAY,KACV,MACE,sBACA,iBACA,6DACD,CACF;CAGH,MAAM,WAAW,wBAAwB,YAAY;CACrD,IAAI,aAAa,KAAA,GACf,YAAY,KACV,MACE,sBACA,gBACA,iFACD,CACF;MACI,IAAK,MAAM,mBAAmB,SAAS,KAAM,UAClD,YAAY,KACV,MACE,sBACA,gBACA,iEACD,CACF;CAGH,MAAM,WAAW;EACf;GAAC,SAAS;GAAa,UAAU;GAAa;GAAc;EAC5D;GAAC,SAAS;GAAa,UAAU;GAAa;GAAc;EAC5D;GAAC,SAAS;GAAqB,UAAU;GAAqB;GAAsB;EACpF;GAAC,SAAS;GAAgB,UAAU;GAAgB;GAAiB;EACrE;GAAC,SAAS;GAAS,UAAU;GAAS;GAAU;EAChD;GAAC,SAAS,SAAS;GAAa,UAAU;GAAqB;GAAuB;EACvF;CACD,KAAK,MAAM,CAAC,UAAU,QAAQ,UAAU,UACtC,IAAI,aAAa,QACf,YAAY,KACV,MAAM,sBAAsB,OAAO,sCAAsC,QAAQ,CAClF;CAIL,IAAI,cAAc,SAAS,cAAc,KACvC,YAAY,KACV,MAAM,sBAAsB,cAAc,0CAA0C,CACrF;CAEH,IAAI,SAAS,aAAa,YAAY,YACpC,YAAY,KACV,MACE,sBACA,wBACA,uDACD,CACF;CAEH,IAAI,SAAS,aAAa,UAAU,GAClC,YAAY,KACV,MACE,sBACA,wBACA,0DACD,CACF;CAGH,YAAY,MAAM,MAAM,UACtB,KAAK,SAAS,MAAM,OAChB,KAAK,OAAO,MAAM,OAChB,KACA,IACF,KAAK,SAAS,MAAM,OAClB,KAAK,OAAO,MAAM,OAChB,KACA,IACF,KAAK,UAAU,MAAM,UACnB,KACA,KAAK,UAAU,MAAM,UACnB,IACA,EACX;CACD,OAAO"}
|
|
1
|
+
{"version":3,"file":"test-run-admission.mjs","names":[],"sources":["../src/test-run-admission.ts"],"sourcesContent":["import type { TestRunEvidence } from \"./test-run-model.js\";\nimport type { MarquetteDiagnostic } from \"./validate.js\";\nimport { parseTestRunAdmissionId, testRunFingerprint } from \"./test-run-canonical.js\";\nimport { validateTestRunEvidence } from \"./test-run-validate.js\";\nimport { error, isStrictTimestamp } from \"./test-run-validate-rules.js\";\n\nexport { TEST_RUN_ADMISSION_PREFIX, parseTestRunAdmissionId } from \"./test-run-canonical.js\";\n\n/**\n * Exact release candidate a deployment gate wants evidence for.\n *\n * Every field must match the record exactly; admission never falls back to a\n * newer, older, or partially matching record.\n */\nexport interface TestRunCandidate {\n /** Application the gate is deploying. */\n readonly application: string;\n /** Deployment environment the gate is promoting into. */\n readonly environment: string;\n /** Lowercase SHA-256 fingerprint of the application contract. */\n readonly contractFingerprint: string;\n /** Exact source revision of the candidate. */\n readonly sourceRevision: string;\n /** Release the candidate belongs to. */\n readonly release: string;\n /** Lowercase SHA-256 fingerprint of the exact artifact being promoted. */\n readonly artifactFingerprint: string;\n}\n\n/**\n * Decides whether one record admits one exact candidate at one instant.\n *\n * An empty result admits the deployment. Any diagnostic rejects it: the\n * record must validate cleanly, its canonical fingerprint must be the one\n * named by `admissionId`, every candidate binding must match exactly, the\n * record must not be expired at `now`, the independent verification must\n * have accepted the run, and no skipped test may remain unaccounted for.\n *\n * Codes, paths, messages, and ordering are identical to the native\n * implementation, so both host families reach the same decision for the\n * same record.\n *\n * `now` must be a millisecond-precision UTC timestamp such as\n * `2026-01-01T00:00:00.000Z`; the fixed-width format keeps the expiry\n * comparison exact. Callers own retrieval: fetch the canonical bytes from an\n * immutable store within their own deadline, refuse oversized content, and\n * hand the parsed record here.\n */\nexport async function admitTestRun(\n evidence: TestRunEvidence,\n candidate: TestRunCandidate,\n admissionId: string,\n now: string,\n): Promise<MarquetteDiagnostic[]> {\n const diagnostics = validateTestRunEvidence(evidence);\n\n const nowIsExact = isStrictTimestamp(now);\n if (!nowIsExact) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_148\",\n \"admission.now\",\n \"admission time must be a millisecond-precision UTC instant\",\n ),\n );\n }\n\n const expected = parseTestRunAdmissionId(admissionId);\n if (expected === undefined) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_141\",\n \"admission.id\",\n \"admission id must be test-run: followed by 64 lowercase hexadecimal characters\",\n ),\n );\n } else if ((await testRunFingerprint(evidence)) !== expected) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_142\",\n \"admission.id\",\n \"admission id does not name this record's canonical fingerprint\",\n ),\n );\n }\n\n const bindings = [\n [evidence.application, candidate.application, \"application\"],\n [evidence.environment, candidate.environment, \"environment\"],\n [evidence.contractFingerprint, candidate.contractFingerprint, \"contractFingerprint\"],\n [evidence.sourceRevision, candidate.sourceRevision, \"sourceRevision\"],\n [evidence.release, candidate.release, \"release\"],\n [evidence.artifact.fingerprint, candidate.artifactFingerprint, \"artifact.fingerprint\"],\n ] as const;\n for (const [recorded, wanted, field] of bindings) {\n if (recorded !== wanted) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_144\", field, `record does not bind the candidate ${field}`),\n );\n }\n }\n\n if (nowIsExact && evidence.validUntil <= now) {\n diagnostics.push(\n error(\"VIZE_MARQUETTE_145\", \"validUntil\", \"record is expired at the admission time\"),\n );\n }\n if (evidence.verification.outcome !== \"accepted\") {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_146\",\n \"verification.outcome\",\n \"only an accepted verification can admit a deployment\",\n ),\n );\n }\n if (evidence.verification.skipped > 0) {\n diagnostics.push(\n error(\n \"VIZE_MARQUETTE_147\",\n \"verification.skipped\",\n \"skipped tests are not approved for deployment admission\",\n ),\n );\n }\n\n diagnostics.sort((left, right) =>\n left.path !== right.path\n ? left.path < right.path\n ? -1\n : 1\n : left.code !== right.code\n ? left.code < right.code\n ? -1\n : 1\n : left.message < right.message\n ? -1\n : left.message > right.message\n ? 1\n : 0,\n );\n return diagnostics;\n}\n\n/**\n * Every denial code, in the stable lexicographic decision order.\n *\n * The vocabulary is shared by every backend family: a JavaScript, Rust, Go,\n * or JVM host must derive the same codes from the same diagnostics, as\n * pinned by the shared `tests/fixtures/test-run-evidence` decision fixtures.\n * Codes are append-only: they are never renamed, renumbered, reused, or\n * removed, and a new rejection cause always ships with a new code.\n */\nexport const TEST_RUN_DENIAL_CODES = [\n \"admission-id-malformed\",\n \"admission-id-mismatch\",\n \"admission-time-malformed\",\n \"candidate-application-mismatch\",\n \"candidate-artifact-fingerprint-mismatch\",\n \"candidate-contract-fingerprint-mismatch\",\n \"candidate-environment-mismatch\",\n \"candidate-release-mismatch\",\n \"candidate-source-revision-mismatch\",\n \"check-candidate-mismatch\",\n \"check-invalid\",\n \"check-observer-not-independent\",\n \"record-expired\",\n \"record-invalid\",\n \"skipped-tests-recorded\",\n \"transition-chain-broken\",\n \"transition-invalid\",\n \"transition-replayed\",\n \"transition-state-mismatch\",\n \"verification-not-accepted\",\n] as const;\n\n/** Stable machine-readable cause class of one admission denial. */\nexport type TestRunDenialCode = (typeof TEST_RUN_DENIAL_CODES)[number];\n\n/**\n * Structured allow-or-deny admission decision for one exact candidate.\n *\n * The decision carries the machine-readable cause classes next to the exact\n * diagnostics, so a deployment gate in any language can act on one bounded\n * vocabulary while operators keep the full explanation. Serialization\n * follows the shared `test-run-admission` schema; decisions are outputs, so\n * a gate must never trust a decision it did not compute itself.\n */\nexport interface TestRunAdmissionDecision {\n /** Whether the record admits the candidate; true only with no diagnostics. */\n readonly allowed: boolean;\n /** Deduplicated denial causes sorted lexicographically; empty when allowed. */\n readonly denialCodes: readonly TestRunDenialCode[];\n /** Complete diagnostics in the stable path, code, message order. */\n readonly diagnostics: readonly MarquetteDiagnostic[];\n}\n\nconst CANDIDATE_MISMATCH_CODES: ReadonlyMap<string, TestRunDenialCode> = new Map([\n [\"application\", \"candidate-application-mismatch\"],\n [\"artifact.fingerprint\", \"candidate-artifact-fingerprint-mismatch\"],\n [\"contractFingerprint\", \"candidate-contract-fingerprint-mismatch\"],\n [\"environment\", \"candidate-environment-mismatch\"],\n [\"release\", \"candidate-release-mismatch\"],\n [\"sourceRevision\", \"candidate-source-revision-mismatch\"],\n]);\n\n/**\n * Returns the stable denial code one admission diagnostic maps to.\n *\n * The mapping is total and identical in every host family: admission codes\n * `VIZE_MARQUETTE_141` through `VIZE_MARQUETTE_148` map to their exact\n * cause, `VIZE_MARQUETTE_144` distinguishes the mismatched candidate binding\n * by its diagnostic path, `VIZE_MARQUETTE_149` through `VIZE_MARQUETTE_151`\n * map to their tests-check cause, `VIZE_MARQUETTE_156` through\n * `VIZE_MARQUETTE_159` map to their transition cause, every other\n * diagnostic at a `check.` path is a `check-invalid` tests-check validation\n * failure, every other diagnostic at a `transition.` path is a\n * `transition-invalid` transition validation failure, and every remaining\n * diagnostic is a `record-invalid` record-validation failure.\n */\nexport function testRunDenialCode(diagnostic: MarquetteDiagnostic): TestRunDenialCode {\n switch (diagnostic.code) {\n case \"VIZE_MARQUETTE_141\":\n return \"admission-id-malformed\";\n case \"VIZE_MARQUETTE_142\":\n return \"admission-id-mismatch\";\n case \"VIZE_MARQUETTE_144\": {\n const mismatch = CANDIDATE_MISMATCH_CODES.get(diagnostic.path);\n if (mismatch !== undefined) {\n return mismatch;\n }\n break;\n }\n case \"VIZE_MARQUETTE_145\":\n return \"record-expired\";\n case \"VIZE_MARQUETTE_146\":\n return \"verification-not-accepted\";\n case \"VIZE_MARQUETTE_147\":\n return \"skipped-tests-recorded\";\n case \"VIZE_MARQUETTE_148\":\n return \"admission-time-malformed\";\n case \"VIZE_MARQUETTE_149\":\n return \"check-candidate-mismatch\";\n case \"VIZE_MARQUETTE_150\":\n return \"check-invalid\";\n case \"VIZE_MARQUETTE_151\":\n return \"check-observer-not-independent\";\n case \"VIZE_MARQUETTE_156\":\n return \"transition-state-mismatch\";\n case \"VIZE_MARQUETTE_157\":\n return \"transition-chain-broken\";\n case \"VIZE_MARQUETTE_158\":\n return \"transition-replayed\";\n case \"VIZE_MARQUETTE_159\":\n return \"transition-state-mismatch\";\n default:\n break;\n }\n if (diagnostic.path.startsWith(\"check.\")) {\n return \"check-invalid\";\n }\n return diagnostic.path.startsWith(\"transition.\") ? \"transition-invalid\" : \"record-invalid\";\n}\n\n/**\n * Decides one candidate and returns the structured admission decision.\n *\n * The decision wraps {@link admitTestRun}: `diagnostics` is exactly its\n * result, `denialCodes` maps every diagnostic through\n * {@link testRunDenialCode} and then deduplicates and sorts the codes\n * lexicographically, and `allowed` is true only when both are empty. Codes,\n * ordering, and diagnostics are identical to the native implementation, as\n * pinned by the shared decision fixtures. Inputs carry the same obligations\n * as {@link admitTestRun}.\n */\nexport async function decideTestRunAdmission(\n evidence: TestRunEvidence,\n candidate: TestRunCandidate,\n admissionId: string,\n now: string,\n): Promise<TestRunAdmissionDecision> {\n const diagnostics = await admitTestRun(evidence, candidate, admissionId, now);\n const denialCodes = [...new Set(diagnostics.map(testRunDenialCode))].sort();\n return { allowed: diagnostics.length === 0, denialCodes, diagnostics };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://vizejs.dev/schemas/marquette/test-run-admission.schema.json",
|
|
4
|
+
"title": "Vize Test Run Admission Decision",
|
|
5
|
+
"description": "Language-neutral allow-or-deny decision that one test-run evidence record produces for one exact release candidate at one instant. Every backend family (JavaScript, Rust, Go, JVM) must produce byte-equivalent decisions for the same evidence, candidate, admission id, and admission time; the shared tests/fixtures/test-run-evidence admission-decision fixtures are the conformance source of truth for new host implementations.",
|
|
6
|
+
"$ref": "#/$defs/decision",
|
|
7
|
+
"$defs": {
|
|
8
|
+
"denialCode": {
|
|
9
|
+
"description": "Stable machine-readable cause class of one admission denial. The vocabulary is append-only: codes are never renamed, renumbered, reused, or removed, and every new distinguishable rejection cause ships with a new code added to this enum in lexicographic position. Hosts must map every diagnostic to exactly one code: VIZE_MARQUETTE_141 to admission-id-malformed; VIZE_MARQUETTE_142 to admission-id-mismatch; VIZE_MARQUETTE_144 to the candidate-*-mismatch code named by its diagnostic path (application, artifact.fingerprint, contractFingerprint, environment, release, sourceRevision); VIZE_MARQUETTE_145 to record-expired; VIZE_MARQUETTE_146 to verification-not-accepted; VIZE_MARQUETTE_147 to skipped-tests-recorded; VIZE_MARQUETTE_148 to admission-time-malformed; VIZE_MARQUETTE_149 to check-candidate-mismatch; VIZE_MARQUETTE_150 to check-invalid; VIZE_MARQUETTE_151 to check-observer-not-independent; VIZE_MARQUETTE_156 to transition-state-mismatch; VIZE_MARQUETTE_157 to transition-chain-broken; VIZE_MARQUETTE_158 to transition-replayed; VIZE_MARQUETTE_159 to transition-state-mismatch; every other diagnostic whose path starts with check. to check-invalid; every other diagnostic whose path starts with transition. to transition-invalid; and every remaining diagnostic, including every record-validation code, to record-invalid.",
|
|
10
|
+
"enum": [
|
|
11
|
+
"admission-id-malformed",
|
|
12
|
+
"admission-id-mismatch",
|
|
13
|
+
"admission-time-malformed",
|
|
14
|
+
"candidate-application-mismatch",
|
|
15
|
+
"candidate-artifact-fingerprint-mismatch",
|
|
16
|
+
"candidate-contract-fingerprint-mismatch",
|
|
17
|
+
"candidate-environment-mismatch",
|
|
18
|
+
"candidate-release-mismatch",
|
|
19
|
+
"candidate-source-revision-mismatch",
|
|
20
|
+
"check-candidate-mismatch",
|
|
21
|
+
"check-invalid",
|
|
22
|
+
"check-observer-not-independent",
|
|
23
|
+
"record-expired",
|
|
24
|
+
"record-invalid",
|
|
25
|
+
"skipped-tests-recorded",
|
|
26
|
+
"transition-chain-broken",
|
|
27
|
+
"transition-invalid",
|
|
28
|
+
"transition-replayed",
|
|
29
|
+
"transition-state-mismatch",
|
|
30
|
+
"verification-not-accepted"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"diagnostic": {
|
|
34
|
+
"description": "One stable, source-addressable diagnostic explaining a denial cause. Codes, severities, paths, messages, and ordering are identical across host families.",
|
|
35
|
+
"type": "object",
|
|
36
|
+
"additionalProperties": false,
|
|
37
|
+
"required": ["code", "severity", "path", "message"],
|
|
38
|
+
"properties": {
|
|
39
|
+
"code": {
|
|
40
|
+
"description": "Stable machine-readable diagnostic code.",
|
|
41
|
+
"type": "string",
|
|
42
|
+
"pattern": "^VIZE_MARQUETTE_[0-9]{3}$"
|
|
43
|
+
},
|
|
44
|
+
"severity": {
|
|
45
|
+
"description": "Diagnostic severity; any diagnostic denies admission.",
|
|
46
|
+
"enum": ["error", "warning"]
|
|
47
|
+
},
|
|
48
|
+
"path": {
|
|
49
|
+
"description": "JSON-style path into the record or the admission input.",
|
|
50
|
+
"type": "string",
|
|
51
|
+
"minLength": 1
|
|
52
|
+
},
|
|
53
|
+
"message": {
|
|
54
|
+
"description": "Human-readable explanation and next action.",
|
|
55
|
+
"type": "string",
|
|
56
|
+
"minLength": 1
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"decision": {
|
|
61
|
+
"description": "Structured admission decision. allowed is true only when denialCodes and diagnostics are both empty. denialCodes maps every diagnostic to its denial code, deduplicated and sorted lexicographically by Unicode code point. diagnostics keeps the stable path, code, message order shared by every implementation.",
|
|
62
|
+
"type": "object",
|
|
63
|
+
"additionalProperties": false,
|
|
64
|
+
"required": ["allowed", "denialCodes", "diagnostics"],
|
|
65
|
+
"properties": {
|
|
66
|
+
"allowed": {
|
|
67
|
+
"description": "Whether the record admits the candidate.",
|
|
68
|
+
"type": "boolean"
|
|
69
|
+
},
|
|
70
|
+
"denialCodes": {
|
|
71
|
+
"description": "Deduplicated denial causes in lexicographic order; empty when allowed.",
|
|
72
|
+
"type": "array",
|
|
73
|
+
"uniqueItems": true,
|
|
74
|
+
"items": { "$ref": "#/$defs/denialCode" }
|
|
75
|
+
},
|
|
76
|
+
"diagnostics": {
|
|
77
|
+
"description": "Complete denial diagnostics; empty when allowed.",
|
|
78
|
+
"type": "array",
|
|
79
|
+
"items": { "$ref": "#/$defs/diagnostic" }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { i as TestRunEvidence } from "./test-run-model-DkllmEsY.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/test-run-canonical.d.ts
|
|
4
|
+
/** Prefix of every test-run deployment admission id. */
|
|
5
|
+
declare const TEST_RUN_ADMISSION_PREFIX = "test-run:";
|
|
6
|
+
/**
|
|
7
|
+
* Serializes a test-run evidence record canonically.
|
|
8
|
+
*
|
|
9
|
+
* Property order matches the record schema, targets sort by id, suites sort
|
|
10
|
+
* by id then shard index, and selection identifiers sort lexicographically,
|
|
11
|
+
* so equivalent records produce byte-identical JSON in every language. Call
|
|
12
|
+
* validation before trusting the record; canonicalization does not make an
|
|
13
|
+
* invalid record valid.
|
|
14
|
+
*/
|
|
15
|
+
declare function canonicalTestRunJson(evidence: TestRunEvidence): string;
|
|
16
|
+
/**
|
|
17
|
+
* Returns the lowercase SHA-256 fingerprint of the canonical record.
|
|
18
|
+
*
|
|
19
|
+
* The fingerprint is the exact value admitted as `test-run:<sha256>` by
|
|
20
|
+
* deployment gates. Uses the Web Crypto API available in every supported
|
|
21
|
+
* runtime.
|
|
22
|
+
*/
|
|
23
|
+
declare function testRunFingerprint(evidence: TestRunEvidence): Promise<string>;
|
|
24
|
+
/** Returns the `test-run:<sha256>` admission id for one record. */
|
|
25
|
+
declare function testRunAdmissionId(evidence: TestRunEvidence): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Returns the fingerprint named by a `test-run:<sha256>` admission id.
|
|
28
|
+
*
|
|
29
|
+
* Returns `undefined` unless the prefix, length, and lowercase hexadecimal
|
|
30
|
+
* grammar are all exact.
|
|
31
|
+
*/
|
|
32
|
+
declare function parseTestRunAdmissionId(id: string): string | undefined;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { testRunFingerprint as a, testRunAdmissionId as i, canonicalTestRunJson as n, parseTestRunAdmissionId as r, TEST_RUN_ADMISSION_PREFIX as t };
|
|
35
|
+
//# sourceMappingURL=test-run-canonical-CB-KzZxk.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-run-canonical-CB-KzZxk.d.mts","names":[],"sources":["../src/test-run-canonical.ts"],"mappings":";;;;cAQa,yBAAA;AAAb;;;;;AAwCA;;;;AAxCA,iBAwCgB,oBAAA,CAAqB,QAAA,EAAU,eAAA;AA6D/C;;;;;;;AAAA,iBAAsB,kBAAA,CAAmB,QAAA,EAAU,eAAA,GAAkB,OAAA;;iBAW/C,kBAAA,CAAmB,QAAA,EAAU,eAAA,GAAkB,OAAA;;;;;;;iBAUrD,uBAAA,CAAwB,EAAA"}
|
|
@@ -1,35 +1,2 @@
|
|
|
1
|
-
import { i as
|
|
2
|
-
|
|
3
|
-
//#region src/test-run-canonical.d.ts
|
|
4
|
-
/** Prefix of every test-run deployment admission id. */
|
|
5
|
-
declare const TEST_RUN_ADMISSION_PREFIX = "test-run:";
|
|
6
|
-
/**
|
|
7
|
-
* Serializes a test-run evidence record canonically.
|
|
8
|
-
*
|
|
9
|
-
* Property order matches the record schema, targets sort by id, suites sort
|
|
10
|
-
* by id then shard index, and selection identifiers sort lexicographically,
|
|
11
|
-
* so equivalent records produce byte-identical JSON in every language. Call
|
|
12
|
-
* validation before trusting the record; canonicalization does not make an
|
|
13
|
-
* invalid record valid.
|
|
14
|
-
*/
|
|
15
|
-
declare function canonicalTestRunJson(evidence: TestRunEvidence): string;
|
|
16
|
-
/**
|
|
17
|
-
* Returns the lowercase SHA-256 fingerprint of the canonical record.
|
|
18
|
-
*
|
|
19
|
-
* The fingerprint is the exact value admitted as `test-run:<sha256>` by
|
|
20
|
-
* deployment gates. Uses the Web Crypto API available in every supported
|
|
21
|
-
* runtime.
|
|
22
|
-
*/
|
|
23
|
-
declare function testRunFingerprint(evidence: TestRunEvidence): Promise<string>;
|
|
24
|
-
/** Returns the `test-run:<sha256>` admission id for one record. */
|
|
25
|
-
declare function testRunAdmissionId(evidence: TestRunEvidence): Promise<string>;
|
|
26
|
-
/**
|
|
27
|
-
* Returns the fingerprint named by a `test-run:<sha256>` admission id.
|
|
28
|
-
*
|
|
29
|
-
* Returns `undefined` unless the prefix, length, and lowercase hexadecimal
|
|
30
|
-
* grammar are all exact.
|
|
31
|
-
*/
|
|
32
|
-
declare function parseTestRunAdmissionId(id: string): string | undefined;
|
|
33
|
-
//#endregion
|
|
34
|
-
export { TEST_RUN_ADMISSION_PREFIX, canonicalTestRunJson, parseTestRunAdmissionId, testRunAdmissionId, testRunFingerprint };
|
|
35
|
-
//# sourceMappingURL=test-run-canonical.d.mts.map
|
|
1
|
+
import { a as testRunFingerprint, i as testRunAdmissionId, n as canonicalTestRunJson, r as parseTestRunAdmissionId, t as TEST_RUN_ADMISSION_PREFIX } from "./test-run-canonical-CB-KzZxk.mjs";
|
|
2
|
+
export { TEST_RUN_ADMISSION_PREFIX, canonicalTestRunJson, parseTestRunAdmissionId, testRunAdmissionId, testRunFingerprint };
|