@sanning/proof 0.19.1 → 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 +339 -822
  9. package/dist/case.js +667 -1160
  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 +814 -1328
  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 +131 -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 +573 -581
  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,1022 +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";
246
66
  import type { EnvironmentMarker } from "./environment.js";
247
67
  import type { SideContentMap } from "./side-content.js";
248
- /**
249
- * The verdicts a case can reach. Every one is already in use in
250
- * `evidence-bundle.md` 3 or `PACK_VERDICTS`, and anchor#198 says in terms that
251
- * no new verdict word is introduced anywhere.
252
- */
253
- 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"];
254
71
  export type CaseVerdict = (typeof CASE_VERDICTS)[number];
255
72
  /**
256
- * The two pack bodies that carry `events[]` and may carry a `declaration`
257
- * (`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.
258
76
  *
259
- * `body_type` selects a code path and is written by the party assembling the
260
- * pack. Without this set, a pack whose records live under a key this reader
261
- * does not know yields zero records, contributes nothing, and lets the rest of
262
- * the case read clean over it: the exact defect class this module is written
263
- * against. An unknown value is `PACK_BODY_TYPE_UNKNOWN`.
264
- *
265
- * **`sanning.evidence.export/v2` is NOT in this set and is NOT unwrapped.** It
266
- * verifies as a bundle and carries a whole `sanning.anchor.trace/v3` pack under
267
- * `body.source_bundle`, so its records do not reach the pile and the export is
268
- * named `PACK_BODY_TYPE_UNKNOWN`, `incomplete`. Reading inside it changes which
269
- * packs this reader grades, so it is a decision of its own. A reader who needs
270
- * the records hands in the source bundle as a pack. The export's wrapper key is
271
- * still read, so wrapping a pack in one does not change which counterpart kind
272
- * 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.
273
80
  */
274
81
  export declare const CASE_BODY_TYPES: readonly ["sanning.anchor.trace/v3", "sanning.stamp.evidence/v2"];
275
82
  /**
276
- * The envelope profiles the case reader reads records under: the ratified
277
- * accept-set (`envelope-spec.md` 5) minus every profile under quarantine in
278
- * `cross-kernel/accept-set.json`. A test in each kernel derives both arrays
279
- * from that file, and a test here pins this one equal to this kernel's own
280
- * `ACCEPTED_SPEC_MAJORS`, which is what `verifyEvidenceBundle` reads under:
281
- * the Python kernel narrows its verifier to this set for the case reader, and
282
- * this kernel's verifier already reads exactly it. If this kernel ever admits
283
- * a quarantined profile, that test fails, and the case reader has to narrow
284
- * 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.
285
86
  */
286
- export declare const CASE_RECORD_PROFILES: readonly ["sanning.agent/v1", "sanning.events/v2"];
287
- /** The ratified profiles under quarantine, pending DL-63. */
87
+ export declare const CASE_RECORD_PROFILES: readonly ["sanning.events/v2"];
88
+ /** Accepted profiles under quarantine. */
288
89
  export declare const CASE_QUARANTINED_PROFILES: readonly ["sanning.mlflow/v1"];
289
90
  /**
290
- * A conformant hand-off record with no counterpart among the records this
291
- * reader read, where every pack in the case verified under a key this reader
292
- * can read and NO pack in the case is signed by the key the record names,
293
- * or where every candidate it had was accounted for by another record under
294
- * A20 (the exception then carries `count`, and names no pack).
295
- * **An exception, never a failure** (anchor#198, criterion 3).
296
- *
297
- * That is ALL it says. It does not say the counterparty's account is absent,
298
- * and it does not say nothing was tampered with: a counterparty that signs its
299
- * own pack under a second key, or hands over an export an issuer signed,
300
- * reaches this reading too, and nothing offline binds a pack's key to the keys
301
- * of the records inside it (proof#143). The rendered text states the fact and
302
- * stops.
303
- *
304
- * It says nothing about RECORDS signed by that key either. A party may hold records
305
- * its counterparty signed, and those are still graded as candidates, but they
306
- * are not the counterparty's account of itself, so they do not move this
307
- * 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.
308
97
  */
309
98
  export declare const COUNTERPART_ABSENT = "counterpart_absent";
310
99
  /**
311
- * THE CLOSURE anchor#198 exists for.
312
- *
313
- * A conformant hand-off record with no counterpart, where a pack that VERIFIES
314
- * and is SIGNED by the key this record names is in the case, and not one
315
- * record this reader could read is this record's counterpart. It states that a
316
- * pack signed by the named key is present and holds no counterpart this reader
317
- * could read, and nothing more. 4.1 (v1.10, A15) and 5.4.2 rule 7 (v1.10) both point here, and
318
- * `declaration/declaration-unclaimed-record-01` is the pack that reaches this
319
- * line.
320
- *
321
- * **"Its own account" is read from the key that signed the PACK, and never
322
- * from the keys that signed records.** A pack's wrapper key signs `body_hash`
323
- * and `body` carries the declaration (`evidence-bundle.md` 5.4, "Who signs
324
- * it"), so a wrapper signed by B is provenance the accusing party cannot forge
325
- * without B's private key. Records signed by B that A merely HOLDS are not B's
326
- * account of itself: keyed on record keys, one pack from A plus one unrelated
327
- * record B signed at some other time reached this line when B had handed over
328
- * 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`).
329
103
  *
330
- * What it does NOT prove, stated so no surface claims it: that B handed the
331
- * pack over for THIS case. A pack B signed for some other purpose and a third
332
- * party replayed reads the same, because the only values that could say
333
- * otherwise (the declaration's `case_id` and `period`) are written by B and
334
- * would let B leave this line by writing them differently. The cost the other
335
- * way: a pack B assembles under any key other than the one its records name, a
336
- * per-organization hand-over key (anchor#196) included, reads
337
- * `COUNTERPART_ABSENT`, because nothing in the case binds that key to B. That
338
- * reading is still true: no pack signed by the named key is here. So a
339
- * counterparty can AVOID this line by choosing the key it signs under; what it
340
- * 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.
341
107
  *
342
- * It is deliberately NOT also conditioned on a record signed by B being in the
343
- * pile. B's pack can hold none this reader reads (a body it does not unwrap,
344
- * bytes B withheld, an empty `events[]`), and each of those is a choice B makes
345
- * and is named on its own. A second conjunct on them hands B the innocent line
346
- * 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.
347
113
  *
348
- * It is a DISTINCT kind from `COUNTERPART_ABSENT` and not a louder spelling of
349
- * it. Collapsing the two would be the defect class again: "the counterparty
350
- * never joined" and "the counterparty joined and its own account does not
351
- * contain this hand-off" are the honest reading and the damning one, and one
352
- * name for both lets the second read as the first.
353
- *
354
- * It is still an EXCEPTION and not a failure, which criterion 3 requires and
355
- * which is also the honest call: the counterparty's pack is a selection over
356
- * its own interval, and a hand-off outside that interval is legitimately not
357
- * in it. What this names is that the question can now be ASKED of the other
358
- * 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.
359
117
  */
