@colophon-claims/verify 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +35 -0
  2. package/dist/admission/application.d.ts +18 -0
  3. package/dist/admission/application.js +53 -0
  4. package/dist/admission/contracts.d.ts +264 -0
  5. package/dist/admission/contracts.js +290 -0
  6. package/dist/admission/index.d.ts +5 -0
  7. package/dist/admission/index.js +5 -0
  8. package/dist/admission/intake.d.ts +75 -0
  9. package/dist/admission/intake.js +70 -0
  10. package/dist/admission/result-evaluation.d.ts +18 -0
  11. package/dist/admission/result-evaluation.js +71 -0
  12. package/dist/admission/screening-sample.d.ts +56 -0
  13. package/dist/admission/screening-sample.js +126 -0
  14. package/dist/admission/verification.d.ts +89 -0
  15. package/dist/admission/verification.js +709 -0
  16. package/dist/anchor/check.d.ts +95 -0
  17. package/dist/anchor/check.js +146 -0
  18. package/dist/anchor/ports.d.ts +6 -0
  19. package/dist/anchor/ports.js +354 -0
  20. package/dist/assets.d.ts +33 -0
  21. package/dist/assets.js +885 -0
  22. package/dist/bin.d.ts +2 -0
  23. package/dist/bin.js +20 -0
  24. package/dist/cli.d.ts +14 -0
  25. package/dist/cli.js +205 -0
  26. package/dist/comparison.d.ts +64 -0
  27. package/dist/comparison.js +235 -0
  28. package/dist/index.d.ts +23 -0
  29. package/dist/index.js +23 -0
  30. package/dist/manifest.d.ts +54 -0
  31. package/dist/manifest.js +240 -0
  32. package/dist/materialize.d.ts +4 -0
  33. package/dist/materialize.js +13 -0
  34. package/dist/profile/admission-receipts.d.ts +11 -0
  35. package/dist/profile/admission-receipts.js +24 -0
  36. package/dist/profile/anchor-claims.d.ts +157 -0
  37. package/dist/profile/anchor-claims.js +330 -0
  38. package/dist/profile/artifacts.d.ts +261 -0
  39. package/dist/profile/artifacts.js +139 -0
  40. package/dist/profile/assembly-ports.d.ts +19 -0
  41. package/dist/profile/assembly-ports.js +42 -0
  42. package/dist/profile/binary-judge-manifest.d.ts +121 -0
  43. package/dist/profile/binary-judge-manifest.js +106 -0
  44. package/dist/profile/binary-qualification.d.ts +9 -0
  45. package/dist/profile/binary-qualification.js +52 -0
  46. package/dist/profile/branding-assets.d.ts +5 -0
  47. package/dist/profile/branding-assets.js +5 -0
  48. package/dist/profile/branding.d.ts +24 -0
  49. package/dist/profile/branding.js +14 -0
  50. package/dist/profile/claim-consistency.d.ts +26 -0
  51. package/dist/profile/claim-consistency.js +91 -0
  52. package/dist/profile/claim.d.ts +324 -0
  53. package/dist/profile/claim.js +724 -0
  54. package/dist/profile/errors.d.ts +49 -0
  55. package/dist/profile/errors.js +78 -0
  56. package/dist/profile/inspect-assurance.d.ts +16 -0
  57. package/dist/profile/inspect-assurance.js +30 -0
  58. package/dist/profile/inspect-disclosure.d.ts +28 -0
  59. package/dist/profile/inspect-disclosure.js +54 -0
  60. package/dist/profile/inspect-manifest.d.ts +706 -0
  61. package/dist/profile/inspect-manifest.js +397 -0
  62. package/dist/profile/isolation.d.ts +21 -0
  63. package/dist/profile/isolation.js +31 -0
  64. package/dist/profile/pinning-evidence.d.ts +41 -0
  65. package/dist/profile/pinning-evidence.js +43 -0
  66. package/dist/profile/ports.d.ts +3 -0
  67. package/dist/profile/ports.js +11 -0
  68. package/dist/profile/preview-log.d.ts +4 -0
  69. package/dist/profile/preview-log.js +3 -0
  70. package/dist/profile/run-results.d.ts +17 -0
  71. package/dist/profile/run-results.js +23 -0
  72. package/dist/profile/signing.d.ts +10 -0
  73. package/dist/profile/signing.js +46 -0
  74. package/dist/profile/trust.d.ts +13 -0
  75. package/dist/profile/trust.js +35 -0
  76. package/dist/profile/venue.d.ts +2 -0
  77. package/dist/profile/venue.js +2 -0
  78. package/dist/profile/verdict.d.ts +28 -0
  79. package/dist/profile/verdict.js +51 -0
  80. package/dist/reader-instructions.d.ts +41 -0
  81. package/dist/reader-instructions.js +52 -0
  82. package/dist/schema.d.ts +370 -0
  83. package/dist/schema.js +484 -0
  84. package/dist/verify.d.ts +72 -0
  85. package/dist/verify.js +1573 -0
  86. package/dist/version.d.ts +2 -0
  87. package/dist/version.js +2 -0
  88. package/package.json +63 -3
  89. package/schemas/assembly-row.schema.json +194 -0
  90. package/schemas/bundle-manifest.schema.json +23 -0
  91. package/schemas/claim-package.schema.json +279 -0
  92. package/schemas/dsse-envelope.schema.json +25 -0
  93. package/schemas/evidence-catalog.schema.json +43 -0
  94. package/schemas/public-trust.schema.json +44 -0
  95. package/schemas/verdict-catalog.schema.json +27 -0
  96. package/scripts/external-verify.py +238 -0
