@colophon-claims/verify 0.1.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.
Files changed (49) hide show
  1. package/README.md +23 -9
  2. package/dist/admission/contracts.d.ts +483 -0
  3. package/dist/admission/contracts.js +285 -0
  4. package/dist/admission/index.d.ts +2 -0
  5. package/dist/admission/index.js +2 -0
  6. package/dist/admission/prompted-commitment.d.ts +19 -0
  7. package/dist/admission/prompted-commitment.js +52 -0
  8. package/dist/admission/prompted-selection.d.ts +24 -0
  9. package/dist/admission/prompted-selection.js +85 -0
  10. package/dist/admission/verification.d.ts +17 -3
  11. package/dist/admission/verification.js +230 -49
  12. package/dist/assets.d.ts +21 -1
  13. package/dist/assets.js +60 -3
  14. package/dist/binding/beacon-binding.d.ts +230 -0
  15. package/dist/binding/beacon-binding.js +325 -0
  16. package/dist/binding/report-face.d.ts +45 -0
  17. package/dist/binding/report-face.js +153 -0
  18. package/dist/cli.js +74 -12
  19. package/dist/index.d.ts +14 -3
  20. package/dist/index.js +16 -3
  21. package/dist/manifest.d.ts +30 -4
  22. package/dist/manifest.js +30 -0
  23. package/dist/materialize.d.ts +7 -0
  24. package/dist/materialize.js +7 -0
  25. package/dist/outcome.d.ts +31 -0
  26. package/dist/outcome.js +45 -0
  27. package/dist/profile/binary-qualification.js +6 -0
  28. package/dist/profile/claim-consistency.d.ts +8 -1
  29. package/dist/profile/claim-consistency.js +4 -4
  30. package/dist/profile/claim.d.ts +203 -1
  31. package/dist/profile/claim.js +192 -43
  32. package/dist/profile/disclosure.d.ts +273 -0
  33. package/dist/profile/disclosure.js +240 -0
  34. package/dist/profile/pinning-evidence.d.ts +1 -1
  35. package/dist/profile/run-results.d.ts +8 -2
  36. package/dist/profile/run-results.js +9 -3
  37. package/dist/profile/task-selection.d.ts +69 -0
  38. package/dist/profile/task-selection.js +140 -0
  39. package/dist/reader-instructions.d.ts +38 -1
  40. package/dist/reader-instructions.js +42 -2
  41. package/dist/schema.d.ts +13 -2
  42. package/dist/schema.js +39 -8
  43. package/dist/signers.d.ts +48 -0
  44. package/dist/signers.js +77 -0
  45. package/dist/verify.d.ts +20 -6
  46. package/dist/verify.js +156 -31
  47. package/dist/version.d.ts +1 -1
  48. package/dist/version.js +1 -1
  49. package/package.json +14 -14