360
118
  export declare const COUNTERPART_ABSENT_FROM_COUNTERPARTY_RECORDS = "counterpart_absent_from_counterparty_records";
361
119
  /**
362
- * A conformant hand-off record with no counterpart, no VERIFIED pack in the
363
- * case signed by the key it names, and at least one pack in the case that this
364
- * reader could not tie to a key: its wrapper key is unreadable
365
- * (`PACK_KEY_UNREADABLE`) or it does not verify (`PACK_DOES_NOT_VERIFY`).
366
- * Whether a pack signed by the named key is in the case is UNKNOWN, so neither
367
- * of the other two kinds is true.
368
- *
369
- * The gate on the previous version of this reader showed why it exists. A
370
- * counterparty that signed its own pack under `" " + key` got
371
- * `PACK_KEY_UNREADABLE` for the pack and the innocent `COUNTERPART_ABSENT` for
372
- * the record, and the output contradicted itself: whose pack it was is
373
- * UNKNOWN, and then no pack is signed by the named key. The second sentence
374
- * was not established.
375
- *
376
- * `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`.
377
124
  */
378
125
  export declare const COUNTERPART_ABSENT_SIGNER_UNKNOWN = "counterpart_absent_signer_unknown";
379
126
  /**
380
- * More than one candidate counterpart that nothing accounts for (4.1 v1.17,
381
- * A20). The reading is the worst of them and the ambiguity is named: a reader
382
- * permitted to take one match can be handed an honest decoy beside a tampered
383
- * record. `count` is how many of the candidates are live, which is the number
384
- * 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.
385
130
  */
386
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";
387
136
  /**
388
- * A formed pair that failed a check. Carries which checks, from 4.1's own
389
- * 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`.
390
142
  */
391
- export declare const PAIR_FAILED = "pair_failed";
143
+ export declare const COUNTERPART_MALFORMED = "counterpart_malformed";
392
144
  /**
393
- * THIS record is wrong: 4.1 governs it and `recordIsMalformed` says so of the
394
- * record itself. A statement about the record this exception names, and about
395
- * 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.
396
148
  */
397
- export declare const RECORD_MALFORMED = "record_malformed";
149
+ export declare const RECORD_NONCONFORMING = "record_nonconforming";
398
150
  /**
399
- * A CANDIDATE counterpart of this record is malformed, and this record is not.
400
- *
401
- * 4.1 grades every candidate and reports the worst of them, and `malformed`
402
- * outranks `failed`, so one junk record signed by the counterparty's key makes
403
- * the reading of a perfectly well formed record `malformed` too. Filed under
404
- * `RECORD_MALFORMED`, that read as "this record is not a well formed hand-off
405
- * record" about the HONEST record, and the branch it took skipped the
406
- * counterpart, ambiguity and `failedChecks` reporting entirely: one junk record
407
- * both blamed the wrong party and stripped a named `message_hash` failure out
408
- * of the case. That is proof#133 ruling 6, which 4.1 closed by keeping the
409
- * UNION of `failedChecks`, reappearing one layer out because this layer
410
- * discarded that union.
411
- *
412
- * A record's own conformance and its candidate's are different facts and do
413
- * not share a kind. This one is `incomplete` and not `malformed`: the proven
414
- * defect belongs to the candidate, which carries its own `RECORD_MALFORMED`
415
- * line, and what is true of THIS record is that its pairing could not be
416
- * determined. Contributing `malformed` here would attribute the candidate's
417
- * 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`.
418
154
  */
419
- export declare const COUNTERPART_MALFORMED = "counterpart_malformed";
155
+ export declare const COUNTERPART_NONCONFORMING = "counterpart_nonconforming";
420
156
  /**
421
- * A record that its own pack DECLARES and its own pack's READABLE declaration
422
- * claims in no `handoffs[]` entry. This is proof#137's door, named at the layer
423
- * that can see it: the holder declared the record and claimed it nowhere, so
424
- * neither 5.4.2 rule 2 nor rule 7 reaches it and the pack reads `verified`.
425
- *
426
- * **It turns on what the PACK said about the record, and never on whether 4.1
427
- * governs it.** Gating it on "is this a conformant hand-off record" put it
428
- * behind a value the audited party writes: the same second receipt, respelled
429
- * with an `ff` ligature in `event_type` and a Cyrillic `е` in
430
- * `counterparty_public_key`, left 4.1's scope, took the `not_a_handoff_record`
431
- * branch, and bought a case reading of `verified` with zero exceptions where
432
- * the conformant spelling read `mixed` with four. proof#136 ratified that a
433
- * respelling buys a DIFFERENT LINE rather than no line; at this layer it
434
- * 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.
435
161
  *
436
- * It is not an accusation. `handoffs[]` MAY be empty and an unclaimed record
437
- * may be entirely innocent. What it is, is the end of the silence: the reader
438
- * cannot tell whether an unclaimed record is part of the hand-over the holder
439
- * meant to give or one it is keeping out of its own account, and that is "I
440
- * could not tell", which is why it carries `incomplete` and not `verified`.
441
- *
442
- * THE COST, stated rather than left to be found: a pack that declares records
443
- * which are not hand-off sides at all, an ordinary log line among them, is
444
- * named once per such record, so a case holding one cannot read `verified`.
445
- * That is the price of a rule with no branch the holder selects, and the only
446
- * narrower rule available keys on a value the holder writes. A surface that
447
- * wants the two apart reads `CaseRecordReading.findings`, where
448
- * `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.
449
165
  */
