@sanning/proof 0.19.0 → 0.20.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 (98) hide show
  1. package/README.md +30 -29
  2. package/dist/agent-record.d.ts +62 -0
  3. package/dist/agent-record.js +527 -0
  4. package/dist/agent-schemas.generated.d.ts +15 -0
  5. package/dist/agent-schemas.generated.js +5759 -0
  6. package/dist/case-file.d.ts +32 -89
  7. package/dist/case-file.js +41 -93
  8. package/dist/case.d.ts +343 -810
  9. package/dist/case.js +693 -1151
  10. package/dist/chain.d.ts +19 -47
  11. package/dist/chain.js +78 -205
  12. package/dist/claims.d.ts +211 -0
  13. package/dist/claims.js +1081 -0
  14. package/dist/cli.d.ts +45 -68
  15. package/dist/cli.js +618 -1022
  16. package/dist/compose.js +23 -65
  17. package/dist/crypto.d.ts +7 -8
  18. package/dist/crypto.js +60 -147
  19. package/dist/declaration.d.ts +68 -194
  20. package/dist/declaration.js +175 -518
  21. package/dist/environment.d.ts +9 -30
  22. package/dist/environment.js +17 -66
  23. package/dist/evidence.d.ts +119 -160
  24. package/dist/evidence.js +744 -1103
  25. package/dist/exit-codes.d.ts +11 -27
  26. package/dist/exit-codes.js +24 -59
  27. package/dist/file-match.d.ts +33 -79
  28. package/dist/file-match.js +33 -69
  29. package/dist/grading.d.ts +98 -135
  30. package/dist/grading.js +224 -380
  31. package/dist/handoff.d.ts +62 -160
  32. package/dist/handoff.js +153 -393
  33. package/dist/index.d.ts +14 -10
  34. package/dist/index.js +60 -167
  35. package/dist/json-schema.d.ts +29 -0
  36. package/dist/json-schema.js +298 -0
  37. package/dist/merkle.js +16 -28
  38. package/dist/message-parts.d.ts +56 -212
  39. package/dist/message-parts.js +142 -502
  40. package/dist/side-content.d.ts +11 -22
  41. package/dist/side-content.js +22 -65
  42. package/dist/types.d.ts +17 -0
  43. package/dist/types.js +19 -5
  44. package/dist/verifier.d.ts +40 -114
  45. package/dist/verifier.js +196 -535
  46. package/dist/witness-cache.d.ts +24 -45
  47. package/dist/witness-cache.js +38 -81
  48. package/dist/witness-lookup.d.ts +36 -57
  49. package/dist/witness-lookup.js +89 -139
  50. package/dist/zip-reader.d.ts +18 -33
  51. package/dist/zip-reader.js +19 -34
  52. package/package.json +11 -3
  53. package/src/agent-record.ts +608 -0
  54. package/src/agent-schemas.generated.ts +5764 -0
  55. package/src/case-file.ts +45 -109
  56. package/src/case.ts +848 -1322
  57. package/src/chain.ts +85 -224
  58. package/src/claims.ts +1253 -0
  59. package/src/cli.ts +714 -1176
  60. package/src/compose.ts +33 -79
  61. package/src/crypto.ts +60 -147
  62. package/src/declaration.ts +219 -656
  63. package/src/environment.ts +17 -65
  64. package/src/evidence.ts +958 -1381
  65. package/src/exit-codes.ts +24 -63
  66. package/src/file-match.ts +40 -89
  67. package/src/grading.ts +287 -421
  68. package/src/handoff.ts +175 -427
  69. package/src/index.ts +132 -177
  70. package/src/json-schema.ts +295 -0
  71. package/src/merkle.ts +16 -28
  72. package/src/message-parts.ts +155 -533
  73. package/src/side-content.ts +25 -68
  74. package/src/types.ts +55 -36
  75. package/src/verifier.ts +202 -526
  76. package/src/witness-cache.ts +45 -94
  77. package/src/witness-lookup.ts +107 -177
  78. package/src/zip-reader.ts +20 -38
  79. package/vectors/README.md +52 -34
  80. package/vectors/agent-record.json +2175 -0
  81. package/vectors/case-file-index.json +1 -1
  82. package/vectors/case-split.json +2 -2
  83. package/vectors/chain-reading.json +3 -3
  84. package/vectors/file-match.json +285 -148
  85. package/vectors/jcs-reader.json +6 -6
  86. package/vectors/pack-grading.json +102 -25
  87. package/vectors/sandbox-evidence.json +597 -580
  88. package/vectors/stamp-on-chain.json +18 -18
  89. package/vectors/witness-cache.json +49 -49
  90. package/dist/agent-proof.d.ts +0 -28
  91. package/dist/agent-proof.js +0 -226
  92. package/dist/run-adapters.d.ts +0 -10
  93. package/dist/run-adapters.js +0 -35
  94. package/dist/run.d.ts +0 -209
  95. package/dist/run.js +0 -1405
  96. package/src/agent-proof.ts +0 -264
  97. package/src/run-adapters.ts +0 -44
  98. package/src/run.ts +0 -1598