@@ -0,0 +1,153 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * The report face for `beacon-binding/1` (issue #2976, acceptance criteria 3 and 4): which binding
4
+ * applied, in plain words, and what it does and does not establish.
5
+ *
6
+ * Two rules carried over from `../profile/anchor-claims.ts`, whose conditional honesty copy this
7
+ * mirrors:
8
+ *
9
+ * - **Single-sourced, never mirrored.** `@colophon-claims/core` already depends on this package, so
10
+ * producer and reader render the same function rather than two copies that can drift.
11
+ * - **The words key on facts, not on configuration.** Every value in a sentence below comes from
12
+ * the verified binding itself, so the text is identical for every reader.
13
+ *
14
+ * A third rule follows from issue #3322: **a sentence claims unchosen-ness only where the seal
15
+ * established it.** A beacon that merely postdates the seal makes the value unpredictable and
16
+ * leaves the operator choosing among the values that postdate it. So both sentences carry
17
+ * `roundChoiceClause`, which asserts the second property under `seal-derived` and retracts it under
18
+ * `operator-chosen` rather than letting either branch imply it.
19
+ *
20
+ * A fourth rule is this module's own, and is the whole point of the originating issue: **the census
21
+ * sentence says it is the weaker binding.** Ordering-only binding shows the run's order was fixed by
22
+ * randomness that postdates the seal; it does not show the population was, because with a census
23
+ * there was no population choice to make. Letting the two modes share one confident sentence would
24
+ * be the failure this feature exists to prevent. Issue #3425 subjects that sentence to the third
25
+ * rule as well: `censusOrderClause` asserts the postdating only under `proven-offline`, and concedes
26
+ * it under `attributive` in the register the sampled opening already uses, because the clauses on
27
+ * either side of it concede exactly that.
28
+ */
29
+ import { BEACON_BINDING_PROCEDURE, BEACON_SOURCES, } from "./beacon-binding.js";
30
+ export function runBindingClass(binding) {
31
+ if (binding === undefined)
32
+ return "none";
33
+ return binding.mode === "sampled" ? "beacon-drawn-slate" : "beacon-ordering-only";
34
+ }
35
+ /** `<source display name> round <n>`, the one rendering every sentence below uses. */
36
+ function beaconName(binding) {
37
+ return `${BEACON_SOURCES[binding.beacon.source].displayName} round ${binding.beacon.round}`;
38
+ }
39
+ /**
40
+ * How the sentence may talk about the beacon postdating the seal. `proven-offline` asserts,
41
+ * because the source's own published schedule turns the round number into a time by arithmetic.
42
+ * `attributive` reports what the chain asserts and names what checking it takes -- the same
43
+ * assertive/attributive split `anchoredPreRegistrationSentence` draws between a timestamp token
44
+ * and an OpenTimestamps commitment.
45
+ */
46
+ function postSealClause(binding) {
47
+ return binding.postSeal === "proven-offline"
48
+ ? `a value that did not exist until ${binding.beaconInstant}, after this run was sealed at ${binding.sealedAt}`
49
+ : `a value the chain places after this run's seal at ${binding.sealedAt} — establishing that ordering `
50
+ + "requires block headers on the reader's side, so it is what the chain asserts rather than something "
51
+ + "this bundle proves";
52
+ }
53
+ /**
54
+ * What the run's choice of ROUND does or does not add (issue #3322). A beacon that merely postdates
55
+ * the seal leaves the operator picking among realized values, and a sentence that names only the
56
+ * value's unpredictability reads as though it had ruled that out. So the clause is not decoration:
57
+ * under `seal-derived` it states the second property and then names the residue that survives it
58
+ * (the source choice), and under `operator-chosen` it retracts the property in the same plain
59
+ * register the census branch uses about its own weaker binding.
60
+ */
61
+ function roundChoiceClause(binding) {
62
+ // Name the thing a chosen round would actually have moved. Saying "the result" for both modes
63
+ // made the census clause read as a retraction of a population claim a census never makes: with a
64
+ // census the population is the whole declared one whatever round applies, and only the ORDER
65
+ // moves. The residue named under `seal-derived` is likewise the SOURCE, not a count of sources:
66
+ // one admitted source is indexed by block height, where no round follows from a seal at all, so
67
+ // its alternatives are every height published since rather than a single candidate.
68
+ const derived = binding.mode === "sampled" ? "slate" : "order";
69
+ if (binding.roundBasis === "seal-derived") {
70
+ // Only a scheduled source reaches here: `requiredBeaconRound` derives nothing for a height, so
71
+ // an `attributive` binding is `operator-chosen` by construction.
72
+ return "The round was not the operator's to pick either: it is the first round this source publishes after "
73
+ + "the seal, so the seal instant alone fixes it. What choosing remains is the source — this procedure "
74
+ + "admits other beacons, one of them indexed by block height, where no round follows from a seal at all — "
75
+ + "so an operator could have bound a different source instead.";
76
+ }
77
+ if (binding.postSeal === "attributive") {
78
+ // Say no more here than the postdating clause two sentences earlier already conceded. Claiming
79
+ // the value was unpredictable would assert exactly what this branch cannot check: nothing in
80
+ // the bundle places this height after the seal, so nothing rules out a height that predates it.
81
+ return `No round follows from a seal on a height-indexed source, so this height was the operator's choice — `
82
+ + `and on the reader's side it is the chain, not this bundle, that places it after the seal at all. Any `
83
+ + `other height would have derived a different ${derived} from the same inputs.`;
84
+ }
85
+ return `Which post-seal value applied was still the operator's choice: this binding names a round selected after `
86
+ + `the seal, and every round the source published in between was an available alternative deriving a `
87
+ + `different ${derived} from the same inputs. The value could not have been predicted; this ${derived} is `
88
+ + `nonetheless one of several the operator could have realized by waiting.`;
89
+ }
90
+ /**
91
+ * What the census binding shows about the ORDER (issue #3425). The postdating half keys on
92
+ * `postSeal` for the same reason the sampled opening does: on a height-indexed source nothing in
93
+ * the bundle places the value after the seal, so asserting that the order was fixed by randomness
94
+ * postdating it would be conceded by `postSealClause` two clauses earlier and again by
95
+ * `roundChoiceClause` two clauses later. The population half is unconditional -- a census makes no
96
+ * population choice on any basis.
97
+ */
98
+ function censusOrderClause(binding) {
99
+ const order = binding.postSeal === "proven-offline"
100
+ ? "It shows the run's order was fixed by randomness postdating the seal"
101
+ : "It shows the run's order was tied to a value this bundle cannot place after the seal";
102
+ return `${order}; it does not show the population was, because a census makes no population choice.`;
103
+ }
104
+ const RECOMPUTE_CLAUSE = `Any reader recomputes the derivation offline from the sealed digest, the published beacon value and the `
105
+ + `item identities alone, by procedure ${BEACON_BINDING_PROCEDURE}; the verifier fails the run when its `
106
+ + "recomputation disagrees.";
107
+ /**
108
+ * The one sentence that states which binding applied. `undefined` yields the unbound statement,
109
+ * which is a claim about the run too: nothing about it was drawn from post-seal randomness.
110
+ */
111
+ export function runBindingSentence(binding) {
112
+ if (binding === undefined) {
113
+ return "This run is bound to no public randomness: its seal shows the design existed by a given time, and "
114
+ + "nothing establishes that execution followed the seal rather than preceding it.";
115
+ }
116
+ if (binding.mode === "sampled") {
117
+ // The "could not have been selected after the fact" claim is the one issue #3322 exists to
118
+ // stop overstating, so it appears ONLY on the branch where the seal named the round. Under
119
+ // `operator-chosen` it is not merely unproven, it is false -- selecting the slate after the
120
+ // fact needed no prediction at all, only waiting for a round whose derivation the operator
121
+ // liked -- so it is dropped rather than hedged.
122
+ //
123
+ // The opening keys on `postSeal` as well, for the same reason `roundChoiceClause` does: under
124
+ // `attributive` nothing in the bundle places the height after the seal, so an opening that
125
+ // called the value unpredictable would assert what the branch cannot check -- and would be
126
+ // retracted two clauses later by the very postdating clause it introduces.
127
+ const opening = binding.roundBasis === "seal-derived"
128
+ ? "This run's slate was drawn, not chosen"
129
+ : binding.postSeal === "attributive"
130
+ ? "This run's slate was drawn from a value this bundle cannot place after the seal"
131
+ : "This run's slate was drawn from a value it could not have predicted";
132
+ const unpredictabilityClause = binding.roundBasis === "seal-derived"
133
+ ? "Selecting the slate after the fact would have required predicting that value. "
134
+ : "";
135
+ return `${opening}: its ${binding.sample.length} items were derived from a `
136
+ + `declared pool of ${binding.poolSize} by procedure ${BEACON_BINDING_PROCEDURE}, keyed on this run's sealed `
137
+ + `digest together with ${beaconName(binding)} — ${postSealClause(binding)}. `
138
+ + `${unpredictabilityClause}${roundChoiceClause(binding)} ${RECOMPUTE_CLAUSE}`;
139
+ }
140
+ return `This run evaluated its whole declared population of ${binding.poolSize} items, so no slate was selected `
141
+ + `and none could be selected after the fact. The beacon binds execution ORDER only: the order was derived by `
142
+ + `procedure ${BEACON_BINDING_PROCEDURE} from this run's sealed digest together with ${beaconName(binding)} — `
143
+ + `${postSealClause(binding)}. This is a weaker binding than a beacon-drawn slate. `
144
+ + `${censusOrderClause(binding)} ${roundChoiceClause(binding)} ${RECOMPUTE_CLAUSE}`;
145
+ }
146
+ /**
147
+ * The venue-limits list with the binding statement appended. Returns the list unchanged when the
148
+ * run carries no binding, so every run that predates this feature keeps its exact limits bytes --
149
+ * the same additive posture `anchoredVenueLimits` takes for an unanchored run.
150
+ */
151
+ export function runBoundVenueLimits(limits, binding) {
152
+ return binding === undefined ? limits : [...limits, runBindingSentence(binding)];
153
+ }
package/dist/cli.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { SUPPORTED_BUNDLE_FORMATS } from "./manifest.js";
3
- import { EVIDENCE_NATIVE_BUNDLE_V5_CHECKS } from "@jinn-network/benchmarking-evidence";
4
- import { PUBLIC_BUNDLE_VERIFICATION_CHECKS, PUBLIC_BUNDLE_V6_CHECKS } from "./reader-instructions.js";
3
+ import { summarizeVerificationOutcome } from "./outcome.js";
5
4
  import { verifyPublicBundle } from "./verify.js";
6
5
  import { VERIFIER_VERSION } from "./version.js";
7
6
  export { VERIFIER_VERSION } from "./version.js";
@@ -75,29 +74,82 @@ function renderAnchorReport(report) {
75
74
  : report.anchors.map(renderAnchor).join("\n");
76
75
  return `\nAnchors\n${anchors}\n\nAnchor subjects\n${report.subjects.map(renderSubject).join("\n")}\n`;
77
76
  }
77
+ const SIGNER_ROLE_NAMES = {
78
+ publisher: "publisher",
79
+ "automated-grader": "automated grader",
80
+ "human-reviewer": "human reviewer",
81
+ "label-admission": "label admission",
82
+ };
83
+ /** The role in plain words. `urn:`/`did:key` identifiers stay in `--json`, where they are the join
84
+ * key a reader actually needs them for; on this surface they are noise a reader has to decode.
85
+ * Undeclared custody is stated rather than left blank: a reader who has seen the same-operator
86
+ * suffix elsewhere would otherwise read its absence as an independence claim, which no bundle
87
+ * format can establish. The publisher takes no suffix -- it *is* the operator the others are
88
+ * measured against. */
89
+ function renderSignerGroup(role, custody, count) {
90
+ const suffix = role === "publisher"
91
+ ? ""
92
+ : custody === "same-operator" ? " \u2014 same operator" : " \u2014 custody not declared";
93
+ return ` ${SIGNER_ROLE_NAMES[role]}${suffix} \u00b7 ${count} ${count === 1 ? "key" : "keys"}`;
94
+ }
95
+ function renderSigners(signers) {
96
+ const counts = new Map();
97
+ for (const signer of signers) {
98
+ const key = `${signer.role} ${signer.custody}`;
99
+ const group = counts.get(key);
100
+ if (group === undefined)
101
+ counts.set(key, { role: signer.role, custody: signer.custody, count: 1 });
102
+ else
103
+ group.count += 1;
104
+ }
105
+ // The role-name record's own key order is the print order; a second parallel list would drift.
106
+ const order = Object.keys(SIGNER_ROLE_NAMES);
107
+ const groups = [...counts.values()]
108
+ .sort((left, right) => order.indexOf(left.role) - order.indexOf(right.role));
109
+ return `\nSigned by\n${groups.map((group) => renderSignerGroup(group.role, group.custody, group.count)).join("\n")}\n`;
110
+ }
78
111
  export function renderVerifiedBundle(result) {
79
- const checks = result.checks.map((check) => `${check.padEnd(24)}passed`).join("\n");
80
- const totalChecks = result.format === "benchmark-product-public-bundle/5"
81
- ? EVIDENCE_NATIVE_BUNDLE_V5_CHECKS.length
82
- : result.format === "benchmark-product-public-bundle/6"
83
- ? PUBLIC_BUNDLE_V6_CHECKS.length
84
- : PUBLIC_BUNDLE_VERIFICATION_CHECKS.length;
112
+ // A metadata-first bundle carries artifact digests without their bytes. Printing "passed" for a
113
+ // check that read nothing would be the one claim this format cannot afford, so the deferred check
114
+ // prints as not fetched and is counted out of the passed total.
115
+ const outcome = summarizeVerificationOutcome(result);
116
+ const artifactContent = outcome.artifactContent;
117
+ const checks = outcome.outcomes
118
+ .map(({ check, state }) => `${check.padEnd(24)}${state}`)
119
+ .join("\n");
120
+ const totalChecks = outcome.total;
85
121
  const identity = result.identity.startsWith("sha256:") ? result.identity : `sha256:${result.identity}`;
86
122
  const anchors = "anchors" in result && result.anchors !== undefined
87
123
  ? renderAnchorReport(result.anchors)
88
124
  : "";
125
+ const signers = result.signers === undefined || result.signers.length === 0
126
+ ? ""
127
+ : renderSigners(result.signers);
128
+ // Naming the digests is what makes the deferred check completable: they are the addresses to
129
+ // fetch and the expectations to check the fetched bytes against. Adding a body to this directory
130
+ // is not the completion path -- it would break the manifest closure the bundle is identified by,
131
+ // so the reader is pointed at the full-evidence bundle instead.
132
+ const artifactContentReport = artifactContent === undefined
133
+ ? ""
134
+ : `\nArtifact content\n ${artifactContent.notFetched} artifact ${artifactContent.notFetched === 1 ? "body was" : "bodies were"} not fetched. This bundle carries their\n exact digests, not their bytes:\n${artifactContent.notFetchedDigests.map((digest) => ` sha256:${digest}`).join("\n")}\n Check fetched bytes against those digests yourself, or verify the\n full-evidence bundle, which carries them.\n`;
135
+ const artifactContentLimit = artifactContent === undefined
136
+ ? ""
137
+ : "\nEverything above was checked against the bytes this bundle carries. The artifact\ncontents themselves were not read, so nothing here says what they contain.";
89
138
  const anchorLimits = anchors === ""
90
139
  ? ""
91
140
  : "\nAn anchor dates the bytes it covers and says nothing else about the run: not\nthat results were produced after it, and not that the anchoring authority is\nindependent of the bundle's owner.";
92
- return `Verified: ${result.checks.length} of ${totalChecks} checks passed
141
+ const verdictLine = outcome.notFetched === 0
142
+ ? `Verified: ${outcome.passed} of ${totalChecks} checks passed`
143
+ : `Verified: ${outcome.passed} of ${totalChecks} checks passed, ${outcome.notFetched} not fetched`;
144
+ return `${verdictLine}
93
145
  Bundle: ${identity}
94
146
  Format: ${result.format}
95
147
 
96
148
  ${checks}
97
- ${anchors}
149
+ ${signers}${artifactContentReport}${anchors}
98
150
  This checks the bundle's integrity, evidence closure, calculations, report,
99
151
  and claim consistency. It does not prove that the machine that produced the
100
- bundle was honest or that the compared identities are independent parties.${anchorLimits}
152
+ bundle was honest or that the compared identities are independent parties.${artifactContentLimit}${anchorLimits}
101
153
  No files were uploaded.
102
154
  Protocol identifiers name https://spec.jinn.network/…. That origin is not hosted yet.
103
155
  Verification uses the exact platform bytes installed from npm.
@@ -132,6 +184,16 @@ function readBlockHeaders(bytes, path) {
132
184
  };
133
185
  });