450
166
  export declare const RECORD_UNCLAIMED = "record_unclaimed_by_its_pack";
451
167
  /**
452
- * The pack supplied no `event_bytes` for a record, so this reader cannot see
453
- * what the record IS in either direction.
454
- *
455
- * `event_bytes` is OPTIONAL (5.1, 5.3) and withholding it is minimum
456
- * disclosure, which the corpus calls a customer's right and not a defect
457
- * (proof#137, defect 3), so this is not a deviation and does not fail. It is
458
- * still SAID, because a withheld record is exactly where a missing counterpart
459
- * would hide, and a reader that dropped it silently would read a trimmed pack
460
- * 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.
461
171
  */
462
172
  export declare const RECORD_BYTES_WITHHELD = "record_bytes_withheld";
463
173
  /**
464
- * The pack SUPPLIED `event_bytes` this reader cannot read as
465
- * `JCS(event record)` (5.4.2 rule 7's parser rule, one implementation in
466
- * `packRecords`). Bytes the holder handed over that do not decode are a defect
467
- * 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`.
468
176
  */
469
177
  export declare const RECORD_BYTES_UNREADABLE = "record_bytes_unreadable";
470
- /**
471
- * The pack deviates from its own declaration (5.4.2). A proven mismatch, so it
472
- * fails: a case cannot be clean over a pack that is proven to be trimmed.
473
- */
178
+ /** The pack deviates from its own declaration (§5.4.2). A proven mismatch: `failed`. */
474
179
  export declare const PACK_DECLARATION_DEVIATES = "pack_declaration_deviates";
475
180
  /**
476
- * The pack and the UNSIGNED content handed in beside it do not line up (5.4.2
477
- * rules 3 and 4, proof#147, ratified by Phil 2026-09-22). A record the pack
478
- * declares `disclosed` for which no bytes reached this reader, or one it
479
- * declares `content_withheld` for which bytes reached this reader from the
480
- * side-content map alone.
481
- *
482
- * `incomplete` and never `failed`, which is the whole point of the kind. 5.4
483
- * grades a pack's declaration partly against content the READER was handed,
484
- * and whoever assembles a case fills that map in. Under
485
- * `PACK_DECLARATION_DEVIATES` an accuser could put an intact pack into a
486
- * `failed` case WITH NO KEY AT ALL: supply bytes for a record its holder
487
- * declared withheld, or drop the bytes of one it declared disclosed. The pack
488
- * was intact, its wrapper verified, and its holder had done nothing. This is
489
- * the mirror of proof#143, where a counterparty makes ITSELF look better by
490
- * 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.
491
184
  *
492
- * It is emitted BESIDE `PACK_DECLARATION_DEVIATES` where a pack does both,
493
- * 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.
494
188
  */
495
189
  export declare const PACK_DECLARATION_CONTENT_DEPENDENT = "pack_declaration_content_dependent";
496
190
  /**
497
- * The pack carries a `declaration` this reader cannot read (5.4.3): an unknown
498
- * `declaration_version`, an absent or `null` one, a non-object, or one that
499
- * does not satisfy 5.4.1. `declaration_version` is chosen by the same party
500
- * that assembles the pack and has no accept-set of its own, so this is UNKNOWN
501
- * 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.
502
193
  */
503
194
  export declare const PACK_DECLARATION_UNREADABLE = "pack_declaration_unreadable";
504
195
  /**
505
- * The pack carries no `declaration` member at all, so it never said what it
506
- * holds. `partial` is 5.4.3's own word for a claim nobody made: never
507
- * `verified`, because there is nothing to check the pack against, and never
508
- * `failed`, because nothing is wrong.
196
+ * The pack carries no `declaration`. `partial` (§5.4.3): nothing to check
197
+ * against, and nothing wrong.
509
198
  */
510
199
  export declare const PACK_UNDECLARED = "pack_undeclared";
511
- /**
512
- * The pack's `body_type` is not one this reader knows to carry `events[]`
513
- * (`CASE_BODY_TYPES`).
514
- */
200
+ /** The pack's `body_type` is not in `CASE_BODY_TYPES`. */
515
201
  export declare const PACK_BODY_TYPE_UNKNOWN = "pack_body_type_unknown";
516
- /**
517
- * The pack carries a body this reader knows and no records in it. A pack that
518
- * contributes nothing is named rather than passed over.
519
- */
202
+ /** The pack carries a known body with no records. */
520
203
  export declare const PACK_HOLDS_NO_RECORDS = "pack_holds_no_records";
521
204
  /**
522
- * The key the pack's wrapper names is not one this reader can read as a key
523
- * (`normalizedSigningKey` returns nothing for it), so who signed the pack is
524
- * UNKNOWN. The pack is not verified and none of its records are read, and
525
- * every one-sided record whose named key signed no verified pack reads
526
- * `COUNTERPART_ABSENT_SIGNER_UNKNOWN`.
527
- *
528
- * Found by constructing it, while enumerating this module's exits: the wrapper
529
- * key is the ONE input that separates the two counterpart kinds, and a key this
530
- * reader could not read used to drop out of that set in silence. The Python
531
- * `verify_evidence_bundle` accepts a wrapper `public_key` with ASCII
532
- * whitespace inside it or around it (`bytes.fromhex` skips it; this kernel's
533
- * verifier rejects it), so a counterparty that signed its own pack under
534
- * `" " + key` handed a Python reader a pack that verified, contributed no key,
535
- * and moved the subject from `COUNTERPART_ABSENT_FROM_COUNTERPARTY_RECORDS` to
536
- * the innocent `COUNTERPART_ABSENT`. The same repair 4.1 made for a RECORD
537
- * whose envelope key it cannot read (proof#133, fourth hunt): a key a reader
538
- * holds and cannot read is named, never treated as absent. It is `incomplete`
539
- * rather than `malformed` because whether the spelling verifies is the envelope
540
- * 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.
541
211
  */
