@colophon-claims/verify 0.2.0 → 0.2.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/README.md +18 -8
- package/dist/admission/contracts.d.ts +241 -0
- package/dist/admission/contracts.js +116 -0
- package/dist/admission/index.d.ts +2 -0
- package/dist/admission/index.js +2 -0
- package/dist/admission/prompted-commitment.d.ts +19 -0
- package/dist/admission/prompted-commitment.js +52 -0
- package/dist/admission/prompted-selection.d.ts +24 -0
- package/dist/admission/prompted-selection.js +85 -0
- package/dist/admission/verification.d.ts +1 -0
- package/dist/admission/verification.js +88 -42
- package/dist/assets.d.ts +21 -1
- package/dist/assets.js +60 -3
- package/dist/binding/beacon-binding.d.ts +230 -0
- package/dist/binding/beacon-binding.js +325 -0
- package/dist/binding/report-face.d.ts +45 -0
- package/dist/binding/report-face.js +153 -0
- package/dist/cli.js +74 -12
- package/dist/index.d.ts +14 -3
- package/dist/index.js +16 -3
- package/dist/manifest.d.ts +30 -4
- package/dist/manifest.js +30 -0
- package/dist/materialize.d.ts +7 -0
- package/dist/materialize.js +7 -0
- package/dist/outcome.d.ts +31 -0
- package/dist/outcome.js +45 -0
- package/dist/profile/claim-consistency.d.ts +8 -1
- package/dist/profile/claim-consistency.js +4 -4
- package/dist/profile/claim.d.ts +201 -2
- package/dist/profile/claim.js +178 -47
- package/dist/profile/disclosure.d.ts +273 -0
- package/dist/profile/disclosure.js +240 -0
- package/dist/profile/run-results.d.ts +8 -2
- package/dist/profile/run-results.js +9 -3
- package/dist/profile/task-selection.d.ts +69 -0
- package/dist/profile/task-selection.js +140 -0
- package/dist/reader-instructions.d.ts +38 -1
- package/dist/reader-instructions.js +42 -2
- package/dist/schema.d.ts +4 -3
- package/dist/schema.js +17 -0
- package/dist/signers.d.ts +48 -0
- package/dist/signers.js +77 -0
- package/dist/verify.d.ts +20 -6
- package/dist/verify.js +156 -31
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +14 -14
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The disclosed closure's projection and check (disclosure-specification-record design §6.4, §6.6,
|
|
3
|
+
* §7; issue #2839).
|
|
4
|
+
*
|
|
5
|
+
* This module is **single-sourced, not mirrored**, exactly as `anchor-claims.ts` is: `@colophon-
|
|
6
|
+
* claims/core` already depends on this package, so the producer and the standalone verifier call
|
|
7
|
+
* ONE `deriveDisclosureSpecification`. The claim-consistency byte-compare then compares one
|
|
8
|
+
* function's output over two byte sets rather than two implementations' guesses.
|
|
9
|
+
*
|
|
10
|
+
* The one posture that matters more than any mechanism here (design R4):
|
|
11
|
+
*
|
|
12
|
+
* **The verifier AUTHENTICATES measurements and CARRIES assertions.**
|
|
13
|
+
*
|
|
14
|
+
* A `measured-here` variable must cite records the bundle actually carries, under a role admissible
|
|
15
|
+
* for the citation. A `disclosed-by-publisher` variable is checked for internal well-formedness and
|
|
16
|
+
* for nothing else — no lookup, no fetch, no cross-check against the Matrix or the Report, no status
|
|
17
|
+
* upgrade or downgrade. An assertion that turns out to be false is a false assertion in a valid
|
|
18
|
+
* record; that is the correct outcome and the honest one, and a verifier that refused it would be
|
|
19
|
+
* claiming a power it does not have.
|
|
20
|
+
*
|
|
21
|
+
* What this check deliberately does NOT do (design §8, each one a thing a well-meaning implementer
|
|
22
|
+
* would add):
|
|
23
|
+
*
|
|
24
|
+
* - It does not reconcile against `Report.disclosures.perSubject[].pinning`. Those counts answer
|
|
25
|
+
* "how well did each executed axis pin"; this record answers "which variables were executed at
|
|
26
|
+
* all". Two surfaces restating one fact are two surfaces that can disagree.
|
|
27
|
+
* - It does not infer a status from the bundle. A bundle that plainly executed a judge model does
|
|
28
|
+
* not license marking `judge-model` as `measured-here` if the record says otherwise.
|
|
29
|
+
* - It does not fetch anything, ever. A verifier that reached the network would make its own result
|
|
30
|
+
* depend on when it ran.
|
|
31
|
+
* - It does not rank, score, compare, or aggregate. There is no disclosure-completeness score:
|
|
32
|
+
* counting statuses would create a number publishers optimize against, and a six-of-six record
|
|
33
|
+
* with six vague assertions would outscore a two-of-six record with two proofs.
|
|
34
|
+
*/
|
|
35
|
+
import { createHash } from "node:crypto";
|
|
36
|
+
import { z } from "zod";
|
|
37
|
+
import { DISCLOSURE_VARIABLE_KEYS, DisclosureVariablesSchema, MATRIX_RECORD_KIND, SIX_VARIABLE_DISCLOSURE_SPECIFICATION, parseDisclosureSpecification, } from "@jinn-network/benchmarking-records";
|
|
38
|
+
/** The evidence-catalog role a disclosure-specification record carries, and the ONLY one it may
|
|
39
|
+
* carry: a record bearing this token together with any second role refuses (§7 step 1). */
|
|
40
|
+
export const DISCLOSURE_SPECIFICATION_BUNDLE_ROLE = "disclosure-specification";
|
|
41
|
+
/**
|
|
42
|
+
* §6.4's binding profile: which of this carrier's evidence roles satisfy which of the standard's two
|
|
43
|
+
* portable disclosure roles. Jinn-side mapping, deliberately NOT part of the portable record — the
|
|
44
|
+
* mapping from the standard's roles onto a specific carrier's record roles is the carrier's business.
|
|
45
|
+
*
|
|
46
|
+
* v1 deliberately does not narrow the admissible set per variable. Which record species fixes
|
|
47
|
+
* `judge-model` depends on the judge profile packet P0 is freezing, and encoding a guess here would
|
|
48
|
+
* put this module in P0's lane. Reserved as a v2 tightening once P0's record vocabulary merges.
|
|
49
|
+
*/
|
|
50
|
+
export const DISCLOSURE_ROLE_BINDING = {
|
|
51
|
+
"pinned-configuration": [
|
|
52
|
+
"task",
|
|
53
|
+
"item-bank",
|
|
54
|
+
"source-item",
|
|
55
|
+
"evaluation-spec",
|
|
56
|
+
"judge-instrument",
|
|
57
|
+
"runtime-selection",
|
|
58
|
+
"admission-manifest",
|
|
59
|
+
],
|
|
60
|
+
"execution-observation": [
|
|
61
|
+
"run-pinning-evidence",
|
|
62
|
+
"solve-submission",
|
|
63
|
+
"solve-delivery",
|
|
64
|
+
"solve-output",
|
|
65
|
+
"evaluation-submission",
|
|
66
|
+
"evaluation-delivery",
|
|
67
|
+
"verdict",
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* The claim section's own grammar, single-sourced from the record's schema so the section cannot
|
|
72
|
+
* describe a variable entry the record could not have carried. It is a second line of defense
|
|
73
|
+
* rather than the primary one: `assertClaimConsistency`'s whole-claim byte-compare against the
|
|
74
|
+
* rebuilt claim is what actually proves the section is this record's projection.
|
|
75
|
+
*/
|
|
76
|
+
export const ClaimDisclosureSectionSchema = z.strictObject({
|
|
77
|
+
recordSha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
78
|
+
specification: z.literal(SIX_VARIABLE_DISCLOSURE_SPECIFICATION),
|
|
79
|
+
subjectSha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
80
|
+
variables: DisclosureVariablesSchema,
|
|
81
|
+
});
|
|
82
|
+
export class DisclosureProjectionError extends Error {
|
|
83
|
+
constructor(message) {
|
|
84
|
+
super(message);
|
|
85
|
+
this.name = "DisclosureProjectionError";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function sha256Hex(bytes) {
|
|
89
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The one shared projection, called by BOTH the workspace producer and the standalone verifier.
|
|
93
|
+
*
|
|
94
|
+
* It carries only facts embedded in the record's own bytes, plus the record's digest — which is the
|
|
95
|
+
* digest of the very bytes handed in, never a separately supplied one. It exists at all so a reader
|
|
96
|
+
* of `claim-package.json` alone sees all six statuses without opening an evidence record, and so
|
|
97
|
+
* `assertClaimConsistency`'s existing whole-claim byte-compare covers the disclosure without a
|
|
98
|
+
* second bespoke comparison.
|
|
99
|
+
*/
|
|
100
|
+
export function deriveDisclosureSpecification(recordBytes) {
|
|
101
|
+
let record;
|
|
102
|
+
try {
|
|
103
|
+
record = parseDisclosureSpecification(recordBytes);
|
|
104
|
+
}
|
|
105
|
+
catch (cause) {
|
|
106
|
+
throw new DisclosureProjectionError(`disclosure-specification record is not a valid sealed record: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
recordSha256: sha256Hex(recordBytes),
|
|
110
|
+
specification: record.specification,
|
|
111
|
+
subjectSha256: record.subject.digest.sha256,
|
|
112
|
+
// Verbatim, in the frozen key order. Building the object key by key rather than spreading
|
|
113
|
+
// `record.variables` is what makes the order a property of this function rather than of
|
|
114
|
+
// whatever order the record's own bytes happened to carry.
|
|
115
|
+
variables: Object.fromEntries(DISCLOSURE_VARIABLE_KEYS.map((key) => [key, record.variables[key]])),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* §7 steps 1–8: authenticate what the Report extension names.
|
|
120
|
+
*
|
|
121
|
+
* Step 9 (claim-id pairing) and step 10 (projection equality) live in `verify.ts` and
|
|
122
|
+
* `assertClaimConsistency` respectively, because both compare against documents this function is
|
|
123
|
+
* deliberately not handed — keeping this function a statement about the RECORD and its evidence.
|
|
124
|
+
*/
|
|
125
|
+
export function assertDisclosureSpecification(input) {
|
|
126
|
+
// Explicitly annotated so TypeScript's control-flow analysis treats every call below as
|
|
127
|
+
// terminating; a destructured member with an inferred type does not narrow through `never`.
|
|
128
|
+
const refuse = input.refuse;
|
|
129
|
+
// ── Step 1: carrier binding, in exact one-to-one correspondence ─────────────────────────────
|
|
130
|
+
//
|
|
131
|
+
// The extension's digest must resolve to EXACTLY ONE catalog record — none and two both refuse —
|
|
132
|
+
// and that record's declared roles must be exactly the one disclosure role. Conversely any catalog
|
|
133
|
+
// record bearing the role that the extension does not name refuses: without the second direction,
|
|
134
|
+
// a second disclosure record could ride along with nothing checking it.
|
|
135
|
+
const bearers = [...input.catalogRoles]
|
|
136
|
+
.filter(([, roles]) => roles.has(DISCLOSURE_SPECIFICATION_BUNDLE_ROLE))
|
|
137
|
+
.map(([digest]) => digest);
|
|
138
|
+
if (bearers.length !== 1 || bearers[0] !== input.extensionDigestSha256) {
|
|
139
|
+
refuse("disclosure-specification", bearers.length === 0
|
|
140
|
+
? "the Report names a disclosure-specification record that no evidence-catalog record carries"
|
|
141
|
+
: bearers.length > 1
|
|
142
|
+
? "the evidence catalog carries more than one disclosure-specification record; the Report names exactly one"
|
|
143
|
+
: "the evidence catalog's disclosure-specification record is not the one the Report extension names");
|
|
144
|
+
}
|
|
145
|
+
const declared = input.catalogRoles.get(input.extensionDigestSha256);
|
|
146
|
+
if (declared === undefined || declared.size !== 1) {
|
|
147
|
+
refuse("disclosure-specification", "the disclosure-specification record must declare exactly that one role and no second");
|
|
148
|
+
}
|
|
149
|
+
// ── Step 2: exact bytes ────────────────────────────────────────────────────────────────────
|
|
150
|
+
//
|
|
151
|
+
// The digest equality with the catalog entry is already established upstream (the bundle walk
|
|
152
|
+
// recomputes every `records/<sha256>.bin`), so what is proved here is that those bytes parse
|
|
153
|
+
// strictly AND are the one exact canonical encoding of what they parse to.
|
|
154
|
+
const bytes = input.recordBytes.get(input.extensionDigestSha256);
|
|
155
|
+
if (bytes === undefined) {
|
|
156
|
+
refuse("disclosure-specification", "the bundle does not carry the disclosure-specification record it names");
|
|
157
|
+
}
|
|
158
|
+
const record = (() => {
|
|
159
|
+
try {
|
|
160
|
+
return parseDisclosureSpecification(bytes);
|
|
161
|
+
}
|
|
162
|
+
catch (cause) {
|
|
163
|
+
refuse(`records/${input.extensionDigestSha256}.bin`, `disclosure-specification record is invalid: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
164
|
+
}
|
|
165
|
+
})();
|
|
166
|
+
// ── Step 3: subject binding (R1) ───────────────────────────────────────────────────────────
|
|
167
|
+
//
|
|
168
|
+
// The Matrix rather than the Report, because the Report cannot name its own digest inside its own
|
|
169
|
+
// signed payload, and because the record must be sealable before the Report is (§5).
|
|
170
|
+
if (record.subject.kind !== MATRIX_RECORD_KIND) {
|
|
171
|
+
refuse("disclosure-specification", `the record's subject kind must be ${MATRIX_RECORD_KIND}`);
|
|
172
|
+
}
|
|
173
|
+
if (record.subject.digest.sha256 !== input.matrixSha256) {
|
|
174
|
+
refuse("disclosure-specification", "the record's subject digest is not this bundle's Matrix digest");
|
|
175
|
+
}
|
|
176
|
+
if (record.specification !== SIX_VARIABLE_DISCLOSURE_SPECIFICATION) {
|
|
177
|
+
refuse("disclosure-specification", `the record must claim ${SIX_VARIABLE_DISCLOSURE_SPECIFICATION}`);
|
|
178
|
+
}
|
|
179
|
+
// ── Step 4: author binding ─────────────────────────────────────────────────────────────────
|
|
180
|
+
//
|
|
181
|
+
// A disclosure record asserting under one identity inside a bundle signed by another is a carrier
|
|
182
|
+
// mismatch, not an extra fact.
|
|
183
|
+
if (record.author !== input.reportAuthor) {
|
|
184
|
+
refuse("disclosure-specification", "the record's author is not the bundle's verified Report author");
|
|
185
|
+
}
|
|
186
|
+
// ── Step 5: vocabulary completeness (R2) ───────────────────────────────────────────────────
|
|
187
|
+
//
|
|
188
|
+
// Structural via the strict object; restated so the refusal names the KEY rather than a schema
|
|
189
|
+
// path a reader would have to decode.
|
|
190
|
+
for (const key of DISCLOSURE_VARIABLE_KEYS) {
|
|
191
|
+
if (record.variables[key] === undefined) {
|
|
192
|
+
refuse("disclosure-specification", `the record does not state the variable "${key}"`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
for (const key of Object.keys(record.variables)) {
|
|
196
|
+
if (!DISCLOSURE_VARIABLE_KEYS.includes(key)) {
|
|
197
|
+
refuse("disclosure-specification", `the record states an unknown variable "${key}"; the six-variable set is closed`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// ── Steps 6–8: authenticate measurements, carry assertions, and let undisclosed carry nothing ─
|
|
201
|
+
const statuses = {};
|
|
202
|
+
for (const key of DISCLOSURE_VARIABLE_KEYS) {
|
|
203
|
+
const entry = record.variables[key];
|
|
204
|
+
statuses[key] = entry.status;
|
|
205
|
+
if (entry.status === "measured-here") {
|
|
206
|
+
// Step 6. THE WHOLE SUBSTANCE of "variables the venue actually ran must match actual pinning
|
|
207
|
+
// evidence in the bundle": no measured-here variable may cite a record the bundle does not
|
|
208
|
+
// carry, and a citation resolving to bytes of the wrong species is as bad as a dangling one.
|
|
209
|
+
for (const citation of entry.evidence) {
|
|
210
|
+
const cited = input.catalogRoles.get(citation.digest.sha256);
|
|
211
|
+
if (cited === undefined) {
|
|
212
|
+
refuse("disclosure-specification", `"${key}" is measured here but cites ${citation.digest.sha256}, which this bundle does not carry`);
|
|
213
|
+
}
|
|
214
|
+
const citedRoles = cited;
|
|
215
|
+
const admissible = DISCLOSURE_ROLE_BINDING[citation.role];
|
|
216
|
+
if (!admissible.some((role) => citedRoles.has(role))) {
|
|
217
|
+
refuse("disclosure-specification", `"${key}" cites ${citation.digest.sha256} as ${citation.role}, but that record's bundle roles are`
|
|
218
|
+
+ ` outside the admissible set for it`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (entry.status === "disclosed-by-publisher") {
|
|
224
|
+
// Step 7. Internal consistency ONLY. The schema has already established the statement bound
|
|
225
|
+
// and the sorted, unique, absolute-IRI source list; there is nothing further to check here and
|
|
226
|
+
// — R4 — nothing further this verifier is entitled to check. Restated as a branch so a later
|
|
227
|
+
// reader sees the deliberate emptiness rather than an omission.
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
// Step 8. Structural via the union; restated so the refusal would name the variable. Reaching
|
|
231
|
+
// this branch at all means the entry is `undisclosed`, which by construction carries a reason
|
|
232
|
+
// token and nothing else.
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
recordSha256: input.extensionDigestSha256,
|
|
236
|
+
specification: record.specification,
|
|
237
|
+
subjectSha256: record.subject.digest.sha256,
|
|
238
|
+
statuses: statuses,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { MatrixCell, RunRecord } from "@jinn-network/benchmarking-records";
|
|
2
2
|
import { type ClaimAnchor } from "./anchor-claims.js";
|
|
3
|
+
import type { VerifiedRunBinding } from "../binding/beacon-binding.js";
|
|
3
4
|
export declare const LOCAL_VENUE_LIMITS: readonly string[];
|
|
4
5
|
export declare function localVenueLimitsForRun(run: Pick<RunRecord, "policy">): readonly string[];
|
|
5
6
|
/** `anchors` is the same derived section the producer used (anchor-evidence §7.4); the conditional
|
|
6
|
-
* copy is the same pure function on both sides, so claim-consistency stays an exact byte-compare.
|
|
7
|
-
|
|
7
|
+
* copy is the same pure function on both sides, so claim-consistency stays an exact byte-compare.
|
|
8
|
+
*
|
|
9
|
+
* `binding` is the run's verified `beacon-binding/1` record (issue #2976) and obeys the same rule:
|
|
10
|
+
* omitting it is the identity, so every run that carries no binding keeps its exact limits bytes.
|
|
11
|
+
* It is only ever passed by a caller that can show BOTH sides the same binding — today that means
|
|
12
|
+
* it is not passed, because the public bundle carries no binding record yet. */
|
|
13
|
+
export declare function buildLocalVenueHonesty(cells: readonly MatrixCell[], run: Pick<RunRecord, "policy">, anchors?: readonly ClaimAnchor[], binding?: VerifiedRunBinding): {
|
|
8
14
|
venue: "self-run";
|
|
9
15
|
preRegistration: "structural-and-append-order-only" | "structural-append-order-and-anchored-time";
|
|
10
16
|
limits: readonly string[];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { venueIsolationPostureForPolicy } from "./isolation.js";
|
|
2
2
|
import { anchoredPreRegistration, anchoredVenueLimits } from "./anchor-claims.js";
|
|
3
|
+
import { runBoundVenueLimits } from "../binding/report-face.js";
|
|
3
4
|
export const LOCAL_VENUE_LIMITS = [
|
|
4
5
|
"This is a local, self-run venue: the same operator controls task dispatch, execution, and evaluation.",
|
|
5
6
|
"Pre-registration here is a discipline enforced by this tool, not a proof against the run's own owner — nothing prevents the owner from having altered the record before publishing it.",
|
|
@@ -12,12 +13,17 @@ export function localVenueLimitsForRun(run) {
|
|
|
12
13
|
return venueIsolationPostureForPolicy(run.policy.submissionBaseline?.isolationPolicy).inventory.length === 1 ? LOCAL_VENUE_LIMITS : [LOCAL_VENUE_LIMITS[0], LOCAL_VENUE_LIMITS[1], MULTI, ...LOCAL_VENUE_LIMITS.slice(3)];
|
|
13
14
|
}
|
|
14
15
|
/** `anchors` is the same derived section the producer used (anchor-evidence §7.4); the conditional
|
|
15
|
-
* copy is the same pure function on both sides, so claim-consistency stays an exact byte-compare.
|
|
16
|
-
|
|
16
|
+
* copy is the same pure function on both sides, so claim-consistency stays an exact byte-compare.
|
|
17
|
+
*
|
|
18
|
+
* `binding` is the run's verified `beacon-binding/1` record (issue #2976) and obeys the same rule:
|
|
19
|
+
* omitting it is the identity, so every run that carries no binding keeps its exact limits bytes.
|
|
20
|
+
* It is only ever passed by a caller that can show BOTH sides the same binding — today that means
|
|
21
|
+
* it is not passed, because the public bundle carries no binding record yet. */
|
|
22
|
+
export function buildLocalVenueHonesty(cells, run, anchors = [], binding) {
|
|
17
23
|
const counts = { harness: 0, model: 0, loadout: 0, isolation: 0 };
|
|
18
24
|
for (const cell of cells)
|
|
19
25
|
for (const axis of Object.keys(counts))
|
|
20
26
|
if (cell.verification[axis] === "unverifiable")
|
|
21
27
|
counts[axis] += 1;
|
|
22
|
-
return { venue: "self-run", preRegistration: anchoredPreRegistration(anchors), limits: anchoredVenueLimits(localVenueLimitsForRun(run), anchors), unverifiableAxisCounts: counts };
|
|
28
|
+
return { venue: "self-run", preRegistration: anchoredPreRegistration(anchors), limits: runBoundVenueLimits(anchoredVenueLimits(localVenueLimitsForRun(run), anchors), binding), unverifiableAxisCounts: counts };
|
|
23
29
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task-selection provenance, verified against the records that carry it (issue #2980).
|
|
3
|
+
*
|
|
4
|
+
* The declaration is a closed-vocabulary value sealed into the Run record's
|
|
5
|
+
* `task-selection/v1` extension. Sealing makes it unforgeable after the lock; it does not make it
|
|
6
|
+
* true. What remains for a cold verifier is the part sealing cannot settle: whether the *other*
|
|
7
|
+
* sealed records are consistent with what the declaration asserts.
|
|
8
|
+
*
|
|
9
|
+
* In this record model the *selection is the Benchmark record itself*: the Run seals a Benchmark
|
|
10
|
+
* digest, and `expectedCellSet` is the full cartesian product `items x arms x replicates`, so the
|
|
11
|
+
* Matrix always covers every Benchmark item exactly. Comparing the Matrix's task digests against
|
|
12
|
+
* the Benchmark's items — the first thing this module tried — is therefore dead code. What is left
|
|
13
|
+
* is the Benchmark's own reveal policy and whether anyone declared the set at all.
|
|
14
|
+
*
|
|
15
|
+
* These checks are refusals, not endorsements, and the asymmetry is the point: each one names a
|
|
16
|
+
* declaration the records positively contradict. None of them can establish that a declaration is
|
|
17
|
+
* TRUE, because the bundle carries no independent witness of an upstream set. `assertTaskSelection-
|
|
18
|
+
* Consistency` below says exactly where that boundary falls and why the tempting rule on the other
|
|
19
|
+
* side of it is unsound.
|
|
20
|
+
*/
|
|
21
|
+
import { type BenchmarkRecord, type RunRecord, type TaskSelectionMode } from "@jinn-network/benchmarking-records";
|
|
22
|
+
export interface TaskSelectionConsistencyInput {
|
|
23
|
+
readonly benchmarkRecord: BenchmarkRecord;
|
|
24
|
+
readonly runRecord: RunRecord;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The declared mode, refusing rather than throwing raw on bytes the Run schema would not have
|
|
28
|
+
* sealed. Exported so any reader that resolves the mode does so through this refusal posture
|
|
29
|
+
* rather than re-deriving it with its own error handling. No presentation asset consumes it today
|
|
30
|
+
* -- the report face renders nothing for task selection until issue #3416 -- so its only callers
|
|
31
|
+
* are `taskSelectionContradiction` below and its own tests.
|
|
32
|
+
*/
|
|
33
|
+
export declare function declaredTaskSelectionMode(runRecord: RunRecord): TaskSelectionMode | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* The way the sealed records contradict a declared task-selection mode, or `undefined` when they
|
|
36
|
+
* do not. Pure, and shared deliberately: the producer calls it BEFORE the lock so a contradiction
|
|
37
|
+
* is a draft-validation refusal the claimant can still act on, and the cold verifier calls it after
|
|
38
|
+
* the fact through {@link assertTaskSelectionConsistency}. One rule, two postures — a second copy
|
|
39
|
+
* would be free to drift into refusing at publish what it accepted at lock, which is the worst
|
|
40
|
+
* possible place to disagree.
|
|
41
|
+
*
|
|
42
|
+
* Two contradictions, both provable from bundle bytes:
|
|
43
|
+
*
|
|
44
|
+
* 1. **`fixed-public-set` over an undeclared set.** A set nobody declared is not a publicly
|
|
45
|
+
* declared set, so the Benchmark record must at least name an `author`.
|
|
46
|
+
* 2. **Reveal policy that cannot coexist with the mode.** `fixed-public-set` is refused when the
|
|
47
|
+
* items were provably withheld at the lock; `drawn-post-lock` is refused when they were
|
|
48
|
+
* `immediate` — open the moment the record existed, so the Run sealed against a set the
|
|
49
|
+
* claimant could already read, and nothing was drawn afterwards.
|
|
50
|
+
*
|
|
51
|
+
* What this deliberately does NOT do is decide the stronger modes from `benchmark.author` versus
|
|
52
|
+
* `run.owner`. That rule looks decisive and is not: `author` is a self-declaration the design spec
|
|
53
|
+
* marks non-authoritative, and every task-set intake in this product re-authors the Benchmark under
|
|
54
|
+
* the workspace's own key (`intake/workspace-authored.ts`), which is also the Run's owner. Enforcing
|
|
55
|
+
* on it would refuse every bundle this product can produce, making two of the three vocabulary
|
|
56
|
+
* values dead letters.
|
|
57
|
+
*
|
|
58
|
+
* So the honest boundary: these checks catch declarations the records positively contradict, but
|
|
59
|
+
* none can prove a `fixed-public-set` claim TRUE — the bundle carries no independent witness of the
|
|
60
|
+
* upstream set. `PUBLIC-BUNDLE.md` says so in the same words, because a gap a reader can see is
|
|
61
|
+
* worth more than one a rule pretends to close.
|
|
62
|
+
*
|
|
63
|
+
* `claimant-chosen` is unconstrained on purpose. It asserts nothing about anyone but the claimant,
|
|
64
|
+
* so nothing can contradict it, and constraining it would only make the honest answer the
|
|
65
|
+
* expensive one.
|
|
66
|
+
*/
|
|
67
|
+
export declare function taskSelectionContradiction(input: TaskSelectionConsistencyInput): string | undefined;
|
|
68
|
+
/** The cold verifier's posture over {@link taskSelectionContradiction}: a typed record refusal. */
|
|
69
|
+
export declare function assertTaskSelectionConsistency(input: TaskSelectionConsistencyInput): void;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task-selection provenance, verified against the records that carry it (issue #2980).
|
|
3
|
+
*
|
|
4
|
+
* The declaration is a closed-vocabulary value sealed into the Run record's
|
|
5
|
+
* `task-selection/v1` extension. Sealing makes it unforgeable after the lock; it does not make it
|
|
6
|
+
* true. What remains for a cold verifier is the part sealing cannot settle: whether the *other*
|
|
7
|
+
* sealed records are consistent with what the declaration asserts.
|
|
8
|
+
*
|
|
9
|
+
* In this record model the *selection is the Benchmark record itself*: the Run seals a Benchmark
|
|
10
|
+
* digest, and `expectedCellSet` is the full cartesian product `items x arms x replicates`, so the
|
|
11
|
+
* Matrix always covers every Benchmark item exactly. Comparing the Matrix's task digests against
|
|
12
|
+
* the Benchmark's items — the first thing this module tried — is therefore dead code. What is left
|
|
13
|
+
* is the Benchmark's own reveal policy and whether anyone declared the set at all.
|
|
14
|
+
*
|
|
15
|
+
* These checks are refusals, not endorsements, and the asymmetry is the point: each one names a
|
|
16
|
+
* declaration the records positively contradict. None of them can establish that a declaration is
|
|
17
|
+
* TRUE, because the bundle carries no independent witness of an upstream set. `assertTaskSelection-
|
|
18
|
+
* Consistency` below says exactly where that boundary falls and why the tempting rule on the other
|
|
19
|
+
* side of it is unsound.
|
|
20
|
+
*/
|
|
21
|
+
import { compareCalendarStrictRfc3339Instants, readTaskSelectionMode, } from "@jinn-network/benchmarking-records";
|
|
22
|
+
import { refuse } from "./errors.js";
|
|
23
|
+
/** The verification path every refusal here is reported under. Adding a new named check would be a
|
|
24
|
+
* bundle-format bump — the claim pins the check list byte-for-byte — so these refusals join the
|
|
25
|
+
* existing `claim-consistency` check, which is exactly the question they answer. */
|
|
26
|
+
const PATH = "claim-consistency";
|
|
27
|
+
/**
|
|
28
|
+
* Whether the Benchmark's items were PROVABLY still withheld when the Run sealed against them.
|
|
29
|
+
*
|
|
30
|
+
* Deliberately one-directional, and the reason is a real trap. The only instant a cold verifier
|
|
31
|
+
* can read out of a sealed Run is `closeAt`, which is the run's CLOSE, not its lock —
|
|
32
|
+
* `closeAt = lockedAt + policy.closeAfterMs` with a strictly positive interval (24h by default),
|
|
33
|
+
* and `lockedAt` lives only in product-local state that no bundle carries. So `notBefore <= closeAt`
|
|
34
|
+
* establishes nothing about the lock: a schedule opening twelve hours into a twenty-four-hour run
|
|
35
|
+
* satisfies it while the items were plainly withheld at the lock. Only the far side is safe —
|
|
36
|
+
* `notBefore >= closeAt > lockedAt` proves withholding — so that is the only comparison made.
|
|
37
|
+
*
|
|
38
|
+
* The comparator is the records package's own, not `Date.parse`, and that is a correctness
|
|
39
|
+
* requirement rather than a preference. These fields are validated by `isCalendarStrictRfc3339`,
|
|
40
|
+
* which accepts leap seconds; V8's `Date.parse` returns `NaN` for exactly those spellings, and
|
|
41
|
+
* `NaN >= x` is `false`. Left on `Date.parse`, a claimant could seal
|
|
42
|
+
* `notBefore: "2026-12-31T23:59:60Z"` — items withheld past the run's end — declare
|
|
43
|
+
* `fixed-public-set`, and have both this check and the producer's pre-lock gate wave it through.
|
|
44
|
+
* An uncomparable pair fails CLOSED for the same reason: it cannot arise from schema-valid records,
|
|
45
|
+
* so treating it as proof of withholding costs nothing and removes the fail-open shape entirely.
|
|
46
|
+
*
|
|
47
|
+
* A `scheduled` reveal with no `notBefore` fails closed on the same principle. The Benchmark schema
|
|
48
|
+
* leaves `notBefore` optional under every policy, so `{ "policy": "scheduled" }` seals cleanly while
|
|
49
|
+
* announcing no instant at which the items become readable — strictly more withheld than
|
|
50
|
+
* `after-run`, which at least names the run's end. Reading it as open would hand a claimant a
|
|
51
|
+
* one-key evasion of the only reveal-policy teeth `fixed-public-set` has: delete the field, and a
|
|
52
|
+
* privately assembled set passes the check that `after-run` fails.
|
|
53
|
+
*/
|
|
54
|
+
function withheldAtLock(benchmark, closeAt) {
|
|
55
|
+
const { policy, notBefore } = benchmark.reveal;
|
|
56
|
+
if (policy === "after-run")
|
|
57
|
+
return true;
|
|
58
|
+
if (policy !== "scheduled")
|
|
59
|
+
return false;
|
|
60
|
+
if (notBefore === undefined)
|
|
61
|
+
return true;
|
|
62
|
+
const order = compareCalendarStrictRfc3339Instants(notBefore, closeAt);
|
|
63
|
+
return order === undefined || order >= 0;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The declared mode, refusing rather than throwing raw on bytes the Run schema would not have
|
|
67
|
+
* sealed. Exported so any reader that resolves the mode does so through this refusal posture
|
|
68
|
+
* rather than re-deriving it with its own error handling. No presentation asset consumes it today
|
|
69
|
+
* -- the report face renders nothing for task selection until issue #3416 -- so its only callers
|
|
70
|
+
* are `taskSelectionContradiction` below and its own tests.
|
|
71
|
+
*/
|
|
72
|
+
export function declaredTaskSelectionMode(runRecord) {
|
|
73
|
+
try {
|
|
74
|
+
return readTaskSelectionMode(runRecord);
|
|
75
|
+
}
|
|
76
|
+
catch (cause) {
|
|
77
|
+
refuse("record-integrity", PATH, `the Run's declared task selection is not one of the recorded selection modes: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The way the sealed records contradict a declared task-selection mode, or `undefined` when they
|
|
82
|
+
* do not. Pure, and shared deliberately: the producer calls it BEFORE the lock so a contradiction
|
|
83
|
+
* is a draft-validation refusal the claimant can still act on, and the cold verifier calls it after
|
|
84
|
+
* the fact through {@link assertTaskSelectionConsistency}. One rule, two postures — a second copy
|
|
85
|
+
* would be free to drift into refusing at publish what it accepted at lock, which is the worst
|
|
86
|
+
* possible place to disagree.
|
|
87
|
+
*
|
|
88
|
+
* Two contradictions, both provable from bundle bytes:
|
|
89
|
+
*
|
|
90
|
+
* 1. **`fixed-public-set` over an undeclared set.** A set nobody declared is not a publicly
|
|
91
|
+
* declared set, so the Benchmark record must at least name an `author`.
|
|
92
|
+
* 2. **Reveal policy that cannot coexist with the mode.** `fixed-public-set` is refused when the
|
|
93
|
+
* items were provably withheld at the lock; `drawn-post-lock` is refused when they were
|
|
94
|
+
* `immediate` — open the moment the record existed, so the Run sealed against a set the
|
|
95
|
+
* claimant could already read, and nothing was drawn afterwards.
|
|
96
|
+
*
|
|
97
|
+
* What this deliberately does NOT do is decide the stronger modes from `benchmark.author` versus
|
|
98
|
+
* `run.owner`. That rule looks decisive and is not: `author` is a self-declaration the design spec
|
|
99
|
+
* marks non-authoritative, and every task-set intake in this product re-authors the Benchmark under
|
|
100
|
+
* the workspace's own key (`intake/workspace-authored.ts`), which is also the Run's owner. Enforcing
|
|
101
|
+
* on it would refuse every bundle this product can produce, making two of the three vocabulary
|
|
102
|
+
* values dead letters.
|
|
103
|
+
*
|
|
104
|
+
* So the honest boundary: these checks catch declarations the records positively contradict, but
|
|
105
|
+
* none can prove a `fixed-public-set` claim TRUE — the bundle carries no independent witness of the
|
|
106
|
+
* upstream set. `PUBLIC-BUNDLE.md` says so in the same words, because a gap a reader can see is
|
|
107
|
+
* worth more than one a rule pretends to close.
|
|
108
|
+
*
|
|
109
|
+
* `claimant-chosen` is unconstrained on purpose. It asserts nothing about anyone but the claimant,
|
|
110
|
+
* so nothing can contradict it, and constraining it would only make the honest answer the
|
|
111
|
+
* expensive one.
|
|
112
|
+
*/
|
|
113
|
+
export function taskSelectionContradiction(input) {
|
|
114
|
+
const { benchmarkRecord, runRecord } = input;
|
|
115
|
+
const declared = declaredTaskSelectionMode(runRecord);
|
|
116
|
+
if (declared === undefined || declared === "claimant-chosen")
|
|
117
|
+
return undefined;
|
|
118
|
+
if (declared === "fixed-public-set") {
|
|
119
|
+
if (benchmarkRecord.author === undefined) {
|
|
120
|
+
return "task selection is declared fixed-public-set but the Benchmark record names no author,"
|
|
121
|
+
+ " so the set it describes was never publicly declared by anyone";
|
|
122
|
+
}
|
|
123
|
+
if (withheldAtLock(benchmarkRecord, runRecord.closeAt)) {
|
|
124
|
+
return "task selection is declared fixed-public-set but the Benchmark's reveal policy withholds"
|
|
125
|
+
+ " its items past the end of the run, so they were not public when the run was locked";
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
if (benchmarkRecord.reveal.policy === "immediate") {
|
|
130
|
+
return "task selection is declared drawn-post-lock but the Benchmark reveals its items"
|
|
131
|
+
+ " immediately, so the run was locked against a set the claimant could already read";
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
/** The cold verifier's posture over {@link taskSelectionContradiction}: a typed record refusal. */
|
|
136
|
+
export function assertTaskSelectionConsistency(input) {
|
|
137
|
+
const contradiction = taskSelectionContradiction(input);
|
|
138
|
+
if (contradiction !== undefined)
|
|
139
|
+
refuse("record-integrity", PATH, contradiction);
|
|
140
|
+
}
|
|
@@ -20,7 +20,36 @@ export declare const PUBLIC_BUNDLE_V6_COMPATIBLE_VERIFICATION_COMMAND: "npx @col
|
|
|
20
20
|
* `EVIDENCE_NATIVE_BUNDLE_V5_CHECKS`.
|
|
21
21
|
*/
|
|
22
22
|
export declare const PUBLIC_BUNDLE_V6_CHECKS: readonly ["manifest", "evidence-closure", "trust", "matrix-rederivation", "report-verification", "claim-consistency", "integrity-anchors"];
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* The anchored binary-qualification closure runs exactly the anchored list (issue #3205): the
|
|
25
|
+
* qualification projection changes what `evidence-closure` and `claim-consistency` examine, not
|
|
26
|
+
* which checks run.
|
|
27
|
+
*/
|
|
28
|
+
export declare const PUBLIC_BUNDLE_V7_CHECKS: readonly ["manifest", "evidence-closure", "trust", "matrix-rederivation", "report-verification", "claim-consistency", "integrity-anchors"];
|
|
29
|
+
/**
|
|
30
|
+
* Unlike every earlier closure, this one does NOT stamp the first public 0.1 line: no released
|
|
31
|
+
* verifier before 0.2.1 understands `benchmark-product-public-bundle/7`, and a claim naming a
|
|
32
|
+
* reader that cannot read it would be an instruction to fail. 0.2.1 is also the line that carries
|
|
33
|
+
* the prompted-screening admission surface a binary claim may need.
|
|
34
|
+
*/
|
|
35
|
+
export declare const PUBLIC_BUNDLE_V7_VERIFICATION_COMMAND: "npx @colophon-claims/verify@0.2.1 <bundle-dir>";
|
|
36
|
+
export declare const PUBLIC_BUNDLE_V7_COMPATIBLE_VERIFICATION_COMMAND: "npx @colophon-claims/verify@0.2 <bundle-dir>";
|
|
37
|
+
/**
|
|
38
|
+
* The disclosed closure's check list (disclosure-specification-record design §7, issue #2839): v7's
|
|
39
|
+
* seven plus `disclosure-specification`, **last**. It runs after `claim-consistency` because the
|
|
40
|
+
* claim's `disclosure` section is among the things it compares, and it is **always present** on this
|
|
41
|
+
* format — a disclosed bundle whose record was stripped is a closure failure, not a shorter list.
|
|
42
|
+
*/
|
|
43
|
+
export declare const PUBLIC_BUNDLE_V8_CHECKS: readonly ["manifest", "evidence-closure", "trust", "matrix-rederivation", "report-verification", "claim-consistency", "integrity-anchors", "disclosure-specification"];
|
|
44
|
+
/**
|
|
45
|
+
* Like v7 and for the same reason: no released reader before 0.2.1 understands
|
|
46
|
+
* `benchmark-product-public-bundle/8`, and a claim naming a reader that cannot read it would be an
|
|
47
|
+
* instruction to fail. The disclosed closure ships in the same unpublished 0.2.1 line as v7, so it
|
|
48
|
+
* pins that line rather than minting a third.
|
|
49
|
+
*/
|
|
50
|
+
export declare const PUBLIC_BUNDLE_V8_VERIFICATION_COMMAND: "npx @colophon-claims/verify@0.2.1 <bundle-dir>";
|
|
51
|
+
export declare const PUBLIC_BUNDLE_V8_COMPATIBLE_VERIFICATION_COMMAND: "npx @colophon-claims/verify@0.2 <bundle-dir>";
|
|
52
|
+
/** Every format through v6 stamps the same first public 0.1 line; v7 is the first that cannot. */
|
|
24
53
|
export declare const PUBLIC_BUNDLE_VERIFICATION_INSTRUCTIONS: {
|
|
25
54
|
readonly "benchmark-product-public-bundle/2": {
|
|
26
55
|
readonly command: "npx @colophon-claims/verify@0.1.0 <bundle-dir>";
|
|
@@ -38,4 +67,12 @@ export declare const PUBLIC_BUNDLE_VERIFICATION_INSTRUCTIONS: {
|
|
|
38
67
|
readonly command: "npx @colophon-claims/verify@0.1.0 <bundle-dir>";
|
|
39
68
|
readonly compatibleCommand: "npx @colophon-claims/verify@0.1 <bundle-dir>";
|
|
40
69
|
};
|
|
70
|
+
readonly "benchmark-product-public-bundle/7": {
|
|
71
|
+
readonly command: "npx @colophon-claims/verify@0.2.1 <bundle-dir>";
|
|
72
|
+
readonly compatibleCommand: "npx @colophon-claims/verify@0.2 <bundle-dir>";
|
|
73
|
+
};
|
|
74
|
+
readonly "benchmark-product-public-bundle/8": {
|
|
75
|
+
readonly command: "npx @colophon-claims/verify@0.2.1 <bundle-dir>";
|
|
76
|
+
readonly compatibleCommand: "npx @colophon-claims/verify@0.2 <bundle-dir>";
|
|
77
|
+
};
|
|
41
78
|
};
|
|
@@ -30,7 +30,39 @@ export const PUBLIC_BUNDLE_V6_CHECKS = [
|
|
|
30
30
|
...PUBLIC_BUNDLE_VERIFICATION_CHECKS,
|
|
31
31
|
"integrity-anchors",
|
|
32
32
|
];
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* The anchored binary-qualification closure runs exactly the anchored list (issue #3205): the
|
|
35
|
+
* qualification projection changes what `evidence-closure` and `claim-consistency` examine, not
|
|
36
|
+
* which checks run.
|
|
37
|
+
*/
|
|
38
|
+
export const PUBLIC_BUNDLE_V7_CHECKS = PUBLIC_BUNDLE_V6_CHECKS;
|
|
39
|
+
/**
|
|
40
|
+
* Unlike every earlier closure, this one does NOT stamp the first public 0.1 line: no released
|
|
41
|
+
* verifier before 0.2.1 understands `benchmark-product-public-bundle/7`, and a claim naming a
|
|
42
|
+
* reader that cannot read it would be an instruction to fail. 0.2.1 is also the line that carries
|
|
43
|
+
* the prompted-screening admission surface a binary claim may need.
|
|
44
|
+
*/
|
|
45
|
+
export const PUBLIC_BUNDLE_V7_VERIFICATION_COMMAND = "npx @colophon-claims/verify@0.2.1 <bundle-dir>";
|
|
46
|
+
export const PUBLIC_BUNDLE_V7_COMPATIBLE_VERIFICATION_COMMAND = "npx @colophon-claims/verify@0.2 <bundle-dir>";
|
|
47
|
+
/**
|
|
48
|
+
* The disclosed closure's check list (disclosure-specification-record design §7, issue #2839): v7's
|
|
49
|
+
* seven plus `disclosure-specification`, **last**. It runs after `claim-consistency` because the
|
|
50
|
+
* claim's `disclosure` section is among the things it compares, and it is **always present** on this
|
|
51
|
+
* format — a disclosed bundle whose record was stripped is a closure failure, not a shorter list.
|
|
52
|
+
*/
|
|
53
|
+
export const PUBLIC_BUNDLE_V8_CHECKS = [
|
|
54
|
+
...PUBLIC_BUNDLE_V7_CHECKS,
|
|
55
|
+
"disclosure-specification",
|
|
56
|
+
];
|
|
57
|
+
/**
|
|
58
|
+
* Like v7 and for the same reason: no released reader before 0.2.1 understands
|
|
59
|
+
* `benchmark-product-public-bundle/8`, and a claim naming a reader that cannot read it would be an
|
|
60
|
+
* instruction to fail. The disclosed closure ships in the same unpublished 0.2.1 line as v7, so it
|
|
61
|
+
* pins that line rather than minting a third.
|
|
62
|
+
*/
|
|
63
|
+
export const PUBLIC_BUNDLE_V8_VERIFICATION_COMMAND = PUBLIC_BUNDLE_V7_VERIFICATION_COMMAND;
|
|
64
|
+
export const PUBLIC_BUNDLE_V8_COMPATIBLE_VERIFICATION_COMMAND = PUBLIC_BUNDLE_V7_COMPATIBLE_VERIFICATION_COMMAND;
|
|
65
|
+
/** Every format through v6 stamps the same first public 0.1 line; v7 is the first that cannot. */
|
|
34
66
|
export const PUBLIC_BUNDLE_VERIFICATION_INSTRUCTIONS = {
|
|
35
67
|
[BUNDLE_FORMAT]: {
|
|
36
68
|
command: PUBLIC_BUNDLE_VERIFICATION_COMMAND,
|
|
@@ -48,5 +80,13 @@ export const PUBLIC_BUNDLE_VERIFICATION_INSTRUCTIONS = {
|
|
|
48
80
|
command: PUBLIC_BUNDLE_V6_VERIFICATION_COMMAND,
|
|
49
81
|
compatibleCommand: PUBLIC_BUNDLE_V6_COMPATIBLE_VERIFICATION_COMMAND,
|
|
50
82
|
},
|
|
83
|
+
[BUNDLE_V7_FORMAT]: {
|
|
84
|
+
command: PUBLIC_BUNDLE_V7_VERIFICATION_COMMAND,
|
|
85
|
+
compatibleCommand: PUBLIC_BUNDLE_V7_COMPATIBLE_VERIFICATION_COMMAND,
|
|
86
|
+
},
|
|
87
|
+
[BUNDLE_V8_FORMAT]: {
|
|
88
|
+
command: PUBLIC_BUNDLE_V8_VERIFICATION_COMMAND,
|
|
89
|
+
compatibleCommand: PUBLIC_BUNDLE_V8_COMPATIBLE_VERIFICATION_COMMAND,
|
|
90
|
+
},
|
|
51
91
|
};
|
|
52
|
-
import { BUNDLE_FORMAT, BUNDLE_V4_FORMAT, BUNDLE_V5_FORMAT, BUNDLE_V6_FORMAT } from "./manifest.js";
|
|
92
|
+
import { BUNDLE_FORMAT, BUNDLE_V4_FORMAT, BUNDLE_V5_FORMAT, BUNDLE_V6_FORMAT, BUNDLE_V7_FORMAT, BUNDLE_V8_FORMAT, } from "./manifest.js";
|