134
186
  }
187
+ /** `urn:…` and `did:key:z…` as they appear inside a refusal message. The base58btc class stops a
188
+ * `did:key` match before a trailing `:reason` suffix the message appended. */
189
+ const RAW_IDENTIFIER = /urn:[^\s,;)"']+|did:key:z[1-9A-HJ-NP-Za-km-z]+/gu;
190
+ /** A refusal names the signer it refused, and on the machine surface that identifier is the whole
191
+ * point. On the human surface it is a string a reader cannot act on, so the same rule as the
192
+ * verified report applies: the identifier lives in `--json` (issue #3024). What failed, and where,
193
+ * is untouched. */
194
+ function withoutRawIdentifiers(message) {
195
+ return message.replace(RAW_IDENTIFIER, "<identifier: see --json>");
196
+ }
135
197
  function parseArguments(args) {
136
198
  const positional = [];
137
199
  const tsaRoots = [];
@@ -199,7 +261,7 @@ export async function runVerifierCli(args, deps = {}) {
199
261
  const stdout = parsed.json
200
262
  ? `${JSON.stringify({ ok: false, verifierVersion: VERIFIER_VERSION, supportedFormats: SUPPORTED_BUNDLE_FORMATS, code, message: error.message })}\n`
201
263
  : "";
202
- const stderr = parsed.json ? "" : `colophon-verify: ${error.message}\n`;
264
+ const stderr = parsed.json ? "" : `colophon-verify: ${withoutRawIdentifiers(error.message)}\n`;
203
265
  return { exitCode: code === "record-integrity" ? 1 : 2, stdout, stderr };
204
266
  }
205
267
  }