542
212
  export declare const PACK_KEY_UNREADABLE = "pack_key_unreadable";
543
213
  /**
544
- * The pack does not verify as a bundle: `verifyEvidenceBundle`, run here
545
- * offline over the pack and its side content, returned `failed` or
546
- * `malformed`. None of its records are read, because a record from it would
547
- * reach 4.1 without the verified envelope `HeldRecord` requires, and the key
548
- * its wrapper claims is never counted as a pack signer: an unverified wrapper
549
- * claiming the counterparty's key is exactly what an accusing party would
550
- * forge.
551
- *
552
- * `partial` is not this exception. `partial` means the wrapper signature and
553
- * `body_hash` held and part of the body could not be determined, which is what
554
- * an unknown body or withheld bytes produce, and those are named on their own.
555
- *
556
- * `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`.
557
224
  */
558
225
  export declare const PACK_DOES_NOT_VERIFY = "pack_does_not_verify";
559
226
  /**
560
- * The pack's wrapper HOLDS and its CASE GRADE is not `verified`
561
- * (evidence-bundle 5.5.1 step 1 v1.20, A23; it replaces
562
- * `pack_bundle_not_verified`, which carried the bundle status alone). The case
563
- * grade is the pack grade of 5.4.5, taken twice: the SIGNED grade over the run
564
- * with the side map empty, and the FULL grade over the run with it. The signed
565
- * grade decides where it is `failed` or `malformed`; a failure only the full
566
- * grade reaches is `incomplete`, because it rests on bytes nobody signed;
567
- * otherwise the full grade. A body the verifier could not read is `malformed`,
568
- * a body that is not one of the two event-carrying bodies takes the signed
569
- * run's status, and a verifier that graded a different number of records than
570
- * 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
571
234
  * `verified`.
572
235
  *
573
- * The exception carries `status`, the case grade; `causes`, the 5.4.5 cause
574
- * kinds of the grade that decided it (none in the first three branches); and
575
- * `reasons`, the verifier's reasons from the run with the side map empty. It
576
- * CONTRIBUTES the grade, and `incomplete` for `pending`, `not_found` or
577
- * `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.
578
239
  */
579
240
  export declare const PACK_GRADE_NOT_VERIFIED = "pack_grade_not_verified";
580
241
  /**
581
- * A record in a pack whose wrapper holds, whose OWN envelope does not verify
582
- * (`EventResult.envelopeOk` is false). Not read by 4.1, and NAMED with the
583
- * verifier's reasons. `failed`: a pack its holder signed carries a record that
584
- * is proven not to verify. As a reason to drop the whole pack it let the
585
- * counterparty add one junk event to its own pack, re-seal it, and strip a
586
- * 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.
587
245
  */
588
246
  export declare const RECORD_ENVELOPE_DOES_NOT_VERIFY = "record_envelope_does_not_verify";
589
247
  /**
590
- * A record whose envelope verifies and which fails another check the bundle
591
- * verifier grades it on inside its pack (its inclusion proof, its join to a
592
- * checkpoint or stamp, a retired field name). NAMED with the verifier's
593
- * reasons, `failed`, and the record STAYS in the pile: a holder that could
594
- * remove its own tampered receipt from pairing by breaking that receipt's
595
- * 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.
596
252
  */
597
253
  export declare const RECORD_DOES_NOT_VERIFY_IN_ITS_PACK = "record_does_not_verify_in_its_pack";
598
254
  /**
599
- * A record whose envelope verifies and whose disclosed content does not hash to
600
- * the hash it commits to, its `content_hash` or a hand-off record's
601
- * `message_hash` (evidence-bundle v1.19, A20) (`EventResult.contentOk` is false), with the
602
- * side map EMPTY: the content is inside the signed body. The same mismatch from
603
- * content handed in beside the pack is `RECORD_CONTENT_DEPENDENT` (proof#159).
604
- * `failed`, and the record STAYS in the pile: content beside a pack is unsigned and often
605
- * supplied by whoever assembled the case, and gate 3 on proof#140 showed that
606
- * 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.
607
258
  */
608
259
  export declare const RECORD_CONTENT_MISMATCH = "record_content_mismatch";
609
260
  /**
610
- * The two readings proof#147 did not reach, given its shape (proof#159, ruled
611
- * by Phil 2026-09-22, evidence-bundle.md 5.5.4 v1.16, A17). A pack whose
612
- * bundle verdict is `failed` ONLY when read with the unsigned content handed
613
- * in beside it, and `failed` no longer when read with that map empty, is
614
- * `PACK_BUNDLE_CONTENT_DEPENDENT`; a record whose content mismatch comes from
615
- * that map alone is `RECORD_CONTENT_DEPENDENT`. Both are `incomplete`: the
616
- * reader established that the bytes it was handed are not the bytes the record
617
- * commits to, and nothing about the party that signed the record, because
618
- * whoever assembled the case chose those bytes with no key. Up to v1.15 both
619
- * readings carried `failed`, and one flipped nibble of accuser-supplied content
620
- * put a `failed` against an intact pack; under A17's headline rule that would
621
- * have become the case's headline.
622
- *
623
- * The test that separates the two is the one proof#159 states for the inverse:
624
- * every deviation a signature covers still reads `failed` with the side map
625
- * EMPTY. So the pack is verified twice, with the map and without it, and what
626
- * fails without it is `PACK_GRADE_NOT_VERIFIED`, `RECORD_CONTENT_MISMATCH`
627
- * (content inside the signed body) or `RECORD_DOES_NOT_VERIFY_IN_ITS_PACK`,
628
- * each still `failed`. Emptying the map is the most a party that signed
629
- * 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.
630
266
  */