package/dist/case.d.ts CHANGED
@@ -1,1006 +1,543 @@
1
1
  /**
2
- * One verdict for a CASE, across packs and across organizations (anchor#198).
3
- *
4
- * The TypeScript half of `src/sanning_proof/case.py`. Both kernels are held to
5
- * the same corpus set, `test-vectors/case/`, so neither can pass by agreeing
6
- * with the other.
7
- *
8
- * WHY THIS LAYER EXISTS, AND WHAT IT IS THE CLOSURE FOR
9
- * =====================================================
10
- *
11
- * `envelope-spec.md` 4.1 reads records. `evidence-bundle.md` 5.4 reads one pack
12
- * against its own declaration. Neither can answer the question an audit team
13
- * actually asks, and 4.1 (v1.10, A15) and 5.4.2 rule 7 (v1.10) now say so in
14
- * these words:
15
- *
16
- * **A pack a party assembles about itself is not self-proving about what
17
- * that party LEFT OUT.**
18
- *
19
- * `handoffs[]` MAY be empty (5.4.1), so a holder declares a record in
20
- * `records[]` and claims it nowhere: rule 2 cannot reach it because it is
21
- * declared, rule 7 cannot reach it because it is unclaimed, and the pack reads
22
- * `verified` with no deviation, which is the same reading the honest pack gets.
23
- * The corpus pins that pack as EXPECTED
24
- * (`test-vectors/declaration/declaration-unclaimed-record-01`) rather than
25
- * leaving it to be rediscovered. Six repairs treated a property of one-sided
26
- * evidence as a defect in a rule (proof#137, ratified by Phil 2026-09-22), and
27
- * the omission happens BEFORE any reader runs, so no rule at either of those
28
- * layers reaches it.
29
- *
30
- * **This module is the closure, and the reason it can be is that it reads
31
- * several parties' packs at once.** A `handoff.sent` with no counterpart among
32
- * the records of the case is always NAMED, and when a pack signed by the key
33
- * that record names is in the case, the reading says that pack holds no
34
- * counterpart. No party can make a missing counterpart read clean.
35
- *
36
- * What it does NOT establish is that a party which presented no pack signed by
37
- * the named key recorded nothing. Nothing binds a pack's wrapper key to the
38
- * keys its records are signed with, so a counterparty that hands over its
39
- * account under any other key, or inside an export an issuer signed, or not at
40
- * all, gets the reading that says only that no pack in the case is signed by
41
- * the named key. Proving that a party did NOT record something needs that
42
- * party's account or a completeness mechanism over its records, and which of
43
- * those this reading waits for is proof#143.
44
- *
45
- * WHAT THAT MEANS FOR HOW THE JOIN IS BUILT
46
- * =========================================
47
- *
48
- * **The case reader does not consult `handoffs[]` to pair anything.** That is
49
- * not an oversight, it is the whole mechanism. The declaration is written by
50
- * the party being audited, so a join keyed on it inherits exactly the hole it
51
- * is meant to close: claim the clean pair, keep the tampered receipt
52
- * unclaimed, and a declaration-keyed reader never grades the receipt at all.
53
- * This reader pairs records with records, through `readHandoff`, over the
54
- * union of every record every pack in the case supplied. `handoffs[]` is read
55
- * for ONE thing and one only: to name a record its own holder DECLARED and
56
- * claimed nowhere (`RECORD_UNCLAIMED`), which is the pinned vector above
57
- * becoming visible. That claim check runs over every record a pack declares,
58
- * and not only over the ones 4.1 governs, because "is this a conformant
59
- * hand-off record" is an answer the audited party writes.
60
- *
61
- * **Matching is on hashes and identifiers alone and NEVER on tenancy**
62
- * (anchor#198, and DL-60: producer identity gates what the plane accepts and
63
- * never whether evidence verifies). The union pile is not partitioned by pack,
64
- * by issuer, by `tenant_id` or by organization anywhere in this module, so a
65
- * pair assembled by two organizations reads exactly as a pair assembled by
66
- * one. There is no code path here that reads a tenant.
67
- *
68
- * THE DEFECT CLASS THIS MODULE IS WRITTEN AGAINST
69
- * ===============================================
70
- *
71
- * Seventeen findings on this program share one generator: **a deviation
72
- * designed as a comparison between two present things, where the deviation
73
- * itself removes one of them, so the code takes the "nothing to compare"
74
- * branch written for the honest case.** A rule shaped that way reads the
75
- * absence of a signal as "fine" rather than as "I could not tell", and a real
76
- * deviation grades clean.
77
- *
78
- * Acceptance criterion 3 of anchor#198 is made of exactly that shape: *a
79
- * one-sided hand-off is an exception, not a failure*. So the four rules below
80
- * are structural and are the thing to preserve in any change here.
81
- *
82
- * 1. **Every exception is NAMED and appears in the output, and every exception
83
- * contributes a verdict.** There is no path through this module that
84
- * removes a record, a pack or a claim from the reading. An empty
85
- * `exceptions` is the ONLY shape a reader may read as clean, and `readCase`
86
- * is invariant: `verified` is reachable only when that array is empty.
87
- * 2. **Nothing a party writes decides whether grading happens.** No graded
88
- * value is part of any key that selects a code path. The pile is everything
89
- * every pack supplied; there is no filter.
90
- * 3. **Every field that selects a code path and is chosen by the party being
91
- * audited has an accept-set, and an unknown value goes to a named exception
92
- * rather than to silence.** `body_type` is `CASE_BODY_TYPES`;
93
- * `declaration_version` is `checkDeclaration`'s; `event_type` and
94
- * `event.counterparty_public_key` are 4.1's, through `readHandoffs`. A pack
95
- * that supplies zero records is `PACK_HOLDS_NO_RECORDS` and a case with no
96
- * hand-off record in it at all is `CASE_HOLDS_NO_HANDOFF_RECORDS`, because
97
- * "nothing to grade" is never "nothing is wrong".
98
- * 4. **The 4.1 predicates are CALLED, never restated.** `readHandoffs`,
99
- * `normalizedSigningKey` and `packRecords` are the one implementation of
100
- * each rule. A validation copied beside the one that enforces it drifts,
101
- * and a copy that is looser blesses a value the real one then rejects.
102
- *
103
- * EVERY EARLY EXIT IN THIS MODULE, AND WHAT IT SKIPS
104
- * ==================================================
105
- *
106
- * Three defects on this layer were one shape: an exit between a record
107
- * entering the pile and a check running over it, with the audited party
108
- * choosing whether the exit was taken. So the exits are enumerated here rather
109
- * than left to be rediscovered a fourth time. `src/sanning_proof/case.py` has
110
- * the same exits in the same order, and the pull request that added this list
111
- * carries the same table.
112
- *
113
- * 1. **No packs** (`return`). Skips everything. Reached by the reader, who
114
- * chose what to hand in. Named: `CASE_HOLDS_NO_PACKS`.
115
- * 2. **A wrapper key this reader cannot read** (`continue`). Skips the bundle
116
- * verification, the declaration check and every record in the pack. The
117
- * holder writes the key, so it steers here freely, and the pack is NAMED
118
- * (`PACK_KEY_UNREADABLE`). Both bundle verifiers now refuse every such
119
- * spelling in the same words (proof#142), so this exit and exit 3 agree on
120
- * which packs they take; it stays first because it names WHY. While any
121
- * pack takes this exit, a one-sided record whose named key signed no pack
122
- * reads `COUNTERPART_ABSENT_SIGNER_UNKNOWN` and never the innocent
123
- * `COUNTERPART_ABSENT`.
124
- * 3. **The wrapper does not hold** (`continue`): `signatureOk` and
125
- * `bodyHashOk` are not both true. Skips the declaration check and every
126
- * record in the pack, and the key it claims is never counted as a pack
127
- * signer. **This is the forger's case and the only exit that turns on
128
- * provenance**: those two fields are the whole of what ties a pack to a
129
- * key. Reached by whoever built the pack, and by anyone who alters its
130
- * unsigned `signature` field, which is indistinguishable from a forgery.
131
- * Named: `PACK_DOES_NOT_VERIFY`, and it moves one-sided records to
132
- * `COUNTERPART_ABSENT_SIGNER_UNKNOWN` exactly as exit 2 does.
133
- *
134
- * A pack whose wrapper HOLDS is never sent here, whatever is wrong inside
135
- * it (gate 3 on proof#140): its key is counted, its own bundle verdict is
136
- * `PACK_BUNDLE_NOT_VERIFIED` when that verdict is not `verified`, and each
137
- * record is graded on its own envelope below.
138
- * 3b. **The bundle verdict is `malformed`** (`continue`, after exit 3 and after
139
- * `PACK_BUNDLE_NOT_VERIFIED`). The body is one the verifier could not read,
140
- * so it graded no record. The key is already counted.
141
- * 4. **`body_type` outside `CASE_BODY_TYPES`** (`continue`). Skips the
142
- * declaration check and every record in the pack. The holder writes
143
- * `body_type`, so the holder steers here freely, which is why it is named
144
- * every time (`PACK_BODY_TYPE_UNKNOWN`) and why the pack's wrapper key is
145
- * counted ABOVE it: a counterparty cannot leave the damning counterpart
146
- * line by choosing a body (F3). `sanning.evidence.export/v2` lands here and
147
- * is not unwrapped.
148
- * 5. **No records** (`continue`). Skips the declaration check, over nothing.
149
- * The holder steers here by handing over an empty `events[]`. Named:
150
- * `PACK_HOLDS_NO_RECORDS`; the wrapper key is already counted.
151
- * 5b. **A record under a quarantined profile** and 5c. **a record whose own
152
- * envelope does not verify** (`continue`). Each skips 4.1 for that record,
153
- * its candidacy for any other record, and the claim check, because 4.1
154
- * compares only envelopes that verified (`HeldRecord`). Named:
155
- * `RECORD_PROFILE_QUARANTINED` (`incomplete`, pending DL-63) and
156
- * `RECORD_ENVELOPE_DOES_NOT_VERIFY` (`failed`). Neither removes a named
157
- * failure from another record: the other records of the pack are still
158
- * graded, and pairs still form over every record that verified.
159
- * 6. **`event_bytes` withheld** and 7. **`event_bytes` unreadable**
160
- * (`continue`). Each skips 4.1 for that record, its candidacy for any other
161
- * record, and the claim check. The holder steers into both by what it
162
- * discloses. Each is named in the claim check's place
163
- * (`RECORD_BYTES_WITHHELD` `incomplete`, `RECORD_BYTES_UNREADABLE`
164
- * `malformed`), so neither buys a cleaner reading than an unclaimed record
165
- * would get, and neither decides which counterpart kind another record
166
- * gets, since that reads wrapper keys.
167
- *
168
- * Not exits: a record whose envelope verifies and whose content does not
169
- * match its commitment is `RECORD_CONTENT_MISMATCH` where that content is
170
- * inside the signed body and `RECORD_CONTENT_DEPENDENT` where it was handed
171
- * in beside the pack (proof#159), and one that fails
172
- * another check inside its pack is `RECORD_DOES_NOT_VERIFY_IN_ITS_PACK`.
173
- * Both are named and both STAY in the pile, so neither unsigned content
174
- * handed in beside a pack nor a broken inclusion proof removes a record
175
- * from pairing.
176
- * 8. **`not_a_handoff_record`** (`continue`). Skips the hand-off count and the
177
- * counterpart, ambiguity and pair reporting, which 4.1 does not give a
178
- * record outside its scope. The holder steers here by respelling a field.
179
- * It no longer skips the claim check, which runs ABOVE it because it asks
180
- * what the pack SAID and not what the record is (F1).
181
- * 9. **THIS record is malformed** (`continue`). Skips the counterpart,
182
- * ambiguity and pair reporting, and 4.1 computed none of them for a
183
- * malformed subject, so nothing is lost. A party steers here only by
184
- * breaking a record it signed, and the record is named `RECORD_MALFORMED`.
185
- * A well formed record whose CANDIDATE is malformed does not take this
186
- * exit: it is `COUNTERPART_MALFORMED` and the reporting below still runs
187
- * (F2).
188
- * 10. **`counterpart_absent`** (`continue`). Skips the ambiguity and pair
189
- * reporting, and there is nothing to skip: 4.1 emits that finding only
190
- * where no candidate is left to read, an EMPTY candidate list or one whose
191
- * every candidate another record accounts for (4.1 v1.17, A20). Which of the three counterpart kinds it
192
- * gets reads the keys of the packs that verified, counted before exit 4,
193
- * and whether any pack took exit 2 or 3.
194
- *
195
- * Not an exit, and listed because it is one more place this shape could
196
- * hide: the claim check runs only where the pack's declaration is READABLE. An
197
- * absent or unreadable one is named once for the pack (`PACK_UNDECLARED`,
198
- * `PACK_DECLARATION_UNREADABLE`) rather than once per record.
199
- *
200
- * THE VERDICT VOCABULARY IS CLOSED
201
- * ================================
202
- *
203
- * `verified`, `failed`, `partial`, `mixed`, `incomplete` and `malformed`,
204
- * every one of them already in use (`evidence-bundle.md` 3 and
205
- * `PACK_VERDICTS`). **No new word is coined anywhere in this module**, and
206
- * `CASE_VERDICTS` is asserted against `PACK_VERDICTS` by the test suite so a
207
- * future addition here cannot invent one quietly. Everything this module has
208
- * to say that is not one of those six is an EXCEPTION KIND reported beside the
209
- * verdict, exactly as `counterpart_absent` and `not_a_handoff_record` are
210
- * findings beside a verdict in 4.1.
211
- *
212
- * A case with any `failed` contribution is `failed`: a proven failure keeps
213
- * the headline (5.5.4 v1.16, A17, proof#158). Otherwise a case with more than
214
- * one outcome is `mixed` (anchor#198, criterion 4).
215
- *
216
- * WHAT THIS READER DOES NOT CLAIM
217
- * ===============================
218
- *
219
- * * **Not that the case is complete.** Joining is voluntary. A counterparty
220
- * that never handed over a pack leaves `COUNTERPART_ABSENT`, which is an
221
- * exception and never a failure. It says that no pack in the case is signed
222
- * by the named key, and nothing about what that party recorded.
223
- * * **Not that a party which presented no pack under the named key recorded
224
- * nothing.** See the top of this comment, and proof#143.
225
- * * **Not that two organizations are independent.** One team holding both keys
226
- * proves which agent said what, not that two parties agreed (4.1, 5.4.4).
227
- * * **Not that a `verified` case has no other problem.** It reads hand-offs.
228
- * It runs `verifyEvidenceBundle` over every pack itself, offline and with no
229
- * gateways, and a pack whose own bundle verdict is not `verified` is named
230
- * with that verdict and its reasons, so the case never reads above its
231
- * weakest pack. On-chain re-fetch and `gradePack` are a caller's to run.
232
- *
233
- * THE COST OF 4.1's "GRADE EVERY CANDIDATE", AND WHAT IS LEFT OF IT (4.1
234
- * v1.17, amendment A20, which reopens proof#134). Up to v1.16 a pile holding
235
- * two or more records of the complementary type signed by one counterparty key
236
- * could not reach `verified`. 4.1 now sets such a candidate aside where a
237
- * record the subject's own key signed pairs with it verified at another
238
- * sequence, so two messages in one direction, and the demo's A to B, B to A,
239
- * A to B, read clean. What is left surfaces as `COUNTERPART_AMBIGUOUS` beside
240
- * the failure: fan-in, fan-out, and the same pair in two cases at the same
241
- * sequence. Narrowing any of it here with `handoffs[]` is precisely the move
242
- * that would reopen proof#137's door.
2
+ * One verdict for a case: several packs, possibly from several organizations.
3
+ * Conformance: `test-vectors/case/`.
4
+ *
5
+ * A pack a party assembles about itself is not self-proving about what that
6
+ * party left out: a holder can declare a record and claim it nowhere, and the
7
+ * pack still reads `verified` (`declaration-unclaimed-record-01`). Reading
8
+ * several parties' packs at once closes that. An `agent.handoff.sent` with no
9
+ * counterpart among the case's records is always named, and when a pack signed
10
+ * by the key that record names is in the case, the reading says that pack
11
+ * holds no counterpart. It does not establish that a party which presented no
12
+ * such pack recorded nothing: nothing binds a pack's wrapper key to its
13
+ * records' keys.
14
+ *
15
+ * How the join works:
16
+ * - Records pair with records, through `readHandoffs`, over the union of every
17
+ * record every pack supplied. `handoffs[]` is written by the audited party,
18
+ * so it never selects a pair; it is read only to name a declared record that
19
+ * is claimed nowhere (`RECORD_UNCLAIMED`).
20
+ * - Matching is on hashes and identifiers only, never tenancy. Nothing here
21
+ * partitions the pile by pack, issuer, `tenant_id` or organization.
22
+ *
23
+ * Structural rules, to preserve in any change:
24
+ * 1. Every exception is named in the output and contributes a verdict.
25
+ * Nothing removes a record, pack or claim from the reading. `verified` is
26
+ * reachable only when `exceptions` is empty.
27
+ * 2. Nothing a party writes decides whether grading happens. The pile is
28
+ * everything every pack supplied.
29
+ * 3. Every party-chosen field that selects a code path has an accept-set, and
30
+ * an unknown value is a named exception. "Nothing to grade" is never
31
+ * "nothing is wrong".
32
+ * 4. The §4.1 predicates (`readHandoffs`, `normalizedSigningKey`,
33
+ * `packRecords`) are called, never restated.
34
+ *
35
+ * Every early exit in `readCase` names its exception, so no exit buys a
36
+ * cleaner reading than the record or pack would otherwise get. In particular:
37
+ * - A pack with an unreadable key or a wrapper that does not hold is skipped
38
+ * and its key never counted; while any pack takes that exit, a one-sided
39
+ * record reads `COUNTERPART_ABSENT_SIGNER_UNKNOWN`, never `COUNTERPART_ABSENT`.
40
+ * - A wrapper key is counted before the body-type and no-records exits, so
41
+ * choosing a body cannot dodge the counterpart line.
42
+ * - The claim check runs before the not-a-hand-off and malformed exits, so
43
+ * respelling or breaking a record cannot switch it off.
44
+ * - Content and in-pack failures are named and the record stays in the pile.
45
+ *
46
+ * The verdict vocabulary is closed: `verified`, `failed`, `incomplete`
47
+ * (evidence-bundle.md section 3). Every exception maps to one claim and one
48
+ * state (`claims.ts`), and the case reads its worst pack: any `failed`
49
+ * contribution makes the case `failed`, otherwise any `incomplete` makes it
50
+ * `incomplete`, and a reading with no contribution is `verified`.
51
+ *
52
+ * Not claimed: that the case is complete (joining is voluntary), that two
53
+ * organizations are independent (one team can hold both keys), or that a
54
+ * `verified` case has no other problem. Every pack is verified offline here
55
+ * and a pack whose bundle is not `verified` is named; on-chain re-fetch and
56
+ * `gradePack` are the caller's.
57
+ *
58
+ * Several records of the complementary type under one counterparty key can
59
+ * still be ambiguous (fan-in, fan-out, one pair in two cases at one sequence):
60
+ * that surfaces as `COUNTERPART_AMBIGUOUS`. Narrowing it with `handoffs[]`
61
+ * would reopen the hole above.
243
62
  */
