@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
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # @colophon-claims/verify
2
+
3
+ Verify a public Colophon claim bundle without the Colophon app or any execution runtime:
4
+
5
+ ```sh
6
+ npx @colophon-claims/verify@0.1 ./bundle
7
+ ```
8
+
9
+ Use `--json` for machine-readable output. The reader runs the checks declared by the
10
+ bundle format, covering its manifest, evidence closure, calculations, report, and claim
11
+ consistency. Exit status is `0` when every check passes, `1` for an invalid bundle, and
12
+ `2` for usage or operational failures.
13
+
14
+ This 0.1 reader supports public bundle formats v2, v4, v5, and v6. It intentionally
15
+ rejects the unrelated accounting bundle v3. Formats v2 and v4 run six checks; the
16
+ evidence-native v5 and anchored v6 formats run seven.
17
+ New claims stamp `npx @colophon-claims/verify@0.1.0` and the compatible
18
+ `@0.1` line.
19
+
20
+ Verification opens no network connection, reads no account or API credential, and uploads
21
+ nothing. It checks the bundle's integrity, evidence closure, calculations, report, and claim
22
+ consistency. It does not prove that the producing machine was honest or that the compared
23
+ identities are independent parties.
24
+
25
+ Bundles are also verifiable without this package: `../EXTERNAL-VERIFICATION.md` specifies
26
+ the external path (openssl plus a dependency-free script, shipped here as
27
+ `scripts/external-verify.py`), the JSON Schemas under `schemas/`, and the conformance kit
28
+ under `fixtures/public-bundle-conformance-v1/` for testing an independent verifier.
29
+
30
+ ## What this does not yet prove
31
+
32
+ Protocol identifiers in the installed platform packages name `https://spec.jinn.network/…`.
33
+ That origin is not hosted yet. This verifier checks the bundle against the exact
34
+ `@jinn-network/*` bytes installed from npm. A third party who fetches those identifiers
35
+ from the live origin will not retrieve them.
@@ -0,0 +1,18 @@
1
+ import { type BinaryJudgmentPayload, type EvaluationSpec } from "@jinn-network/task-execution-profiles";
2
+ export declare const HUMAN_REVIEW_FORM: {
3
+ readonly protocol: "https://spec.jinn.network/binary-judgment/human-review-form/v1";
4
+ readonly question: "Is the candidate answer correct relative to the question and reference answer?";
5
+ readonly labels: readonly ["CORRECT", "WRONG", "indeterminate"];
6
+ readonly completeReviewRequired: true;
7
+ readonly rationale: "optional";
8
+ };
9
+ export declare const HUMAN_REVIEW_FORM_SEALED: {
10
+ bytes: Uint8Array;
11
+ digest: `sha256:${string}`;
12
+ };
13
+ export declare function binaryJudgmentItemBytes(item: BinaryJudgmentPayload): Uint8Array;
14
+ export declare function buildBinaryJudgmentHumanReviewEvaluationSpec(): EvaluationSpec;
15
+ export declare const BINARY_JUDGMENT_HUMAN_REVIEW_EVALUATION_SPEC_SEALED: {
16
+ bytes: Uint8Array;
17
+ digest: `sha256:${string}`;
18
+ };
@@ -0,0 +1,53 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { EVALUATION_SPEC_FORMAT_URI, EVAL_SEMANTICS_VERSION, canonicalJsonBytes, sealDocument, sealEvaluationSpec, } from "@jinn-network/task-execution-profiles";
3
+ export const HUMAN_REVIEW_FORM = {
4
+ protocol: "https://spec.jinn.network/binary-judgment/human-review-form/v1",
5
+ question: "Is the candidate answer correct relative to the question and reference answer?",
6
+ labels: ["CORRECT", "WRONG", "indeterminate"],
7
+ completeReviewRequired: true,
8
+ rationale: "optional",
9
+ };
10
+ export const HUMAN_REVIEW_FORM_SEALED = sealDocument(HUMAN_REVIEW_FORM);
11
+ export function binaryJudgmentItemBytes(item) {
12
+ return canonicalJsonBytes(item);
13
+ }
14
+ export function buildBinaryJudgmentHumanReviewEvaluationSpec() {
15
+ return {
16
+ protocol: EVALUATION_SPEC_FORMAT_URI,
17
+ semanticsVersion: EVAL_SEMANTICS_VERSION,
18
+ family: "human-review",
19
+ grader: {
20
+ name: "external-human-review",
21
+ uri: HUMAN_REVIEW_FORM.protocol,
22
+ digest: { sha256: HUMAN_REVIEW_FORM_SEALED.digest.slice("sha256:".length) },
23
+ mediaType: "application/vnd.jinn.binary-judgment.human-review-form.v1+json",
24
+ accessClass: "public",
25
+ },
26
+ familyBlock: {
27
+ reviewForm: {
28
+ name: "human-review-form.json",
29
+ uri: HUMAN_REVIEW_FORM.protocol,
30
+ digest: { sha256: HUMAN_REVIEW_FORM_SEALED.digest.slice("sha256:".length) },
31
+ mediaType: "application/vnd.jinn.binary-judgment.human-review-form.v1+json",
32
+ },
33
+ reviewerQualifications: { roleDeclaredInRoster: true },
34
+ attestationShape: {
35
+ labels: ["CORRECT", "WRONG", "indeterminate"],
36
+ completeReviewRequired: true,
37
+ },
38
+ },
39
+ measurements: [
40
+ { name: "truthLabel", type: "string", direction: "none", required: true },
41
+ { name: "reviewComplete", type: "boolean", direction: "none", required: true },
42
+ { name: "reviewPacketSha256", type: "string", direction: "none", required: true },
43
+ { name: "visibilityReceiptSha256", type: "string", direction: "none", required: true },
44
+ { name: "responseSha256", type: "string", direction: "none", required: true },
45
+ ],
46
+ verdictRule: { threshold: { measurement: "truthLabel", op: "eq", value: "CORRECT" } },
47
+ unscorable: [{ name: "indeterminate", disposition: "recorded-inconclusive" }],
48
+ evidenceConventions: {
49
+ requiredRefs: ["human-review-packet", "visibility-receipt", "human-review-response"],
50
+ },
51
+ };
52
+ }
53
+ export const BINARY_JUDGMENT_HUMAN_REVIEW_EVALUATION_SPEC_SEALED = sealEvaluationSpec(buildBinaryJudgmentHumanReviewEvaluationSpec());
@@ -0,0 +1,264 @@
1
+ import { z } from "zod";
2
+ export declare const HUMAN_REVIEW_PACKET_PROTOCOL: "https://spec.jinn.network/binary-judgment/human-review-packet/v1";
3
+ export declare const HUMAN_REVIEW_RESPONSE_PROTOCOL: "https://spec.jinn.network/binary-judgment/human-review-response/v1";
4
+ export declare const HUMAN_REVIEW_VISIBILITY_RECEIPT_PROTOCOL: "https://spec.jinn.network/binary-judgment/visibility-receipt/v1";
5
+ export declare const HUMAN_REVIEW_ROSTER_PROTOCOL: "https://spec.jinn.network/binary-judgment/reviewer-roster/v1";
6
+ export declare const HUMAN_REVIEW_REVEAL_RECEIPT_PROTOCOL: "https://spec.jinn.network/binary-judgment/reveal-receipt/v1";
7
+ export declare const HUMAN_REVIEW_REPLACEMENT_LEDGER_PROTOCOL: "https://spec.jinn.network/binary-judgment/replacement-ledger/v1";
8
+ export declare const HUMAN_REVIEW_OPERATOR_ASSERTION_PROTOCOL: "https://spec.jinn.network/binary-judgment/operator-truth-assertion/v1";
9
+ export declare const BINARY_JUDGMENT_ADMISSION_MANIFEST_PROTOCOL: "https://spec.jinn.network/binary-judgment/admission-manifest/v1";
10
+ export declare const SCREENING_TABLE_PROTOCOL: "https://spec.jinn.network/binary-judgment/screening-table/v1";
11
+ export declare const SCREENING_REVEAL_RECEIPT_PROTOCOL: "https://spec.jinn.network/binary-judgment/screening-reveal-receipt/v1";
12
+ export declare const HUMAN_REVIEW_PACKET_MEDIA_TYPE: "application/vnd.jinn.binary-judgment.human-review-packet.v1+json";
13
+ export declare const HUMAN_REVIEW_RESPONSE_MEDIA_TYPE: "application/vnd.jinn.binary-judgment.human-review-response.v1+json";
14
+ export declare const HUMAN_REVIEW_VISIBILITY_RECEIPT_MEDIA_TYPE: "application/vnd.jinn.binary-judgment.visibility-receipt.v1+json";
15
+ export declare const HUMAN_REVIEW_ROSTER_MEDIA_TYPE: "application/vnd.jinn.binary-judgment.reviewer-roster.v1+json";
16
+ export declare const HUMAN_REVIEW_REVEAL_RECEIPT_MEDIA_TYPE: "application/vnd.jinn.binary-judgment.reveal-receipt.v1+json";
17
+ export declare const HUMAN_REVIEW_OPERATOR_ASSERTION_MEDIA_TYPE: "application/vnd.jinn.binary-judgment.operator-truth-assertion.v1+json";
18
+ export declare const SCREENING_TABLE_MEDIA_TYPE: "application/vnd.jinn.binary-judgment.screening-table.v1+json";
19
+ export declare const SCREENING_REVEAL_RECEIPT_MEDIA_TYPE: "application/vnd.jinn.binary-judgment.screening-reveal-receipt.v1+json";
20
+ export declare const HUMAN_REVIEW_OMITTED_FIELDS: readonly ["instrumentIdentity", "candidateClass", "stratum", "otherReview", "postRunOutcomes"];
21
+ export declare const HumanReviewPacketSchema: z.ZodObject<{
22
+ protocol: z.ZodLiteral<"https://spec.jinn.network/binary-judgment/human-review-packet/v1">;
23
+ itemSha256: z.ZodString;
24
+ item: z.ZodObject<{
25
+ itemId: z.ZodString;
26
+ question: z.ZodString;
27
+ referenceAnswer: z.ZodString;
28
+ candidateAnswer: z.ZodString;
29
+ evidence: z.ZodOptional<z.ZodString>;
30
+ provenance: z.ZodObject<{
31
+ sourceCommitment: z.ZodString;
32
+ timestamp: z.ZodString;
33
+ }, z.core.$strict>;
34
+ sources: z.ZodArray<z.ZodObject<{
35
+ digest: z.ZodObject<{
36
+ sha256: z.ZodString;
37
+ }, z.core.$strict>;
38
+ }, z.core.$strict>>;
39
+ }, z.core.$strict>;
40
+ evaluationSpecSha256: z.ZodString;
41
+ reviewerId: z.ZodString;
42
+ form: z.ZodObject<{
43
+ question: z.ZodLiteral<"Is the candidate answer correct relative to the question and reference answer?">;
44
+ labels: z.ZodTuple<[z.ZodLiteral<"CORRECT">, z.ZodLiteral<"WRONG">, z.ZodLiteral<"indeterminate">], null>;
45
+ completeReviewRequired: z.ZodLiteral<true>;
46
+ }, z.core.$strict>;
47
+ }, z.core.$strict>;
48
+ export type HumanReviewPacket = z.infer<typeof HumanReviewPacketSchema>;
49
+ export declare const HumanReviewVisibilityReceiptSchema: z.ZodObject<{
50
+ protocol: z.ZodLiteral<"https://spec.jinn.network/binary-judgment/visibility-receipt/v1">;
51
+ packetSha256: z.ZodString;
52
+ itemSha256: z.ZodString;
53
+ reviewerId: z.ZodString;
54
+ omittedFields: z.ZodTuple<[z.ZodLiteral<"instrumentIdentity">, z.ZodLiteral<"candidateClass">, z.ZodLiteral<"stratum">, z.ZodLiteral<"otherReview">, z.ZodLiteral<"postRunOutcomes">], null>;
55
+ issuedAt: z.ZodString;
56
+ }, z.core.$strict>;
57
+ export type HumanReviewVisibilityReceipt = z.infer<typeof HumanReviewVisibilityReceiptSchema>;
58
+ export declare const HumanReviewResponseSchema: z.ZodObject<{
59
+ protocol: z.ZodLiteral<"https://spec.jinn.network/binary-judgment/human-review-response/v1">;
60
+ packetSha256: z.ZodString;
61
+ visibilityReceiptSha256: z.ZodString;
62
+ itemSha256: z.ZodString;
63
+ evaluatorId: z.ZodString;
64
+ label: z.ZodUnion<readonly [z.ZodEnum<{
65
+ CORRECT: "CORRECT";
66
+ WRONG: "WRONG";
67
+ }>, z.ZodLiteral<"indeterminate">]>;
68
+ complete: z.ZodBoolean;
69
+ completedAt: z.ZodString;
70
+ }, z.core.$strict>;
71
+ export type HumanReviewResponse = z.infer<typeof HumanReviewResponseSchema>;
72
+ export declare const ReviewerDeclarationSchema: z.ZodObject<{
73
+ evaluatorId: z.ZodString;
74
+ keyId: z.ZodString;
75
+ personId: z.ZodString;
76
+ role: z.ZodString;
77
+ conflicts: z.ZodArray<z.ZodString>;
78
+ }, z.core.$strict>;
79
+ export declare const HumanReviewRosterSchema: z.ZodObject<{
80
+ protocol: z.ZodLiteral<"https://spec.jinn.network/binary-judgment/reviewer-roster/v1">;
81
+ itemSha256: z.ZodString;
82
+ reviewers: z.ZodTuple<[z.ZodObject<{
83
+ evaluatorId: z.ZodString;
84
+ keyId: z.ZodString;
85
+ personId: z.ZodString;
86
+ role: z.ZodString;
87
+ conflicts: z.ZodArray<z.ZodString>;
88
+ }, z.core.$strict>, z.ZodObject<{
89
+ evaluatorId: z.ZodString;
90
+ keyId: z.ZodString;
91
+ personId: z.ZodString;
92
+ role: z.ZodString;
93
+ conflicts: z.ZodArray<z.ZodString>;
94
+ }, z.core.$strict>], null>;
95
+ attestedBy: z.ZodString;
96
+ attestorKeyId: z.ZodString;
97
+ attestorRole: z.ZodLiteral<"roster-attestor">;
98
+ attestedAt: z.ZodString;
99
+ }, z.core.$strict>;
100
+ export type HumanReviewRoster = z.infer<typeof HumanReviewRosterSchema>;
101
+ export declare const HumanReviewRevealReceiptSchema: z.ZodObject<{
102
+ protocol: z.ZodLiteral<"https://spec.jinn.network/binary-judgment/reveal-receipt/v1">;
103
+ draftId: z.ZodString;
104
+ itemSha256: z.ZodString;
105
+ truthFrozenAt: z.ZodString;
106
+ judgeExecutionState: z.ZodLiteral<"not-started">;
107
+ attestedBy: z.ZodString;
108
+ attestorKeyId: z.ZodString;
109
+ attestorRole: z.ZodLiteral<"truth-reveal-attestor">;
110
+ }, z.core.$strict>;
111
+ export type HumanReviewRevealReceipt = z.infer<typeof HumanReviewRevealReceiptSchema>;
112
+ /**
113
+ * `screening-reveal-receipt/v1` (spec §6.6): a bank-scoped sibling of `HumanReviewRevealReceiptSchema`
114
+ * above. Same ordering primitive, same attestor role, same `judgeExecutionState: "not-started"`
115
+ * gate — differing only in subject: the bank (`screeningTableSha256`), not an item (`itemSha256`).
116
+ * `AdmissionAuthorityRole` (`verify/src/admission/verification.ts`) is deliberately unchanged: this
117
+ * reuses `truth-reveal-attestor` rather than minting a new role.
118
+ */
119
+ export declare const ScreeningRevealReceiptSchema: z.ZodObject<{
120
+ protocol: z.ZodLiteral<"https://spec.jinn.network/binary-judgment/screening-reveal-receipt/v1">;
121
+ draftId: z.ZodString;
122
+ screeningTableSha256: z.ZodString;
123
+ truthFrozenAt: z.ZodString;
124
+ judgeExecutionState: z.ZodLiteral<"not-started">;
125
+ attestedBy: z.ZodString;
126
+ attestorKeyId: z.ZodString;
127
+ attestorRole: z.ZodLiteral<"truth-reveal-attestor">;
128
+ }, z.core.$strict>;
129
+ export type ScreeningRevealReceipt = z.infer<typeof ScreeningRevealReceiptSchema>;
130
+ /**
131
+ * One row of the `screening-table/v1` record (spec §6.3). `handVerdict` is present if and only if
132
+ * `handChecked === true`; both directions refuse. `handVerdict` deliberately does not reuse
133
+ * `BinaryJudgmentTruthLabelSchema` — the hand-check outcome space is confirm-or-exclude only, with
134
+ * no label corrections.
135
+ */
136
+ export declare const ScreeningRowSchema: z.ZodObject<{
137
+ itemSha256: z.ZodString;
138
+ intendedLabel: z.ZodEnum<{
139
+ CORRECT: "CORRECT";
140
+ WRONG: "WRONG";
141
+ }>;
142
+ screeningVerdict: z.ZodUnion<readonly [z.ZodEnum<{
143
+ CORRECT: "CORRECT";
144
+ WRONG: "WRONG";
145
+ }>, z.ZodLiteral<"indeterminate">]>;
146
+ handChecked: z.ZodBoolean;
147
+ handVerdict: z.ZodOptional<z.ZodEnum<{
148
+ confirm: "confirm";
149
+ exclude: "exclude";
150
+ }>>;
151
+ }, z.core.$strict>;
152
+ export type ScreeningRow = z.infer<typeof ScreeningRowSchema>;
153
+ /**
154
+ * `screening-table/v1` (spec §6.3): one signed record per bank. Every derived quantity (the
155
+ * flagged set, the sample, the exclusions, the agreement rate) is a *view* of this table, never a
156
+ * stored field that can disagree with it — those recomputations live in `screening-sample.ts` and
157
+ * its siblings, not here. `screeningModel` is deliberately absent: it is derivable from
158
+ * `screeningInstrumentSha256`, and a second copy is a second thing to drift. The table is signed
159
+ * once, over the whole record, on the existing DSSE signing path used by the other admission
160
+ * records (`sealRoleEvidence`), not the ledger's non-DSSE path.
161
+ */
162
+ export declare const ScreeningTableSchema: z.ZodObject<{
163
+ protocol: z.ZodLiteral<"https://spec.jinn.network/binary-judgment/screening-table/v1">;
164
+ draftId: z.ZodString;
165
+ screeningInstrumentSha256: z.ZodString;
166
+ sampleSeed: z.ZodString;
167
+ sampleSize: z.ZodNumber;
168
+ samplingScriptSha256: z.ZodString;
169
+ rawOutputsSha256: z.ZodString;
170
+ rows: z.ZodArray<z.ZodObject<{
171
+ itemSha256: z.ZodString;
172
+ intendedLabel: z.ZodEnum<{
173
+ CORRECT: "CORRECT";
174
+ WRONG: "WRONG";
175
+ }>;
176
+ screeningVerdict: z.ZodUnion<readonly [z.ZodEnum<{
177
+ CORRECT: "CORRECT";
178
+ WRONG: "WRONG";
179
+ }>, z.ZodLiteral<"indeterminate">]>;
180
+ handChecked: z.ZodBoolean;
181
+ handVerdict: z.ZodOptional<z.ZodEnum<{
182
+ confirm: "confirm";
183
+ exclude: "exclude";
184
+ }>>;
185
+ }, z.core.$strict>>;
186
+ sealedAt: z.ZodString;
187
+ }, z.core.$strict>;
188
+ export type ScreeningTable = z.infer<typeof ScreeningTableSchema>;
189
+ export declare const HumanReviewReplacementLedgerEntrySchema: z.ZodObject<{
190
+ excludedItemSha256: z.ZodString;
191
+ replacementItemSha256: z.ZodString;
192
+ candidateClass: z.ZodString;
193
+ stratum: z.ZodString;
194
+ excludedPoolPosition: z.ZodNumber;
195
+ replacementPoolPosition: z.ZodNumber;
196
+ reason: z.ZodEnum<{
197
+ "review-disagreement": "review-disagreement";
198
+ "review-indeterminate": "review-indeterminate";
199
+ "review-incomplete": "review-incomplete";
200
+ "screening-disagreement": "screening-disagreement";
201
+ "screening-indeterminate": "screening-indeterminate";
202
+ "screening-hand-excluded": "screening-hand-excluded";
203
+ }>;
204
+ reviewVerdictSha256s: z.ZodOptional<z.ZodTuple<[z.ZodString, z.ZodString], null>>;
205
+ visibilityReceiptSha256s: z.ZodOptional<z.ZodTuple<[z.ZodString, z.ZodString], null>>;
206
+ reviewerRosterSha256: z.ZodOptional<z.ZodString>;
207
+ revealReceiptSha256: z.ZodOptional<z.ZodString>;
208
+ }, z.core.$strict>;
209
+ export declare const HumanReviewReplacementLedgerSchema: z.ZodObject<{
210
+ protocol: z.ZodLiteral<"https://spec.jinn.network/binary-judgment/replacement-ledger/v1">;
211
+ draftId: z.ZodString;
212
+ entries: z.ZodArray<z.ZodObject<{
213
+ excludedItemSha256: z.ZodString;
214
+ replacementItemSha256: z.ZodString;
215
+ candidateClass: z.ZodString;
216
+ stratum: z.ZodString;
217
+ excludedPoolPosition: z.ZodNumber;
218
+ replacementPoolPosition: z.ZodNumber;
219
+ reason: z.ZodEnum<{
220
+ "review-disagreement": "review-disagreement";
221
+ "review-indeterminate": "review-indeterminate";
222
+ "review-incomplete": "review-incomplete";
223
+ "screening-disagreement": "screening-disagreement";
224
+ "screening-indeterminate": "screening-indeterminate";
225
+ "screening-hand-excluded": "screening-hand-excluded";
226
+ }>;
227
+ reviewVerdictSha256s: z.ZodOptional<z.ZodTuple<[z.ZodString, z.ZodString], null>>;
228
+ visibilityReceiptSha256s: z.ZodOptional<z.ZodTuple<[z.ZodString, z.ZodString], null>>;
229
+ reviewerRosterSha256: z.ZodOptional<z.ZodString>;
230
+ revealReceiptSha256: z.ZodOptional<z.ZodString>;
231
+ }, z.core.$strict>>;
232
+ sealedAt: z.ZodString;
233
+ }, z.core.$strict>;
234
+ export type HumanReviewReplacementLedger = z.infer<typeof HumanReviewReplacementLedgerSchema>;
235
+ export declare const HumanReviewOperatorAssertionSchema: z.ZodObject<{
236
+ protocol: z.ZodLiteral<"https://spec.jinn.network/binary-judgment/operator-truth-assertion/v1">;
237
+ itemSha256: z.ZodString;
238
+ truthLabel: z.ZodEnum<{
239
+ CORRECT: "CORRECT";
240
+ WRONG: "WRONG";
241
+ }>;
242
+ assertedBy: z.ZodString;
243
+ assertedAt: z.ZodString;
244
+ attestorKeyId: z.ZodString;
245
+ attestorRole: z.ZodLiteral<"operator-truth-attestor">;
246
+ limitation: z.ZodLiteral<"operator-only-not-publication-grade">;
247
+ }, z.core.$strict>;
248
+ export type HumanReviewOperatorAssertion = z.infer<typeof HumanReviewOperatorAssertionSchema>;
249
+ export declare const BinaryJudgmentAdmissionManifestSchema: z.ZodObject<{
250
+ protocol: z.ZodLiteral<"https://spec.jinn.network/binary-judgment/admission-manifest/v1">;
251
+ draftId: z.ZodString;
252
+ truthAdmission: z.ZodEnum<{
253
+ "two-human-unanimous": "two-human-unanimous";
254
+ "operator-only": "operator-only";
255
+ "screened-operator-sampled": "screened-operator-sampled";
256
+ }>;
257
+ labelResolutionSha256s: z.ZodArray<z.ZodString>;
258
+ analysisContextSha256s: z.ZodArray<z.ZodString>;
259
+ excludedItemSha256s: z.ZodArray<z.ZodString>;
260
+ replacementLedgerSha256: z.ZodString;
261
+ screeningTableSha256: z.ZodOptional<z.ZodString>;
262
+ admittedAt: z.ZodString;
263
+ }, z.core.$strict>;
264
+ export type BinaryJudgmentAdmissionManifest = z.infer<typeof BinaryJudgmentAdmissionManifestSchema>;
@@ -0,0 +1,290 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { z } from "zod";
3
+ import { BINARY_JUDGMENT_TIMESTAMP_PATTERN, compareCodeUnitStrings, } from "@jinn-network/task-execution-profiles";
4
+ export const HUMAN_REVIEW_PACKET_PROTOCOL = "https://spec.jinn.network/binary-judgment/human-review-packet/v1";
5
+ export const HUMAN_REVIEW_RESPONSE_PROTOCOL = "https://spec.jinn.network/binary-judgment/human-review-response/v1";
6
+ export const HUMAN_REVIEW_VISIBILITY_RECEIPT_PROTOCOL = "https://spec.jinn.network/binary-judgment/visibility-receipt/v1";
7
+ export const HUMAN_REVIEW_ROSTER_PROTOCOL = "https://spec.jinn.network/binary-judgment/reviewer-roster/v1";
8
+ export const HUMAN_REVIEW_REVEAL_RECEIPT_PROTOCOL = "https://spec.jinn.network/binary-judgment/reveal-receipt/v1";
9
+ export const HUMAN_REVIEW_REPLACEMENT_LEDGER_PROTOCOL = "https://spec.jinn.network/binary-judgment/replacement-ledger/v1";
10
+ export const HUMAN_REVIEW_OPERATOR_ASSERTION_PROTOCOL = "https://spec.jinn.network/binary-judgment/operator-truth-assertion/v1";
11
+ export const BINARY_JUDGMENT_ADMISSION_MANIFEST_PROTOCOL = "https://spec.jinn.network/binary-judgment/admission-manifest/v1";
12
+ // screened-operator-sampled admission branch (spec §6.1, §6.3, §6.6; packet P6).
13
+ export const SCREENING_TABLE_PROTOCOL = "https://spec.jinn.network/binary-judgment/screening-table/v1";
14
+ export const SCREENING_REVEAL_RECEIPT_PROTOCOL = "https://spec.jinn.network/binary-judgment/screening-reveal-receipt/v1";
15
+ export const HUMAN_REVIEW_PACKET_MEDIA_TYPE = "application/vnd.jinn.binary-judgment.human-review-packet.v1+json";
16
+ export const HUMAN_REVIEW_RESPONSE_MEDIA_TYPE = "application/vnd.jinn.binary-judgment.human-review-response.v1+json";
17
+ export const HUMAN_REVIEW_VISIBILITY_RECEIPT_MEDIA_TYPE = "application/vnd.jinn.binary-judgment.visibility-receipt.v1+json";
18
+ export const HUMAN_REVIEW_ROSTER_MEDIA_TYPE = "application/vnd.jinn.binary-judgment.reviewer-roster.v1+json";
19
+ export const HUMAN_REVIEW_REVEAL_RECEIPT_MEDIA_TYPE = "application/vnd.jinn.binary-judgment.reveal-receipt.v1+json";
20
+ export const HUMAN_REVIEW_OPERATOR_ASSERTION_MEDIA_TYPE = "application/vnd.jinn.binary-judgment.operator-truth-assertion.v1+json";
21
+ // screened-operator-sampled admission branch (spec §6.3, §6.6; packet P6).
22
+ export const SCREENING_TABLE_MEDIA_TYPE = "application/vnd.jinn.binary-judgment.screening-table.v1+json";
23
+ export const SCREENING_REVEAL_RECEIPT_MEDIA_TYPE = "application/vnd.jinn.binary-judgment.screening-reveal-receipt.v1+json";
24
+ const DigestSchema = z.string().regex(/^sha256:[0-9a-f]{64}$/u);
25
+ const TimestampSchema = z.string().datetime({ offset: true });
26
+ const IdentitySchema = z.string().min(1).max(256);
27
+ const EvaluatorIdentitySchema = IdentitySchema.regex(/^(?:https?:\/\/|urn:)[^\s]+$/u);
28
+ // §3.1 rule 1: one identifier dialect, not two. Shared shape with `candidateClass`.
29
+ const IDENTIFIER_NAME = /^[A-Za-z][A-Za-z0-9._-]{0,63}$/u;
30
+ const CandidateClassSchema = z.string().regex(IDENTIFIER_NAME);
31
+ const BinaryJudgmentTruthLabelSchema = z.enum(["CORRECT", "WRONG"]);
32
+ const BinaryJudgmentStratumSchema = z.string().regex(IDENTIFIER_NAME);
33
+ /**
34
+ * RFC 3339 instant SHAPE, seconds precision, no fractional part. The pattern is imported from the
35
+ * profiles package rather than restated, so this mirrored payload copy cannot drift from the
36
+ * canonical one it mirrors.
37
+ */
38
+ const BinaryJudgmentTimestampShapeSchema = z.string().regex(new RegExp(BINARY_JUDGMENT_TIMESTAMP_PATTERN, "u"));
39
+ const BinaryJudgmentPayloadSchema = z.strictObject({
40
+ itemId: z.string().regex(/^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u),
41
+ question: z.string(),
42
+ referenceAnswer: z.string(),
43
+ candidateAnswer: z.string(),
44
+ evidence: z.string().optional(),
45
+ provenance: z.strictObject({
46
+ sourceCommitment: DigestSchema,
47
+ timestamp: BinaryJudgmentTimestampShapeSchema,
48
+ }),
49
+ sources: z.array(z.strictObject({
50
+ digest: z.strictObject({ sha256: z.string().regex(/^[0-9a-f]{64}$/u) }),
51
+ })).min(1),
52
+ });
53
+ const ConflictListSchema = z.array(z.string().min(1)).superRefine((values, ctx) => {
54
+ for (let index = 1; index < values.length; index += 1) {
55
+ if (compareCodeUnitStrings(values[index - 1], values[index]) >= 0) {
56
+ ctx.addIssue({ code: "custom", message: "conflicts must be sorted and unique" });
57
+ return;
58
+ }
59
+ }
60
+ });
61
+ const SortedUniqueDigestListSchema = z.array(DigestSchema).superRefine((values, ctx) => {
62
+ for (let index = 1; index < values.length; index += 1) {
63
+ if (compareCodeUnitStrings(values[index - 1], values[index]) >= 0) {
64
+ ctx.addIssue({ code: "custom", message: "digest references must be sorted and unique" });
65
+ return;
66
+ }
67
+ }
68
+ });
69
+ const SortedUniqueDigestPairSchema = z.tuple([DigestSchema, DigestSchema]).superRefine((values, ctx) => {
70
+ if (compareCodeUnitStrings(values[0], values[1]) >= 0)
71
+ ctx.addIssue({ code: "custom", message: "digest references must be sorted and unique" });
72
+ });
73
+ export const HUMAN_REVIEW_OMITTED_FIELDS = [
74
+ "instrumentIdentity", "candidateClass", "stratum", "otherReview", "postRunOutcomes",
75
+ ];
76
+ export const HumanReviewPacketSchema = z.strictObject({
77
+ protocol: z.literal(HUMAN_REVIEW_PACKET_PROTOCOL),
78
+ itemSha256: DigestSchema,
79
+ item: BinaryJudgmentPayloadSchema,
80
+ evaluationSpecSha256: DigestSchema,
81
+ reviewerId: EvaluatorIdentitySchema,
82
+ form: z.strictObject({
83
+ question: z.literal("Is the candidate answer correct relative to the question and reference answer?"),
84
+ labels: z.tuple([z.literal("CORRECT"), z.literal("WRONG"), z.literal("indeterminate")]),
85
+ completeReviewRequired: z.literal(true),
86
+ }),
87
+ });
88
+ export const HumanReviewVisibilityReceiptSchema = z.strictObject({
89
+ protocol: z.literal(HUMAN_REVIEW_VISIBILITY_RECEIPT_PROTOCOL),
90
+ packetSha256: DigestSchema,
91
+ itemSha256: DigestSchema,
92
+ reviewerId: EvaluatorIdentitySchema,
93
+ omittedFields: z.tuple(HUMAN_REVIEW_OMITTED_FIELDS.map((value) => z.literal(value))),
94
+ issuedAt: TimestampSchema,
95
+ });
96
+ export const HumanReviewResponseSchema = z.strictObject({
97
+ protocol: z.literal(HUMAN_REVIEW_RESPONSE_PROTOCOL),
98
+ packetSha256: DigestSchema,
99
+ visibilityReceiptSha256: DigestSchema,
100
+ itemSha256: DigestSchema,
101
+ evaluatorId: EvaluatorIdentitySchema,
102
+ label: z.union([BinaryJudgmentTruthLabelSchema, z.literal("indeterminate")]),
103
+ complete: z.boolean(),
104
+ completedAt: TimestampSchema,
105
+ });
106
+ export const ReviewerDeclarationSchema = z.strictObject({
107
+ evaluatorId: EvaluatorIdentitySchema,
108
+ keyId: IdentitySchema,
109
+ personId: IdentitySchema,
110
+ role: IdentitySchema,
111
+ conflicts: ConflictListSchema,
112
+ });
113
+ export const HumanReviewRosterSchema = z.strictObject({
114
+ protocol: z.literal(HUMAN_REVIEW_ROSTER_PROTOCOL),
115
+ itemSha256: DigestSchema,
116
+ reviewers: z.tuple([ReviewerDeclarationSchema, ReviewerDeclarationSchema]),
117
+ attestedBy: IdentitySchema,
118
+ attestorKeyId: IdentitySchema,
119
+ attestorRole: z.literal("roster-attestor"),
120
+ attestedAt: TimestampSchema,
121
+ });
122
+ export const HumanReviewRevealReceiptSchema = z.strictObject({
123
+ protocol: z.literal(HUMAN_REVIEW_REVEAL_RECEIPT_PROTOCOL),
124
+ draftId: IdentitySchema,
125
+ itemSha256: DigestSchema,
126
+ truthFrozenAt: TimestampSchema,
127
+ judgeExecutionState: z.literal("not-started"),
128
+ attestedBy: IdentitySchema,
129
+ attestorKeyId: IdentitySchema,
130
+ attestorRole: z.literal("truth-reveal-attestor"),
131
+ });
132
+ /**
133
+ * `screening-reveal-receipt/v1` (spec §6.6): a bank-scoped sibling of `HumanReviewRevealReceiptSchema`
134
+ * above. Same ordering primitive, same attestor role, same `judgeExecutionState: "not-started"`
135
+ * gate — differing only in subject: the bank (`screeningTableSha256`), not an item (`itemSha256`).
136
+ * `AdmissionAuthorityRole` (`verify/src/admission/verification.ts`) is deliberately unchanged: this
137
+ * reuses `truth-reveal-attestor` rather than minting a new role.
138
+ */
139
+ export const ScreeningRevealReceiptSchema = z.strictObject({
140
+ protocol: z.literal(SCREENING_REVEAL_RECEIPT_PROTOCOL),
141
+ draftId: IdentitySchema,
142
+ screeningTableSha256: DigestSchema,
143
+ truthFrozenAt: TimestampSchema,
144
+ judgeExecutionState: z.literal("not-started"),
145
+ attestedBy: IdentitySchema,
146
+ attestorKeyId: IdentitySchema,
147
+ attestorRole: z.literal("truth-reveal-attestor"),
148
+ });
149
+ /**
150
+ * One row of the `screening-table/v1` record (spec §6.3). `handVerdict` is present if and only if
151
+ * `handChecked === true`; both directions refuse. `handVerdict` deliberately does not reuse
152
+ * `BinaryJudgmentTruthLabelSchema` — the hand-check outcome space is confirm-or-exclude only, with
153
+ * no label corrections.
154
+ */
155
+ export const ScreeningRowSchema = z.strictObject({
156
+ itemSha256: DigestSchema,
157
+ intendedLabel: BinaryJudgmentTruthLabelSchema,
158
+ screeningVerdict: z.union([BinaryJudgmentTruthLabelSchema, z.literal("indeterminate")]),
159
+ handChecked: z.boolean(),
160
+ handVerdict: z.enum(["confirm", "exclude"]).optional(),
161
+ }).superRefine((row, ctx) => {
162
+ if (row.handChecked && row.handVerdict === undefined) {
163
+ ctx.addIssue({ code: "custom", path: ["handVerdict"], message: "handVerdict is required when handChecked is true" });
164
+ }
165
+ if (!row.handChecked && row.handVerdict !== undefined) {
166
+ ctx.addIssue({ code: "custom", path: ["handVerdict"], message: "handVerdict must be absent when handChecked is false" });
167
+ }
168
+ });
169
+ /**
170
+ * `screening-table/v1` (spec §6.3): one signed record per bank. Every derived quantity (the
171
+ * flagged set, the sample, the exclusions, the agreement rate) is a *view* of this table, never a
172
+ * stored field that can disagree with it — those recomputations live in `screening-sample.ts` and
173
+ * its siblings, not here. `screeningModel` is deliberately absent: it is derivable from
174
+ * `screeningInstrumentSha256`, and a second copy is a second thing to drift. The table is signed
175
+ * once, over the whole record, on the existing DSSE signing path used by the other admission
176
+ * records (`sealRoleEvidence`), not the ledger's non-DSSE path.
177
+ */
178
+ export const ScreeningTableSchema = z.strictObject({
179
+ protocol: z.literal(SCREENING_TABLE_PROTOCOL),
180
+ draftId: IdentitySchema,
181
+ screeningInstrumentSha256: DigestSchema,
182
+ sampleSeed: z.string().min(1),
183
+ // Positive integer, `1 <= sampleSize`. The `<= rows.length` half is a verifier recomputation
184
+ // (spec §6.5 check (0)) owned elsewhere; this schema owns only the positivity.
185
+ sampleSize: z.number().int().positive(),
186
+ samplingScriptSha256: DigestSchema,
187
+ // Digest-bound opaque sidecar; the verifier resolves and hashes its exact bytes but never parses
188
+ // or interprets their content (spec §6.5).
189
+ rawOutputsSha256: DigestSchema,
190
+ rows: z.array(ScreeningRowSchema),
191
+ sealedAt: TimestampSchema,
192
+ }).superRefine((table, ctx) => {
193
+ if (table.rows.length === 0) {
194
+ ctx.addIssue({ code: "custom", path: ["rows"], message: "rows must be non-empty" });
195
+ return;
196
+ }
197
+ for (let index = 1; index < table.rows.length; index += 1) {
198
+ if (compareCodeUnitStrings(table.rows[index - 1].itemSha256, table.rows[index].itemSha256) >= 0) {
199
+ ctx.addIssue({ code: "custom", path: ["rows"], message: "rows must be sorted by itemSha256 and unique" });
200
+ return;
201
+ }
202
+ }
203
+ });
204
+ // The two-human-review reasons (unchanged) and the screened-branch reasons this packet adds
205
+ // (spec §6.4; packet P6). This is one of four widened copies of the closed reason vocabulary
206
+ // (the other three: verify/src/schema.ts's exclusions[].reason, verify/src/admission/
207
+ // verification.ts's VerifiedBinaryJudgmentAdmissionExclusion.reason, and
208
+ // core/src/operations/human-review.ts's HumanAdmissionExclusionSummary.reason) — duplicated
209
+ // with a comment at each site rather than imported, matching this package's existing pattern for
210
+ // shared closed vocabularies (e.g. IDENTIFIER_NAME above) and its standalone-verifier posture.
211
+ // Two further sites carry the vocabulary but are deliberately left narrow: human-review.ts's
212
+ // exclusion-reason derivation ternary and its downstream type guard structurally cannot produce
213
+ // a screening-* value without the screened branch's own construction logic (out of scope, see
214
+ // this packet's report), so widening their literal sets would be inert. A seventh site,
215
+ // verification.ts's `expectedExclusionReason` (recomputation logic, S3's), is out of scope too.
216
+ const HUMAN_REVIEW_LEDGER_REASONS = ["review-disagreement", "review-indeterminate", "review-incomplete"];
217
+ // Derived from §6.4's admission rule (`admitted := handChecked ? (handVerdict === "confirm") :
218
+ // agreed`): a row is excluded because the screen disagreed with the intended label, because the
219
+ // screen's verdict was indeterminate, or because the operator's hand check excluded it (including
220
+ // the R-3 tie-break: a screen-agreed row the hand check overrode).
221
+ const SCREENING_LEDGER_REASONS = ["screening-disagreement", "screening-indeterminate", "screening-hand-excluded"];
222
+ export const HumanReviewReplacementLedgerEntrySchema = z.strictObject({
223
+ excludedItemSha256: DigestSchema,
224
+ replacementItemSha256: DigestSchema,
225
+ candidateClass: CandidateClassSchema,
226
+ stratum: BinaryJudgmentStratumSchema,
227
+ excludedPoolPosition: z.number().int().positive(),
228
+ replacementPoolPosition: z.number().int().positive(),
229
+ reason: z.enum([...HUMAN_REVIEW_LEDGER_REASONS, ...SCREENING_LEDGER_REASONS]),
230
+ // Per-item two-human apparatus (spec §6.9 deliberately drops it for the screened branch):
231
+ // present if and only if `reason` is one of the three two-human reasons above.
232
+ reviewVerdictSha256s: SortedUniqueDigestPairSchema.optional(),
233
+ visibilityReceiptSha256s: SortedUniqueDigestPairSchema.optional(),
234
+ reviewerRosterSha256: DigestSchema.optional(),
235
+ revealReceiptSha256: DigestSchema.optional(),
236
+ }).superRefine((entry, ctx) => {
237
+ const isTwoHuman = HUMAN_REVIEW_LEDGER_REASONS.includes(entry.reason);
238
+ const twoHumanFields = [
239
+ ["reviewVerdictSha256s", entry.reviewVerdictSha256s],
240
+ ["visibilityReceiptSha256s", entry.visibilityReceiptSha256s],
241
+ ["reviewerRosterSha256", entry.reviewerRosterSha256],
242
+ ["revealReceiptSha256", entry.revealReceiptSha256],
243
+ ];
244
+ for (const [field, value] of twoHumanFields) {
245
+ if (isTwoHuman && value === undefined) {
246
+ ctx.addIssue({ code: "custom", path: [field], message: `${field} is required for a two-human-review reason` });
247
+ }
248
+ if (!isTwoHuman && value !== undefined) {
249
+ ctx.addIssue({ code: "custom", path: [field], message: `${field} must be absent for a screening reason` });
250
+ }
251
+ }
252
+ });
253
+ export const HumanReviewReplacementLedgerSchema = z.strictObject({
254
+ protocol: z.literal(HUMAN_REVIEW_REPLACEMENT_LEDGER_PROTOCOL),
255
+ draftId: IdentitySchema,
256
+ entries: z.array(HumanReviewReplacementLedgerEntrySchema),
257
+ sealedAt: TimestampSchema,
258
+ });
259
+ export const HumanReviewOperatorAssertionSchema = z.strictObject({
260
+ protocol: z.literal(HUMAN_REVIEW_OPERATOR_ASSERTION_PROTOCOL),
261
+ itemSha256: DigestSchema,
262
+ truthLabel: BinaryJudgmentTruthLabelSchema,
263
+ assertedBy: IdentitySchema,
264
+ assertedAt: TimestampSchema,
265
+ attestorKeyId: IdentitySchema,
266
+ attestorRole: z.literal("operator-truth-attestor"),
267
+ limitation: z.literal("operator-only-not-publication-grade"),
268
+ });
269
+ export const BinaryJudgmentAdmissionManifestSchema = z.strictObject({
270
+ protocol: z.literal(BINARY_JUDGMENT_ADMISSION_MANIFEST_PROTOCOL),
271
+ draftId: IdentitySchema,
272
+ truthAdmission: z.enum(["two-human-unanimous", "operator-only", "screened-operator-sampled"]),
273
+ labelResolutionSha256s: SortedUniqueDigestListSchema,
274
+ analysisContextSha256s: SortedUniqueDigestListSchema,
275
+ excludedItemSha256s: SortedUniqueDigestListSchema,
276
+ replacementLedgerSha256: DigestSchema,
277
+ // Present if and only if truthAdmission === "screened-operator-sampled" (spec §6.8). Existing
278
+ // two-human-unanimous and operator-only manifests stay byte-identical: this key is absent for
279
+ // both, exactly as it is today.
280
+ screeningTableSha256: DigestSchema.optional(),
281
+ admittedAt: TimestampSchema,
282
+ }).superRefine((manifest, ctx) => {
283
+ const isScreened = manifest.truthAdmission === "screened-operator-sampled";
284
+ if (isScreened && manifest.screeningTableSha256 === undefined) {
285
+ ctx.addIssue({ code: "custom", path: ["screeningTableSha256"], message: "screeningTableSha256 is required for screened-operator-sampled admission" });
286
+ }
287
+ if (!isScreened && manifest.screeningTableSha256 !== undefined) {
288
+ ctx.addIssue({ code: "custom", path: ["screeningTableSha256"], message: "screeningTableSha256 must be absent outside screened-operator-sampled admission" });
289
+ }
290
+ });