631
267
  export declare const PACK_BUNDLE_CONTENT_DEPENDENT = "pack_bundle_content_dependent";
632
268
  export declare const RECORD_CONTENT_DEPENDENT = "record_content_dependent";
633
269
  /**
634
- * A record signed under a ratified profile that is under quarantine
635
- * (`CASE_QUARANTINED_PROFILES`, pending DL-63). Not read, in any kernel.
636
- * `incomplete`: whether it verifies is what the kernels disagree about.
270
+ * A record under a quarantined profile (`CASE_QUARANTINED_PROFILES`). Not read.
271
+ * `incomplete`.
637
272
  */
638
273
  export declare const RECORD_PROFILE_QUARANTINED = "record_profile_quarantined";
639
274
  /** The reader was handed no packs at all. */
640
275
  export declare const CASE_HOLDS_NO_PACKS = "case_holds_no_packs";
641
276
  /**
642
- * The three NOTEs (5.5.3 v1.20, extended proof#148): findings on the reading
643
- * with no contribution, printed in the scope sentence so a clean verdict
644
- * never hides any of them. `pack_undeclared` is a note only where the
645
- * reading holds no hand-off record; where it holds one, it is the exception
646
- * 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.
647
281
  */
648
282
  export declare const CONTENT_WITHHELD_BY_DECLARATION = "content_withheld_by_declaration";
649
283
  /**
650
- * A pack's wrapper carries a present `co_signatures` (evidence-bundle.md §2,
651
- * decided by Phil 2026-09-24 on proof#148). The field verifies nothing and
652
- * sits outside the wrapper's signed scope, so the case reading names it
653
- * 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.
654
287
  */
655
288
  export declare const CO_SIGNATURES_PRESENT = "co_signatures_present";
656
289
  export declare const CASE_NOTE_KINDS: readonly ["pack_undeclared", "content_withheld_by_declaration", "co_signatures_present"];
657
290
  export type CaseNoteKind = (typeof CASE_NOTE_KINDS)[number];
658
- /**
659
- * Every exception this reader can report, as a closed set. A surface may render
660
- * them and a test may assert over them; nothing outside this array is emitted.
661
- */
662
- 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"];
663
293
  export type CaseExceptionKind = (typeof CASE_EXCEPTION_KINDS)[number];
664
294
  /**
665
- * The status every record in every pack carries (evidence-bundle 5.5.4
666
- * v1.35, A43, proof#204), as a closed set. A record is listed for each element
667
- * of its pack's `body.events` as `packRecords` reads them, or, for a
668
- * `sanning.evidence.export/v2` pack, of the events of the inline source
669
- * bundle it wraps (`resolveTraceEvents`), and gets exactly one:
670
- *
671
- * - `read_clean`: the record went into the pile, no exception carries its
672
- * `packIndex` and `recordIndex`, and its pack's declaration check
673
- * names it by nothing (`declarationFindings` is empty). It says nothing
674
- * about its PACK: the pack's own grade and exceptions are on the pack, and a
675
- * surface MUST show that grade beside the status.
676
- * - `read_flagged`: the record went into the pile, and at least one
677
- * exception carries its `packIndex` and `recordIndex`, or its pack's
678
- * declaration check names its `eventId` by a deviation or a content
679
- * finding (`declarationFindings` is not empty; under an `eventId` the
680
- * pack carries twice, every record with that id). An exception about the
681
- * pack alone flags no record.
682
- * - `not_read`: the record never went into the pile. `reason` is the kind
683
- * of the exception that says why, on the record
684
- * (`record_profile_quarantined`, `record_envelope_does_not_verify`,
685
- * `record_bytes_withheld`, `record_bytes_unreadable`) or on its pack
686
- * (`pack_key_unreadable`, `pack_does_not_verify`,
687
- * `pack_body_type_unknown`, `pack_grade_not_verified` where the body
688
- * could not be read or the verifier graded a different number of records).
689
- * An export's wrapped records carry the reason of the exception their pack
690
- * actually got: `pack_body_type_unknown` where its wrapper holds, and
691
- * otherwise `pack_key_unreadable`, `pack_does_not_verify` or
692
- * `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):
693
297
  *
694
- * Before A43 the reading listed only the records it read, so a surface had to
695
- * 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.
696
306
  */
697
307
  export declare const CASE_RECORD_STATUSES: readonly ["read_clean", "read_flagged", "not_read"];
698
308
  export type CaseRecordStatus = (typeof CASE_RECORD_STATUSES)[number];
699
309
  /**
700
- * One pack in a case, as a reader was handed it.
701
- *
702
- * `ref` is the reader's own name for the pack (a file name, an archive member)
703
- * and is never read from the pack itself: a value inside the pack is written by
704
- * the party that assembled it, and two packs are free to claim one name.
705
- *
706
- * A caller passes packs as it received them. `readCase` runs
707
- * `verifyEvidenceBundle` over each one itself, so `HeldRecord`'s precondition
708
- * (every envelope it compares has verified) is met by this module and cannot
709
- * be skipped by a caller. A pack that does not verify is
710
- * `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.
711
314
  */
712
315
  export interface CasePack {
713
316
  ref: string;
714
317
  bundle: unknown;
715
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;
716
321
  }