244
63
  import { type DeclarationRole } from "./declaration.js";
64
+ import { type AgentRecordReading } from "./agent-record.js";
245
65
  import { type PackGradeCauseKind, type PackVerdict } from "./grading.js";
66
+ import type { EnvironmentMarker } from "./environment.js";
246
67
  import type { SideContentMap } from "./side-content.js";
247
- /**
248
- * The verdicts a case can reach. Every one is already in use in
249
- * `evidence-bundle.md` 3 or `PACK_VERDICTS`, and anchor#198 says in terms that
250
- * no new verdict word is introduced anywhere.
251
- */
252
- export declare const CASE_VERDICTS: readonly ["verified", "failed", "partial", "mixed", "incomplete", "malformed"];
68
+ import { type Problem, type ReasonKind } from "./claims.js";
69
+ /** The three verdicts (evidence-bundle.md section 3). A case reads its worst pack. */
70
+ export declare const CASE_VERDICTS: readonly ["verified", "failed", "incomplete"];
253
71
  export type CaseVerdict = (typeof CASE_VERDICTS)[number];
254
72
  /**
255
- * The two pack bodies that carry `events[]` and may carry a `declaration`
256
- * (`evidence-bundle.md` 5.4.1). This is an ACCEPT-SET and not a hint.
73
+ * The pack bodies that carry `events[]` and may carry a `declaration`
74
+ * (evidence-bundle.md §5.4.1). An accept-set: `body_type` is party-written, so
75
+ * an unknown value is `PACK_BODY_TYPE_UNKNOWN`, never zero records read clean.
257
76
  *
258
- * `body_type` selects a code path and is written by the party assembling the
259
- * pack. Without this set, a pack whose records live under a key this reader
260
- * does not know yields zero records, contributes nothing, and lets the rest of
261
- * the case read clean over it: the exact defect class this module is written
262
- * against. An unknown value is `PACK_BODY_TYPE_UNKNOWN`.
263
- *
264
- * **`sanning.evidence.export/v2` is NOT in this set and is NOT unwrapped.** It
265
- * verifies as a bundle and carries a whole `sanning.anchor.trace/v3` pack under
266
- * `body.source_bundle`, so its records do not reach the pile and the export is
267
- * named `PACK_BODY_TYPE_UNKNOWN`, `incomplete`. Reading inside it changes which
268
- * packs this reader grades, so it is a decision of its own. A reader who needs
269
- * the records hands in the source bundle as a pack. The export's wrapper key is
270
- * still read, so wrapping a pack in one does not change which counterpart kind
271
- * another record gets.
77
+ * `sanning.evidence.export/v2` is not unwrapped: it is named
78
+ * `PACK_BODY_TYPE_UNKNOWN`, and a reader who needs its records hands in the
79
+ * source bundle. Its wrapper key is still read.
272
80
  */
273
81
  export declare const CASE_BODY_TYPES: readonly ["sanning.anchor.trace/v3", "sanning.stamp.evidence/v2"];
274
82
  /**
275
- * The envelope profiles the case reader reads records under: the ratified
276
- * accept-set (`envelope-spec.md` 5) minus every profile under quarantine in
277
- * `cross-kernel/accept-set.json`. A test in each kernel derives both arrays
278
- * from that file, and a test here pins this one equal to this kernel's own
279
- * `ACCEPTED_SPEC_MAJORS`, which is what `verifyEvidenceBundle` reads under:
280
- * the Python kernel narrows its verifier to this set for the case reader, and
281
- * this kernel's verifier already reads exactly it. If this kernel ever admits
282
- * a quarantined profile, that test fails, and the case reader has to narrow
283
- * its verifier the way the Python one does before it can pass.
83
+ * The profiles records are read under: the accept-set (envelope-spec.md §5)
84
+ * minus quarantined profiles in `cross-kernel/accept-set.json`. A test pins
85
+ * this equal to `ACCEPTED_SPEC_MAJORS`, which `verifyEvidenceBundle` reads under.
284
86
  */
285
- export declare const CASE_RECORD_PROFILES: readonly ["sanning.agent/v1", "sanning.events/v2"];
286
- /** The ratified profiles under quarantine, pending DL-63. */
87
+ export declare const CASE_RECORD_PROFILES: readonly ["sanning.events/v2"];
88
+ /** Accepted profiles under quarantine. */
287
89
  export declare const CASE_QUARANTINED_PROFILES: readonly ["sanning.mlflow/v1"];
288
90
  /**
289
- * A conformant hand-off record with no counterpart among the records this
290
- * reader read, where every pack in the case verified under a key this reader
291
- * can read and NO pack in the case is signed by the key the record names,
292
- * or where every candidate it had was accounted for by another record under
293
- * A20 (the exception then carries `count`, and names no pack).
294
- * **An exception, never a failure** (anchor#198, criterion 3).
295
- *
296
- * That is ALL it says. It does not say the counterparty's account is absent,
297
- * and it does not say nothing was tampered with: a counterparty that signs its
298
- * own pack under a second key, or hands over an export an issuer signed,
299
- * reaches this reading too, and nothing offline binds a pack's key to the keys
300
- * of the records inside it (proof#143). The rendered text states the fact and
301
- * stops.
302
- *
303
- * It says nothing about RECORDS signed by that key either. A party may hold records
304
- * its counterparty signed, and those are still graded as candidates, but they
305
- * are not the counterparty's account of itself, so they do not move this
306
- * record to `COUNTERPART_ABSENT_FROM_COUNTERPARTY_RECORDS`.
91
+ * A conformant hand-off record with no counterpart, where every pack verified
92
+ * under a readable key and none is signed by the key the record names, or every
93
+ * candidate was accounted for by another record (then `count` is set and no
94
+ * pack is named). An exception, never a failure. It says only that no pack is
95
+ * signed by the named key; records that key signed and others hold are not that
96
+ * party's own account and do not change it.
307
97
  */
308
98
  export declare const COUNTERPART_ABSENT = "counterpart_absent";
309
99
  /**
310
- * THE CLOSURE anchor#198 exists for.
311
- *
312
- * A conformant hand-off record with no counterpart, where a pack that VERIFIES
313
- * and is SIGNED by the key this record names is in the case, and not one
314
- * record this reader could read is this record's counterpart. It states that a
315
- * pack signed by the named key is present and holds no counterpart this reader
316
- * could read, and nothing more. 4.1 (v1.10, A15) and 5.4.2 rule 7 (v1.10) both point here, and
317
- * `declaration/declaration-unclaimed-record-01` is the pack that reaches this
318
- * line.
319
- *
320
- * **"Its own account" is read from the key that signed the PACK, and never
321
- * from the keys that signed records.** A pack's wrapper key signs `body_hash`
322
- * and `body` carries the declaration (`evidence-bundle.md` 5.4, "Who signs
323
- * it"), so a wrapper signed by B is provenance the accusing party cannot forge
324
- * without B's private key. Records signed by B that A merely HOLDS are not B's
325
- * account of itself: keyed on record keys, one pack from A plus one unrelated
326
- * record B signed at some other time reached this line when B had handed over
327
- * nothing at all.
100
+ * A conformant hand-off record with no counterpart, where a verified pack
101
+ * signed by the key the record names is in the case and holds no counterpart.
102
+ * The closure this module exists for (`declaration-unclaimed-record-01`).
328
103
  *
329
- * What it does NOT prove, stated so no surface claims it: that B handed the
330
- * pack over for THIS case. A pack B signed for some other purpose and a third
331
- * party replayed reads the same, because the only values that could say
332
- * otherwise (the declaration's `case_id` and `period`) are written by B and
333
- * would let B leave this line by writing them differently. The cost the other
334
- * way: a pack B assembles under any key other than the one its records name, a
335
- * per-organization hand-over key (anchor#196) included, reads
336
- * `COUNTERPART_ABSENT`, because nothing in the case binds that key to B. That
337
- * reading is still true: no pack signed by the named key is here. So a
338
- * counterparty can AVOID this line by choosing the key it signs under; what it
339
- * cannot do is make the missing counterpart read clean.
104
+ * "Its own account" is read from the key that signed the pack, never from
105
+ * record keys: a wrapper signed by B is provenance an accuser cannot forge, but
106
+ * records signed by B that A holds are not B's account.
340
107
  *
341
- * It is deliberately NOT also conditioned on a record signed by B being in the
342
- * pile. B's pack can hold none this reader reads (a body it does not unwrap,
343
- * bytes B withheld, an empty `events[]`), and each of those is a choice B makes
344
- * and is named on its own. A second conjunct on them hands B the innocent line
345
- * for making one.
108
+ * Not proven: that B handed the pack over for this case. A pack B signs under
109
+ * any other key reads `COUNTERPART_ABSENT`, which is still true; B can avoid
110
+ * this line by choice of key, but cannot make the missing counterpart read
111
+ * clean. Not conditioned on B's records being readable, since each way B could
112
+ * make them unreadable is its own named choice.
346
113
  *
347
- * It is a DISTINCT kind from `COUNTERPART_ABSENT` and not a louder spelling of
348
- * it. Collapsing the two would be the defect class again: "the counterparty
349
- * never joined" and "the counterparty joined and its own account does not
350
- * contain this hand-off" are the honest reading and the damning one, and one
351
- * name for both lets the second read as the first.
352
- *
353
- * It is still an EXCEPTION and not a failure, which criterion 3 requires and
354
- * which is also the honest call: the counterparty's pack is a selection over
355
- * its own interval, and a hand-off outside that interval is legitimately not
356
- * in it. What this names is that the question can now be ASKED of the other
357
- * party's own evidence, which is what no single pack could ever support.
114
+ * A distinct kind from `COUNTERPART_ABSENT`: one name for both would let the
115
+ * damning reading pass as the honest one. Still `incomplete`, not a failure: a
116
+ * hand-off outside the interval B's pack covers is legitimately absent.
358
117
  */