@@ -0,0 +1,709 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { z } from "zod";
3
+ import { VERDICT_DSSE_PAYLOAD_TYPE, canonicalJsonBytes, compareCodeUnitStrings, parseBinaryJudgmentAnalysisContext, parseBinaryJudgmentInstrument, parseBinaryJudgmentLabelResolution, parseBinaryJudgmentPayload, recordDigest, sealBinaryJudgmentInstrument, } from "@jinn-network/task-execution-profiles";
4
+ import { parseExactDsseEnvelope } from "@jinn-network/trust-core";
5
+ import { BINARY_JUDGMENT_HUMAN_REVIEW_EVALUATION_SPEC_SEALED, HUMAN_REVIEW_FORM_SEALED, } from "./application.js";
6
+ import { BinaryJudgmentAdmissionManifestSchema, HUMAN_REVIEW_OPERATOR_ASSERTION_MEDIA_TYPE, HUMAN_REVIEW_PACKET_MEDIA_TYPE, HUMAN_REVIEW_RESPONSE_MEDIA_TYPE, HUMAN_REVIEW_REVEAL_RECEIPT_MEDIA_TYPE, HUMAN_REVIEW_ROSTER_MEDIA_TYPE, HUMAN_REVIEW_VISIBILITY_RECEIPT_MEDIA_TYPE, SCREENING_REVEAL_RECEIPT_MEDIA_TYPE, SCREENING_TABLE_MEDIA_TYPE, HumanReviewOperatorAssertionSchema, HumanReviewPacketSchema, HumanReviewReplacementLedgerSchema, HumanReviewResponseSchema, HumanReviewRevealReceiptSchema, HumanReviewRosterSchema, HumanReviewVisibilityReceiptSchema, ScreeningRevealReceiptSchema, ScreeningTableSchema, } from "./contracts.js";
7
+ import { readOrderedAdmissionVerdictMeasurements, readAdmissionVerdictEnvelope } from "./result-evaluation.js";
8
+ import { computeScreeningSample, ScreeningSampleError } from "./screening-sample.js";
9
+ // Verbatim duplicate of `schema.ts`'s `BUNDLE_V4_ADMISSION_EVIDENCE_ROLES`, hand-kept in sync
10
+ // (spec §6.8a Group C).
11
+ export const BINARY_JUDGMENT_ADMISSION_RECORD_ROLES = [
12
+ "admission-manifest", "replacement-ledger", "source-item", "label-resolution",
13
+ "analysis-context", "human-review-evaluation-spec", "human-review-form",
14
+ "human-review-packet", "human-review-response", "human-review-verdict",
15
+ "reviewer-roster", "review-visibility-receipt", "review-reveal-receipt",
16
+ "operator-assertion",
17
+ // Appended after operator-assertion (spec §6.8a Group C, first bullet; packet P6).
18
+ "screening-table",
19
+ "screening-reveal-receipt",
20
+ // Appended only: these exact nested records are authenticated by the signed screening table.
21
+ "screening-instrument",
22
+ "screening-sampling-script",
23
+ "screening-raw-outputs",
24
+ ];
25
+ export class BinaryJudgmentAdmissionClosureError extends Error {
26
+ path;
27
+ constructor(path, detail, options) {
28
+ super(`${path}: ${detail}`, options);
29
+ this.name = "BinaryJudgmentAdmissionClosureError";
30
+ this.path = path;
31
+ }
32
+ }
33
+ function fail(path, detail, cause) {
34
+ throw new BinaryJudgmentAdmissionClosureError(path, detail, cause === undefined ? undefined : { cause });
35
+ }
36
+ function bytesEqual(left, right) {
37
+ return left.byteLength === right.byteLength && left.every((byte, index) => byte === right[index]);
38
+ }
39
+ function exactCanonical(schema, bytes, path) {
40
+ let json;
41
+ try {
42
+ json = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
43
+ }
44
+ catch (cause) {
45
+ fail(path, "record is not UTF-8 JSON", cause);
46
+ }
47
+ const parsed = schema.safeParse(json);
48
+ if (!parsed.success)
49
+ fail(path, parsed.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; "));
50
+ if (!bytesEqual(bytes, canonicalJsonBytes(parsed.data)))
51
+ fail(path, "record is not in exact canonical JSON encoding");
52
+ return parsed.data;
53
+ }
54
+ function prefixed(value) {
55
+ return (value.startsWith("sha256:") ? value : `sha256:${value}`);
56
+ }
57
+ function bare(value) {
58
+ return value.startsWith("sha256:") ? value.slice("sha256:".length) : value;
59
+ }
60
+ function sameStrings(left, right) {
61
+ return left.length === right.length && left.every((value, index) => value === right[index]);
62
+ }
63
+ function resolve(state, digest, path, role) {
64
+ if (!/^sha256:[0-9a-f]{64}$/u.test(digest))
65
+ fail(path, "record digest is not canonical sha256");
66
+ const exactDigest = digest;
67
+ let bytes;
68
+ try {
69
+ bytes = state.ports.resolveExactRecord(exactDigest);
70
+ }
71
+ catch (cause) {
72
+ fail(path, `cannot resolve ${digest}`, cause);
73
+ }
74
+ if (recordDigest(bytes) !== digest)
75
+ fail(path, `resolved bytes do not hash to ${digest}`);
76
+ state.reachable.add(exactDigest);
77
+ const roles = state.roles.get(exactDigest) ?? new Set();
78
+ roles.add(role);
79
+ state.roles.set(exactDigest, roles);
80
+ return bytes;
81
+ }
82
+ function parseProfileRecord(state, digest, path, parse, role) {
83
+ const bytes = resolve(state, digest, path, role);
84
+ let value;
85
+ try {
86
+ value = parse(bytes);
87
+ }
88
+ catch (cause) {
89
+ fail(path, "record does not satisfy its registered schema", cause);
90
+ }
91
+ if (!bytesEqual(bytes, canonicalJsonBytes(value)))
92
+ fail(path, "record is not in exact canonical JSON encoding");
93
+ return { value, bytes };
94
+ }
95
+ function verifyFrozenHumanReviewSpec(state, path) {
96
+ const specBytes = resolve(state, BINARY_JUDGMENT_HUMAN_REVIEW_EVALUATION_SPEC_SEALED.digest, `${path}.evaluationSpecification`, "human-review-evaluation-spec");
97
+ if (!bytesEqual(specBytes, BINARY_JUDGMENT_HUMAN_REVIEW_EVALUATION_SPEC_SEALED.bytes)) {
98
+ fail(path, "frozen human-review EvaluationSpec bytes do not match the registered contract");
99
+ }
100
+ const formBytes = resolve(state, HUMAN_REVIEW_FORM_SEALED.digest, `${path}.reviewForm`, "human-review-form");
101
+ if (!bytesEqual(formBytes, HUMAN_REVIEW_FORM_SEALED.bytes)) {
102
+ fail(path, "frozen human-review form bytes do not match the registered contract");
103
+ }
104
+ }
105
+ function verifyReview(state, verdictSha256, expectedItemSha256, path) {
106
+ const envelopeBytes = resolve(state, verdictSha256, path, "human-review-verdict");
107
+ let envelope;
108
+ try {
109
+ envelope = parseExactDsseEnvelope(envelopeBytes);
110
+ }
111
+ catch (cause) {
112
+ fail(path, "review verdict is not an exact compact DSSE envelope", cause);
113
+ }
114
+ if (envelope.payloadType !== VERDICT_DSSE_PAYLOAD_TYPE || envelope.signatures.length !== 1) {
115
+ fail(path, "review verdict must be one Result Evaluation envelope with one signature");
116
+ }
117
+ let statement;
118
+ try {
119
+ statement = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(envelope.payloadBytes));
120
+ }
121
+ catch (cause) {
122
+ fail(path, "Result Evaluation payload is not UTF-8 JSON", cause);
123
+ }
124
+ if (!bytesEqual(canonicalJsonBytes(statement), envelope.payloadBytes))
125
+ fail(path, "Result Evaluation payload is not exact canonical JSON");
126
+ let view;
127
+ let ordered;
128
+ try {
129
+ view = readAdmissionVerdictEnvelope(envelopeBytes);
130
+ ordered = readOrderedAdmissionVerdictMeasurements(envelopeBytes);
131
+ }
132
+ catch (cause) {
133
+ fail(path, "Result Evaluation payload is outside the closed verdict grammar", cause);
134
+ }
135
+ const signature = envelope.signatures[0];
136
+ const signatureKeyId = signature.keyid;
137
+ if (typeof signatureKeyId !== "string" || !state.ports.verifyReviewerSignature({ envelopeBytes, evaluatorId: view.evaluatorId, keyId: signatureKeyId })) {
138
+ fail(path, "reviewer signature or evaluator/key binding is invalid");
139
+ }
140
+ if (view.evaluationSpecificationSha256 !== bare(BINARY_JUDGMENT_HUMAN_REVIEW_EVALUATION_SPEC_SEALED.digest)) {
141
+ fail(path, "review names a different human-review EvaluationSpec");
142
+ }
143
+ verifyFrozenHumanReviewSpec(state, path);
144
+ const expectedNames = ["truthLabel", "reviewComplete", "reviewPacketSha256", "visibilityReceiptSha256", "responseSha256"];
145
+ if (ordered.length !== expectedNames.length || ordered.some((entry, index) => entry.name !== expectedNames[index])) {
146
+ fail(path, "review measurements do not match the frozen human-review EvaluationSpec");
147
+ }
148
+ const [labelRaw, completeRaw, packetRaw, visibilityRaw, responseRaw] = ordered.map((entry) => entry.value);
149
+ const labelResult = z.enum(["CORRECT", "WRONG", "indeterminate"]).safeParse(labelRaw);
150
+ if (!labelResult.success || typeof completeRaw !== "boolean" || typeof packetRaw !== "string" || typeof visibilityRaw !== "string" || typeof responseRaw !== "string") {
151
+ fail(path, "review measurement values are invalid");
152
+ }
153
+ const packetSha256 = prefixed(packetRaw);
154
+ const visibilityReceiptSha256 = prefixed(visibilityRaw);
155
+ const responseSha256 = prefixed(responseRaw);
156
+ const packet = exactCanonical(HumanReviewPacketSchema, resolve(state, packetSha256, `${path}.packet`, "human-review-packet"), `${path}.packet`);
157
+ const visibility = exactCanonical(HumanReviewVisibilityReceiptSchema, resolve(state, visibilityReceiptSha256, `${path}.visibility`, "review-visibility-receipt"), `${path}.visibility`);
158
+ const response = exactCanonical(HumanReviewResponseSchema, resolve(state, responseSha256, `${path}.response`, "human-review-response"), `${path}.response`);
159
+ const itemBytes = resolve(state, expectedItemSha256, `${path}.item`, "source-item");
160
+ const item = parseProfileRecord(state, expectedItemSha256, `${path}.item`, (bytes) => parseBinaryJudgmentPayload(bytes), "source-item").value;
161
+ if (!bytesEqual(itemBytes, canonicalJsonBytes(packet.item)) || packet.item.itemId !== item.itemId) {
162
+ fail(path, "packet embedded item does not equal the exact referenced item record");
163
+ }
164
+ const subjects = statement.subject;
165
+ const predicate = statement.predicate;
166
+ if (!Array.isArray(subjects) || subjects.length !== 2 || typeof predicate !== "object" || predicate === null || Array.isArray(predicate)) {
167
+ fail(path, "Result Evaluation subjects or predicate are malformed");
168
+ }
169
+ const subjectView = subjects;
170
+ const predicateView = predicate;
171
+ const expectedVerdict = !completeRaw || labelResult.data === "indeterminate"
172
+ ? "inconclusive"
173
+ : labelResult.data === "CORRECT" ? "pass" : "fail";
174
+ const expectedEvidence = [
175
+ ["human-review-packet", bare(packetSha256), HUMAN_REVIEW_PACKET_MEDIA_TYPE],
176
+ ["visibility-receipt", bare(visibilityReceiptSha256), HUMAN_REVIEW_VISIBILITY_RECEIPT_MEDIA_TYPE],
177
+ ["human-review-response", bare(responseSha256), HUMAN_REVIEW_RESPONSE_MEDIA_TYPE],
178
+ ];
179
+ const evidenceMatches = Array.isArray(predicateView.evidence)
180
+ && predicateView.evidence.length === expectedEvidence.length
181
+ && predicateView.evidence.every((raw, index) => {
182
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
183
+ return false;
184
+ const entry = raw;
185
+ const expected = expectedEvidence[index];
186
+ return entry.name === expected[0]
187
+ && entry.digest?.sha256 === expected[1]
188
+ && entry.mediaType === expected[2]
189
+ && Object.keys(entry).every((key) => ["name", "digest", "mediaType"].includes(key));
190
+ });
191
+ if (packet.itemSha256 !== expectedItemSha256
192
+ || recordDigest(canonicalJsonBytes(packet.item)) !== expectedItemSha256
193
+ || packet.evaluationSpecSha256 !== BINARY_JUDGMENT_HUMAN_REVIEW_EVALUATION_SPEC_SEALED.digest
194
+ || packet.reviewerId !== view.evaluatorId
195
+ || visibility.packetSha256 !== packetSha256
196
+ || visibility.itemSha256 !== expectedItemSha256
197
+ || visibility.reviewerId !== view.evaluatorId
198
+ || response.packetSha256 !== packetSha256
199
+ || response.visibilityReceiptSha256 !== visibilityReceiptSha256
200
+ || response.itemSha256 !== expectedItemSha256
201
+ || response.evaluatorId !== view.evaluatorId
202
+ || response.label !== labelResult.data
203
+ || response.complete !== completeRaw
204
+ || response.completedAt !== view.evaluatedAt
205
+ || Date.parse(response.completedAt) < Date.parse(visibility.issuedAt)
206
+ || subjectView[0]?.name !== "binary-judgment-item"
207
+ || subjectView[0]?.digest?.sha256 !== bare(expectedItemSha256)
208
+ || Object.keys(subjectView[0] ?? {}).some((key) => !["name", "digest"].includes(key))
209
+ || subjectView[1]?.name !== "human-review-response"
210
+ || subjectView[1]?.digest?.sha256 !== bare(responseSha256)
211
+ || subjectView[1]?.mediaType !== HUMAN_REVIEW_RESPONSE_MEDIA_TYPE
212
+ || Object.keys(subjectView[1] ?? {}).some((key) => !["name", "digest", "mediaType"].includes(key))
213
+ || predicateView.taskSubject !== "binary-judgment-item"
214
+ || !Array.isArray(predicateView.resultSubjects)
215
+ || predicateView.resultSubjects.length !== 1
216
+ || predicateView.resultSubjects[0] !== "human-review-response"
217
+ || predicateView.evaluator?.id !== view.evaluatorId
218
+ || predicateView.evaluator?.["network.jinn.reviewer.keyid"] !== signatureKeyId
219
+ || predicateView.verdict !== expectedVerdict
220
+ || predicateView.evaluatedAt !== response.completedAt
221
+ || !evidenceMatches
222
+ || !Array.isArray(predicateView.limitations)
223
+ || !sameStrings(predicateView.limitations, ["reviewer-person-distinctness-is-roster-attested"])
224
+ || view.evidence?.length !== 3)
225
+ fail(path, "Result Evaluation packet/response/visibility/item/evaluator joins do not close");
226
+ return {
227
+ evaluatorId: view.evaluatorId,
228
+ keyId: signatureKeyId,
229
+ label: labelResult.data,
230
+ complete: completeRaw,
231
+ completedAt: response.completedAt,
232
+ itemId: item.itemId,
233
+ packetSha256,
234
+ visibilityReceiptSha256,
235
+ };
236
+ }
237
+ function authorityPayload(state, options) {
238
+ const { digest, mediaType, role, evidenceRole, schema, path } = options;
239
+ const envelopeBytes = resolve(state, digest, path, evidenceRole);
240
+ let envelope;
241
+ try {
242
+ envelope = parseExactDsseEnvelope(envelopeBytes);
243
+ }
244
+ catch (cause) {
245
+ fail(path, "authority evidence is not an exact compact DSSE envelope", cause);
246
+ }
247
+ if (envelope.payloadType !== mediaType || envelope.signatures.length !== 1)
248
+ fail(path, "authority evidence has the wrong payload type or signature count");
249
+ const signature = envelope.signatures[0];
250
+ const signatureKeyId = signature.keyid;
251
+ if (typeof signatureKeyId !== "string" || !state.ports.verifyAuthoritySignature({ envelopeBytes, keyId: signatureKeyId, role })) {
252
+ fail(path, `authority signature is invalid for role ${role}`);
253
+ }
254
+ return { value: exactCanonical(schema, envelope.payloadBytes, `${path}.payload`), keyId: signatureKeyId };
255
+ }
256
+ function verifyHumanEvidence(state, refs, expectedItemSha256, expectedDraftId, admittedAt, path) {
257
+ const reviews = refs.reviewVerdictSha256s.map((digest, index) => verifyReview(state, digest, expectedItemSha256, `${path}.reviews.${index}`));
258
+ if (reviews[0].evaluatorId === reviews[1].evaluatorId || reviews[0].keyId === reviews[1].keyId) {
259
+ fail(path, "reviewers must have distinct evaluator identities and keys");
260
+ }
261
+ if (reviews[0].itemId !== reviews[1].itemId)
262
+ fail(path, "review packets disagree on itemId");
263
+ const visibilityDigests = reviews.map((review) => review.visibilityReceiptSha256).sort(compareCodeUnitStrings);
264
+ if (!sameStrings(visibilityDigests, refs.visibilityReceiptSha256s))
265
+ fail(path, "declared visibility receipt coverage differs from signed reviews");
266
+ const rosterEvidence = authorityPayload(state, {
267
+ digest: refs.reviewerRosterSha256,
268
+ mediaType: HUMAN_REVIEW_ROSTER_MEDIA_TYPE,
269
+ role: "roster-attestor",
270
+ evidenceRole: "reviewer-roster",
271
+ schema: HumanReviewRosterSchema,
272
+ path: `${path}.roster`,
273
+ });
274
+ const roster = rosterEvidence.value;
275
+ if (roster.attestorRole !== "roster-attestor" || roster.attestorKeyId !== rosterEvidence.keyId || roster.itemSha256 !== expectedItemSha256) {
276
+ fail(path, "roster authority role/key/item binding is invalid");
277
+ }
278
+ const declarations = [...roster.reviewers];
279
+ if (compareCodeUnitStrings(declarations[0].evaluatorId, declarations[1].evaluatorId) >= 0)
280
+ fail(path, "roster reviewers are not sorted and unique");
281
+ const reviewByEvaluator = new Map(reviews.map((review) => [review.evaluatorId, review]));
282
+ if (declarations.some((declaration) => reviewByEvaluator.get(declaration.evaluatorId)?.keyId !== declaration.keyId)) {
283
+ fail(path, "roster does not bind the exact reviewer evaluator/key pairs");
284
+ }
285
+ if (declarations[0].personId === declarations[1].personId || declarations.some((declaration) => declaration.conflicts.length > 0)) {
286
+ fail(path, "roster does not attest distinct conflict-free people");
287
+ }
288
+ const revealEvidence = authorityPayload(state, {
289
+ digest: refs.revealReceiptSha256,
290
+ mediaType: HUMAN_REVIEW_REVEAL_RECEIPT_MEDIA_TYPE,
291
+ role: "truth-reveal-attestor",
292
+ evidenceRole: "review-reveal-receipt",
293
+ schema: HumanReviewRevealReceiptSchema,
294
+ path: `${path}.reveal`,
295
+ });
296
+ const reveal = revealEvidence.value;
297
+ if (reveal.attestorRole !== "truth-reveal-attestor"
298
+ || reveal.attestorKeyId !== revealEvidence.keyId
299
+ || revealEvidence.keyId !== rosterEvidence.keyId
300
+ || reveal.draftId !== expectedDraftId
301
+ || reveal.itemSha256 !== expectedItemSha256
302
+ || reveal.judgeExecutionState !== "not-started"
303
+ || reveal.truthFrozenAt !== admittedAt
304
+ || reviews.some((review) => Date.parse(review.completedAt) > Date.parse(reveal.truthFrozenAt)))
305
+ fail(path, "signed reveal does not follow both completed reviews under the same authority");
306
+ return { reviews, itemId: reviews[0].itemId };
307
+ }
308
+ function expectedExclusionReason(reviews) {
309
+ if (reviews.some((review) => !review.complete))
310
+ return "review-incomplete";
311
+ if (reviews.some((review) => review.label === "indeterminate"))
312
+ return "review-indeterminate";
313
+ if (reviews[0].label !== reviews[1].label)
314
+ return "review-disagreement";
315
+ return undefined;
316
+ }
317
+ /**
318
+ * The screened branch's sibling of `expectedExclusionReason` above, derived from §6.4's admission
319
+ * rule instead of two signed reviews (spec §6.4, §6.5 check (4)). The seventh copy of the
320
+ * replacement-ledger reason vocabulary this file carries (contracts.ts's own comment names the
321
+ * other six and defers this one to S3, "recomputation logic").
322
+ *
323
+ * A row this file already knows is admitted returns `undefined` (no exclusion). Among excluded
324
+ * rows: a FLAGGED row (`screeningVerdict !== intendedLabel`) is always hand-checked in a passing
325
+ * closure (check (2) requires it), so its reason names the screen's own finding regardless of
326
+ * that hand check having run -- `screening-indeterminate` when the screen itself was
327
+ * indeterminate, `screening-disagreement` for a definite-label disagreement. `screening-hand-
328
+ * excluded` is reserved for the one case a flagged-first read would miss: R-3's tie-break, a row
329
+ * the screen AGREED with that the hand check overrode to exclude.
330
+ */
331
+ function expectedScreeningExclusionReason(row) {
332
+ const agreed = row.screeningVerdict === row.intendedLabel;
333
+ const admitted = row.handChecked ? row.handVerdict === "confirm" : agreed;
334
+ if (admitted)
335
+ return undefined;
336
+ if (!agreed)
337
+ return row.screeningVerdict === "indeterminate" ? "screening-indeterminate" : "screening-disagreement";
338
+ return "screening-hand-excluded";
339
+ }
340
+ /** The reviewer Result Evaluation verifier shared by admission construction and closure replay. */
341
+ export function verifyBinaryJudgmentReviewerResult(input, ports) {
342
+ return verifyReview({ ports, reachable: new Set(), roles: new Map() }, input.verdictSha256, input.expectedItemSha256, "reviewVerdictSha256");
343
+ }
344
+ /**
345
+ * Portable, fail-closed replay of the entire binary-judgment admission graph. The caller supplies
346
+ * immutable record resolution and role-separated trust; no candidate truth is caller-authored.
347
+ */
348
+ export function verifyBinaryJudgmentAdmissionClosure(input, ports) {
349
+ const state = { ports, reachable: new Set(), roles: new Map() };
350
+ const manifest = exactCanonical(BinaryJudgmentAdmissionManifestSchema, resolve(state, input.admissionManifestSha256, "admissionManifestSha256", "admission-manifest"), "admissionManifest");
351
+ if (manifest.draftId !== input.expectedDraftId)
352
+ fail("admissionManifest.draftId", "manifest belongs to a different draft");
353
+ const ledger = exactCanonical(HumanReviewReplacementLedgerSchema, resolve(state, manifest.replacementLedgerSha256, "admissionManifest.replacementLedgerSha256", "replacement-ledger"), "replacementLedger");
354
+ if (ledger.draftId !== input.expectedDraftId || ledger.sealedAt !== manifest.admittedAt)
355
+ fail("replacementLedger", "ledger draft/time does not bind the manifest");
356
+ // Screened-operator-sampled admission (spec §6.4, §6.5; packet P6 item A): the table and S2's
357
+ // `screening-sample/1` recomputation are resolved and checked ONCE per bank, ahead of the
358
+ // resolutions and ledger loops below, which read `screeningRowsByItem` per item. Checks (1)-(3)
359
+ // run here, over `rows` alone; check (0)'s coverage half runs after both loops, once
360
+ // `acceptedItems`/`excludedItems` (the closure's own reconstruction of the frozen bank's
361
+ // candidate pool -- see the coverage check below) are fully known; check (4) runs per item in
362
+ // both loops.
363
+ let screeningTable;
364
+ let screeningRowsByItem = new Map();
365
+ let screeningSample = new Set();
366
+ let screeningSampleAgreementRate;
367
+ let screeningInstrumentSha256;
368
+ if (manifest.truthAdmission === "screened-operator-sampled") {
369
+ if (manifest.screeningTableSha256 === undefined) {
370
+ fail("admissionManifest.screeningTableSha256", "screened admission manifest carries no screening table reference");
371
+ }
372
+ // The table is signed ONCE, DSSE-sealed on the same path as the other admission records
373
+ // (spec §6.3, final sentence, verbatim) -- this is what makes §6.9's dropping of 240 per-item
374
+ // signatures sound ("one key signing 240 rows separately proves nothing beyond one signature
375
+ // on the whole table"). The table schema carries no attestorRole field (unlike the roster and
376
+ // reveal receipt), but `sealRoleEvidence` (core/src/operations/human-review.ts:218-233) DSSE-
377
+ // wraps arbitrary bytes under any payload type regardless of the payload's own shape, so
378
+ // `screeningTableSha256` names the DSSE ENVELOPE digest, exactly like `reviewerRosterSha256`
379
+ // and `revealReceiptSha256` already do -- not the bare canonical table bytes. The screened
380
+ // branch's sole legal authority set is `["truth-reveal-attestor"]` alone (§6.8a Group C), so
381
+ // that is the one key that signs both the bank-scoped table and the bank-scoped reveal
382
+ // receipt: "signed once per bank" as D1 describes.
383
+ screeningTable = authorityPayload(state, {
384
+ digest: manifest.screeningTableSha256,
385
+ mediaType: SCREENING_TABLE_MEDIA_TYPE,
386
+ role: "truth-reveal-attestor",
387
+ evidenceRole: "screening-table",
388
+ schema: ScreeningTableSchema,
389
+ path: "admissionManifest.screeningTableSha256",
390
+ }).value;
391
+ if (screeningTable.draftId !== input.expectedDraftId || screeningTable.sealedAt !== manifest.admittedAt) {
392
+ fail("screeningTable", "table draft/time does not bind the manifest");
393
+ }
394
+ const screeningInstrument = parseProfileRecord(state, screeningTable.screeningInstrumentSha256, "screeningTable.screeningInstrumentSha256", parseBinaryJudgmentInstrument, "screening-instrument");
395
+ const resealedScreeningInstrument = sealBinaryJudgmentInstrument(screeningInstrument.value);
396
+ if (resealedScreeningInstrument.digest !== screeningTable.screeningInstrumentSha256
397
+ || !bytesEqual(resealedScreeningInstrument.bytes, screeningInstrument.bytes)) {
398
+ fail("screeningTable.screeningInstrumentSha256", "instrument does not reseal to its table commitment");
399
+ }
400
+ screeningInstrumentSha256 = screeningTable.screeningInstrumentSha256;
401
+ // These two sidecars are intentionally opaque. Resolving them is sufficient to prove exact
402
+ // digest-bound reachability; screening-sample/1 remains the independent sampling authority.
403
+ resolve(state, screeningTable.samplingScriptSha256, "screeningTable.samplingScriptSha256", "screening-sampling-script");
404
+ resolve(state, screeningTable.rawOutputsSha256, "screeningTable.rawOutputsSha256", "screening-raw-outputs");
405
+ screeningRowsByItem = new Map(screeningTable.rows.map((row) => [row.itemSha256, row]));
406
+ // (1) Sample membership: recomputed by screening-sample/1, never by executing the sealed
407
+ // script (R-4). The recomputation is authoritative for checks (2) and (3) below -- there is
408
+ // no separate "compare against the operator's script" step, because the script is never run.
409
+ let sampleResult;
410
+ try {
411
+ sampleResult = computeScreeningSample({
412
+ itemSha256s: screeningTable.rows.map((row) => row.itemSha256),
413
+ sampleSeed: screeningTable.sampleSeed,
414
+ sampleSize: screeningTable.sampleSize,
415
+ });
416
+ }
417
+ catch (cause) {
418
+ fail("admissionManifest.screeningTableSha256", cause instanceof ScreeningSampleError
419
+ ? `sample recomputation refused its own input: ${cause.message}`
420
+ : "sample recomputation (screening-sample/1) failed", cause);
421
+ }
422
+ screeningSample = new Set(sampleResult.sample);
423
+ // (2) Required hand checks: flagged := rows where NOT agreed. Every row in flagged ∪ sample
424
+ // must carry handChecked === true.
425
+ for (const row of screeningTable.rows) {
426
+ const flagged = row.screeningVerdict !== row.intendedLabel;
427
+ if ((flagged || screeningSample.has(row.itemSha256)) && !row.handChecked) {
428
+ fail("screeningTable.rows", `row ${row.itemSha256} is flagged or sampled but was never hand-checked`);
429
+ }
430
+ }
431
+ // (3) Sample agreement rate -- the one definition (spec §6.5(3)), symmetric on purpose. Every
432
+ // row in `screeningSample` is hand-checked by check (2) above, so `handVerdict` is defined.
433
+ const sampleMatches = [...screeningSample].filter((sampledItemSha256) => {
434
+ const row = screeningRowsByItem.get(sampledItemSha256);
435
+ return (row.screeningVerdict === row.intendedLabel) === (row.handVerdict === "confirm");
436
+ }).length;
437
+ screeningSampleAgreementRate = sampleMatches / screeningSample.size;
438
+ }
439
+ const resolutions = manifest.labelResolutionSha256s.map((digest, index) => ({
440
+ digest,
441
+ ...parseProfileRecord(state, digest, `admissionManifest.labelResolutionSha256s.${index}`, (bytes) => parseBinaryJudgmentLabelResolution(bytes), "label-resolution"),
442
+ }));
443
+ const contexts = manifest.analysisContextSha256s.map((digest, index) => ({
444
+ digest,
445
+ ...parseProfileRecord(state, digest, `admissionManifest.analysisContextSha256s.${index}`, (bytes) => parseBinaryJudgmentAnalysisContext(bytes), "analysis-context"),
446
+ }));
447
+ if (resolutions.length !== contexts.length)
448
+ fail("admissionManifest", "resolution and analysis-context coverage differs");
449
+ const contextByResolution = new Map(contexts.map((entry) => [entry.value.labelResolutionSha256, entry]));
450
+ if (contextByResolution.size !== contexts.length)
451
+ fail("admissionManifest.analysisContextSha256s", "multiple contexts cover one resolution");
452
+ const acceptedItems = new Set();
453
+ const accepted = [];
454
+ for (const [index, entry] of resolutions.entries()) {
455
+ const resolution = entry.value;
456
+ const path = `resolutions.${index}`;
457
+ if (acceptedItems.has(resolution.itemSha256))
458
+ fail(path, "item is admitted more than once");
459
+ acceptedItems.add(resolution.itemSha256);
460
+ if (resolution.truthAdmission !== manifest.truthAdmission || resolution.resolvedAt !== manifest.admittedAt)
461
+ fail(path, "resolution admission kind/time differs from manifest");
462
+ // A screened resolution carries no humanReviewEvaluationSpecSha256 (spec §6.7: a row never
463
+ // hand-checked has no human-review evaluation), so this equality check only applies to the
464
+ // two branches that still carry the field. `verifyFrozenHumanReviewSpec` stays unconditional
465
+ // below regardless: the frozen spec+form are required admission-record roles for every mode
466
+ // (§6.8a's ratified G-5), not only the two that reference the spec by digest.
467
+ if (resolution.truthAdmission !== "screened-operator-sampled") {
468
+ if (resolution.humanReviewEvaluationSpecSha256 !== BINARY_JUDGMENT_HUMAN_REVIEW_EVALUATION_SPEC_SEALED.digest) {
469
+ fail(path, "resolution names a different human-review EvaluationSpec");
470
+ }
471
+ }
472
+ verifyFrozenHumanReviewSpec(state, path);
473
+ const item = parseProfileRecord(state, resolution.itemSha256, `${path}.itemSha256`, (bytes) => parseBinaryJudgmentPayload(bytes), "source-item").value;
474
+ if (item.itemId !== resolution.itemId)
475
+ fail(path, "resolution itemId differs from exact item record");
476
+ const contextEntry = contextByResolution.get(entry.digest);
477
+ if (contextEntry === undefined)
478
+ fail(path, "resolution has no analysis context");
479
+ const context = contextEntry.value;
480
+ if (context.itemSha256 !== resolution.itemSha256
481
+ || context.itemId !== resolution.itemId
482
+ || context.truthLabel !== resolution.truthLabel
483
+ || context.candidateClass !== resolution.candidateClass
484
+ || context.stratum !== resolution.stratum)
485
+ fail(path, "analysis context does not exactly project the resolution");
486
+ // Exhaustive switch over the three-member truthAdmission union, never-typed default (spec
487
+ // §6.8a Group B / this packet's G-3): an unrecognized future mode is a compile error here,
488
+ // not a silent reroute into the two-human or operator-only path.
489
+ switch (resolution.truthAdmission) {
490
+ case "two-human-unanimous": {
491
+ const human = verifyHumanEvidence(state, resolution, resolution.itemSha256, input.expectedDraftId, manifest.admittedAt, path);
492
+ if (human.itemId !== resolution.itemId
493
+ || human.reviews.some((review) => !review.complete || review.label === "indeterminate" || review.label !== resolution.truthLabel))
494
+ fail(path, "admitted human truth is not two complete unanimous reviews");
495
+ break;
496
+ }
497
+ case "operator-only": {
498
+ const assertionEvidence = authorityPayload(state, {
499
+ digest: resolution.operatorAssertionSha256,
500
+ mediaType: HUMAN_REVIEW_OPERATOR_ASSERTION_MEDIA_TYPE,
501
+ role: "operator-truth-attestor",
502
+ evidenceRole: "operator-assertion",
503
+ schema: HumanReviewOperatorAssertionSchema,
504
+ path: `${path}.operatorAssertion`,
505
+ });
506
+ const assertion = assertionEvidence.value;
507
+ if (assertion.attestorRole !== "operator-truth-attestor"
508
+ || assertion.attestorKeyId !== assertionEvidence.keyId
509
+ || assertion.itemSha256 !== resolution.itemSha256
510
+ || assertion.truthLabel !== resolution.truthLabel
511
+ || assertion.assertedAt !== resolution.resolvedAt
512
+ || assertion.limitation !== "operator-only-not-publication-grade")
513
+ fail(path, "operator assertion does not exactly bind the non-publication resolution");
514
+ break;
515
+ }
516
+ case "screened-operator-sampled": {
517
+ if (resolution.screeningTableSha256 !== manifest.screeningTableSha256) {
518
+ fail(path, "resolution names a different screening table than the manifest");
519
+ }
520
+ const revealEvidence = authorityPayload(state, {
521
+ digest: resolution.screeningRevealReceiptSha256,
522
+ mediaType: SCREENING_REVEAL_RECEIPT_MEDIA_TYPE,
523
+ role: "truth-reveal-attestor",
524
+ evidenceRole: "screening-reveal-receipt",
525
+ schema: ScreeningRevealReceiptSchema,
526
+ path: `${path}.screeningReveal`,
527
+ });
528
+ const reveal = revealEvidence.value;
529
+ if (reveal.attestorRole !== "truth-reveal-attestor"
530
+ || reveal.attestorKeyId !== revealEvidence.keyId
531
+ || reveal.draftId !== input.expectedDraftId
532
+ || reveal.screeningTableSha256 !== resolution.screeningTableSha256
533
+ || reveal.judgeExecutionState !== "not-started"
534
+ || reveal.truthFrozenAt !== manifest.admittedAt)
535
+ fail(path, "signed screening reveal does not bind the screening table under the same authority");
536
+ // (4) Per-row admission (spec §6.4): an admitted item's own table row must itself be
537
+ // admitted, and the resolution's truth label must be the row's intended label -- the
538
+ // screened branch never corrects a label, only confirms or excludes.
539
+ const row = screeningRowsByItem.get(resolution.itemSha256);
540
+ if (row === undefined)
541
+ fail(path, "accepted screened item has no covering screening-table row");
542
+ const agreed = row.screeningVerdict === row.intendedLabel;
543
+ const admitted = row.handChecked ? row.handVerdict === "confirm" : agreed;
544
+ if (!admitted)
545
+ fail(path, "admitted screened item's table row is not itself admitted per §6.4's admission rule");
546
+ if (row.intendedLabel !== resolution.truthLabel)
547
+ fail(path, "resolution truth label differs from the screening table's intended label");
548
+ break;
549
+ }
550
+ default: {
551
+ const exhaustive = resolution;
552
+ fail(path, `unsupported truthAdmission ${String(exhaustive.truthAdmission)}`);
553
+ }
554
+ }
555
+ accepted.push({
556
+ itemSha256: prefixed(resolution.itemSha256),
557
+ itemId: resolution.itemId,
558
+ truthLabel: resolution.truthLabel,
559
+ candidateClass: resolution.candidateClass,
560
+ stratum: resolution.stratum,
561
+ truthAdmission: resolution.truthAdmission,
562
+ labelResolutionSha256: prefixed(entry.digest),
563
+ analysisContextSha256: prefixed(contextEntry.digest),
564
+ });
565
+ }
566
+ if (contextByResolution.size !== resolutions.length || contexts.some((entry) => !manifest.labelResolutionSha256s.includes(entry.value.labelResolutionSha256))) {
567
+ fail("admissionManifest.analysisContextSha256s", "analysis-context coverage has an orphan or omission");
568
+ }
569
+ const excludedDigests = ledger.entries.map((entry) => entry.excludedItemSha256).sort(compareCodeUnitStrings);
570
+ if (!sameStrings(excludedDigests, manifest.excludedItemSha256s))
571
+ fail("replacementLedger.entries", "ledger exclusion coverage differs from manifest");
572
+ if (manifest.truthAdmission === "operator-only" && ledger.entries.length > 0)
573
+ fail("replacementLedger.entries", "operator-only admission cannot claim human-review exclusions");
574
+ const excludedItems = new Set();
575
+ const replacementItems = new Set();
576
+ const excluded = [];
577
+ let previousPosition = 0;
578
+ for (const [index, entry] of ledger.entries.entries()) {
579
+ const path = `replacementLedger.entries.${index}`;
580
+ if (entry.excludedPoolPosition <= previousPosition)
581
+ fail(path, "ledger entries are not in deterministic pool order");
582
+ previousPosition = entry.excludedPoolPosition;
583
+ if (excludedItems.has(entry.excludedItemSha256) || acceptedItems.has(entry.excludedItemSha256))
584
+ fail(path, "excluded item is duplicated or also admitted");
585
+ excludedItems.add(entry.excludedItemSha256);
586
+ if (replacementItems.has(entry.replacementItemSha256))
587
+ fail(path, "one admitted reserve is reused for multiple exclusions");
588
+ replacementItems.add(entry.replacementItemSha256);
589
+ const replacement = accepted.find((candidate) => candidate.itemSha256 === entry.replacementItemSha256);
590
+ if (replacement === undefined)
591
+ fail(path, "replacement is not in the admitted resolution set");
592
+ if (entry.replacementPoolPosition <= entry.excludedPoolPosition
593
+ || replacement.candidateClass !== entry.candidateClass
594
+ || replacement.stratum !== entry.stratum)
595
+ fail(path, "replacement is not a later admitted reserve in the same class and stratum");
596
+ // Branches on admission mode (spec §6.4, §6.5 check (4); ratified resolution to G-1): a
597
+ // screened exclusion is verified against the screening table's own per-row admission rule
598
+ // rather than against two signed reviews. §6.9 deliberately DROPS the four per-item two-human
599
+ // digests for the screened branch (one signature on the whole table, not 240 on individual
600
+ // rows), so those four fields are never read here for a screened entry. `operator-only` is
601
+ // unreachable in this loop body: any operator-only manifest with ledger.entries.length > 0
602
+ // already refused above, before this loop began.
603
+ let itemId;
604
+ let derivedReason;
605
+ switch (manifest.truthAdmission) {
606
+ case "screened-operator-sampled": {
607
+ const row = screeningRowsByItem.get(entry.excludedItemSha256);
608
+ if (row === undefined)
609
+ fail(path, "excluded item has no covering screening-table row");
610
+ derivedReason = expectedScreeningExclusionReason(row);
611
+ const item = parseProfileRecord(state, entry.excludedItemSha256, `${path}.item`, (bytes) => parseBinaryJudgmentPayload(bytes), "source-item").value;
612
+ itemId = item.itemId;
613
+ break;
614
+ }
615
+ case "two-human-unanimous": {
616
+ // TypeScript narrows each optional property read below, but not `entry`'s own declared
617
+ // (still-optional) structural type, so the narrowed fields are passed on explicitly
618
+ // rather than passing `entry` itself. Defensive: the schema's own present-iff refinement
619
+ // (checked at `exactCanonical` above) already guarantees these four digests are present
620
+ // for a two-human reason, so this guard should be unreachable, not load-bearing.
621
+ if (entry.reviewVerdictSha256s === undefined
622
+ || entry.visibilityReceiptSha256s === undefined
623
+ || entry.reviewerRosterSha256 === undefined
624
+ || entry.revealReceiptSha256 === undefined)
625
+ fail(path, "two-human ledger entry is missing its required review evidence digests");
626
+ const human = verifyHumanEvidence(state, {
627
+ reviewVerdictSha256s: entry.reviewVerdictSha256s,
628
+ visibilityReceiptSha256s: entry.visibilityReceiptSha256s,
629
+ reviewerRosterSha256: entry.reviewerRosterSha256,
630
+ revealReceiptSha256: entry.revealReceiptSha256,
631
+ }, entry.excludedItemSha256, input.expectedDraftId, manifest.admittedAt, path);
632
+ derivedReason = expectedExclusionReason(human.reviews);
633
+ itemId = human.itemId;
634
+ break;
635
+ }
636
+ case "operator-only":
637
+ fail(path, "operator-only admission cannot claim a replacement-ledger entry");
638
+ break;
639
+ default: {
640
+ const exhaustive = manifest.truthAdmission;
641
+ fail(path, `unsupported truthAdmission ${String(exhaustive)}`);
642
+ }
643
+ }
644
+ if (derivedReason === undefined || derivedReason !== entry.reason)
645
+ fail(path, "ledger reason does not derive from the admitted evidence");
646
+ excluded.push({
647
+ itemSha256: prefixed(entry.excludedItemSha256),
648
+ itemId,
649
+ candidateClass: entry.candidateClass,
650
+ stratum: entry.stratum,
651
+ reason: entry.reason,
652
+ replacementItemSha256: prefixed(entry.replacementItemSha256),
653
+ });
654
+ }
655
+ // (0) Coverage, the other half (spec §6.5 check (0)): the table's rows must cover the frozen
656
+ // bank's candidate pool EXACTLY. The pool itself is not a separate input to this function --
657
+ // it is reconstructed as accepted ∪ excluded, the closure's own admission decision, which by
658
+ // construction is exactly the frozen bank (every candidate item is either admitted or excluded
659
+ // with a same-class same-stratum replacement, and every replacement is itself an admitted pool
660
+ // member). `sampleSize <= rows.length` (check (0)'s other clause) is enforced transitively:
661
+ // `computeScreeningSample` above refuses a `sampleSize` exceeding the pool size on its own.
662
+ if (manifest.truthAdmission === "screened-operator-sampled") {
663
+ if (screeningTable === undefined) {
664
+ // Unreachable today: the screened arm resolves the table before any row is admitted and fails
665
+ // closed when it is absent. Written as a refusal anyway, because the `&& screeningTable !==
666
+ // undefined` conjunct this replaces was a type-narrowing device whose false branch silently
667
+ // SKIPPED the coverage check. A coverage check with a skip path is not a coverage check, and a
668
+ // future caller that reordered the resolution would have been passed rather than refused.
669
+ fail("screeningTable", "screened admission reached the coverage check with no resolved screening table");
670
+ }
671
+ const pool = [...acceptedItems, ...excludedItems].sort(compareCodeUnitStrings);
672
+ const rowIdentities = screeningTable.rows.map((row) => row.itemSha256);
673
+ if (!sameStrings(pool, rowIdentities)) {
674
+ fail("screeningTable.rows", "screening table rows do not cover the admitted-plus-excluded closure exactly");
675
+ }
676
+ }
677
+ const classes = [...new Set([...accepted.map((entry) => entry.candidateClass), ...excluded.map((entry) => entry.candidateClass)])].sort(compareCodeUnitStrings);
678
+ const strata = [...new Set([...accepted.map((entry) => entry.stratum), ...excluded.map((entry) => entry.stratum)])].sort(compareCodeUnitStrings);
679
+ return {
680
+ manifestSha256: input.admissionManifestSha256,
681
+ manifest,
682
+ replacementLedger: ledger,
683
+ // Derived by exclusion, not by equality (spec §6.8a Group A, first bullet; ratified fix):
684
+ // `=== "two-human-unanimous"` derives `false` for a screened manifest, which is
685
+ // publication-grade under §6.8, and would make schema.ts's third `superRefine` branch (which
686
+ // REQUIRES `true`) refuse every screened bundle. `operator-only` is the one mode that is
687
+ // never publication-grade.
688
+ publicationGrade: manifest.truthAdmission !== "operator-only",
689
+ classes,
690
+ strata,
691
+ accepted,
692
+ excluded,
693
+ reachableSha256s: [...state.reachable].sort(compareCodeUnitStrings),
694
+ reachableRecords: [...state.roles]
695
+ .sort(([left], [right]) => compareCodeUnitStrings(left, right))
696
+ .map(([sha256, roles]) => ({
697
+ sha256,
698
+ roles: BINARY_JUDGMENT_ADMISSION_RECORD_ROLES.filter((role) => roles.has(role)),
699
+ })),
700
+ // Present iff screened (spec §6.5 check (3)): the one definition of the screen-versus-hand
701
+ // agreement rate on the random sample, so no caller recomputes it independently.
702
+ ...(screeningSampleAgreementRate === undefined || screeningInstrumentSha256 === undefined ? {} : {
703
+ screening: {
704
+ sampleAgreementRate: screeningSampleAgreementRate,
705
+ instrumentSha256: screeningInstrumentSha256,
706
+ },
707
+ }),
708
+ };
709
+ }