717
- /** One named exception, as data. A surface renders its own prose or calls
718
- * `renderException`. */
322
+ /** One named exception, as data. A surface renders it or calls `renderException`. */
719
323
  export interface CaseException {
720
324
  kind: CaseExceptionKind;
721
- /**
722
- * Which pack this is about, by the reader's own name for it. Two packs can
723
- * carry one `ref`, so it is a label and never the join: `packIndex` is.
724
- */
325
+ /** The reader's name for the pack: a label, never the join (`packIndex` is). */
725
326
  packRef?: string;
726
- /**
727
- * Which pack this is about, by its position in `CaseReading.packs` (0
728
- * first), present wherever `packRef` is (A43). Unique in the reading.
729
- */
327
+ /** The pack's position in `CaseReading.packs`, wherever `packRef` is set. */
730
328
  packIndex?: number;
731
329
  /** Which record, by its envelope `event_id`. */
732
330
  eventId?: string;
733
331
  /**
734
- * Which record, by its position in its pack's `body.events` (0 first),
735
- * present wherever `eventId` is (A43). One pack can carry one `event_id`
736
- * twice, so `packIndex` and `recordIndex` together are the join to
737
- * `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.
738
335
  */
739
336
  recordIndex?: number;
740
- /**
741
- * The counterparty key the record named, for the two counterpart kinds.
742
- * Normalized (`normalizedSigningKey`), so a reader compares what this module
743
- * compared.
744
- */
337
+ /** The counterparty key the record named, normalized as this module compared it. */
745
338
  counterpartyKey?: string | null;
746
- /** 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. */
747
340
  failedChecks?: string[];
748
341
  /**
749
- * How many of a thing the exception is about, where it is about a count. On
750
- * `COUNTERPART_ABSENT` it is set only where 4.1 set every candidate aside
751
- * (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.
752
344
  */
753
345
  count?: number;
754
346
  /** The pack's case grade, for `PACK_GRADE_NOT_VERIFIED`. */
755
347
  status?: CaseVerdict;
756
348
  /**
757
- * The 5.4.5 cause kinds of the grade that decided the case grade, for
758
- * `PACK_GRADE_NOT_VERIFIED`, in the grade's order. Empty where the grade was
759
- * not computed (an unreadable body, a body that is not one of the two
760
- * 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.
761
351
  */
762
352
  causes?: PackGradeCauseKind[];
763
353
  /**
764
- * What the bundle verifier said, as it said it, for the kinds that carry a
765
- * verifier's grading: `PACK_GRADE_NOT_VERIFIED` and the three record kinds
766
- * 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.
767
356
  */
768
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[];
769
365
  }
770
366
  /**
771
- * One record of one pack, with its status and, where it was read, 4.1's
772
- * reading of it.
773
- *
774
- * EVERY record of every pack gets one of these (A43), in pack order and then
775
- * in bundle order: the ones this reader read, including the ones that are not
776
- * hand-off records at all (`findings` carries `not_a_handoff_record`), and the
777
- * ones it did not, with `status` `not_read` and the `reason`. 4.1 (v1.9, A14)
778
- * names why: the one thing an attacker wants is for a record to be absent from
779
- * the output. An export's wrapped records are listed, `not_read`. A pack whose
780
- * body carries no `events` array, and wraps none, lists none, and its own
781
- * 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.
782
371
  */
783
372
  export interface CaseRecordReading {
784
373
  packRef: string;
785
- /** The pack, by its position in `CaseReading.packs` (A43). */
374
+ /** The pack's position in `CaseReading.packs`. */
786
375
  packIndex: number;
787
376
  eventId: string;
788
377
  /**
789
- * The record, by its position in the list of records its pack lists (A43):
790
- * `body.events`, or for a `sanning.evidence.export/v2` pack the events of the
791
- * source bundle it wraps.
378
+ * The record's position in its pack's events (for an export, its source
379
+ * bundle's events).
792
380
  */
793
381
  recordIndex: number;
794
- /** One of `CASE_RECORD_STATUSES` (A43). */
795
382
  status: CaseRecordStatus;
796
- /**
797
- * For `not_read`, the kind of the exception that says why; `null` for a
798
- * record that was read.
799
- */
383
+ /** For `not_read`, the exception kind that says why; else `null`. */
800
384
  reason: CaseExceptionKind | null;
801
385
  /**
802
- * The kinds its pack's declaration check names this record by, deviations
803
- * (`DECLARATION_DEVIATION_KINDS`) then content findings
804
- * (`DECLARATION_CONTENT_KINDS`), each in the check's own order. Matched on
805
- * `eventId`, so under an `event_id` the pack carries twice every record with
806
- * that id carries it. A record that went into the pile with any is
807
- * `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`.
808
389
  */
809
390
  declarationFindings: string[];
810
- /** 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. */
811
392
  verdict: string | null;
812
393
  findings: string[];
813
394
  failedChecks: string[];
814
395
  /**
815
- * 4.1's candidate counterparts, each as `<pack ref>/<event_id>`. A LABEL for
816
- * a person to read and never the join: two packs can carry one ref, and one
817
- * 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.
818
398
  */
819
399
  counterparts: string[];
820
400
  /**
821
- * The role the pack's v2 declaration signs for this record, `handoff` or
822
- * `step`, or `null` where the pack's declaration is absent, unreadable or
823
- * 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).
824
403
  */
825
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;
826
412
  }
827
- /**
828
- * One NOTE on the reading (5.5.3 v1.20): something true a reader needs and
829
- * nothing a reader could not tell, so it carries no contribution.
830
- */
413
+ /** A note on the reading (§5.5.3): true and needed, with no contribution. */
831
414
  export interface CaseNote {
832
415
  kind: CaseNoteKind;
833
- /** Which pack, by the reader's own name for it. */
834
416
  packRef: string;
835
- /** Which pack, by its position in `CaseReading.packs` (A43). */
836
417
  packIndex: number;
837
- /**
838
- * For `CONTENT_WITHHELD_BY_DECLARATION`, how many of the pack's records its
839
- * declaration withholds.
840
- */
418
+ /** For `CONTENT_WITHHELD_BY_DECLARATION`, how many records are withheld. */
841
419
  count?: number;
842
420
  }