359
118
  export declare const COUNTERPART_ABSENT_FROM_COUNTERPARTY_RECORDS = "counterpart_absent_from_counterparty_records";
360
119
  /**
361
- * A conformant hand-off record with no counterpart, no VERIFIED pack in the
362
- * case signed by the key it names, and at least one pack in the case that this
363
- * reader could not tie to a key: its wrapper key is unreadable
364
- * (`PACK_KEY_UNREADABLE`) or it does not verify (`PACK_DOES_NOT_VERIFY`).
365
- * Whether a pack signed by the named key is in the case is UNKNOWN, so neither
366
- * of the other two kinds is true.
367
- *
368
- * The gate on the previous version of this reader showed why it exists. A
369
- * counterparty that signed its own pack under `" " + key` got
370
- * `PACK_KEY_UNREADABLE` for the pack and the innocent `COUNTERPART_ABSENT` for
371
- * the record, and the output contradicted itself: whose pack it was is
372
- * UNKNOWN, and then no pack is signed by the named key. The second sentence
373
- * was not established.
374
- *
375
- * `incomplete`, like the other two one-sided kinds: "I could not tell".
120
+ * A conformant hand-off record with no counterpart, no verified pack signed by
121
+ * the key it names, and at least one pack whose signer is unknown
122
+ * (`PACK_KEY_UNREADABLE` or `PACK_DOES_NOT_VERIFY`). Whether the named key
123
+ * signed a pack is unknown, so neither other kind is true. `incomplete`.
376
124
  */
377
125
  export declare const COUNTERPART_ABSENT_SIGNER_UNKNOWN = "counterpart_absent_signer_unknown";
378
126
  /**
379
- * More than one candidate counterpart that nothing accounts for (4.1 v1.17,
380
- * A20). The reading is the worst of them and the ambiguity is named: a reader
381
- * permitted to take one match can be handed an honest decoy beside a tampered
382
- * record. `count` is how many of the candidates are live, which is the number
383
- * the ambiguity is about.
127
+ * More than one live candidate counterpart (§4.1). The reading is the worst of
128
+ * them and the ambiguity is named, so an honest decoy cannot hide a tampered
129
+ * record. `count` is the number of live candidates.
384
130
  */
385
131
  export declare const COUNTERPART_AMBIGUOUS = "counterpart_ambiguous";
132
+ /** A formed pair that failed checks, listed in §4.1's fixed order. */
133
+ export declare const PAIR_FAILED = "pair_failed";
134
+ /** This record itself is malformed under §4.1 (`recordIsMalformed`). */
135
+ export declare const RECORD_MALFORMED = "record_malformed";
386
136
  /**
387
- * A formed pair that failed a check. Carries which checks, from 4.1's own
388
- * fixed order, so a reader is told the record AND the check.
137
+ * A candidate counterpart is malformed and this record is not. §4.1 reports
138
+ * the worst candidate, so without this kind one junk record signed by the
139
+ * counterparty's key would blame the honest record and drop its other failures.
140
+ * `incomplete`: the defect is the candidate's, which carries its own
141
+ * `RECORD_MALFORMED`.
389
142
  */
390
- export declare const PAIR_FAILED = "pair_failed";
143
+ export declare const COUNTERPART_MALFORMED = "counterpart_malformed";
391
144
  /**
392
- * THIS record is wrong: 4.1 governs it and `recordIsMalformed` says so of the
393
- * record itself. A statement about the record this exception names, and about
394
- * no other.
145
+ * This record is one of the two hand-off types, signed and intact, and does
146
+ * not follow its vocabulary (`recordIsNonconforming`). A producer's error,
147
+ * never a proven alteration.
395
148
  */
396
- export declare const RECORD_MALFORMED = "record_malformed";
149
+ export declare const RECORD_NONCONFORMING = "record_nonconforming";
397
150
  /**
398
- * A CANDIDATE counterpart of this record is malformed, and this record is not.
399
- *
400
- * 4.1 grades every candidate and reports the worst of them, and `malformed`
401
- * outranks `failed`, so one junk record signed by the counterparty's key makes
402
- * the reading of a perfectly well formed record `malformed` too. Filed under
403
- * `RECORD_MALFORMED`, that read as "this record is not a well formed hand-off
404
- * record" about the HONEST record, and the branch it took skipped the
405
- * counterpart, ambiguity and `failedChecks` reporting entirely: one junk record
406
- * both blamed the wrong party and stripped a named `message_hash` failure out
407
- * of the case. That is proof#133 ruling 6, which 4.1 closed by keeping the
408
- * UNION of `failedChecks`, reappearing one layer out because this layer
409
- * discarded that union.
410
- *
411
- * A record's own conformance and its candidate's are different facts and do
412
- * not share a kind. This one is `incomplete` and not `malformed`: the proven
413
- * defect belongs to the candidate, which carries its own `RECORD_MALFORMED`
414
- * line, and what is true of THIS record is that its pairing could not be
415
- * determined. Contributing `malformed` here would attribute the candidate's
416
- * defect to the record a party being accused did not write.
151
+ * A candidate counterpart is nonconforming and this record is not.
152
+ * `incomplete` for the reason `COUNTERPART_MALFORMED` is: the defect is the
153
+ * candidate's, which carries its own `RECORD_NONCONFORMING`.
417
154
  */
418
- export declare const COUNTERPART_MALFORMED = "counterpart_malformed";
155
+ export declare const COUNTERPART_NONCONFORMING = "counterpart_nonconforming";
419
156
  /**
420
- * A record that its own pack DECLARES and its own pack's READABLE declaration
421
- * claims in no `handoffs[]` entry. This is proof#137's door, named at the layer
422
- * that can see it: the holder declared the record and claimed it nowhere, so
423
- * neither 5.4.2 rule 2 nor rule 7 reaches it and the pack reads `verified`.
424
- *
425
- * **It turns on what the PACK said about the record, and never on whether 4.1
426
- * governs it.** Gating it on "is this a conformant hand-off record" put it
427
- * behind a value the audited party writes: the same second receipt, respelled
428
- * with an `ff` ligature in `event_type` and a Cyrillic `е` in
429
- * `counterparty_public_key`, left 4.1's scope, took the `not_a_handoff_record`
430
- * branch, and bought a case reading of `verified` with zero exceptions where
431
- * the conformant spelling read `mixed` with four. proof#136 ratified that a
432
- * respelling buys a DIFFERENT LINE rather than no line; at this layer it
433
- * bought no line at all, and a strictly better outcome than honesty.
157
+ * A record its pack declares and its pack's readable declaration claims in no
158
+ * `handoffs[]` entry (§5.4.2 rules 2 and 7 cannot reach it). Turns on what the
159
+ * pack said, never on whether §4.1 governs the record, so respelling
160
+ * `event_type` cannot move a record out of this check.
434
161
  *
435
- * It is not an accusation. `handoffs[]` MAY be empty and an unclaimed record
436
- * may be entirely innocent. What it is, is the end of the silence: the reader
437
- * cannot tell whether an unclaimed record is part of the hand-over the holder
438
- * meant to give or one it is keeping out of its own account, and that is "I
439
- * could not tell", which is why it carries `incomplete` and not `verified`.
440
- *
441
- * THE COST, stated rather than left to be found: a pack that declares records
442
- * which are not hand-off sides at all, an ordinary log line among them, is
443
- * named once per such record, so a case holding one cannot read `verified`.
444
- * That is the price of a rule with no branch the holder selects, and the only
445
- * narrower rule available keys on a value the holder writes. A surface that
446
- * wants the two apart reads `CaseRecordReading.findings`, where
447
- * `not_a_handoff_record` still says which is which.
162
+ * Not an accusation: `handoffs[]` may be empty and the record innocent, so it
163
+ * is `incomplete`. The cost is that a declared non-hand-off record (a log line)
164
+ * is named too; `CaseRecordReading.findings` tells the two apart.
448
165
  */
449
166
  export declare const RECORD_UNCLAIMED = "record_unclaimed_by_its_pack";
450
167
  /**
451
- * The pack supplied no `event_bytes` for a record, so this reader cannot see
452
- * what the record IS in either direction.
453
- *
454
- * `event_bytes` is OPTIONAL (5.1, 5.3) and withholding it is minimum
455
- * disclosure, which the corpus calls a customer's right and not a defect
456
- * (proof#137, defect 3), so this is not a deviation and does not fail. It is
457
- * still SAID, because a withheld record is exactly where a missing counterpart
458
- * would hide, and a reader that dropped it silently would read a trimmed pack
459
- * as an honest one.
168
+ * The pack withheld `event_bytes` for a record. Withholding is minimal
169
+ * disclosure (§5.1, §5.3), not a deviation, but it is said: a withheld record is
170
+ * where a missing counterpart would hide.
460
171
  */
461
172
  export declare const RECORD_BYTES_WITHHELD = "record_bytes_withheld";
462
173
  /**
463
- * The pack SUPPLIED `event_bytes` this reader cannot read as
464
- * `JCS(event record)` (5.4.2 rule 7's parser rule, one implementation in
465
- * `packRecords`). Bytes the holder handed over that do not decode are a defect
466
- * in what was handed over, not an absence, so this one is `malformed`.
174
+ * The pack supplied `event_bytes` that do not read as `JCS(event record)`.
175
+ * A defect in what was handed over, so `malformed`.
467
176
  */
468
177
  export declare const RECORD_BYTES_UNREADABLE = "record_bytes_unreadable";
469
- /**
470
- * The pack deviates from its own declaration (5.4.2). A proven mismatch, so it
471
- * fails: a case cannot be clean over a pack that is proven to be trimmed.
472
- */
178
+ /** The pack deviates from its own declaration (§5.4.2). A proven mismatch: `failed`. */
473
179
  export declare const PACK_DECLARATION_DEVIATES = "pack_declaration_deviates";
474
180
  /**
475
- * The pack and the UNSIGNED content handed in beside it do not line up (5.4.2
476
- * rules 3 and 4, proof#147, ratified by Phil 2026-09-22). A record the pack
477
- * declares `disclosed` for which no bytes reached this reader, or one it
478
- * declares `content_withheld` for which bytes reached this reader from the
479
- * side-content map alone.
480
- *
481
- * `incomplete` and never `failed`, which is the whole point of the kind. 5.4
482
- * grades a pack's declaration partly against content the READER was handed,
483
- * and whoever assembles a case fills that map in. Under
484
- * `PACK_DECLARATION_DEVIATES` an accuser could put an intact pack into a
485
- * `failed` case WITH NO KEY AT ALL: supply bytes for a record its holder
486
- * declared withheld, or drop the bytes of one it declared disclosed. The pack
487
- * was intact, its wrapper verified, and its holder had done nothing. This is
488
- * the mirror of proof#143, where a counterparty makes ITSELF look better by
489
- * choosing what it hands over.
181
+ * The pack and the unsigned content beside it do not line up (§5.4.2 rules 3
182
+ * and 4): a record declared `disclosed` with no bytes, or declared
183
+ * `content_withheld` with bytes from the side-content map alone.
490
184
  *
491
- * It is emitted BESIDE `PACK_DECLARATION_DEVIATES` where a pack does both,
492
- * because the two say different things about different records.
185
+ * `incomplete`, never `failed`: whoever assembles a case fills in that map, so
186
+ * otherwise an accuser could fail an intact pack with no key. Emitted beside
187
+ * `PACK_DECLARATION_DEVIATES` when a pack does both.
493
188
  */
494
189
  export declare const PACK_DECLARATION_CONTENT_DEPENDENT = "pack_declaration_content_dependent";