package/dist/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  export { verifyPublicBundle, verifyPublicBundleSnapshot } from "./verify.js";
2
2
  export type { PublicBundleVerificationCheck, PublicBundleVerificationResult, VerifiedPublicBundleSnapshot, VerifyPublicBundleDeps, } from "./verify.js";
3
+ export type { PublicBundleSigner, PublicBundleSignerRole } from "./signers.js";
3
4
  export { derivePublicComparison } from "./comparison.js";
4
5
  export type { DerivePublicComparisonInput, PublicComparisonCell, PublicComparisonOutput, PublicComparisonTask, PublicComparisonVerdict, PublicComparisonView, PublicDescriptiveComparison, } from "./comparison.js";
5
6
  export { buildPublicAssets } from "./assets.js";
7
+ export { assertTaskSelectionConsistency, taskSelectionContradiction } from "./profile/task-selection.js";
6
8
  export type { PublicAssetInput } from "./assets.js";
7
9
  export { anchorCertificateReader, anchorChainVerifier, anchorSignatureVerifier, nodeCryptoAnchorPorts, } from "./anchor/ports.js";
8
10
  export { evaluateIntegrityAnchors } from "./anchor/check.js";
@@ -10,14 +12,23 @@ export type { AnchorProofStatus, AnchorSubjectOutcome, AnchorSubjectReport, Anch
10
12
  export type { InspectRuntimeMethodDisclosure, InspectScoringProjectionDisclosure, } from "./profile/inspect-disclosure.js";
11
13
  export { ANCHORED_PRE_REGISTRATION, ANCHORED_TRUST_ROOT, ClaimAnchorProjectionError, ClaimAnchorSchema, SELF_RUN_TRUST_ROOT, STRUCTURAL_PRE_REGISTRATION, additionalLockAnchorLine, anchoredPreRegistration, anchoredPreRegistrationSentence, anchoredTrustRoot, anchoredVenueLimits, carriedMatrixAnchors, deriveClaimAnchors, governingLockAnchors, matrixAnchorLine, } from "./profile/anchor-claims.js";
12
14
  export type { CarriedAnchorRecord, ClaimAnchor, ClaimAnchorFacts, ClaimAnchorSubject, DeriveClaimAnchorsInput, OpenTimestampsClaimAnchorFacts, Rfc3161ClaimAnchorFacts, } from "./profile/anchor-claims.js";
13
- export { BUNDLE_FORMAT, BUNDLE_V4_FORMAT, BUNDLE_V5_FORMAT, BUNDLE_V6_FORMAT, SUPPORTED_BUNDLE_FORMATS, } from "./manifest.js";
15
+ export { BUNDLE_FORMAT, BUNDLE_V4_FORMAT, BUNDLE_V5_FORMAT, BUNDLE_V6_FORMAT, BUNDLE_V7_FORMAT, BUNDLE_V8_FORMAT, SUPPORTED_BUNDLE_FORMATS, } from "./manifest.js";
16
+ export { ClaimDisclosureSectionSchema, DISCLOSURE_ROLE_BINDING, DISCLOSURE_SPECIFICATION_BUNDLE_ROLE, DisclosureProjectionError, assertDisclosureSpecification, deriveDisclosureSpecification, } from "./profile/disclosure.js";
17
+ export type { AssertDisclosureSpecificationInput, ClaimDisclosureSection, DisclosureSpecificationReport, } from "./profile/disclosure.js";
14
18
  export type { BundleManifest, VerifiedBundleSnapshot } from "./manifest.js";
15
19
  export * from "./admission/index.js";
16
20
  export * from "./schema.js";
17
21
  export * from "./profile/binary-judge-manifest.js";
18
22
  export * from "./profile/binary-qualification.js";
19
- export { ClaimPackageSchema } from "./profile/claim.js";
23
+ export { ClaimPackageSchema, DISCLOSED_CLAIM_PACKAGE_SCHEMA_ID } from "./profile/claim.js";
20
24
  export type { ClaimPackage } from "./profile/claim.js";
25
+ export { firstDifference } from "./profile/claim-consistency.js";
26
+ export { NOT_FETCHED_CHECK, summarizeVerificationOutcome } from "./outcome.js";
27
+ export type { VerificationCheckOutcome, VerificationCheckState, VerificationOutcome, } from "./outcome.js";
21
28
  export { VERIFIER_VERSION, renderVerifiedBundle, runVerifierCli } from "./cli.js";
22
29
  export type { VerifierCliDeps, VerifierCliResult } from "./cli.js";
23
- export { PUBLIC_BUNDLE_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_VERIFICATION_CHECKS, PUBLIC_BUNDLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V4_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V4_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V5_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V5_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V6_CHECKS, PUBLIC_BUNDLE_V6_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V6_VERIFICATION_COMMAND, PUBLIC_BUNDLE_VERIFICATION_INSTRUCTIONS, PUBLIC_BUNDLE_VERIFIER_MAJOR, PUBLIC_BUNDLE_V4_VERIFIER_MAJOR, } from "./reader-instructions.js";
30
+ export { PUBLIC_BUNDLE_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_VERIFICATION_CHECKS, PUBLIC_BUNDLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V4_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V4_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V5_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V5_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V6_CHECKS, PUBLIC_BUNDLE_V6_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V6_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V7_CHECKS, PUBLIC_BUNDLE_V7_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V7_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V8_CHECKS, PUBLIC_BUNDLE_V8_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V8_VERIFICATION_COMMAND, PUBLIC_BUNDLE_VERIFICATION_INSTRUCTIONS, PUBLIC_BUNDLE_VERIFIER_MAJOR, PUBLIC_BUNDLE_V4_VERIFIER_MAJOR, } from "./reader-instructions.js";
31
+ export { BEACON_BINDING_PROCEDURE, BEACON_SOURCES, BEACON_SOURCE_IDS, MAX_BEACON_ROUND, BeaconReferenceSchema, RunBindingError, RunBindingSchema, beaconRoundInstant, computeBeaconOrder, requiredBeaconRound, verifyRunBinding, } from "./binding/beacon-binding.js";
32
+ export type { BeaconOrderParams, BeaconOrderResult, BeaconPostSealBasis, BeaconReference, BeaconRoundBasis, BeaconSourceDefinition, BeaconSourceId, BeaconSourceTimeBasis, RequiredBeaconRound, RunBinding, VerifiedRunBinding, } from "./binding/beacon-binding.js";
33
+ export { runBindingClass, runBindingSentence, runBoundVenueLimits } from "./binding/report-face.js";
34
+ export type { RunBindingClass } from "./binding/report-face.js";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export { verifyPublicBundle, verifyPublicBundleSnapshot } from "./verify.js";
2
2
  export { derivePublicComparison } from "./comparison.js";
3
3
  export { buildPublicAssets } from "./assets.js";
4
+ export { assertTaskSelectionConsistency, taskSelectionContradiction } from "./profile/task-selection.js";
4
5
  // The three node:crypto ports the RFC 3161 anchor rule engine injects
5
6
  // (anchor-evidence design §6.1 "Placement"). They live here, in the standalone
6
7
  // verifier, and are reused by the product core -- which already depends on this
@@ -13,11 +14,23 @@ export { evaluateIntegrityAnchors } from "./anchor/check.js";
13
14
  // than mirrored into the product core, so the producer's claim section and the verifier's rebuild
14
15
  // of it cannot drift -- the byte-compare depends on them being one function.
15
16
  export { ANCHORED_PRE_REGISTRATION, ANCHORED_TRUST_ROOT, ClaimAnchorProjectionError, ClaimAnchorSchema, SELF_RUN_TRUST_ROOT, STRUCTURAL_PRE_REGISTRATION, additionalLockAnchorLine, anchoredPreRegistration, anchoredPreRegistrationSentence, anchoredTrustRoot, anchoredVenueLimits, carriedMatrixAnchors, deriveClaimAnchors, governingLockAnchors, matrixAnchorLine, } from "./profile/anchor-claims.js";
16
- export { BUNDLE_FORMAT, BUNDLE_V4_FORMAT, BUNDLE_V5_FORMAT, BUNDLE_V6_FORMAT, SUPPORTED_BUNDLE_FORMATS, } from "./manifest.js";
17
+ export { BUNDLE_FORMAT, BUNDLE_V4_FORMAT, BUNDLE_V5_FORMAT, BUNDLE_V6_FORMAT, BUNDLE_V7_FORMAT, BUNDLE_V8_FORMAT, SUPPORTED_BUNDLE_FORMATS, } from "./manifest.js";
18
+ // The disclosed-closure projection and check (disclosure-specification-record design §6.4/§6.6/§7,
19
+ // issue #2839). Single-sourced here for exactly the reason the anchored projection above is: the
20
+ // product core imports this package, so the producer's claim section and the verifier's rebuild of
21
+ // it are one function, and the byte-compare is a comparison of outputs rather than of guesses.
22
+ export { ClaimDisclosureSectionSchema, DISCLOSURE_ROLE_BINDING, DISCLOSURE_SPECIFICATION_BUNDLE_ROLE, DisclosureProjectionError, assertDisclosureSpecification, deriveDisclosureSpecification, } from "./profile/disclosure.js";
17
23
  export * from "./admission/index.js";
18
24
  export * from "./schema.js";
19
25
  export * from "./profile/binary-judge-manifest.js";
20
26
  export * from "./profile/binary-qualification.js";
21
- export { ClaimPackageSchema } from "./profile/claim.js";
27
+ export { ClaimPackageSchema, DISCLOSED_CLAIM_PACKAGE_SCHEMA_ID } from "./profile/claim.js";
28
+ export { firstDifference } from "./profile/claim-consistency.js";
29
+ export { NOT_FETCHED_CHECK, summarizeVerificationOutcome } from "./outcome.js";
22
30
  export { VERIFIER_VERSION, renderVerifiedBundle, runVerifierCli } from "./cli.js";
23
- export { PUBLIC_BUNDLE_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_VERIFICATION_CHECKS, PUBLIC_BUNDLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V4_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V4_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V5_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V5_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V6_CHECKS, PUBLIC_BUNDLE_V6_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V6_VERIFICATION_COMMAND, PUBLIC_BUNDLE_VERIFICATION_INSTRUCTIONS, PUBLIC_BUNDLE_VERIFIER_MAJOR, PUBLIC_BUNDLE_V4_VERIFIER_MAJOR, } from "./reader-instructions.js";
31
+ export { PUBLIC_BUNDLE_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_VERIFICATION_CHECKS, PUBLIC_BUNDLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V4_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V4_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V5_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V5_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V6_CHECKS, PUBLIC_BUNDLE_V6_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V6_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V7_CHECKS, PUBLIC_BUNDLE_V7_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V7_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V8_CHECKS, PUBLIC_BUNDLE_V8_COMPATIBLE_VERIFICATION_COMMAND, PUBLIC_BUNDLE_V8_VERIFICATION_COMMAND, PUBLIC_BUNDLE_VERIFICATION_INSTRUCTIONS, PUBLIC_BUNDLE_VERIFIER_MAJOR, PUBLIC_BUNDLE_V4_VERIFIER_MAJOR, } from "./reader-instructions.js";
32
+ // `beacon-binding/1` (issue #2976): the post-seal public-randomness binding, and the report face
33
+ // that states which binding a run carries. Single-sourced here for the same reason the anchored
34
+ // projection is -- the product core imports this package, so producer and reader run one function.
35
+ export { BEACON_BINDING_PROCEDURE, BEACON_SOURCES, BEACON_SOURCE_IDS, MAX_BEACON_ROUND, BeaconReferenceSchema, RunBindingError, RunBindingSchema, beaconRoundInstant, computeBeaconOrder, requiredBeaconRound, verifyRunBinding, } from "./binding/beacon-binding.js";
36
+ export { runBindingClass, runBindingSentence, runBoundVenueLimits } from "./binding/report-face.js";
@@ -6,7 +6,33 @@ export declare const BUNDLE_V5_FORMAT: "benchmark-product-public-bundle/5";
6
6
  * lists, byte shapes, and values; the `anchors/` member and the `integrity-anchors` check exist
7
7
  * only here. */
8
8
  export declare const BUNDLE_V6_FORMAT: "benchmark-product-public-bundle/6";
9
- export declare const SUPPORTED_BUNDLE_FORMATS: readonly ["benchmark-product-public-bundle/2", "benchmark-product-public-bundle/4", "benchmark-product-public-bundle/5", "benchmark-product-public-bundle/6"];
9
+ /**
10
+ * The anchored binary-qualification closure (issue #3205): v4's member list — `qualification.json`,
11
+ * the v4 evidence catalog, the v4 trust document — plus v6's `anchors/` member and its
12
+ * `integrity-anchors` check. Additive in exactly the same way v6 was: v2, v4, v5, and v6 keep their
13
+ * member lists, check lists, and bytes, and only a run that is BOTH anchored and
14
+ * qualification-projecting emits this one.
15
+ */
16
+ export declare const BUNDLE_V7_FORMAT: "benchmark-product-public-bundle/7";
17
+ /**
18
+ * The disclosed anchored binary-qualification closure (issue #2839, disclosure-specification-record
19
+ * design §6.5): v7's complete member list plus one `records/<sha256>.bin` member carrying the sealed
20
+ * disclosure-specification record, and the `disclosure-specification` check.
21
+ *
22
+ * **Why it stacks on v7 rather than on v4.** The design reserved this closure for the UNANCHORED
23
+ * qualified branch, on the premise (its §12.2) that anchoring and qualification could never
24
+ * combine. Issue #3205 dissolved that premise: the published official bundle IS anchored and
25
+ * qualified, on v7. Allocating disclosure on the unanchored branch would have forced the flagship to
26
+ * choose between its anchor and its disclosure record, so the allocation follows the real material.
27
+ * Anything narrower — a second, unanchored disclosed cell — would double the enumeration that
28
+ * #2889 exists to kill, and is deliberately not built.
29
+ *
30
+ * Additive in exactly the way v6 and v7 were: v2, v4, v5, v6, and v7 keep their member lists, check
31
+ * lists, and bytes, and only a run that is anchored AND qualification-projecting AND carrying a
32
+ * sealed disclosure declaration emits this one.
33
+ */
34
+ export declare const BUNDLE_V8_FORMAT: "benchmark-product-public-bundle/8";
35
+ export declare const SUPPORTED_BUNDLE_FORMATS: readonly ["benchmark-product-public-bundle/2", "benchmark-product-public-bundle/4", "benchmark-product-public-bundle/5", "benchmark-product-public-bundle/6", "benchmark-product-public-bundle/7", "benchmark-product-public-bundle/8"];
10
36
  export declare const BUNDLE_MANIFEST_FILENAME: "bundle.json";
11
37
  export declare const BundleManifestFileSchema: z.ZodObject<{
12
38
  path: z.ZodString;
@@ -14,7 +40,7 @@ export declare const BundleManifestFileSchema: z.ZodObject<{
14
40
  bytes: z.ZodNumber;
15
41
  }, z.core.$strip>;
16
42
  export declare const BundleManifestSchema: z.ZodUnion<readonly [z.ZodObject<{
17
- format: z.ZodUnion<readonly [z.ZodLiteral<"benchmark-product-public-bundle/2">, z.ZodLiteral<"benchmark-product-public-bundle/4">, z.ZodLiteral<"benchmark-product-public-bundle/6">]>;
43
+ format: z.ZodUnion<readonly [z.ZodLiteral<"benchmark-product-public-bundle/2">, z.ZodLiteral<"benchmark-product-public-bundle/4">, z.ZodLiteral<"benchmark-product-public-bundle/6">, z.ZodLiteral<"benchmark-product-public-bundle/7">, z.ZodLiteral<"benchmark-product-public-bundle/8">]>;
18
44
  files: z.ZodArray<z.ZodObject<{
19
45
  path: z.ZodString;
20
46
  sha256: z.ZodString;
@@ -22,7 +48,7 @@ export declare const BundleManifestSchema: z.ZodUnion<readonly [z.ZodObject<{
22
48
  }, z.core.$strip>>;
23
49
  }, z.core.$strip>, z.ZodObject<{
24
50
  format: z.ZodLiteral<"benchmark-product-public-bundle/5">;
25
- profile: z.ZodLiteral<"https://spec.jinn.network/profiles/benchmark-product-public-bundle/5">;
51
+ profile: z.ZodUnion<readonly [z.ZodLiteral<"https://spec.jinn.network/profiles/benchmark-product-public-bundle/5">, z.ZodLiteral<"https://spec.jinn.network/profiles/benchmark-product-public-bundle/5/metadata-first">]>;
26
52
  files: z.ZodArray<z.ZodObject<{
27
53
  path: z.ZodString;
28
54
  sha256: z.ZodString;
@@ -45,7 +71,7 @@ export interface VerifyBundleSnapshotDeps {
45
71
  }
46
72
  export interface BuildBundleManifestOptions {
47
73
  /** Defaults to v2 so existing producer and golden bytes remain immutable. */
48
- readonly format?: typeof BUNDLE_FORMAT | typeof BUNDLE_V4_FORMAT | typeof BUNDLE_V6_FORMAT;
74
+ readonly format?: typeof BUNDLE_FORMAT | typeof BUNDLE_V4_FORMAT | typeof BUNDLE_V6_FORMAT | typeof BUNDLE_V7_FORMAT | typeof BUNDLE_V8_FORMAT;
49
75
  }
50
76
  export declare function buildBundleManifest(bundleDir: string, filePaths: readonly string[], options?: BuildBundleManifestOptions): BuiltBundleManifest;
51
77
  export declare function verifyBundleManifest(bundleDir: string): BuiltBundleManifest;
package/dist/manifest.js CHANGED
@@ -12,11 +12,39 @@ export const BUNDLE_V5_FORMAT = "benchmark-product-public-bundle/5";
12
12
  * lists, byte shapes, and values; the `anchors/` member and the `integrity-anchors` check exist
13
13
  * only here. */
14
14
  export const BUNDLE_V6_FORMAT = "benchmark-product-public-bundle/6";
15
+ /**
16
+ * The anchored binary-qualification closure (issue #3205): v4's member list — `qualification.json`,
17
+ * the v4 evidence catalog, the v4 trust document — plus v6's `anchors/` member and its
18
+ * `integrity-anchors` check. Additive in exactly the same way v6 was: v2, v4, v5, and v6 keep their
19
+ * member lists, check lists, and bytes, and only a run that is BOTH anchored and
20
+ * qualification-projecting emits this one.
21
+ */
22
+ export const BUNDLE_V7_FORMAT = "benchmark-product-public-bundle/7";
23
+ /**
24
+ * The disclosed anchored binary-qualification closure (issue #2839, disclosure-specification-record
25
+ * design §6.5): v7's complete member list plus one `records/<sha256>.bin` member carrying the sealed
26
+ * disclosure-specification record, and the `disclosure-specification` check.
27
+ *
28
+ * **Why it stacks on v7 rather than on v4.** The design reserved this closure for the UNANCHORED
29
+ * qualified branch, on the premise (its §12.2) that anchoring and qualification could never
30
+ * combine. Issue #3205 dissolved that premise: the published official bundle IS anchored and
31
+ * qualified, on v7. Allocating disclosure on the unanchored branch would have forced the flagship to
32
+ * choose between its anchor and its disclosure record, so the allocation follows the real material.
33
+ * Anything narrower — a second, unanchored disclosed cell — would double the enumeration that
34
+ * #2889 exists to kill, and is deliberately not built.
35
+ *
36
+ * Additive in exactly the way v6 and v7 were: v2, v4, v5, v6, and v7 keep their member lists, check
37
+ * lists, and bytes, and only a run that is anchored AND qualification-projecting AND carrying a
38
+ * sealed disclosure declaration emits this one.
39
+ */
40
+ export const BUNDLE_V8_FORMAT = "benchmark-product-public-bundle/8";
15
41
  export const SUPPORTED_BUNDLE_FORMATS = [
16
42
  BUNDLE_FORMAT,
17
43
  BUNDLE_V4_FORMAT,
18
44
  BUNDLE_V5_FORMAT,
19
45
  BUNDLE_V6_FORMAT,
46
+ BUNDLE_V7_FORMAT,
47
+ BUNDLE_V8_FORMAT,
20
48
  ];
21
49
  export const BUNDLE_MANIFEST_FILENAME = "bundle.json";
22
50
  const SHA256_HEX = /^[a-f0-9]{64}$/;
@@ -30,6 +58,8 @@ const LegacyBundleManifestSchema = z.object({
30
58
  z.literal(BUNDLE_FORMAT),
31
59
  z.literal(BUNDLE_V4_FORMAT),
32
60
  z.literal(BUNDLE_V6_FORMAT),
61
+ z.literal(BUNDLE_V7_FORMAT),
62
+ z.literal(BUNDLE_V8_FORMAT),
33
63
  ]),
34
64
  files: z.array(BundleManifestFileSchema).min(1),
35
65
  });
@@ -2,3 +2,10 @@
2
2
  export declare const PUBLIC_BUNDLE_FILES: readonly ["static-bundle.json", "benchmark.json", "run.json", "matrix.json", "report.json", "report-envelope.json", "claim-package.json", "verdicts.json", "evidence.json", "verification/assembly.jsonl", "trust/public-keys.json", "index.html", "badge.svg", "social-card.svg", "README.md", "share.txt"];
3
3
  /** V4 is the fixed v2 graph with exactly one canonical qualification projection added. */
4
4
  export declare const PUBLIC_BUNDLE_V4_FILES: readonly ("benchmark.json" | "run.json" | "matrix.json" | "report.json" | "report-envelope.json" | "claim-package.json" | "qualification.json" | "static-bundle.json" | "evidence.json" | "verdicts.json" | "verification/assembly.jsonl" | "trust/public-keys.json" | "index.html" | "badge.svg" | "social-card.svg" | "README.md" | "share.txt")[];
5
+ /**
6
+ * V8 (issue #2839) adds no mandatory MEMBER. The disclosure-specification record travels at the
7
+ * already-allowlisted `records/<sha256>.bin` path, driven by the evidence catalog exactly like every
8
+ * other sealed record, so v8's mandatory list is v7's — which is v4's. The alias exists so the
9
+ * equality is stated in the file that owns member lists rather than inferred at the call site.
10
+ */
11
+ export declare const PUBLIC_BUNDLE_V8_FILES: readonly ("benchmark.json" | "run.json" | "matrix.json" | "report.json" | "report-envelope.json" | "claim-package.json" | "qualification.json" | "static-bundle.json" | "evidence.json" | "verdicts.json" | "verification/assembly.jsonl" | "trust/public-keys.json" | "index.html" | "badge.svg" | "social-card.svg" | "README.md" | "share.txt")[];
@@ -11,3 +11,10 @@ export const PUBLIC_BUNDLE_V4_FILES = [
11
11
  "qualification.json",
12
12
  ...PUBLIC_BUNDLE_FILES.slice(7),
13
13
  ];
14
+ /**
15
+ * V8 (issue #2839) adds no mandatory MEMBER. The disclosure-specification record travels at the
16
+ * already-allowlisted `records/<sha256>.bin` path, driven by the evidence catalog exactly like every
17
+ * other sealed record, so v8's mandatory list is v7's — which is v4's. The alias exists so the
18
+ * equality is stated in the file that owns member lists rather than inferred at the call site.
19
+ */
20
+ export const PUBLIC_BUNDLE_V8_FILES = PUBLIC_BUNDLE_V4_FILES;
@@ -0,0 +1,31 @@
1
+ import type { PublicBundleVerificationResult } from "./verify.js";
2
+ /**
3
+ * The one check a metadata-first `benchmark-product-public-bundle/5` defers (issue #2986): its
4
+ * artifact digests are carried, its artifact bodies are not.
5
+ */
6
+ export declare const NOT_FETCHED_CHECK: "artifact-integrity";
7
+ export type VerificationCheckState = "passed" | "not fetched";
8
+ export interface VerificationCheckOutcome {
9
+ readonly check: string;
10
+ readonly state: VerificationCheckState;
11
+ }
12
+ export interface VerificationOutcome {
13
+ readonly outcomes: readonly VerificationCheckOutcome[];
14
+ /** Checks that actually passed. A deferred check is never counted here. */
15
+ readonly passed: number;
16
+ readonly notFetched: number;
17
+ /** The check count the bundle's declared format promises. */
18
+ readonly total: number;
19
+ /** Present exactly when the bundle deferred artifact content. */
20
+ readonly artifactContent?: {
21
+ readonly notFetched: number;
22
+ readonly notFetchedDigests: readonly string[];
23
+ };
24
+ }
25
+ /**
26
+ * The single derivation of what a verification result may be said to have proved. Every reader
27
+ * surface -- the standalone CLI, the product CLI, the local viewer, the web action -- reads this
28
+ * rather than counting `checks` itself, because a surface that counts for itself is a surface that
29
+ * can print a pass over bytes nobody read (issue #2986).
30
+ */
31
+ export declare function summarizeVerificationOutcome(result: PublicBundleVerificationResult): VerificationOutcome;
@@ -0,0 +1,45 @@
1
+ import { EVIDENCE_NATIVE_BUNDLE_V5_CHECKS } from "@jinn-network/benchmarking-evidence";
2
+ import { PUBLIC_BUNDLE_VERIFICATION_CHECKS, PUBLIC_BUNDLE_V6_CHECKS, PUBLIC_BUNDLE_V7_CHECKS, PUBLIC_BUNDLE_V8_CHECKS, } from "./reader-instructions.js";
3
+ /**
4
+ * The one check a metadata-first `benchmark-product-public-bundle/5` defers (issue #2986): its
5
+ * artifact digests are carried, its artifact bodies are not.
6
+ */
7
+ export const NOT_FETCHED_CHECK = "artifact-integrity";
8
+ /**
9
+ * The single derivation of what a verification result may be said to have proved. Every reader
10
+ * surface -- the standalone CLI, the product CLI, the local viewer, the web action -- reads this
11
+ * rather than counting `checks` itself, because a surface that counts for itself is a surface that
12
+ * can print a pass over bytes nobody read (issue #2986).
13
+ */
14
+ export function summarizeVerificationOutcome(result) {
15
+ const total = result.format === "benchmark-product-public-bundle/5"
16
+ ? EVIDENCE_NATIVE_BUNDLE_V5_CHECKS.length
17
+ : result.format === "benchmark-product-public-bundle/6"
18
+ ? PUBLIC_BUNDLE_V6_CHECKS.length
19
+ : result.format === "benchmark-product-public-bundle/7"
20
+ ? PUBLIC_BUNDLE_V7_CHECKS.length
21
+ : result.format === "benchmark-product-public-bundle/8"
22
+ ? PUBLIC_BUNDLE_V8_CHECKS.length
23
+ : PUBLIC_BUNDLE_VERIFICATION_CHECKS.length;
24
+ const deferred = result.format === "benchmark-product-public-bundle/5"
25
+ && result.artifactContent.status === "not-fetched"
26
+ ? result.artifactContent
27
+ : undefined;
28
+ const outcomes = result.checks.map((check) => ({
29
+ check,
30
+ state: (deferred !== undefined && check === NOT_FETCHED_CHECK ? "not fetched" : "passed"),
31
+ }));
32
+ const notFetched = outcomes.filter((outcome) => outcome.state === "not fetched").length;
33
+ return {
34
+ outcomes,
35
+ passed: outcomes.length - notFetched,
36
+ notFetched,
37
+ total,
38
+ ...(deferred === undefined ? {} : {
39
+ artifactContent: {
40
+ notFetched: deferred.notFetched,
41
+ notFetchedDigests: deferred.notFetchedDigests,
42
+ },
43
+ }),
44
+ };
45
+ }
@@ -1,4 +1,5 @@
1
1
  import { validateBinaryInstrumentParameters } from "@jinn-network/benchmarking-aggregate";
2
+ import { PROMPTED_SCREENING_LIMITATIONS, PROMPTED_SCREENING_PROFILE, } from "../admission/contracts.js";
2
3
  export const BINARY_INSTRUMENT_REPORT_LIMITATIONS = {
3
4
  mutableModelAlias: "The gpt-5.6-luna identifier is a mutable provider alias; this evidence does not prove invariant model weights across calls.",
4
5
  reviewerKeyPerson: "Distinct reviewer signing keys prove key control, not that the controllers are distinct people.",
@@ -48,5 +49,10 @@ export function binaryInstrumentReportLimitations(parameters) {
48
49
  ...(parameters["truthAdmission"] === "screened-operator-sampled"
49
50
  ? [BINARY_INSTRUMENT_REPORT_LIMITATIONS.screenedNotIndependentlyLabeled]
50
51
  : []),
52
+ // Appended only for an authenticated screening-table/v2 closure. Legacy parameter sets omit
53
+ // the profile and therefore retain their exact limitation arrays and sealed Report bytes.
54
+ ...(parameters["promptedScreeningProfile"] === PROMPTED_SCREENING_PROFILE
55
+ ? PROMPTED_SCREENING_LIMITATIONS
56
+ : []),
51
57
  ];
52
58
  }
@@ -1,6 +1,7 @@
1
1
  import { type BenchmarkRecord, type MatrixRecord, type ReportRecord, type RunRecord } from "@jinn-network/benchmarking-records";
2
2
  import { type ClaimPackage } from "./claim.js";
3
3
  import type { ClaimAnchor } from "./anchor-claims.js";
4
+ import type { ClaimDisclosureSection } from "./disclosure.js";
4
5
  export interface ClaimRecordIdentities {
5
6
  readonly benchmarkSha256: string;
6
7
  readonly runSha256: string;
@@ -8,6 +9,11 @@ export interface ClaimRecordIdentities {
8
9
  readonly reportSha256: string | undefined;
9
10
  readonly reportEnvelopeSha256: string;
10
11
  }
12
+ /** The path of the first field that actually differs, or `undefined` when the two values are
13
+ * equal — equal leaves must report NO difference, or the object branch returns on its first key
14
+ * and every mismatch names the first key in sorted order instead of the edited field. Single
15
+ * implementation: product core imports this helper rather than keeping a second copy. */
16
+ export declare function firstDifference(actual: unknown, expected: unknown, path?: string): string | undefined;
11
17
  export declare function assertClaimConsistency(input: {
12
18
  readonly claim: ClaimPackage;
13
19
  readonly identities: ClaimRecordIdentities;
@@ -22,5 +28,6 @@ export declare function assertClaimConsistency(input: {
22
28
  readonly previewCount: number;
23
29
  readonly timestamps: readonly string[];
24
30
  }; /** anchor-evidence §7.4: the anchors section re-derived from the carried AnchorEvidence bytes, never read from the claim under test. */
25
- readonly anchors?: readonly ClaimAnchor[];
31
+ readonly anchors?: readonly ClaimAnchor[]; /** disclosure-specification-record design §7 step 10: the disclosure section re-derived from the carried record's own bytes, never read from the claim under test. */
32
+ readonly disclosure?: ClaimDisclosureSection;
26
33
  }): void;