843
421
  /**
844
- * One pack as the reading read it, in the order the reader was given them
845
- * (5.5.4 v1.20). A surface names a pack by `signingKey`, the key that signed
846
- * 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.
847
424
  */
848
425
  export interface CasePackReading {
849
- /**
850
- * The pack's position in the order the reader was given the packs, 0 first
851
- * (A43): the one name for a pack that is unique in the reading. Every
852
- * exception, note and record names its pack by it as `packIndex`.
853
- */
426
+ /** The pack's position in the order given: its one unique name in the reading. */
854
427
  index: number;
855
- /** 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. */
856
429
  ref: string;
857
430
  /** The wrapper key, normalized, or `null` where this reader could not read it. */
858
431
  signingKey: string | null;
859
- /**
860
- * The case grade (5.5.1 step 1), or `null` where the wrapper did not hold or
861
- * the key could not be read.
862
- */
432
+ /** The case grade (§5.5.1), or `null` where the wrapper did not hold or the key is unreadable. */
863
433
  grade: PackVerdict | null;
864
- /**
865
- * Records withheld by the pack's declaration (5.4.5): in scope for their
866
- * signature and commitment only. Never counted as verified records.
867
- */
434
+ /** Records withheld by the declaration (§5.4.5). Never counted as verified. */
868
435
  recordsWithheld: number;
869
- /**
870
- * `sanning.declaration/v1`, `sanning.declaration/v2`, or `null` where the
871
- * pack carries no declaration, one this reader does not know, or a body this
872
- * reader does not read.
873
- */
436
+ /** The declaration version, or `null` where absent, unknown or in an unread body. */
874
437
  declarationVersion: string | null;
875
438
  }
876
439
  export interface CaseReading {
877
440
  verdict: CaseVerdict;
441
+ /** `changed` or `nonconforming` where `verdict` is `failed`. */
442
+ reasonKind?: ReasonKind;
878
443
  /**
879
- * Every exception, in a fixed order: the pack-level ones for each pack in the
880
- * order the reader was given the packs, then the record-level ones in pack
881
- * order and bundle order.
882
- *
883
- * **An empty array is the only shape a reader may read as clean**, and it is
884
- * 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`.
885
446
  */
886
- exceptions: CaseException[];
447
+ problems: Problem[];
887
448
  /**
888
- * One entry per record of every pack, read or not, in pack order and then
889
- * 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"`.
890
452
  */
453
+ exceptions: CaseException[];
454
+ /** One entry per record of every pack, read or not. */
891
455
  records: CaseRecordReading[];
892
456
  /**
893
- * Every `[contributor, verdict]` the case verdict was computed from, in
894
- * order. Carried so the verdict is auditable rather than asserted, and so two
895
- * 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.
896
459
  */
897
460
  contributions: [string, CaseVerdict][];
898
- /** 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. */
899
462
  handoffRecords: number;
900
463
  /**
901
- * Whether any witness behind any pack in this case was re-fetched on chain.
902
- * It is **false** in every reading this function produces, and it is here to
903
- * be READ rather than inferred.
904
- *
905
- * Gate 4 on proof#140, finding 3. A pack is `partial` only when `anchor_ref`
906
- * is ABSENT, so the honest omission is named and the unverifiable assertion
907
- * is not: a holder that writes any `anchor_ref` at all reads `verified`,
908
- * because this reader makes no network call. That is the standing lens
909
- * inverted, and the answer is not to refuse the assertion, which no offline
910
- * reader can grade. It is to say what was not checked, so nobody reads a
911
- * clean case as "a witness held". Re-fetching on chain stays the caller's
912
- * job, exactly as `evidence-bundle.md` 5.5's does-not-claim list says.
913
- *
914
- * Derived from each pack's own `EvidenceBundleResult.onChainChecked` rather
915
- * than written as a constant, so it stays true of what was actually run:
916
- * this reader passes no gateways, and it is the ALL of an empty list of
917
- * 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.
918
469
  */
919
470
  onChainChecked: boolean;
920
- /** The NOTEs, in pack order (5.5.3 v1.20). No contribution. */
471
+ /** Notes, in pack order (§5.5.3). No contribution. */
921
472
  notes: CaseNote[];
922
- /** One reading per pack, in the order the reader was given them. */
473
+ /** One reading per pack, in the order given. */
923
474
  packs: CasePackReading[];
924
475
  }
925
476
  /**
926
- * Read a whole case: several packs in, one verdict and its exceptions out.
927
- *
928
- * 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.
929
483
  *
930
- * 1. **Each pack is read into records**, through `packRecords`, which is the
931
- * one implementation of "what does this pack hold". A pack whose `body_type`
932
- * this reader does not know, or which holds no records, is NAMED here and
933
- * its records are not silently zero.
934
- * 2. **Each pack is graded against its own declaration**, through
935
- * `checkDeclaration`, which is the one implementation of 5.4. A pack proven
936
- * to deviate from its own claim fails the case.
937
- * 3. **Every record every pack supplied goes into ONE pile**, with no partition
938
- * by pack, by issuer or by organization, and the pile is read by
939
- * `readHandoffs`, which is the one implementation of 4.1. This is where
940
- * matching happens, and it is on keys and hashes alone.
941
- * 4. **Each reading becomes an exception or a clean contribution**, and the two
942
- * counterpart kinds are separated by asking whether a pack SIGNED by the
943
- * counterparty's key is in the case, a key read from every pack's wrapper
944
- * 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.
945
491
  *
946
- * A record whose bytes the pack did not supply never reaches the pile, because
947
- * there is nothing to put in it. That is exactly the shape this module has to
948
- * refuse to pass over in silence, so it is named at step 1 and carries
949
- * `incomplete`: the reader genuinely cannot tell whether the record it cannot
950
- * 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).
951
495
  */
952
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
+ */
953
501
  export declare function renderException(exception: CaseException): string;