495
190
  /**
496
- * The pack carries a `declaration` this reader cannot read (5.4.3): an unknown
497
- * `declaration_version`, an absent or `null` one, a non-object, or one that
498
- * does not satisfy 5.4.1. `declaration_version` is chosen by the same party
499
- * that assembles the pack and has no accept-set of its own, so this is UNKNOWN
500
- * and never clean.
191
+ * The pack's `declaration` cannot be read (§5.4.3). Its version is chosen by
192
+ * the pack's assembler with no accept-set, so this is unknown, never clean.
501
193
  */
502
194
  export declare const PACK_DECLARATION_UNREADABLE = "pack_declaration_unreadable";
503
195
  /**
504
- * The pack carries no `declaration` member at all, so it never said what it
505
- * holds. `partial` is 5.4.3's own word for a claim nobody made: never
506
- * `verified`, because there is nothing to check the pack against, and never
507
- * `failed`, because nothing is wrong.
196
+ * The pack carries no `declaration`. `partial` (§5.4.3): nothing to check
197
+ * against, and nothing wrong.
508
198
  */
509
199
  export declare const PACK_UNDECLARED = "pack_undeclared";
510
- /**
511
- * The pack's `body_type` is not one this reader knows to carry `events[]`
512
- * (`CASE_BODY_TYPES`).
513
- */
200
+ /** The pack's `body_type` is not in `CASE_BODY_TYPES`. */
514
201
  export declare const PACK_BODY_TYPE_UNKNOWN = "pack_body_type_unknown";
515
- /**
516
- * The pack carries a body this reader knows and no records in it. A pack that
517
- * contributes nothing is named rather than passed over.
518
- */
202
+ /** The pack carries a known body with no records. */
519
203
  export declare const PACK_HOLDS_NO_RECORDS = "pack_holds_no_records";
520
204
  /**
521
- * The key the pack's wrapper names is not one this reader can read as a key
522
- * (`normalizedSigningKey` returns nothing for it), so who signed the pack is
523
- * UNKNOWN. The pack is not verified and none of its records are read, and
524
- * every one-sided record whose named key signed no verified pack reads
525
- * `COUNTERPART_ABSENT_SIGNER_UNKNOWN`.
526
- *
527
- * Found by constructing it, while enumerating this module's exits: the wrapper
528
- * key is the ONE input that separates the two counterpart kinds, and a key this
529
- * reader could not read used to drop out of that set in silence. The Python
530
- * `verify_evidence_bundle` accepts a wrapper `public_key` with ASCII
531
- * whitespace inside it or around it (`bytes.fromhex` skips it; this kernel's
532
- * verifier rejects it), so a counterparty that signed its own pack under
533
- * `" " + key` handed a Python reader a pack that verified, contributed no key,
534
- * and moved the subject from `COUNTERPART_ABSENT_FROM_COUNTERPARTY_RECORDS` to
535
- * the innocent `COUNTERPART_ABSENT`. The same repair 4.1 made for a RECORD
536
- * whose envelope key it cannot read (proof#133, fourth hunt): a key a reader
537
- * holds and cannot read is named, never treated as absent. It is `incomplete`
538
- * rather than `malformed` because whether the spelling verifies is the envelope
539
- * layer's call (proof#135), and this reader only knows it cannot read it.
205
+ * The pack's wrapper key cannot be read (`normalizedSigningKey` returns
206
+ * nothing), so its signer is unknown. None of its records are read, and a
207
+ * one-sided record whose named key signed no verified pack reads
208
+ * `COUNTERPART_ABSENT_SIGNER_UNKNOWN`. A key a reader holds and cannot read is
209
+ * named, never treated as absent. `incomplete`: whether the spelling verifies
210
+ * is the envelope layer's call.
540
211
  */
541
212
  export declare const PACK_KEY_UNREADABLE = "pack_key_unreadable";
542
213
  /**
543
- * The pack does not verify as a bundle: `verifyEvidenceBundle`, run here
544
- * offline over the pack and its side content, returned `failed` or
545
- * `malformed`. None of its records are read, because a record from it would
546
- * reach 4.1 without the verified envelope `HeldRecord` requires, and the key
547
- * its wrapper claims is never counted as a pack signer: an unverified wrapper
548
- * claiming the counterparty's key is exactly what an accusing party would
549
- * forge.
550
- *
551
- * `partial` is not this exception. `partial` means the wrapper signature and
552
- * `body_hash` held and part of the body could not be determined, which is what
553
- * an unknown body or withheld bytes produce, and those are named on their own.
554
- *
555
- * `failed`: a pack in the case is proven broken.
214
+ * The pack's `bundle.json` cannot be read as JSON: it does not parse, nests too deep, or
215
+ * names one member twice. The pack is not verified and none of its records are read, and
216
+ * every other pack in the case still is. `incomplete`: the signature could not be checked.
217
+ */
218
+ export declare const PACK_UNREADABLE = "pack_unreadable";
219
+ /**
220
+ * `verifyEvidenceBundle`, run offline here, returned `failed` or `malformed`.
221
+ * None of its records are read (§4.1 requires verified envelopes), and its
222
+ * wrapper key is never counted as a pack signer: an unverified wrapper claiming
223
+ * the counterparty's key is what an accuser would forge. `failed`.
556
224
  */
557
225
  export declare const PACK_DOES_NOT_VERIFY = "pack_does_not_verify";
558
226
  /**
559
- * The pack's wrapper HOLDS and its CASE GRADE is not `verified`
560
- * (evidence-bundle 5.5.1 step 1 v1.20, A23; it replaces
561
- * `pack_bundle_not_verified`, which carried the bundle status alone). The case
562
- * grade is the pack grade of 5.4.5, taken twice: the SIGNED grade over the run
563
- * with the side map empty, and the FULL grade over the run with it. The signed
564
- * grade decides where it is `failed` or `malformed`; a failure only the full
565
- * grade reaches is `incomplete`, because it rests on bytes nobody signed;
566
- * otherwise the full grade. A body the verifier could not read is `malformed`,
567
- * a body that is not one of the two event-carrying bodies takes the signed
568
- * run's status, and a verifier that graded a different number of records than
569
- * the pack holds takes the signed status, or `malformed` where that status is
227
+ * The pack's wrapper holds and its case grade is not `verified`
228
+ * (evidence-bundle §5.5.1). The case grade is the §5.4.5 pack grade taken
229
+ * twice: signed (side map empty) and full (with it). The signed grade decides
230
+ * where it is `failed` or `malformed`; a failure only the full grade reaches is
231
+ * `incomplete`, since it rests on unsigned bytes; otherwise the full grade.
232
+ * An unreadable body is `malformed`; a non-event body takes the signed status;
233
+ * a record-count mismatch takes the signed status, or `malformed` where that is
570
234
  * `verified`.
571
235
  *
572
- * The exception carries `status`, the case grade; `causes`, the 5.4.5 cause
573
- * kinds of the grade that decided it (none in the first three branches); and
574
- * `reasons`, the verifier's reasons from the run with the side map empty. It
575
- * CONTRIBUTES the grade, and `incomplete` for `pending`, `not_found` or
576
- * `mixed`, so the case verdict never reads above its weakest pack.
236
+ * Carries `status`, `causes` (the deciding grade's cause kinds) and `reasons`
237
+ * (from the signed run). Contributes the grade, or `incomplete` for `pending`,
238
+ * `not_found` or `mixed`, so the case never reads above its weakest pack.
577
239
  */
578
240
  export declare const PACK_GRADE_NOT_VERIFIED = "pack_grade_not_verified";
579
241
  /**
580
- * A record in a pack whose wrapper holds, whose OWN envelope does not verify
581
- * (`EventResult.envelopeOk` is false). Not read by 4.1, and NAMED with the
582
- * verifier's reasons. `failed`: a pack its holder signed carries a record that
583
- * is proven not to verify. As a reason to drop the whole pack it let the
584
- * counterparty add one junk event to its own pack, re-seal it, and strip a
585
- * proven `message_hash` failure out of the case (gate 3 on proof#140).
242
+ * A record whose own envelope does not verify, in a pack whose wrapper holds.
243
+ * Not read by §4.1. `failed`. Scoped to the record so one junk event cannot
244
+ * drop the whole pack and a proven failure with it.
586
245
  */
587
246
  export declare const RECORD_ENVELOPE_DOES_NOT_VERIFY = "record_envelope_does_not_verify";
588
247
  /**
589
- * A record whose envelope verifies and which fails another check the bundle
590
- * verifier grades it on inside its pack (its inclusion proof, its join to a
591
- * checkpoint or stamp, a retired field name). NAMED with the verifier's
592
- * reasons, `failed`, and the record STAYS in the pile: a holder that could
593
- * remove its own tampered receipt from pairing by breaking that receipt's
594
- * inclusion proof would be choosing its own reading.
248
+ * A record whose envelope verifies but which fails another check in its pack
249
+ * (inclusion proof, checkpoint or stamp join, a retired field). `failed`, and
250
+ * the record stays in the pile, so breaking its own proof cannot remove a
251
+ * tampered receipt from pairing.
595
252
  */
596
253
  export declare const RECORD_DOES_NOT_VERIFY_IN_ITS_PACK = "record_does_not_verify_in_its_pack";
597
254
  /**
598
- * A record whose envelope verifies and whose disclosed content does not hash to
599
- * the hash it commits to, its `content_hash` or a hand-off record's
600
- * `message_hash` (evidence-bundle v1.19, A20) (`EventResult.contentOk` is false), with the
601
- * side map EMPTY: the content is inside the signed body. The same mismatch from
602
- * content handed in beside the pack is `RECORD_CONTENT_DEPENDENT` (proof#159).
603
- * `failed`, and the record STAYS in the pile: content beside a pack is unsigned and often
604
- * supplied by whoever assembled the case, and gate 3 on proof#140 showed that
605
- * party erasing an honest verified pair by swapping it.
255
+ * A record whose disclosed content does not hash to its `content_hash` (or a
256
+ * hand-off record's `message_hash`) with the side map empty: the content is in
257
+ * the signed body. `failed`, and the record stays in the pile.
606
258
  */
607
259
  export declare const RECORD_CONTENT_MISMATCH = "record_content_mismatch";
608
260
  /**
609
- * The two readings proof#147 did not reach, given its shape (proof#159, ruled
610
- * by Phil 2026-09-22, evidence-bundle.md 5.5.4 v1.16, A17). A pack whose
611
- * bundle verdict is `failed` ONLY when read with the unsigned content handed
612
- * in beside it, and `failed` no longer when read with that map empty, is
613
- * `PACK_BUNDLE_CONTENT_DEPENDENT`; a record whose content mismatch comes from
614
- * that map alone is `RECORD_CONTENT_DEPENDENT`. Both are `incomplete`: the
615
- * reader established that the bytes it was handed are not the bytes the record
616
- * commits to, and nothing about the party that signed the record, because
617
- * whoever assembled the case chose those bytes with no key. Up to v1.15 both
618
- * readings carried `failed`, and one flipped nibble of accuser-supplied content
619
- * put a `failed` against an intact pack; under A17's headline rule that would
620
- * have become the case's headline.
621
- *
622
- * The test that separates the two is the one proof#159 states for the inverse:
623
- * every deviation a signature covers still reads `failed` with the side map
624
- * EMPTY. So the pack is verified twice, with the map and without it, and what
625
- * fails without it is `PACK_GRADE_NOT_VERIFIED`, `RECORD_CONTENT_MISMATCH`
626
- * (content inside the signed body) or `RECORD_DOES_NOT_VERIFY_IN_ITS_PACK`,
627
- * each still `failed`. Emptying the map is the most a party that signed
628
- * nothing can do to starve the check, and it cannot remove any of those.
261
+ * A pack whose bundle verdict is `failed` only with the unsigned side content,
262
+ * and a record whose content mismatch comes from that content alone
263
+ * (evidence-bundle §5.5.4). Both `incomplete`: whoever assembled the case chose
264
+ * those bytes with no key. Every deviation a signature covers still fails with
265
+ * the side map empty, so starving the check cannot hide one.
629
266
  */
630
267
  export declare const PACK_BUNDLE_CONTENT_DEPENDENT = "pack_bundle_content_dependent";
631
268
  export declare const RECORD_CONTENT_DEPENDENT = "record_content_dependent";
632
269
  /**
633
- * A record signed under a ratified profile that is under quarantine
634
- * (`CASE_QUARANTINED_PROFILES`, pending DL-63). Not read, in any kernel.
635
- * `incomplete`: whether it verifies is what the kernels disagree about.
270
+ * A record under a quarantined profile (`CASE_QUARANTINED_PROFILES`). Not read.
271
+ * `incomplete`.
636
272
  */
637
273
  export declare const RECORD_PROFILE_QUARANTINED = "record_profile_quarantined";
638
274
  /** The reader was handed no packs at all. */
639
275
  export declare const CASE_HOLDS_NO_PACKS = "case_holds_no_packs";
640
276
  /**
641
- * The three NOTEs (5.5.3 v1.20, extended proof#148): findings on the reading
642
- * with no contribution, printed in the scope sentence so a clean verdict
643
- * never hides any of them. `pack_undeclared` is a note only where the
644
- * reading holds no hand-off record; where it holds one, it is the exception
645
- * of that name.
277
+ * Notes (§5.5.3): findings with no verdict contribution, printed in the scope
278
+ * sentence so a clean verdict never hides them. `pack_undeclared` is a note
279
+ * only where the reading holds no hand-off record; otherwise it is the
280
+ * exception.
646
281
  */
647
282
  export declare const CONTENT_WITHHELD_BY_DECLARATION = "content_withheld_by_declaration";
648
283
  /**
649
- * A pack's wrapper carries a present `co_signatures` (evidence-bundle.md §2,
650
- * decided by Phil 2026-09-24 on proof#148). The field verifies nothing and
651
- * sits outside the wrapper's signed scope, so the case reading names it
652
- * rather than presenting it as evidence, and it contributes no verdict.
284
+ * A pack's wrapper carries `co_signatures` (evidence-bundle section 2). It verifies
285
+ * nothing and sits outside the signed scope, so it is named, not presented as
286
+ * evidence.
653
287
  */
654
288
  export declare const CO_SIGNATURES_PRESENT = "co_signatures_present";
655
289
  export declare const CASE_NOTE_KINDS: readonly ["pack_undeclared", "content_withheld_by_declaration", "co_signatures_present"];
656
290
  export type CaseNoteKind = (typeof CASE_NOTE_KINDS)[number];
657
- /**
658
- * Every exception this reader can report, as a closed set. A surface may render
659
- * them and a test may assert over them; nothing outside this array is emitted.
660
- */
661
- export declare const CASE_EXCEPTION_KINDS: readonly ["counterpart_absent", "counterpart_absent_from_counterparty_records", "counterpart_absent_signer_unknown", "counterpart_ambiguous", "counterpart_malformed", "pair_failed", "record_malformed", "record_unclaimed_by_its_pack", "record_bytes_withheld", "record_bytes_unreadable", "pack_declaration_deviates", "pack_declaration_content_dependent", "pack_declaration_unreadable", "pack_undeclared", "pack_body_type_unknown", "pack_holds_no_records", "pack_key_unreadable", "pack_does_not_verify", "pack_grade_not_verified", "pack_bundle_content_dependent", "record_envelope_does_not_verify", "record_does_not_verify_in_its_pack", "record_content_mismatch", "record_content_dependent", "record_profile_quarantined", "case_holds_no_packs"];
291
+ /** Every exception this reader can report. Nothing outside this set is emitted. */
292
+ export declare const CASE_EXCEPTION_KINDS: readonly ["counterpart_absent", "counterpart_absent_from_counterparty_records", "counterpart_absent_signer_unknown", "counterpart_ambiguous", "counterpart_malformed", "counterpart_nonconforming", "pair_failed", "record_malformed", "record_nonconforming", "record_unclaimed_by_its_pack", "record_bytes_withheld", "record_bytes_unreadable", "pack_declaration_deviates", "pack_declaration_content_dependent", "pack_declaration_unreadable", "pack_undeclared", "pack_body_type_unknown", "pack_holds_no_records", "pack_unreadable", "pack_key_unreadable", "pack_does_not_verify", "pack_grade_not_verified", "pack_bundle_content_dependent", "record_envelope_does_not_verify", "record_does_not_verify_in_its_pack", "record_content_mismatch", "record_content_dependent", "record_profile_quarantined", "case_holds_no_packs"];
662
293
  export type CaseExceptionKind = (typeof CASE_EXCEPTION_KINDS)[number];
663
294
  /**
664
- * The status every record in every pack carries (evidence-bundle 5.5.4
665
- * v1.35, A43, proof#204), as a closed set. A record is listed for each element
666
- * of its pack's `body.events` as `packRecords` reads them, or, for a
667
- * `sanning.evidence.export/v2` pack, of the events of the inline source
668
- * bundle it wraps (`resolveTraceEvents`), and gets exactly one:
669
- *
670
- * - `read_clean`: the record went into the pile, no exception carries its
671
- * `packIndex` and `recordIndex`, and its pack's declaration check
672
- * names it by nothing (`declarationFindings` is empty). It says nothing
673
- * about its PACK: the pack's own grade and exceptions are on the pack, and a
674
- * surface MUST show that grade beside the status.
675
- * - `read_flagged`: the record went into the pile, and at least one
676
- * exception carries its `packIndex` and `recordIndex`, or its pack's
677
- * declaration check names its `eventId` by a deviation or a content
678
- * finding (`declarationFindings` is not empty; under an `eventId` the
679
- * pack carries twice, every record with that id). An exception about the
680
- * pack alone flags no record.
681
- * - `not_read`: the record never went into the pile. `reason` is the kind
682
- * of the exception that says why, on the record
683
- * (`record_profile_quarantined`, `record_envelope_does_not_verify`,
684
- * `record_bytes_withheld`, `record_bytes_unreadable`) or on its pack
685
- * (`pack_key_unreadable`, `pack_does_not_verify`,
686
- * `pack_body_type_unknown`, `pack_grade_not_verified` where the body
687
- * could not be read or the verifier graded a different number of records).
688
- * An export's wrapped records carry the reason of the exception their pack
689
- * actually got: `pack_body_type_unknown` where its wrapper holds, and
690
- * otherwise `pack_key_unreadable`, `pack_does_not_verify` or
691
- * `pack_grade_not_verified`.
295
+ * The status each record of each pack gets (evidence-bundle §5.5.4), one per
296
+ * element of `body.events` (or, for an export, its source bundle's events):
692
297
  *
693
- * Before A43 the reading listed only the records it read, so a surface had to
694
- * work out which records were missing, and why, by a rule of its own.
298
+ * - `read_clean`: in the pile, named by no exception and no declaration
299
+ * finding. Says nothing about its pack; a surface must show the pack grade
300
+ * beside it.
301
+ * - `read_flagged`: in the pile, and named by an exception or a declaration
302
+ * finding (every record sharing a duplicated `eventId`). A pack-only
303
+ * exception flags no record.
304
+ * - `not_read`: never in the pile. `reason` is the record or pack exception
305
+ * that says why; an export's wrapped records take their pack's exception.
695
306
  */
696
307
  export declare const CASE_RECORD_STATUSES: readonly ["read_clean", "read_flagged", "not_read"];
697
308
  export type CaseRecordStatus = (typeof CASE_RECORD_STATUSES)[number];
698
309
  /**
699
- * One pack in a case, as a reader was handed it.
700
- *
701
- * `ref` is the reader's own name for the pack (a file name, an archive member)
702
- * and is never read from the pack itself: a value inside the pack is written by
703
- * the party that assembled it, and two packs are free to claim one name.
704
- *
705
- * A caller passes packs as it received them. `readCase` runs
706
- * `verifyEvidenceBundle` over each one itself, so `HeldRecord`'s precondition
707
- * (every envelope it compares has verified) is met by this module and cannot
708
- * be skipped by a caller. A pack that does not verify is
709
- * `PACK_DOES_NOT_VERIFY`.
310
+ * One pack in a case, as handed to the reader. `ref` is the reader's own name
311
+ * for it, never read from the pack, since two packs can claim one name.
312
+ * `readCase` verifies each pack itself, so the verified-envelope precondition
313
+ * of `HeldRecord` cannot be skipped by a caller.
710
314
  */
711
315
  export interface CasePack {
712
316
  ref: string;
713
317
  bundle: unknown;
714
318
  content?: SideContentMap | null;
319
+ /** Why the pack's `bundle.json` could not be read, where it could not; `bundle` is then ignored (`PACK_UNREADABLE`). */
320
+ unreadable?: string | null;
715
321
  }
716
- /** One named exception, as data. A surface renders its own prose or calls
717
- * `renderException`. */
322
+ /** One named exception, as data. A surface renders it or calls `renderException`. */
718
323
  export interface CaseException {
719
324
  kind: CaseExceptionKind;
720
- /**
721
- * Which pack this is about, by the reader's own name for it. Two packs can
722
- * carry one `ref`, so it is a label and never the join: `packIndex` is.
723
- */
325
+ /** The reader's name for the pack: a label, never the join (`packIndex` is). */
724
326
  packRef?: string;
725
- /**
726
- * Which pack this is about, by its position in `CaseReading.packs` (0
727
- * first), present wherever `packRef` is (A43). Unique in the reading.
728
- */
327
+ /** The pack's position in `CaseReading.packs`, wherever `packRef` is set. */
729
328
  packIndex?: number;
730
329
  /** Which record, by its envelope `event_id`. */
731
330
  eventId?: string;
732
331
  /**
733
- * Which record, by its position in its pack's `body.events` (0 first),
734
- * present wherever `eventId` is (A43). One pack can carry one `event_id`
735
- * twice, so `packIndex` and `recordIndex` together are the join to
736
- * `CaseReading.records`.
332
+ * The record's position in its pack's events, wherever `eventId` is set.
333
+ * With `packIndex`, the join to `CaseReading.records`, since one pack can
334
+ * carry an `event_id` twice.
737
335
  */
738
336
  recordIndex?: number;
739
- /**
740
- * The counterparty key the record named, for the two counterpart kinds.
741
- * Normalized (`normalizedSigningKey`), so a reader compares what this module
742
- * compared.
743
- */
337
+ /** The counterparty key the record named, normalized as this module compared it. */
744
338
  counterpartyKey?: string | null;
745
- /** Which checks a formed pair failed, in 4.1's fixed order. */
339
+ /** Which checks a formed pair failed, in §4.1's fixed order. */
746
340
  failedChecks?: string[];
747
341
  /**
748
- * How many of a thing the exception is about, where it is about a count. On
749
- * `COUNTERPART_ABSENT` it is set only where 4.1 set every candidate aside
750
- * (v1.17, A20), and it is how many were set aside.
342
+ * The count the exception is about. On `COUNTERPART_ABSENT`, set only where
343
+ * every candidate was set aside, and it is how many were.
751
344
  */
752
345
  count?: number;
753
346
  /** The pack's case grade, for `PACK_GRADE_NOT_VERIFIED`. */
754
347
  status?: CaseVerdict;
755
348
  /**
756
- * The 5.4.5 cause kinds of the grade that decided the case grade, for
757
- * `PACK_GRADE_NOT_VERIFIED`, in the grade's order. Empty where the grade was
758
- * not computed (an unreadable body, a body that is not one of the two
759
- * event-carrying ones, a verifier that graded too few records).
349
+ * For `PACK_GRADE_NOT_VERIFIED`, the §5.4.5 cause kinds of the deciding grade.
350
+ * Empty where no grade was computed.
760
351
  */
761
352
  causes?: PackGradeCauseKind[];
762
353
  /**
763
- * What the bundle verifier said, as it said it, for the kinds that carry a
764
- * verifier's grading: `PACK_GRADE_NOT_VERIFIED` and the three record kinds
765
- * graded on the record's own envelope and content.
354
+ * The bundle verifier's own reasons, for `PACK_GRADE_NOT_VERIFIED` and the
355
+ * record kinds graded on the record's envelope and content.
766
356
  */
767
357
  reasons?: string[];
358
+ /**
359
+ * For `PACK_GRADE_NOT_VERIFIED`, every problem behind the grade, each mapped to one
360
+ * claim and one state (`claims.ts`), at this pack's position. A grade that is not
361
+ * `failed` carries no disproof: one that only the unsigned side map reached reads
362
+ * `not_proven`.
363
+ */
364
+ problems?: Problem[];
768
365
  }