954
- /** The house wording for one NOTE. Offered, not imposed, as `renderException` is. */
502
+ /** The house wording for one note. */
955
503
  export declare function renderCaseNote(note: CaseNote): string;
956
504
  /**
957
- * The scope sentence, printed directly under the headline (evidence-bundle
958
- * 5.5.6 v1.20, A26), byte for byte the text `render_case_summary` returns.
959
- *
960
- * The packs in the reading; the distinct keys, compared as keys, that signed
961
- * the wrapper of a pack whose wrapper held; the hand-off records; the
962
- * exceptions; and that the reading was made offline. Where any pack holds
963
- * records withheld by its declaration, their total; where any pack is noted
964
- * `pack_undeclared`, how many. Those two lines are part of what the headline
965
- * MEANS, and a surface MUST print them with it.
966
- *
967
- * What was not checked on the public record is no longer a clause here. It is
968
- * 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.
969
510
  */
970
511
  export declare function renderCaseSummary(reading: CaseReading): string;
971
512
  /**
972
- * The one display title of each of the eight 3 words. A surface prints the
973
- * title, never the word, and never a title of its own (#173, A18). The wording
974
- * is Will's to change through the glossary (DL-71); what each MEANS is pinned
975
- * 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.
976
515
  */
977
516
  export declare const VERDICT_DISPLAY_TITLES: Readonly<Record<string, string>>;
978
517
  /**
979
- * What a reader may rely on, for each of the six words a case reading reaches
980
- * (5.5.6 v1.20, A26; the `verified` text at v1.28, A35), the same for every
981
- * drop. `pending` and `not_found` never head a case reading: they are
982
- * witness states. `verified` is the base sentence, printed alone where the
983
- * drop holds no hand-off record; a caller reads `caseReliance` for
984
- * `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.
985
521
  */
986
- export declare const CASE_RELIANCE: Readonly<Record<CaseVerdict, string>>;
522
+ export declare const CASE_RELIANCE: Readonly<Record<CaseVerdict | "failed_nonconforming", string>>;
987
523
  /**
988
- * The clause a `verified` drop that holds a hand-off record gains, inserted
989
- * after `CASE_RELIANCE`'s first sentence (5.5.6, A35). Never printed alone,
990
- * and never where `handoffRecords` is zero: most drops hold one agent's
991
- * 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.
992
526
  */
993
527
  export declare const CASE_RELIANCE_HANDOFF_CLAUSE = "Every hand-off with both sides in this drop is intact.";
994
528
  /**
995
- * The sentence a case reading's reliance line gains where its packs hold an
996
- * envelope signed `environment: "dev"` (5.5.6, A48), keyed by the environment
997
- * marker (`environmentMarker`): `dev` where every envelope is signed dev,
998
- * `mixed` where only some are. Inserted after the line's first sentence, for
999
- * every verdict word. A drop with no `dev` envelope has no marker and gains
1000
- * nothing.
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.
1001
531
  */
1002
532
  export declare const CASE_RELIANCE_SANDBOX: Readonly<Record<EnvironmentMarker, string>>;
1003
533
  /**
1004
- * What a reader may rely on for one case reading (5.5.6 v1.38, A35 and A48),
1005
- * byte for byte the text `case_reliance`'s Python sibling returns.
1006
- *
1007
- * For every word but `verified` the base is `CASE_RELIANCE` unchanged. For
1008
- * `verified`, the hand-off clause is inserted after the first sentence
1009
- * where `handoffRecords` is not zero, and left out where it is zero: the
1010
- * base sentence never claims a hand-off the drop does not hold.
1011
- *
1012
- * Where `environmentMarker` is `dev` or `mixed`, the `CASE_RELIANCE_SANDBOX`
1013
- * sentence for it is inserted after the first sentence, for every word, so
1014
- * it directly follows the claim it qualifies (and precedes the hand-off
1015
- * clause). Where it is null or absent, the text is exactly what it was
1016
- * before A48.
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.
1017
538
  */
1018
- export declare function caseReliance(verdict: CaseVerdict, handoffRecords: number, environmentMarker?: EnvironmentMarker | null): string;
1019
- /** 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. */
1020
541
  export declare const WITNESS_STATES: {
1021
542
  readonly confirmed: "Confirmed on the public record";
1022
543
  readonly not_checked: "Not checked";
@@ -1024,34 +545,30 @@ export declare const WITNESS_STATES: {
1024
545
  };
1025
546
  export type WitnessState = keyof typeof WITNESS_STATES;
1026
547
  /**
1027
- * The witness line: a lookup of the public record, reported beside the
1028
- * headline and never in it (D7). It never raises or lowers the headline and
1029
- * never changes its title. `reasons` says why a `not_checked` line did not
1030
- * settle, in a fixed order; `services` names each service that answered, as
1031
- * 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.
1032
551
  */
1033
552
  export interface WitnessLine {
1034
553
  state: WitnessState;
1035
554
  reasons: string[];
1036
555
  services: string[];
1037
556
  /**
1038
- * Witness times a pack caches that no lookup settled, as `[blockHeight,
1039
- * blockTime]`, distinct and in time order (5.6.6, v1.25, A30). Printed labelled
1040
- * 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.
1041
560
  */
1042
561
  cached?: [number, number][];
1043
562
  /**
1044
- * Witness times the public record returned where they matched a pack's
1045
- * cache, distinct and in time order. The gateway's answer, named with the
1046
- * 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.
1047
565
  */
1048
566
  recorded?: [number, number][];
1049
567
  }
1050
- /** 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. */
1051
569
  export declare const WITNESS_NOT_LOOKED_UP: Readonly<WitnessLine>;
1052
- /** The witness line as both kernels print it. */
1053
- /** 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). */
1054
571
  export declare const WITNESS_TIME_CACHED = "the pack dates the witness, not checked";
1055
- /** 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. */
1056
573
  export declare const WITNESS_TIME_RECORDED = "the public record dates the witness";
1057
574
  export declare function renderWitnessLine(line: WitnessLine): string;