769
366
  /**
770
- * One record of one pack, with its status and, where it was read, 4.1's
771
- * reading of it.
772
- *
773
- * EVERY record of every pack gets one of these (A43), in pack order and then
774
- * in bundle order: the ones this reader read, including the ones that are not
775
- * hand-off records at all (`findings` carries `not_a_handoff_record`), and the
776
- * ones it did not, with `status` `not_read` and the `reason`. 4.1 (v1.9, A14)
777
- * names why: the one thing an attacker wants is for a record to be absent from
778
- * the output. An export's wrapped records are listed, `not_read`. A pack whose
779
- * body carries no `events` array, and wraps none, lists none, and its own
780
- * exception says why.
367
+ * One record of one pack, with its status and, where read, §4.1's reading.
368
+ * Every record of every pack is listed, in pack then bundle order, read or
369
+ * not, because the one thing an attacker wants is a record absent from the
370
+ * output. A pack with no `events` array lists none; its exception says why.
781
371
  */
782
372
  export interface CaseRecordReading {
783
373
  packRef: string;
784
- /** The pack, by its position in `CaseReading.packs` (A43). */
374
+ /** The pack's position in `CaseReading.packs`. */
785
375
  packIndex: number;
786
376
  eventId: string;
787
377
  /**
788
- * The record, by its position in the list of records its pack lists (A43):
789
- * `body.events`, or for a `sanning.evidence.export/v2` pack the events of the
790
- * source bundle it wraps.
378
+ * The record's position in its pack's events (for an export, its source
379
+ * bundle's events).
791
380
  */
792
381
  recordIndex: number;
793
- /** One of `CASE_RECORD_STATUSES` (A43). */
794
382
  status: CaseRecordStatus;
795
- /**
796
- * For `not_read`, the kind of the exception that says why; `null` for a
797
- * record that was read.
798
- */
383
+ /** For `not_read`, the exception kind that says why; else `null`. */
799
384
  reason: CaseExceptionKind | null;
800
385
  /**
801
- * The kinds its pack's declaration check names this record by, deviations
802
- * (`DECLARATION_DEVIATION_KINDS`) then content findings
803
- * (`DECLARATION_CONTENT_KINDS`), each in the check's own order. Matched on
804
- * `eventId`, so under an `event_id` the pack carries twice every record with
805
- * that id carries it. A record that went into the pile with any is
806
- * `read_flagged` (A43).
386
+ * The declaration check's deviations then content findings for this record's
387
+ * `eventId` (every record sharing a duplicated id). Any makes a read record
388
+ * `read_flagged`.
807
389
  */
808
390
  declarationFindings: string[];
809
- /** 4.1's verdict, or `null` where 4.1 gave none or the record was not read. */
391
+ /** §4.1's verdict, or `null` where it gave none or the record was not read. */
810
392
  verdict: string | null;
811
393
  findings: string[];
812
394
  failedChecks: string[];
813
395
  /**
814
- * 4.1's candidate counterparts, each as `<pack ref>/<event_id>`. A LABEL for
815
- * a person to read and never the join: two packs can carry one ref, and one
816
- * pack one `event_id` twice (A43).
396
+ * §4.1's candidate counterparts as `<pack ref>/<event_id>`: labels for a
397
+ * person, never the join.
817
398
  */
818
399
  counterparts: string[];
819
400
  /**
820
- * The role the pack's v2 declaration signs for this record, `handoff` or
821
- * `step`, or `null` where the pack's declaration is absent, unreadable or
822
- * v1, or does not declare the record (5.5.1 v1.20).
401
+ * The role the pack's v2 declaration signs for this record, or `null` where
402
+ * the declaration is absent, unreadable, v1 or silent on it (§5.5.1).
823
403
  */
824
404
  role: DeclarationRole | null;
405
+ /**
406
+ * The `sanning.agent` reading of this record (agent-events.md section 10), read with
407
+ * every record of every pack in the case, or `null` where step 1 gives it no
408
+ * verdict or it was not read. Its findings move no verdict; a `nonconforming`
409
+ * or `partial` reading moves its pack's grade (section 5.4.5).
410
+ */
411
+ agent: AgentRecordReading | null;
825
412
  }
826
- /**
827
- * One NOTE on the reading (5.5.3 v1.20): something true a reader needs and
828
- * nothing a reader could not tell, so it carries no contribution.
829
- */
413
+ /** A note on the reading (§5.5.3): true and needed, with no contribution. */
830
414
  export interface CaseNote {
831
415
  kind: CaseNoteKind;
832
- /** Which pack, by the reader's own name for it. */
833
416
  packRef: string;
834
- /** Which pack, by its position in `CaseReading.packs` (A43). */
835
417
  packIndex: number;
836
- /**
837
- * For `CONTENT_WITHHELD_BY_DECLARATION`, how many of the pack's records its
838
- * declaration withholds.
839
- */
418
+ /** For `CONTENT_WITHHELD_BY_DECLARATION`, how many records are withheld. */
840
419
  count?: number;
841
420
  }
842
421
  /**
843
- * One pack as the reading read it, in the order the reader was given them
844
- * (5.5.4 v1.20). A surface names a pack by `signingKey`, the key that signed
845
- * its wrapper, and never by an organization (D10).
422
+ * One pack as read, in the order given (§5.5.4). A surface names a pack by
423
+ * `signingKey`, never by an organization.
846
424
  */
847
425
  export interface CasePackReading {
848
- /**
849
- * The pack's position in the order the reader was given the packs, 0 first
850
- * (A43): the one name for a pack that is unique in the reading. Every
851
- * exception, note and record names its pack by it as `packIndex`.
852
- */
426
+ /** The pack's position in the order given: its one unique name in the reading. */
853
427
  index: number;
854
- /** The reader's own name for the pack. Two packs can carry the same one. */
428
+ /** The reader's own name for the pack. Not unique. */
855
429
  ref: string;
856
430
  /** The wrapper key, normalized, or `null` where this reader could not read it. */
857
431
  signingKey: string | null;
858
- /**
859
- * The case grade (5.5.1 step 1), or `null` where the wrapper did not hold or
860
- * the key could not be read.
861
- */
432
+ /** The case grade (§5.5.1), or `null` where the wrapper did not hold or the key is unreadable. */
862
433
  grade: PackVerdict | null;
863
- /**
864
- * Records withheld by the pack's declaration (5.4.5): in scope for their
865
- * signature and commitment only. Never counted as verified records.
866
- */
434
+ /** Records withheld by the declaration (§5.4.5). Never counted as verified. */
867
435
  recordsWithheld: number;
868
- /**
869
- * `sanning.declaration/v1`, `sanning.declaration/v2`, or `null` where the
870
- * pack carries no declaration, one this reader does not know, or a body this
871
- * reader does not read.
872
- */
436
+ /** The declaration version, or `null` where absent, unknown or in an unread body. */
873
437
  declarationVersion: string | null;
874
438
  }
875
439
  export interface CaseReading {
876
440
  verdict: CaseVerdict;
441
+ /** `changed` or `nonconforming` where `verdict` is `failed`. */
442
+ reasonKind?: ReasonKind;
877
443
  /**
878
- * Every exception, in a fixed order: the pack-level ones for each pack in the
879
- * order the reader was given the packs, then the record-level ones in pack
880
- * order and bundle order.
881
- *
882
- * **An empty array is the only shape a reader may read as clean**, and it is
883
- * reachable only together with `verdict === "verified"`.
444
+ * Every problem behind the verdict, each mapped to one claim and one state, in
445
+ * exception order. Empty exactly where `verdict` is `verified`.
884
446
  */
885
- exceptions: CaseException[];
447
+ problems: Problem[];
886
448
  /**
887
- * One entry per record of every pack, read or not, in pack order and then
888
- * bundle order, each with its status (A43).
449
+ * Every exception: pack-level ones in pack order, then record-level ones in
450
+ * pack and bundle order. Empty is the only clean shape, and only with
451
+ * `verdict === "verified"`.
889
452
  */
453
+ exceptions: CaseException[];
454
+ /** One entry per record of every pack, read or not. */
890
455
  records: CaseRecordReading[];
891
456
  /**
892
- * Every `[contributor, verdict]` the case verdict was computed from, in
893
- * order. Carried so the verdict is auditable rather than asserted, and so two
894
- * kernels compare the derivation and not only its answer.
457
+ * Every `[contributor, verdict]` the verdict was computed from, so the
458
+ * verdict is auditable rather than asserted.
895
459
  */
896
460
  contributions: [string, CaseVerdict][];
897
- /** How many hand-off records the case held. Zero is its own exception. */
461
+ /** Hand-off records in the case. Zero is its own exception. */
898
462
  handoffRecords: number;
899
463
  /**
900
- * Whether any witness behind any pack in this case was re-fetched on chain.
901
- * It is **false** in every reading this function produces, and it is here to
902
- * be READ rather than inferred.
903
- *
904
- * Gate 4 on proof#140, finding 3. A pack is `partial` only when `anchor_ref`
905
- * is ABSENT, so the honest omission is named and the unverifiable assertion
906
- * is not: a holder that writes any `anchor_ref` at all reads `verified`,
907
- * because this reader makes no network call. That is the standing lens
908
- * inverted, and the answer is not to refuse the assertion, which no offline
909
- * reader can grade. It is to say what was not checked, so nobody reads a
910
- * clean case as "a witness held". Re-fetching on chain stays the caller's
911
- * job, exactly as `evidence-bundle.md` 5.5's does-not-claim list says.
912
- *
913
- * Derived from each pack's own `EvidenceBundleResult.onChainChecked` rather
914
- * than written as a constant, so it stays true of what was actually run:
915
- * this reader passes no gateways, and it is the ALL of an empty list of
916
- * checked packs, which is false.
464
+ * Whether any witness was re-fetched on chain. Always false here: this reader
465
+ * makes no network call, so a pack with any `anchor_ref` reads `verified`
466
+ * without the witness being checked. Stated so nobody reads a clean case as
467
+ * "a witness held"; re-fetching is the caller's (evidence-bundle §5.5).
468
+ * Derived from each pack's `onChainChecked`, not written as a constant.
917
469
  */
918
470
  onChainChecked: boolean;
919
- /** The NOTEs, in pack order (5.5.3 v1.20). No contribution. */
471
+ /** Notes, in pack order (§5.5.3). No contribution. */
920
472
  notes: CaseNote[];
921
- /** One reading per pack, in the order the reader was given them. */
473
+ /** One reading per pack, in the order given. */
922
474
  packs: CasePackReading[];
923
475
  }
924
476
  /**
925
- * Read a whole case: several packs in, one verdict and its exceptions out.
926
- *
927
- * The order of the work, and why it is this order:
477
+ * Every exception, mapped to its claim and state, in exception order. A pack grade
478
+ * carries its own problems; a pack grade with none (a defence) reads as its status.
479
+ */
480
+ export declare function caseProblems(exceptions: readonly CaseException[]): Problem[];
481
+ /**
482
+ * Reads a case: packs in, one verdict and its exceptions out.
928
483
  *
929
- * 1. **Each pack is read into records**, through `packRecords`, which is the
930
- * one implementation of "what does this pack hold". A pack whose `body_type`
931
- * this reader does not know, or which holds no records, is NAMED here and
932
- * its records are not silently zero.
933
- * 2. **Each pack is graded against its own declaration**, through
934
- * `checkDeclaration`, which is the one implementation of 5.4. A pack proven
935
- * to deviate from its own claim fails the case.
936
- * 3. **Every record every pack supplied goes into ONE pile**, with no partition
937
- * by pack, by issuer or by organization, and the pile is read by
938
- * `readHandoffs`, which is the one implementation of 4.1. This is where
939
- * matching happens, and it is on keys and hashes alone.
940
- * 4. **Each reading becomes an exception or a clean contribution**, and the two
941
- * counterpart kinds are separated by asking whether a pack SIGNED by the
942
- * counterparty's key is in the case, a key read from every pack's wrapper
943
- * before any exit can skip it.
484
+ * 1. Each pack is read into records (`packRecords`); an unknown body or no
485
+ * records is named.
486
+ * 2. Each pack is graded against its own declaration (`checkDeclaration`).
487
+ * 3. Every record goes into one pile, unpartitioned, read by `readHandoffs`.
488
+ * 4. Each reading becomes an exception or a clean contribution. The
489
+ * counterpart kinds are told apart by whether a pack signed by the
490
+ * counterparty's key is in the case, read from every wrapper before any exit.
944
491
  *
945
- * A record whose bytes the pack did not supply never reaches the pile, because
946
- * there is nothing to put in it. That is exactly the shape this module has to
947
- * refuse to pass over in silence, so it is named at step 1 and carries
948
- * `incomplete`: the reader genuinely cannot tell whether the record it cannot
949
- * see is the counterpart that is missing.
492
+ * Before step 1 the agent records of every pack are read as one pile
493
+ * (agent-events.md section 10), and each pack is graded with those readings
494
+ * (evidence-bundle.md section 5.4.5, section 5.5.1).
950
495
  */
951
496
  export declare function readCase(packs: readonly CasePack[]): Promise<CaseReading>;
497
+ /**
498
+ * The house wording for one exception. Optional: the verdict and the set of
499
+ * exceptions are what must not vary between surfaces.
500
+ */
952
501
  export declare function renderException(exception: CaseException): string;
953
- /** The house wording for one NOTE. Offered, not imposed, as `renderException` is. */
502
+ /** The house wording for one note. */
954
503
  export declare function renderCaseNote(note: CaseNote): string;
955
504
  /**
956
- * The scope sentence, printed directly under the headline (evidence-bundle
957
- * 5.5.6 v1.20, A26), byte for byte the text `render_case_summary` returns.
958
- *
959
- * The packs in the reading; the distinct keys, compared as keys, that signed
960
- * the wrapper of a pack whose wrapper held; the hand-off records; the
961
- * exceptions; and that the reading was made offline. Where any pack holds
962
- * records withheld by its declaration, their total; where any pack is noted
963
- * `pack_undeclared`, how many. Those two lines are part of what the headline
964
- * MEANS, and a surface MUST print them with it.
965
- *
966
- * What was not checked on the public record is no longer a clause here. It is
967
- * the witness line (`renderWitnessLine`), printed with every headline.
505
+ * The scope sentence printed under the headline (evidence-bundle §5.5.6): packs,
506
+ * distinct signing keys of packs whose wrapper held, hand-off records,
507
+ * exceptions, and that it was read offline, plus withheld and undeclared
508
+ * counts where non-zero. Those counts are part of what the headline means, and
509
+ * a surface must print them with it.
968
510
  */
969
511
  export declare function renderCaseSummary(reading: CaseReading): string;
970
512
  /**
971
- * The one display title of each of the eight 3 words. A surface prints the
972
- * title, never the word, and never a title of its own (#173, A18). The wording
973
- * is Will's to change through the glossary (DL-71); what each MEANS is pinned
974
- * in the spec.
513
+ * The one display title of each of the three section 3 verdicts. A surface prints the
514
+ * title, never the word, and never a title of its own.
975
515
  */
976
516
  export declare const VERDICT_DISPLAY_TITLES: Readonly<Record<string, string>>;
977
517
  /**
978
- * What a reader may rely on, for each of the six words a case reading reaches
979
- * (5.5.6 v1.20, A26; the `verified` text at v1.28, A35), the same for every
980
- * drop. `pending` and `not_found` never head a case reading: they are
981
- * witness states. `verified` is the base sentence, printed alone where the
982
- * drop holds no hand-off record; a caller reads `caseReliance` for
983
- * `verified`, never this mapping directly.
518
+ * What a reader may rely on, for each verdict a case reading reaches (section 5.5.6), the
519
+ * same for every drop, keyed by the verdict and, for `failed`, its reason kind. Call
520
+ * `caseReliance` rather than reading `verified` here directly.
984
521
  */
985
- export declare const CASE_RELIANCE: Readonly<Record<CaseVerdict, string>>;
522
+ export declare const CASE_RELIANCE: Readonly<Record<CaseVerdict | "failed_nonconforming", string>>;
986
523
  /**
987
- * The clause a `verified` drop that holds a hand-off record gains, inserted
988
- * after `CASE_RELIANCE`'s first sentence (5.5.6, A35). Never printed alone,
989
- * and never where `handoffRecords` is zero: most drops hold one agent's
990
- * pack and no hand-off to say anything about.
524
+ * Added to a `verified` reliance line after its first sentence, only where the
525
+ * drop holds a hand-off record.
991
526
  */
992
527
  export declare const CASE_RELIANCE_HANDOFF_CLAUSE = "Every hand-off with both sides in this drop is intact.";
993
528
  /**
994
- * What a reader may rely on for one case reading (5.5.6 v1.28, A35), byte
995
- * for byte the text `case_reliance`'s Python sibling returns.
996
- *
997
- * For every word but `verified` this is `CASE_RELIANCE` unchanged. For
998
- * `verified`, the hand-off clause is inserted after the first sentence
999
- * where `handoffRecords` is not zero, and left out where it is zero: the
1000
- * base sentence never claims a hand-off the drop does not hold.
529
+ * Added after the reliance line's first sentence, for every verdict, where the
530
+ * drop holds `environment: "dev"` envelopes: `dev` if all are, `mixed` if some.
531
+ */
532
+ export declare const CASE_RELIANCE_SANDBOX: Readonly<Record<EnvironmentMarker, string>>;
533
+ /**
534
+ * What a reader may rely on for one case reading (§5.5.6). The sandbox
535
+ * sentence, if any, follows the first sentence and precedes the hand-off
536
+ * clause, which a `verified` reading gains only with `handoffRecords > 0`, so
537
+ * the line never claims a hand-off the drop does not hold.
1001
538
  */
1002
- export declare function caseReliance(verdict: CaseVerdict, handoffRecords: number): string;
1003
- /** The three witness states, and how each is printed (5.5.6, D7). No other. */
539
+ export declare function caseReliance(verdict: CaseVerdict, handoffRecords: number, environmentMarker?: EnvironmentMarker | null, reasonKind?: ReasonKind | null): string;
540
+ /** The three witness states and how each is printed (§5.5.6). No others. */
1004
541
  export declare const WITNESS_STATES: {
1005
542
  readonly confirmed: "Confirmed on the public record";
1006
543
  readonly not_checked: "Not checked";
@@ -1008,34 +545,30 @@ export declare const WITNESS_STATES: {
1008
545
  };
1009
546
  export type WitnessState = keyof typeof WITNESS_STATES;
1010
547
  /**
1011
- * The witness line: a lookup of the public record, reported beside the
1012
- * headline and never in it (D7). It never raises or lowers the headline and
1013
- * never changes its title. `reasons` says why a `not_checked` line did not
1014
- * settle, in a fixed order; `services` names each service that answered, as
1015
- * the URL the lookup used: named, never trusted (6).
548
+ * The result of a public-record lookup, reported beside the headline, never
549
+ * in it, and never moving it. `reasons` says why `not_checked` did not settle;
550
+ * `services` names each gateway that answered: named, never trusted.
1016
551
  */
1017
552
  export interface WitnessLine {
1018
553
  state: WitnessState;
1019
554
  reasons: string[];
1020
555
  services: string[];
1021
556
  /**
1022
- * Witness times a pack caches that no lookup settled, as `[blockHeight,
1023
- * blockTime]`, distinct and in time order (5.6.6, v1.25, A30). Printed labelled
1024
- * not checked, and never as a time a reader may rely on. Absent reads as none.
557
+ * Cached witness times no lookup settled, as `[blockHeight, blockTime]`,
558
+ * distinct and in time order (§5.6.6). Printed as not checked, never as a
559
+ * time a reader may rely on.
1025
560
  */
1026
561
  cached?: [number, number][];
1027
562
  /**
1028
- * Witness times the public record returned where they matched a pack's
1029
- * cache, distinct and in time order. The gateway's answer, named with the
1030
- * service, never the cache's value. Absent reads as none.
563
+ * Witness times the public record returned that matched the cache, distinct
564
+ * and in time order. Always the gateway's answer, never the cache's value.
1031
565
  */
1032
566
  recorded?: [number, number][];
1033
567
  }
1034
- /** The line printed where no lookup ran, which is every reading this kernel produces on its own. */
568
+ /** The line where no lookup ran: every reading this module produces on its own. */
1035
569
  export declare const WITNESS_NOT_LOOKED_UP: Readonly<WitnessLine>;
1036
- /** The witness line as both kernels print it. */
1037
- /** How a witness time a pack caches is printed where nothing checked it (5.5.6, 5.6.6, v1.25, A30). */
570
+ /** A cached witness time no lookup checked (§5.6.6). */
1038
571
  export declare const WITNESS_TIME_CACHED = "the pack dates the witness, not checked";
1039
- /** How a witness time the public record returned is printed where it matched the cache. */
572
+ /** A witness time the public record returned that matched the cache. */
1040
573
  export declare const WITNESS_TIME_RECORDED = "the public record dates the witness";
1041
574
  export declare function renderWitnessLine(line: WitnessLine): string;