@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
@@ -1,136 +1,35 @@
1
- // The declaration: what a pack SAYS it holds, checked against what it does.
1
+ // The declaration: what a pack says it holds, checked against what it does
2
+ // (evidence-bundle.md §5.4).
2
3
  //
3
- // WHY THIS EXISTS.
4
- // A pack is a selection out of an interval. Every per-record check in this
5
- // kernel is a check on a record that is PRESENT: signatures, payload bindings,
6
- // inclusion proofs, content hashes. Drop half the records and every survivor
7
- // still verifies perfectly, so an auditor holding a pack cannot tell a full
8
- // hand-over from a trimmed one. `completenessNotes` in `./grading.ts` closes
9
- // one narrow case of that, where a checkpoint's own signed record commits to a
10
- // leaf count. Nothing closes it for a hand-over pack built out of a stamp, and
11
- // nothing at all says which records were withheld ON PURPOSE.
4
+ // Every per-record check verifies a record that is present, so a trimmed pack
5
+ // verifies as well as a full one. The declaration is the pack's own signed
6
+ // statement of what it was meant to contain. It rides inside `body`, so the
7
+ // bundle signature and `body_hash` cover it.
12
8
  //
13
- // The declaration is the pack's own signed statement of what it was meant to
14
- // contain: which agents, which period, which case, the records expected, which
15
- // of those are disclosed and which are withheld deliberately, and the hand-off
16
- // pairs it claims. It rides INSIDE `body`, so the bundle's signature covers it
17
- // and `body_hash` commits to it: the claim has an author, and re-declaring a
18
- // different selection later means signing a different pack.
9
+ // verified the pack holds exactly what the declaration says. A record
10
+ // declared `content_withheld` is checked for its signature and
11
+ // commitment only.
12
+ // failed any deviation, and every deviation is named. A deviation never
13
+ // surfaces as an absence a reader is left to interpret.
14
+ // incomplete no declaration, or one this reader cannot read. Nothing to
15
+ // check against, and nothing proven wrong.
19
16
  //
20
- // ── the rule ────────────────────────────────────────────────────────────────
17
+ // When no record declared `disclosed` supplied bytes, nothing was selected:
18
+ // the bytes travelled separately. That is a note, not a deviation, and the
19
+ // missing bytes still carry the pack to `incomplete`. Partial disclosure is a
20
+ // selection and fails.
21
21
  //
22
- // verified the pack holds exactly what the declaration says it holds. A
23
- // record declared `content_withheld` is in scope for its
24
- // signature and its commitment only, and its absent bytes are
25
- // not a deviation, because the declaration said so in advance.
26
- // failed ANY deviation, and EVERY deviation is named. Declared and
27
- // absent, present and undeclared, the pack carrying one
28
- // `event_id` twice, withheld without being declared, disclosed
29
- // after being declared withheld, or a claimed hand-off naming a
30
- // record the declaration never declared.
31
- // partial no declaration, or one the reader cannot interpret as this
32
- // version. The pack makes no claim this reader can check, so
33
- // what it was meant to contain is UNKNOWN. Never `verified`,
34
- // because there is nothing to check against, and never `failed`,
35
- // because nothing is wrong: absence is not failure.
22
+ // Pure: data in, data out. "The holder supplied bytes" is defined once, in
23
+ // `./side-content.ts`. This check does not verify that a claimed hand-off pair
24
+ // is a real pair (the case verdict does), but it does require each claimed side
25
+ // to be a conformant hand-off record (`./handoff.ts`).
36
26
  //
37
- // A deviation is a FAILURE and is NAMED. It must never surface as an absence
38
- // a reader is left to interpret. That is the shape the hand-off record
39
- // contract caught in review (anchor#189): a check made a condition of pairing
40
- // turns a deviation into two records that did not pair for no stated reason,
41
- // and a reader reads a missing thing as an innocent one.
42
- //
43
- // ── the bytes that did not travel ────────────────────────────────────
44
- //
45
- // One case inside rule 3 is not a selection and is not graded as one. When NO
46
- // record declared `disclosed` supplied bytes, nothing was selected: either the
47
- // bytes travelled separately or the reader was handed the JSON alone. A
48
- // hostile holder gains nothing from that shape, because declaring those
49
- // records `content_withheld` grades `verified` today and is cheaper, so the
50
- // only party who reaches it is the honest one who forgot `--logs`. This check
51
- // emits a NAMED note instead of a deviation, and the pack's missing bytes
52
- // carry it to `incomplete` through the disclosure account that already counts
53
- // them.
54
- //
55
- // PARTIAL disclosure is a selection and stays a deviation: nineteen of twenty
56
- // declared-disclosed records supplying bytes and one not is a choice about
57
- // which record, and it fails.
58
- //
59
- // ── the seam ────────────────────────────────────────────────────────────────
60
- //
61
- // PURE, like `./grading.ts`: data in, data out, no I/O and no key lifecycle.
62
- // It reads the bundle it is handed and, optionally, the same side-content map
63
- // `verifyEvidenceBundle` takes, because "the holder supplied bytes for this
64
- // record" has exactly one definition in this kernel (`./side-content.ts`) and
65
- // this module calls it rather than restating it.
66
- //
67
- // ── what this check does NOT claim ──────────────────────────────────────────
68
- //
69
- // It compares the declaration to the pack. It does not read a record's
70
- // internals and cross-check `agents`, `period` or `case_id` against them, and
71
- // it does not verify that a claimed hand-off PAIR is a real pair: the hashes
72
- // match, the sequence holds, the two sides name each other's keys. Those are
73
- // the CASE verdict, across packs and across organizations, and they are
74
- // anchor#198. Doing half of that here would put two implementations of one
75
- // rule in the stack, which is the defect `./grading.ts` was brought into the
76
- // kernel to end.
77
- //
78
- // It does now check that each SIDE of a claimed hand-off is a conformant
79
- // hand-off record (proof#136, ratified by Phil 2026-09-22), and it asks
80
- // `./handoff.ts` rather than restating that rule. `handoffs[]` carried the
81
- // claim and checked nothing about it, and that is what made an unreadable
82
- // record useful: a receiving side respelled one code point of `event_type`
83
- // and one of `event.counterparty_public_key`, which put the record outside
84
- // envelope-spec.md 4.1 by both of its scope clauses, then declared it and
85
- // claimed it as the received half. The record layer said nothing and the pack
86
- // layer said `verified`. A record cannot be unreadable now and usable later.
87
- //
88
- // What that does NOT close (proof#137, ratified by Phil 2026-09-22). The
89
- // sentence deleted from the spec beside this one said the other door was
90
- // closed too: omit the record from `handoffs[]` and it is a
91
- // present-and-undeclared record. It is not. `handoffs[]` MAY be empty, so a
92
- // holder declares the record in `records[]` and claims it nowhere, and then
93
- // rule 2 cannot reach it because it is declared and rule 7 cannot reach it
94
- // because it is unclaimed: this check reads `verified`, exactly as it does
95
- // over the honest pack. The corpus pins that pack as EXPECTED
96
- // (`declaration/declaration-unclaimed-record-01`). A pack a party assembles
97
- // about itself is not self-proving about what that party LEFT OUT, because
98
- // the omission happens before any reader runs; the closure is the case
99
- // verdict across BOTH parties' packs, anchor#198.
100
- //
101
- // ── the three defects the sixth hunt landed on this check (proof#137) ───────
102
- //
103
- // Three. The fourth door the hunt found, whether a pack a party assembles
104
- // about itself can prove what it LEFT OUT, is a founder call and is not
105
- // answered here.
106
- //
107
- // ONE PARSER, NOT TWO BUILT-INS. The two kernels read one `event_bytes`
108
- // differently, three of the four ways verdict-level on identical bytes:
109
- // Python's `json.loads` accepts `NaN`, `Infinity` and `-Infinity` and this
110
- // kernel's `JSON.parse` rejects them, and Python's `.decode("utf-8")` keeps
111
- // a leading byte-order mark where `TextDecoder` eats it. One pack read
112
- // `verified` here and `failed[pair-record-not-a-handoff]` there. The rule is
113
- // now stated rather than delegated: bytes that are not RFC 8785 JSON are not
114
- // a conformant record, so a leading BOM and each of the three non-finite
115
- // literals are rejected in BOTH kernels, spelled out in each, exactly as the
116
- // hex charset already was.
117
- //
118
- // ONE `event_id`, TWO RECORDS. Both kernels built a map by id, which is
119
- // last-write-wins in both languages, so a second record under a declared id
120
- // was graded by neither rule 2 nor rule 7 and was never reported as surplus.
121
- // FIRST in bundle order is now the record every other rule grades, and every
122
- // record after it under the same id is a `duplicate_event_id` deviation.
123
- //
124
- // MINIMUM DISCLOSURE IS NOT A DEVIATION. A claimed side whose `event_bytes`
125
- // the pack withholds was graded "not a conformant hand-off record", so an
126
- // honest holder exercising the disclosure posture this product sells proved
127
- // a deviation, and the cheapest escape from that was to stop claiming pairs
128
- // at all. A withheld side now gets its OWN reading, distinct from both
129
- // conformant and non-conformant, and is a NAMED NOTE.
130
-
131
- // The kernel's one reader for bytes a producer signed: strict UTF-8, no leading
132
- // byte-order mark, no non-finite literal. `jcs_loads` in
133
- // `src/sanning_proof/canonicalize.py` is the twin (proof#140 gate 5).
27
+ // A pack cannot prove what its holder left out: a record declared in
28
+ // `records[]` and claimed in no `handoffs[]` entry reads `verified` here. The
29
+ // case verdict across both parties' packs closes that.
30
+
31
+ // The one reader for bytes a producer signed: strict UTF-8, no leading BOM, no
32
+ // non-finite literal. Python's twin is `jcs_loads`.
134
33
  import type { ChainLink } from "./chain.js";
135
34
  import { hexToBytes, utf8 } from "./crypto.js";
136
35
  import { jcs, jcsParse } from "./verifier.js";
@@ -142,25 +41,20 @@ import {
142
41
  type SideContentMap,
143
42
  } from "./side-content.js";
144
43
 
145
- /** The first declaration schema, which every earlier reader knows. */
44
+ /** The first declaration schema. */
146
45
  export const DECLARATION_VERSION = "sanning.declaration/v1";
147
46
 
148
- /**
149
- * v1 plus a signed `records[].role` on every record (evidence-bundle.md
150
- * 5.4.1 v1.20, A21). A reader that knows only v1 reads a v2 declaration as one
151
- * it cannot read, which is the safe direction.
152
- */
47
+ /** v1 plus a signed `records[].role` on every record (§5.4.1). */
153
48
  export const DECLARATION_VERSION_V2 = "sanning.declaration/v2";
154
49
 
155
50
  /**
156
51
  * v2 plus a signed `chains[]`: the `chain_key`s the holder claims are complete
157
- * and in order (evidence-bundle.md 5.4.1 v1.36, A45). A reader that knows only
158
- * v1 and v2 reads a v3 declaration as one it cannot read, which is the safe
52
+ * and in order (§5.4.1). An older reader cannot read it, which is the safe
159
53
  * direction.
160
54
  */
161
55
  export const DECLARATION_VERSION_V3 = "sanning.declaration/v3";
162
56
 
163
- /** Every declaration schema this reader knows, in the order they were added. */
57
+ /** Every declaration schema this reader knows, oldest first. */
164
58
  export const DECLARATION_VERSIONS = [
165
59
  DECLARATION_VERSION,
166
60
  DECLARATION_VERSION_V2,
@@ -171,15 +65,13 @@ export const DECLARATION_VERSIONS = [
171
65
  const ROLE_VERSIONS: readonly string[] = [DECLARATION_VERSION_V2, DECLARATION_VERSION_V3];
172
66
 
173
67
  /**
174
- * What a v2 declaration may sign a record IS, compared exactly: `handoff`, a
175
- * record 4.1 governs that the pack claims as a side in `handoffs[]`, or
176
- * `step`, any other record.
68
+ * The roles a v2 declaration can sign, compared exactly: `handoff` for a
69
+ * record §4.1 governs that the pack claims in `handoffs[]`, `step` otherwise.
177
70
  */
178
71
  export const DECLARATION_ROLES = ["handoff", "step"] as const;
179
72
  export type DeclarationRole = (typeof DECLARATION_ROLES)[number];
180
73
 
181
- /** The three states a declaration check can reach, from the §3 vocabulary. */
182
- export const DECLARATION_VERDICTS = ["verified", "failed", "partial"] as const;
74
+ export const DECLARATION_VERDICTS = ["verified", "failed", "incomplete"] as const;
183
75
  export type DeclarationVerdict = (typeof DECLARATION_VERDICTS)[number];
184
76
 
185
77
  export const DECLARATION_DEVIATION_KINDS = [
@@ -189,47 +81,27 @@ export const DECLARATION_DEVIATION_KINDS = [
189
81
  "declared_absent",
190
82
  /** A record in the pack the declaration never expected. */
191
83
  "undeclared_present",
192
- /**
193
- * A record declared `content_withheld` whose bytes the pack supplies INSIDE
194
- * its own signed body, in `events[].content`.
195
- */
84
+ /** A record declared `content_withheld` whose bytes the pack's own body supplies. */
196
85
  "withheld_but_disclosed",
197
86
  /** The pack carries one `event_id` on two or more records. */
198
87
  "duplicate_event_id",
199
88
  /** A claimed hand-off names a record the declaration does not declare. */
200
89
  "pair_record_undeclared",
201
- /**
202
- * A claimed hand-off names a record that is not a conformant hand-off
203
- * record of the side it is claimed on (envelope-spec.md 4.1).
204
- */
90
+ /** A claimed hand-off side that is not a conformant hand-off record of that side (§4.1). */
205
91
  "pair_record_not_a_handoff",
206
- /**
207
- * Rule 9 (v1.20, A21), `sanning.declaration/v2` only. A record declared
208
- * `handoff` that no `handoffs[]` entry names as a side.
209
- */
92
+ /** v2 only: a record declared `handoff` that no `handoffs[]` entry claims. */
210
93
  "role_handoff_unclaimed",
211
94
  /** A record a `handoffs[]` entry names as a side, declared `step`. */
212
95
  "role_step_claimed",
213
- /**
214
- * A record declared `step` that 4.1's scope test puts in scope: its `event`
215
- * carries `counterparty_public_key`, or its folded `event_type` has a
216
- * `handoff` segment. The holder signed that a hand-off record is not one.
217
- */
96
+ /** A record declared `step` that §4.1's scope test puts in scope. */
218
97
  "role_step_governed",
219
- /**
220
- * A record declared `step` whose supplied `event_bytes` this reader cannot
221
- * read under rule 7's parser rule.
222
- */
98
+ /** A record declared `step` whose supplied `event_bytes` cannot be read. */
223
99
  "role_step_unreadable",
224
- /**
225
- * Rule 11 (v1.36, A45), `sanning.declaration/v3` only. The holder signed
226
- * that a chain is complete and in order, and a second record under that key,
227
- * under any producing key, begins it.
228
- */
100
+ /** v3 only: a second record beginning a claimed chain, under any producing key. */
229
101
  "chain_second_root",
230
102
  /** A record of a claimed chain is the second successor of one predecessor. */
231
103
  "chain_fork",
232
- /** A record of a claimed chain points at a predecessor its chain does not present in the pack. */
104
+ /** A claimed chain's record points at a predecessor the pack does not present. */
233
105
  "chain_link_dangling",
234
106
  /** A claimed chain of which the pack presents no record at all. */
235
107
  "chain_claimed_absent",
@@ -237,115 +109,67 @@ export const DECLARATION_DEVIATION_KINDS = [
237
109
  export type DeclarationDeviationKind = (typeof DECLARATION_DEVIATION_KINDS)[number];
238
110
 
239
111
  /**
240
- * Every way a pack and the UNSIGNED content handed in beside it fail to line
241
- * up, as a closed set. Neither of these is a deviation, and neither of them
242
- * fails (proof#147, ratified by Phil 2026-09-22).
243
- *
244
- * WHY THEY ARE NOT DEVIATIONS. 5.4 grades a pack's declaration partly against
245
- * content the reader was HANDED, and that content is not signed by anybody.
246
- * `events[].content` rides inside `body`, so the wrapper signature and
247
- * `body_hash` cover it and what it says about the holder is the holder's own
248
- * word. The side-content map is the other channel, and whoever assembles a
249
- * case fills it in: an accuser could put an intact pack into
250
- * `pack_declaration_deviates`, which contributed `failed`, WITH NO KEY AT
251
- * ALL, by dropping the bytes of a record the pack declared `disclosed` or by
252
- * adding bytes for one it declared `content_withheld`. The pack was intact,
253
- * its wrapper verified, and its holder had done nothing.
254
- *
255
- * This is the mirror of proof#143. There a counterparty makes itself look
256
- * better by choosing what it hands over; here an accuser makes a counterparty
257
- * look worse the same way. Both now read as what the READER established, so
258
- * `failed` stays for what a signature covers.
259
- *
260
- * WHAT A HOSTILE HOLDER GAINS FROM THE WEAKER WORD: nothing, because a
261
- * cheaper move already dominates it. Declaring a record `content_withheld` is
262
- * legal and grades `verified`, which is strictly better than the `incomplete`
263
- * these carry.
112
+ * Every way a pack and the unsigned content handed in beside it fail to line
113
+ * up. Not deviations, and never `failed`: whoever assembles a case fills the
114
+ * side-content map, so an accuser could otherwise fail an intact pack with no
115
+ * key at all. `failed` stays for what a signature covers.
264
116
  */
265
117
  export const DECLARATION_CONTENT_KINDS = [
266
118
  /**
267
- * A record declared `disclosed` whose bytes reached this reader from
268
- * neither channel. ALWAYS content-dependent: 5.4.1 lets those bytes travel
269
- * beside the pack as `logs/<event_id>.json`, so "no bytes arrived" is a
270
- * fact about what the READER was handed and never a fact about what the
271
- * holder supplied. No signature covers the absence of a file nobody signed.
119
+ * A record declared `disclosed` whose bytes arrived from neither channel.
120
+ * Always content-dependent: the bytes may travel beside the pack, so this is
121
+ * a fact about what the reader was handed.
272
122
  */
273
123
  "content_dependent_withholding",
274
124
  /**
275
- * A record declared `content_withheld` whose bytes reached this reader ONLY
276
- * from the unsigned side-content map. The pack's own body supplies none, so
277
- * the holder committed to no such disclosure. The in-body half of the same
278
- * rule is the `withheld_but_disclosed` DEVIATION and still fails.
125
+ * A record declared `content_withheld` whose bytes arrived only through the
126
+ * side-content map. The in-body case is the `withheld_but_disclosed`
127
+ * deviation.
279
128
  */
280
129
  "content_dependent_disclosure",
281
130
  ] as const;
282
131
  export type DeclarationContentKind = (typeof DECLARATION_CONTENT_KINDS)[number];
283
132
 
284
- /**
285
- * One way the pack and the unsigned content beside it do not line up. Named
286
- * like a deviation and weighed like an absence.
287
- */
133
+ /** One content finding: named like a deviation, weighed like an absence. */
288
134
  export interface DeclarationContentFinding {
289
135
  kind: DeclarationContentKind;
290
- /** The record this is about. Always one: both kinds are per-record. */
136
+ /** The record this is about. */
291
137
  eventId?: string;
292
138
  }
293
139
 
294
- /** Everything this check reports that is NOT a deviation, as a closed set. */
140
+ /** Everything this check reports that is not a deviation. */
295
141
  export const DECLARATION_NOTE_KINDS = [
296
- /**
297
- * Every record declared `disclosed` supplied no bytes, so nothing was
298
- * selected: the bytes did not travel with the pack.
299
- */
142
+ /** No record declared `disclosed` supplied bytes: they did not travel with the pack. */
300
143
  "disclosure_did_not_travel",
301
144
  /**
302
- * A claimed hand-off side whose `event_bytes` the pack withholds. The reader
303
- * cannot see what the record is either way, so it is neither a conformant
304
- * hand-off record nor a non-conformant one.
145
+ * A claimed hand-off side whose `event_bytes` the pack withholds. Withholding
146
+ * is minimum disclosure, so it is neither conformant nor non-conformant.
305
147
  */
306
148
  "pair_record_withheld",
307
149
  /**
308
- * A record a v2 declaration signs `step` whose `event_bytes` the pack
309
- * withholds (rule 9, v1.20). Its role cannot be checked in either direction,
310
- * so it is a note; the pack grade still holds the pack at `incomplete`,
311
- * because its payload binding is undetermined.
150
+ * A record a v2 declaration signs `step` whose `event_bytes` are withheld, so
151
+ * its role cannot be checked. The undetermined binding keeps the pack at
152
+ * `incomplete`.
312
153
  */
313
154
  "role_step_withheld",
314
155
  /**
315
- * Rule 10 (v1.29, A36). A record whose signed `event_type` is `declaration`
316
- * and whose content HANDED IN to this reader is exactly `JCS(object)` of a
317
- * declaration object of a version this reader knows, carrying exactly that
318
- * version's keys, and not this pack's own. The note names the content handed
319
- * in, never the record: whether those bytes bind to the record's
320
- * `content_hash` is the bundle verifier's check, and an accuser can hand in
321
- * declaration-shaped bytes for an honest record. What the pack holds is what
322
- * the declaration in its body says, so this moves no verdict.
156
+ * A record signed as `event_type` `declaration` whose handed-in content is
157
+ * `JCS` of a known declaration other than this pack's own. Names the content,
158
+ * not the record: an accuser can hand in such bytes for an honest record.
159
+ * Moves no verdict.
323
160
  */
324
161
  "other_declaration_held",
325
- /**
326
- * Rule 10. A record whose signed `event_type` is `declaration` and whose
327
- * content did not reach this reader from either channel, so whether it is a
328
- * declaration other than this pack's own is UNKNOWN.
329
- */
162
+ /** A record signed `declaration` whose content did not arrive, so what it declares is unknown. */
330
163
  "other_declaration_withheld",
331
164
  /**
332
- * Rule 11 (v1.36, A45). A claimed chain has a dangling link, and a record
333
- * signed by the same producing key has an undetermined payload binding. That
334
- * record takes no part in a chain, so whether the missing link is it is
335
- * UNKNOWN. Moves nothing: the undetermined binding holds the pack at
336
- * `incomplete`.
165
+ * A claimed chain has a dangling link, and a same-key record with an
166
+ * undetermined payload binding could be the missing link. Moves nothing.
337
167
  */
338
168
  "chain_claim_withheld",
339
169
  ] as const;
340
170
  export type DeclarationNoteKind = (typeof DECLARATION_NOTE_KINDS)[number];
341
171
 
342
- /**
343
- * One named non-deviation finding, as data.
344
- *
345
- * A note never moves the declaration verdict. It exists so the one shape rule
346
- * 3 deliberately does not fail is still SAID, rather than leaving a reader to
347
- * notice nothing was reported.
348
- */
172
+ /** One named finding that is not a deviation. A note never moves the verdict. */
349
173
  export interface DeclarationNote {
350
174
  kind: DeclarationNoteKind;
351
175
  /** How many records the note is about, where it is about a count. */
@@ -360,44 +184,38 @@ export interface DeclarationNote {
360
184
  chainKey?: string;
361
185
  }
362
186
 
363
- /** One deviation, as data. A surface renders its own prose or calls `renderDeviation`. */
187
+ /** One deviation, as data. */
364
188
  export interface DeclarationDeviation {
365
189
  kind: DeclarationDeviationKind;
366
190
  /** The record this is about, where it is about one. */
367
191
  eventId?: string;
368
- /** Which side of a claimed hand-off named it, for the two `pair-record-` deviations. */
192
+ /** Which side of a claimed hand-off, for the `pair_record_` deviations. */
369
193
  side?: "sent" | "received";
370
- /** Which claimed hand-off, by position, for the two `pair-record-` deviations. */
194
+ /** Which claimed hand-off, by position, for the `pair_record_` deviations. */
371
195
  pairIndex?: number;
372
196
  /** What is wrong with the declaration itself, for `declaration_malformed`. */
373
197
  reason?: string;
374
- /**
375
- * The `declaration_version` the object claimed, for `declaration_malformed`,
376
- * so the rendered sentence names the version the holder wrote to rather than
377
- * the first one this reader learned.
378
- */
198
+ /** The `declaration_version` the object claimed, for `declaration_malformed`. */
379
199
  version?: string;
380
- /** The claimed chain, for the four rule 11 `chain_` deviations. */
200
+ /** The claimed chain, for the `chain_` deviations. */
381
201
  chainKey?: string;
382
202
  }
383
203
 
384
204
  export interface DeclarationCheck {
385
205
  verdict: DeclarationVerdict;
386
- /** Every deviation, in a fixed order. Empty is only a pass when the verdict says so. */
206
+ /** Every deviation, in a fixed order. */
387
207
  deviations: DeclarationDeviation[];
388
208
  /**
389
- * Every way the pack and the unsigned content beside it do not line up, in
390
- * a fixed order. These are NOT deviations: they carry the check to
391
- * `partial` and the pack to `incomplete`, never to `failed`.
209
+ * Mismatches with unsigned content, in a fixed order. They carry the check to
210
+ * `incomplete` and the pack to `incomplete`, never to `failed`.
392
211
  */
393
212
  contentFindings: DeclarationContentFinding[];
394
- /** Named findings that are NOT deviations, in a fixed order. */
213
+ /** Named findings that are not deviations, in a fixed order. */
395
214
  notes: DeclarationNote[];
396
215
  /**
397
- * Whether the body carried a `declaration` member at all, WHATEVER its
398
- * shape. A string, an array or a `null` under that key is a claim the holder
399
- * made and this reader cannot read, which is not the same thing as a pack
400
- * that claimed nothing: `gradePack` grades the two differently.
216
+ * Whether the body carried a `declaration` member at all, whatever its shape.
217
+ * An unreadable claim is not the same as no claim: `gradePack` grades them
218
+ * differently.
401
219
  */
402
220
  present: boolean;
403
221
  /** `event_id`s the declaration expects, in declaration order. */
@@ -405,28 +223,16 @@ export interface DeclarationCheck {
405
223
  /** Of those, the ones declared withheld on purpose. */
406
224
  declaredWithheld: string[];
407
225
  /**
408
- * The `declaration_version` this reader read the object as, one of
409
- * `DECLARATION_VERSIONS`, or `null` where the body carries no declaration or
410
- * one whose version this reader does not know. Set for a malformed object
411
- * too: the version was read, the rest was not. Optional so a check a caller
412
- * builds by hand, as rule version 5 adapters do, still type-checks.
226
+ * The version the object was read as, or `null` where there is no
227
+ * declaration or its version is unknown. Set for a malformed object too.
413
228
  */
414
229
  version?: string | null;
415
- /**
416
- * Every record a v2 declaration signs a role for, in declaration order.
417
- * Empty for v1, and for anything this reader could not read.
418
- */
230
+ /** Every record a v2 or v3 declaration signs a role for, in declaration order. */
419
231
  roles?: { eventId: string; role: DeclarationRole }[];
420
- /**
421
- * Every `chain_key` a v3 declaration claims is complete and in order, in
422
- * declaration order. Empty for v1 and v2, and for anything this reader could
423
- * not read.
424
- */
232
+ /** Every `chain_key` a v3 declaration claims, in declaration order. */
425
233
  chains?: string[];
426
234
  }
427
235
 
428
- // ---- Reading the pack --------------------------------------------------------
429
-
430
236
  interface DeclarationBundleLike {
431
237
  body?: {
432
238
  declaration?: unknown;
@@ -439,25 +245,10 @@ interface DeclarationBundleLike {
439
245
  }
440
246
 
441
247
  /**
442
- * The record committed inside one `events[]` entry, or `null`.
443
- *
444
- * `null` is "this reader cannot read the record", which is not the same as
445
- * "the record is fine": a claimed hand-off side whose bytes the pack supplied
446
- * and this reader cannot read is a deviation, because the claim is that it IS
447
- * a hand-off record and nothing here supports that. It is not the same as "the
448
- * pack supplied no bytes" either: see `bytesSupplied`.
449
- *
450
- * ONE RULE, SPELLED OUT, BECAUSE TWO BUILT-INS ARE NOT ONE RULE. The bytes are
451
- * `JCS(event record)` (RFC 8785) or they are not a conformant record. Three
452
- * checks carry that: lowercase-or-uppercase hex in pairs, no leading byte-order
453
- * mark, and no non-finite literal. The hex is `hexToBytes`, because it and
454
- * `bytes.fromhex` disagree about embedded whitespace (proof#135); the rest is
455
- * `jcsParse`, which every site in either kernel that reads signed bytes now
456
- * shares (proof#140 gate 5). Both halves were a hand-rolled copy in this module
457
- * until proof#146, and both are the shared reader now: the answer on every
458
- * input is the same, and there is one fewer place for the rule to drift. The
459
- * cross-kernel declaration leg and `declaration.test.ts` hold both kernels to
460
- * the same answer on the same bytes rather than to either built-in.
248
+ * The record committed inside one `events[]` entry, or `null` where this
249
+ * reader cannot read it. Bytes that are not `JCS(event record)` are not a
250
+ * conformant record; parsing goes through the shared `hexToBytes` and
251
+ * `jcsParse` so both kernels agree on every input.
461
252
  */
462
253
  function committedRecord(raw: unknown): HandoffRecord | null {
463
254
  let parsed: unknown;
@@ -469,33 +260,19 @@ function committedRecord(raw: unknown): HandoffRecord | null {
469
260
  return isObject(parsed) ? (parsed as HandoffRecord) : null;
470
261
  }
471
262
 
472
- // Which record type each side of a claimed hand-off has to be. A pack that
473
- // names its receipt as the sent side has mislabeled a record, and the reading
474
- // layer grades the two types differently.
475
- const SIDE_EVENT_TYPES = { sent: "handoff.sent", received: "handoff.received" } as const;
263
+ // A pack that names its receipt as the sent side has mislabelled a record.
264
+ const SIDE_EVENT_TYPES = { sent: "agent.handoff.sent", received: "agent.handoff.received" } as const;
476
265
 
477
266
  /**
478
- * The three readings a claimed hand-off side can get. THREE, not two
479
- * (proof#137, defect 3). A reader that has only "conformant" and "not
480
- * conformant" has to put a record whose `event_bytes` the pack withholds into
481
- * one of them, and both are wrong: `event_bytes` is OPTIONAL and withholding
482
- * it is minimum disclosure, which the corpus calls a customer's right and not
483
- * a defect. Grading it "not a conformant hand-off record" made an honest
484
- * minimum-disclosure pack prove a deviation, and the cheapest way out of that
485
- * was to stop claiming pairs at all.
267
+ * How a claimed hand-off side reads. Three readings, not two: `event_bytes` is
268
+ * optional, and withholding it is minimum disclosure, not a non-conformant
269
+ * record.
486
270
  */
487
271
  type SideReading = "conformant" | "withheld" | "not-a-handoff";
488
272
 
489
273
  /**
490
- * How a claimed side reads: conformant, withheld, or not a hand-off record.
491
- *
492
- * The definition of conformant is 4.1's own (`isConformantHandoffRecord`),
493
- * read once and never restated here, plus the record type the side names.
494
- *
495
- * WITHHELD IS ITS OWN READING. The pack supplied no `event_bytes`, so this
496
- * reader cannot see what the record is in either direction. Saying it is not a
497
- * hand-off record is a claim the reader cannot support, and it punishes the
498
- * disclosure posture the product sells.
274
+ * How a claimed side reads. Conformant is §4.1's own rule
275
+ * (`isConformantHandoffRecord`) plus the record type the side names.
499
276
  */
500
277
  function sideReading(
501
278
  p: {
@@ -517,60 +294,41 @@ function sideReading(
517
294
  const bodyOf = (bundle: unknown) => (bundle as DeclarationBundleLike | null)?.body;
518
295
 
519
296
  /**
520
- * One of the pack's own records: the id, and whether the pack supplied its
521
- * bytes.
522
- *
523
- * An event with no readable `event_id` is given a positional name, the same
524
- * `#n` convention `accountDisclosure` uses, so it is still reported rather
525
- * than dropped. An unnameable record cannot be declared, so it can only ever
526
- * be surplus, which is a deviation and is named as one.
297
+ * One of the pack's own records. An event with no readable `event_id` gets a
298
+ * positional `#n` name, so it is reported as surplus rather than dropped.
527
299
  */
528
300
  export interface PackRecord {
529
301
  eventId: string;
530
302
  nameable: boolean;
531
- /**
532
- * Whether bytes for this record reached the reader at ALL, from either
533
- * channel. This is what the disclosure account counts.
534
- */
303
+ /** Whether bytes reached the reader from either channel. */
535
304
  disclosed: boolean;
536
305
  /**
537
- * Whether the bytes came from `events[].content`, INSIDE the body the
538
- * wrapper signed and `body_hash` commits to. The two channels are kept
539
- * apart because only this one is the holder's own word: the side-content
540
- * map is filled in by whoever assembled the case (proof#147).
306
+ * Whether the bytes came from `events[].content`, inside the signed body.
307
+ * Only this channel is the holder's own word.
541
308
  */
542
309
  disclosedInBody: boolean;
543
310
  publicKey: string;
544
311
  record: HandoffRecord | null;
545
312
  /**
546
- * Whether the pack SUPPLIED `event_bytes` for this record at all.
547
- * `event_bytes` is OPTIONAL (evidence-bundle.md 5.1 and 5.3), and
548
- * withholding it is minimum disclosure rather than a defect, so "the pack
549
- * supplied bytes this reader cannot read" and "the pack supplied none" are
550
- * two different readings and never one.
313
+ * Whether the pack supplied `event_bytes` at all. Supplied-but-unreadable and
314
+ * not supplied are different readings: withholding is minimum disclosure.
551
315
  */
552
316
  bytesSupplied: boolean;
553
317
  /**
554
- * Whether this record's payload binding is UNDETERMINED: no `event_bytes`
555
- * string and no inline envelope `payload`, the two things the envelope
556
- * verifier binds `payload_hash` against. Rule 11 excuses a dangling link
557
- * only for such a record's producing key.
318
+ * Whether the payload binding is undetermined: no `event_bytes` and no inline
319
+ * `payload`. Rule 11 excuses a dangling link only for such a record's key.
558
320
  */
559
321
  bindingUndetermined: boolean;
560
322
  /**
561
- * The envelope's signed `payload_hash` as written, or `""`. For a record
562
- * whose binding is undetermined it is the identity a successor's pointer
563
- * names: rule 11 excuses a dangling link only where the pointer equals it.
323
+ * The envelope's signed `payload_hash`, or `""`. Rule 11 excuses a dangling
324
+ * link only where the pointer equals it.
564
325
  */
565
326
  payloadHash: string;
566
327
  }
567
328
 
568
329
  /**
569
- * The pack's own records, in bundle order. Exported so the CASE layer
570
- * (`./case.ts`, anchor#198) reads a pack through the SAME parser that grades
571
- * it here, rather than through a second one beside it. A validation copied
572
- * next to the one that enforces it drifts, and a copy that is looser blesses
573
- * a value the real one then rejects.
330
+ * The pack's own records, in bundle order. Exported so `./case.ts` reads a
331
+ * pack through the same parser that grades it.
574
332
  */
575
333
  export function packRecords(
576
334
  bundle: unknown,
@@ -582,19 +340,13 @@ export function packRecords(
582
340
  const raw = ev?.envelope?.event_id;
583
341
  const nameable = typeof raw === "string" && raw.length > 0;
584
342
  const eventId = nameable ? (raw as string) : `#${i + 1}`;
585
- // One definition of "the holder supplied bytes", in both of the two
586
- // shapes a pack can supply them: in the body, or beside it. Both are
587
- // `./side-content.ts`, never restated here.
588
343
  const disclosedInBody = inBodyContentOffered(ev?.content);
589
344
  const disclosed = disclosedInBody || (nameable && hasSideContent(content, eventId));
590
- // The key comes from the ENVELOPE and never from inside the record, which
591
- // is 4.1's whole point: a value that names no key makes the record
592
- // `malformed` there, so a claimed side carrying one is a deviation here.
345
+ // From the envelope, never the record: an unparseable key makes the record
346
+ // `malformed` under §4.1.
593
347
  const key = ev?.envelope?.public_key;
594
- // The family convention for "the holder supplied this hex field": a
595
- // non-empty string. Anything else, an absent key, a `null` or a number, is
596
- // the field not travelling, which is minimum disclosure and not a record
597
- // this reader was handed and could not read.
348
+ // Only a non-empty string counts as supplied; anything else is the field
349
+ // not travelling.
598
350
  const rawBytes = ev?.event_bytes;
599
351
  return {
600
352
  eventId,
@@ -610,14 +362,9 @@ export function packRecords(
610
362
  });
611
363
  }
612
364
 
613
- // ---- Reading the declaration -------------------------------------------------
614
-
615
365
  const DISCLOSURE_STATES = new Set(["disclosed", "content_withheld"]);
616
366
 
617
- // RFC 3339 `date-time` with an uppercase `Z`, the family's convention for an
618
- // instant. Format only: this is not an ordering check and not a calendar
619
- // check, and it deliberately does not restate the `signed_at` rule of
620
- // envelope-spec §2, which grades a different field on a different layer.
367
+ // RFC 3339 `date-time` with an uppercase `Z`. Format only, not ordering.
621
368
  const INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
622
369
 
623
370
  const isObject = (v: unknown): v is Record<string, unknown> =>
@@ -633,21 +380,15 @@ interface ReadDeclaration {
633
380
  }
634
381
 
635
382
  /**
636
- * Read a declaration written to the version this reader knows, or say what is
637
- * wrong with it.
638
- *
639
- * Every malformation is collected, not the first: a declaration is a claim,
640
- * and a reader that names one defect in a claim with four sends its holder
641
- * round the loop four times.
383
+ * Read a declaration of a known version, or collect every malformation in it
384
+ * (all of them, so the holder fixes them in one pass).
642
385
  */
643
386
  function readDeclaration(
644
387
  declaration: Record<string, unknown>,
645
388
  version: string = DECLARATION_VERSION,
646
389
  ): { ok: true; read: ReadDeclaration } | { ok: false; reasons: string[] } {
647
390
  const reasons: string[] = [];
648
- // Under v2 every `records[]` entry must also carry `role`, exactly `handoff`
649
- // or `step` (5.4.1 v1.20, A21); under v1 the member is not read at all, so a
650
- // v1 declaration carrying `role` reads exactly as the same object without it.
391
+ // v2 and v3 require `role` on every entry; v1 does not read the member.
651
392
  const roles: { eventId: string; role: DeclarationRole }[] = [];
652
393
  const v2 = ROLE_VERSIONS.includes(version);
653
394
  const note = (r: string) => reasons.push(r);
@@ -686,8 +427,7 @@ function readDeclaration(
686
427
  note(`records[${i}] (${eventId}) has no disclosure of "disclosed" or "content_withheld"`);
687
428
  continue;
688
429
  }
689
- // Compared exactly: `Handoff`, `hand-off` and `step ` are other values,
690
- // and a non-string never equals one.
430
+ // Exact: `Handoff`, `hand-off` and `step ` are other values.
691
431
  const role = r["role"];
692
432
  if (v2 && !(role === "handoff" || role === "step")) {
693
433
  note(`records[${i}] (${eventId}) has no role of "handoff" or "step"`);
@@ -727,8 +467,7 @@ function readDeclaration(
727
467
  }
728
468
  }
729
469
 
730
- // v3 is v2 plus a required `chains[]` (5.4.1 v1.36, A45); v1 and v2 do not
731
- // read the member.
470
+ // v3 requires `chains[]`; v1 and v2 do not read the member.
732
471
  const chains: string[] = [];
733
472
  if (version === DECLARATION_VERSION_V3) {
734
473
  const rawChains = declaration["chains"];
@@ -754,10 +493,8 @@ function readDeclaration(
754
493
  : { ok: true, read: { records, pairs, roles, chains } };
755
494
  }
756
495
 
757
- // ---- The check ---------------------------------------------------------------
758
-
759
496
  const noDeclaration = (): DeclarationCheck => ({
760
- verdict: "partial",
497
+ verdict: "incomplete",
761
498
  deviations: [],
762
499
  contentFindings: [],
763
500
  notes: [],
@@ -770,9 +507,7 @@ const noDeclaration = (): DeclarationCheck => ({
770
507
  });
771
508
 
772
509
  /**
773
- * The `declaration_version` of an object this reader knows, or `null`: a
774
- * declaration that is not an object, an absent or `null` version and a version
775
- * outside `DECLARATION_VERSIONS` are each a claim this reader cannot read.
510
+ * The `declaration_version` of a declaration this reader knows, or `null`.
776
511
  */
777
512
  function knownVersion(declaration: unknown): string | null {
778
513
  if (!isObject(declaration)) return null;
@@ -783,10 +518,8 @@ function knownVersion(declaration: unknown): string | null {
783
518
  }
784
519
 
785
520
  /**
786
- * Rule 9 over one record the pack holds: the deviation or note it earns, or
787
- * `null` where the signed role agrees with the pack and the record. The first
788
- * of the four deviations that applies, in the order 5.4.2 lists them. Whether
789
- * 4.1 governs the record is `isHandoffNamespace`, CALLED, never restated.
521
+ * Rule 9 for one held record: the first deviation that applies in §5.4.2
522
+ * order, a note, or `null` where the signed role agrees.
790
523
  */
791
524
  function roleReading(
792
525
  p: { eventId: string; publicKey: string; record: HandoffRecord | null; bytesSupplied: boolean },
@@ -809,17 +542,10 @@ function roleReading(
809
542
  return null;
810
543
  }
811
544
 
812
- /**
813
- * The `event_type` a declaration record is signed under: a reserved
814
- * single-segment name, and the one `@sanning/anchor`'s `bundle` anchors a
815
- * declaration as.
816
- */
545
+ /** The reserved `event_type` a declaration record is signed under. */
817
546
  const DECLARATION_EVENT_TYPE = "declaration";
818
547
 
819
- /**
820
- * The keys a declaration object carries, under every version this reader
821
- * knows: exactly these, no more and no fewer (5.4.1).
822
- */
548
+ /** The exact keys of a declaration object, under every known version (§5.4.1). */
823
549
  const DECLARATION_KEYS = ["declaration_version", "case_id", "agents", "period", "records", "handoffs"];
824
550
 
825
551
  function hasExactKeys(value: unknown, keys: string[]): value is Record<string, unknown> {
@@ -829,14 +555,9 @@ function hasExactKeys(value: unknown, keys: string[]): value is Record<string, u
829
555
  }
830
556
 
831
557
  /**
832
- * Whether `obj` is shaped like a declaration this reader knows: one of
833
- * `DECLARATION_VERSIONS` compared exactly, exactly the keys that version
834
- * defines, and the value types this module reads.
835
- *
836
- * It is the shape test `@sanning/anchor`'s `isDeclarationShape` makes, and it
837
- * checks shape only, not the 5.4.1 rules `readDeclaration` grades: a stale
838
- * declaration is somebody else's claim, and this reader does not grade it, it
839
- * names it.
558
+ * Whether `obj` has the shape of a known declaration: a known version, exactly
559
+ * its keys, and the value types read here. Shape only: another holder's stale
560
+ * declaration is named, not graded.
840
561
  */
841
562
  function isDeclarationShape(obj: unknown): boolean {
842
563
  if (!isObject(obj)) return false;
@@ -871,9 +592,8 @@ function isDeclarationShape(obj: unknown): boolean {
871
592
  }
872
593
 
873
594
  /**
874
- * The content bytes disclosed for one record, or `null` where none are usable:
875
- * `events[].content` first, then the side-content map, chosen the way the
876
- * bundle verifier chooses the bytes it binds.
595
+ * The content bytes disclosed for one record, or `null`: `events[].content`
596
+ * first, then the side-content map, as the bundle verifier chooses.
877
597
  */
878
598
  function disclosedBytes(
879
599
  ev: unknown,
@@ -908,9 +628,8 @@ function sameBytes(a: Uint8Array, b: Uint8Array): boolean {
908
628
  }
909
629
 
910
630
  /**
911
- * Whether `raw` is exactly `JCS(object)` of a declaration-shaped object.
912
- * Canonical bytes, not merely parseable ones: a declaration is anchored as
913
- * `JCS(declaration)`, and nothing an agent writes by accident.
631
+ * Whether `raw` is exactly `JCS` of a declaration-shaped object. Canonical
632
+ * bytes only, so an agent does not write one by accident.
914
633
  */
915
634
  function isDeclarationBytes(raw: Uint8Array): boolean {
916
635
  try {
@@ -923,13 +642,8 @@ function isDeclarationBytes(raw: Uint8Array): boolean {
923
642
 
924
643
  /**
925
644
  * Rule 10: every record that is a declaration other than the pack's own, in
926
- * bundle order.
927
- *
928
- * The record's `event_type` is read from its signed `event_bytes`, so a record
929
- * whose bytes the pack withholds is not named: the reader cannot see what it
930
- * was signed as. Its content is read from either channel; a record whose
931
- * content is byte for byte `JCS` of the pack's own declaration is the pack's
932
- * own and is never named.
645
+ * bundle order. The `event_type` comes from the signed `event_bytes`, so a
646
+ * record with withheld bytes is not named.
933
647
  */
934
648
  function otherDeclarationNotes(
935
649
  bundle: unknown,
@@ -949,9 +663,8 @@ function otherDeclarationNotes(
949
663
  if (p.record === null || p.record.event_type !== DECLARATION_EVENT_TYPE) return;
950
664
  const raw = disclosedBytes(events[i], p, content);
951
665
  if (raw === null) {
952
- // Offered and unusable is not withheld: something arrived, it is not a
953
- // declaration, and the bundle verifier names what is wrong with it.
954
- // Nothing arrived is the reading the reader cannot settle.
666
+ // Unusable content is the bundle verifier's finding; only content that
667
+ // never arrived is unknown here.
955
668
  if (!p.disclosed) notes.push({ kind: "other_declaration_withheld", eventId: p.eventId });
956
669
  return;
957
670
  }
@@ -964,50 +677,27 @@ function otherDeclarationNotes(
964
677
  /**
965
678
  * Check a pack against the declaration inside its body.
966
679
  *
967
- * `content` is the same side-content map `verifyEvidenceBundle` takes, and is
968
- * optional: a pack that discloses its bytes in the body needs no side input,
969
- * and a pack that discloses them beside itself passes the map a consumer
970
- * already built.
971
- *
972
- * `chains` is `readChains` over the SAME bundle. Only a v3 declaration that
973
- * claims a chain reads it (rule 11). It is an argument rather than computed
974
- * here because `readChains` is asynchronous and this check is not, and Python's
975
- * `check_declaration` takes it the same way so the two kernels share one
976
- * contract. A v3 declaration that claims a chain, checked without it, reads as
977
- * a reader that does not know v3 reads it: `partial`, nothing named, and the
978
- * pack `incomplete`. Leaving it out can never read `verified`.
680
+ * @param content - The side-content map `verifyEvidenceBundle` takes.
681
+ * @param chains - `readChains` over the same bundle, read only for a v3 chain
682
+ * claim. Omitted, such a claim reads `incomplete`, never `verified`.
979
683
  *
980
- * The verdict is this check's own. It does not restate the bundle verdict and
981
- * it does not read it: a declaration can match perfectly over a pack whose
982
- * signatures are forged, and saying so is `gradePack`'s job, which takes both.
684
+ * The verdict does not read the bundle verdict: a declaration can match over
685
+ * forged signatures, and `gradePack` combines the two.
983
686
  */
984
687
  export function checkDeclaration(
985
688
  bundle: unknown,
986
689
  content?: SideContentMap | null,
987
690
  links?: readonly ChainLink[] | null,
988
691
  ): DeclarationCheck {
989
- // `body.declaration` as WRITTEN, not as hoped. The key being absent and the
990
- // key holding a string, an array or `null` are graded differently and must
991
- // not collapse into one another: a body with no `declaration` key claimed
992
- // nothing, which every pack built before §5.4 did, while a body whose
993
- // `declaration` will not read DID claim something. Reading the second as the
994
- // first is how a one-field edit to a re-sealed pack graded clean.
692
+ // An absent `declaration` claimed nothing; one present but unreadable did
693
+ // claim something. The two must not collapse into one.
995
694
  const body = bodyOf(bundle);
996
695
  if (!isObject(body) || !("declaration" in body)) return noDeclaration();
997
696
  const declaration = (body as Record<string, unknown>)["declaration"];
998
697
 
999
- // A declaration this reader cannot interpret as `sanning.declaration/v1` is
1000
- // UNKNOWN and not an accusation: the same posture §4 step 1 takes for an
1001
- // unknown `body_type`, one layer down. `present` says a declaration was
1002
- // THERE, so a surface can tell "nothing was claimed" from "something was
1003
- // claimed in a language I do not speak", and `gradePack` carries the second
1004
- // to `incomplete`.
1005
- //
1006
- // Every shape that lands here is one field away from a valid declared pack
1007
- // the holder can re-seal with its own key: an unknown `declaration_version`,
1008
- // a deleted one, a null one, and a `declaration` that is a string, an array
1009
- // or null. Each of those hides the evidence of a trim inside a signed body,
1010
- // so none of them may reach a clean pack verdict.
698
+ // An unreadable declaration is unknown, not an accusation, but each such
699
+ // shape is one field from a valid re-sealed pack, so none may reach a clean
700
+ // pack verdict.
1011
701
  const version = knownVersion(declaration);
1012
702
  if (version === null) {
1013
703
  return { ...noDeclaration(), present: true };
@@ -1038,13 +728,8 @@ export function checkDeclaration(
1038
728
  const declared = records.map((r) => r.eventId);
1039
729
  const declaredSet = new Set(declared);
1040
730
  const pack = packRecords(bundle, content);
1041
- // FIRST WINS, and the second record under one id is named below. A map built
1042
- // by id is last-write-wins in both languages, so a pack carrying one
1043
- // `event_id` twice had its second record graded by rule 7 and its first by
1044
- // nothing, or the reverse once the order flipped, and neither record was
1045
- // ever reported as surplus (proof#137, defect 2). First in bundle order is
1046
- // the record every other rule grades, and it does not depend on how the
1047
- // holder ordered `events[]`.
731
+ // First in bundle order wins; later records under the same id are named.
732
+ // A map by id would be last-write-wins and grade one of them by nothing.
1048
733
  const byId = new Map<string, (typeof pack)[number]>();
1049
734
  for (const p of pack) {
1050
735
  if (p.nameable && !byId.has(p.eventId)) byId.set(p.eventId, p);
@@ -1052,24 +737,13 @@ export function checkDeclaration(
1052
737
 
1053
738
  const deviations: DeclarationDeviation[] = [];
1054
739
 
1055
- // Declared and absent. The trimmed hand-over, which is the whole reason a
1056
- // declaration exists: nothing else in the pack can see it.
740
+ // Declared and absent: the trimmed hand-over.
1057
741
  for (const r of records) {
1058
742
  if (!byId.has(r.eventId)) deviations.push({ kind: "declared_absent", eventId: r.eventId });
1059
743
  }
1060
744
 
1061
- // Present and undeclared, and the pack carrying one id twice.
1062
- //
1063
- // An unnameable record is present-and-undeclared, because a record the
1064
- // declaration could not have named is surplus by construction.
1065
- //
1066
- // A record under an id the pack already carries is named SEPARATELY, in
1067
- // bundle order, and is not also reported as surplus: one defect, one
1068
- // finding. 5.4.1 says declaring an id twice is a malformed declaration and
1069
- // said nothing about the PACK carrying one twice, so a second record under a
1070
- // declared id was graded by no rule at all and was never reported
1071
- // (proof#137, defect 2). A declaration names records by id, so an id that
1072
- // names two records names neither.
745
+ // Present and undeclared. An unnameable record is surplus by construction.
746
+ // A repeated id is named once as `duplicate_event_id`, not also as surplus.
1073
747
  const seenInPack = new Set<string>();
1074
748
  for (const p of pack) {
1075
749
  if (p.nameable && seenInPack.has(p.eventId)) {
@@ -1082,11 +756,8 @@ export function checkDeclaration(
1082
756
  }
1083
757
  }
1084
758
 
1085
- // The bytes that did not travel. Over the records declared `disclosed` that
1086
- // the pack actually holds: if NONE of them supplied bytes, no record was
1087
- // selected over another and the honest reading is that the bytes travelled
1088
- // separately, so this is a note and not a deviation. One of them supplying
1089
- // bytes and another not IS a selection, and every shortfall is named.
759
+ // If no held `disclosed` record supplied bytes, nothing was selected: a note.
760
+ // Some supplying and some not is a selection, and every shortfall is named.
1090
761
  const disclosedHeld = records
1091
762
  .filter((r) => !r.withheld)
1092
763
  .map((r) => byId.get(r.eventId))
@@ -1096,28 +767,16 @@ export function checkDeclaration(
1096
767
  ? [{ kind: "disclosure_did_not_travel", count: disclosedHeld.length }]
1097
768
  : [];
1098
769
 
1099
- // The two disclosure rules, in declaration order, over the records the pack
1100
- // actually holds: an absent record is already named above, and naming it
1101
- // twice would report one defect as two.
1102
- //
1103
- // EACH OF THEM SPLITS ON WHICH CHANNEL THE BYTES CAME FROM (proof#147,
1104
- // ratified by Phil 2026-09-22). `events[].content` is inside the signed
1105
- // body, so what it says is the holder's own word and a mismatch with the
1106
- // holder's own declaration is a proven deviation. The side-content map is
1107
- // unsigned and is filled in by whoever assembled the case, so a reading
1108
- // that rests on it says what the READER established and not what the holder
1109
- // did. The weaker readings are named, and they carry the check to `partial`
1110
- // and the pack to `incomplete`.
770
+ // The disclosure rules, over held records only. Each splits on the channel:
771
+ // in-body content is the holder's signed word and a mismatch is a deviation;
772
+ // side content is unsigned and yields a content finding.
1111
773
  const contentFindings: DeclarationContentFinding[] = [];
1112
774
  for (const r of records) {
1113
775
  const p = byId.get(r.eventId);
1114
776
  if (p === undefined) continue;
1115
777
  if (!r.withheld && !p.disclosed) {
1116
778
  if (nothingTravelled) continue;
1117
- // ALWAYS content-dependent. 5.4.1 lets a record's bytes travel beside
1118
- // the pack, so no pack can prove it supplied them and no reader can
1119
- // prove it did not: an accuser holding a twenty-file drop reaches this
1120
- // reading by deleting one file.
779
+ // Always content-dependent: deleting one file beside the pack reaches it.
1121
780
  contentFindings.push({ kind: "content_dependent_withholding", eventId: r.eventId });
1122
781
  } else if (r.withheld && p.disclosed) {
1123
782
  if (p.disclosedInBody) {
@@ -1128,26 +787,9 @@ export function checkDeclaration(
1128
787
  }
1129
788
  }
1130
789
 
1131
- // A claimed hand-off that names a record the declaration does not declare,
1132
- // or names one that is not a conformant hand-off record of that side.
1133
- //
1134
- // The PAIR itself, that the hashes match and the sequence holds, is the case
1135
- // verdict (anchor#198) and is deliberately not checked here. Each SIDE is
1136
- // checked, and that is the belt for 4.1's own repair: a record respelled out
1137
- // of 4.1's scope was unreadable at the record layer and still usable here,
1138
- // because this array carried the claim and checked nothing about it
1139
- // (proof#136). A record declared and absent is already named above and is
1140
- // not named twice.
1141
- //
1142
- // A claimed side whose `event_bytes` the pack WITHHELD is a NAMED NOTE and
1143
- // not a deviation (proof#137, defect 3). `event_bytes` is OPTIONAL, and a
1144
- // holder that withholds it is exercising minimum disclosure, which this
1145
- // corpus calls a customer's right and not a defect. Grading it "not a
1146
- // conformant hand-off record" made an honest pack prove a deviation over a
1147
- // record the reader cannot see in either direction, and the cheapest escape
1148
- // from that was to stop claiming pairs, which is the door proof#137 is
1149
- // about. A withheld side is still not a clean claim: the pack's own
1150
- // disclosure account already carries those absent bytes to `incomplete`.
790
+ // Each claimed side must be declared and be a conformant hand-off record of
791
+ // that side. A withheld side is a note, not a deviation. The pair itself is
792
+ // the case verdict's to check.
1151
793
  for (const [i, pair] of pairs.entries()) {
1152
794
  for (const side of ["sent", "received"] as const) {
1153
795
  const id = pair[side];
@@ -1167,12 +809,8 @@ export function checkDeclaration(
1167
809
  }
1168
810
  }
1169
811
 
1170
- // Rule 9 (5.4.2 v1.20, A21), `sanning.declaration/v2` only: a record's
1171
- // signed role against the pack and against the record. Its deviations follow
1172
- // every deviation rules 1 to 8 report, and its note follows every other
1173
- // note, both in `records[]` order. It reads the signed body alone and NEVER
1174
- // the side-content map, so no party that signed nothing can create, move or
1175
- // remove one of its findings.
812
+ // Rule 9, v2 and v3: reads the signed body alone, never the side-content
813
+ // map, so no party that signed nothing can move its findings.
1176
814
  if (roles.length > 0) {
1177
815
  const claimed = new Set<string>();
1178
816
  for (const pair of pairs) {
@@ -1181,7 +819,7 @@ export function checkDeclaration(
1181
819
  }
1182
820
  for (const { eventId, role } of roles) {
1183
821
  const p = byId.get(eventId);
1184
- // Declared and absent: rule 1 named it, and one defect is one finding.
822
+ // Already named by rule 1.
1185
823
  if (p === undefined) continue;
1186
824
  const kind = roleReading(p, role, claimed.has(eventId));
1187
825
  if (kind === "role_step_withheld") notes.push({ kind, eventId });
@@ -1189,12 +827,8 @@ export function checkDeclaration(
1189
827
  }
1190
828
  }
1191
829
 
1192
- // Rule 11 (5.4.2 v1.36, A45), `sanning.declaration/v3` only: a chain the
1193
- // holder signed as complete and in order, against the chain reading of the
1194
- // same pack. Its deviations follow rule 9's, and its notes follow rule 9's
1195
- // and precede rule 10's, both in `chains[]` order.
1196
- // A caller that passed no links leaves the claim UNREAD: rules 1 to 10 still
1197
- // run, and the verdict below is never `verified`.
830
+ // Rule 11, v3 only. Without chain links the claim is unread, and the verdict
831
+ // is never `verified`.
1198
832
  const unread = claimedChains.length > 0 && (links === undefined || links === null);
1199
833
  if (claimedChains.length > 0 && links !== undefined && links !== null) {
1200
834
  const found = chainClaimFindings(claimedChains, links, pack);
@@ -1202,23 +836,16 @@ export function checkDeclaration(
1202
836
  notes.push(...found.notes);
1203
837
  }
1204
838
 
1205
- // Rule 10 (5.4.2 v1.29, A36): content handed in for a record signed
1206
- // `declaration` that is a declaration other than this pack's own. Notes only, in bundle order, after every other note, and
1207
- // never a deviation: the pack holds what its own declaration says whatever
1208
- // one of its records says.
839
+ // Rule 10: notes only, after every other note.
1209
840
  notes.push(...otherDeclarationNotes(bundle, pack, declaration, content));
1210
841
 
1211
- // A proven deviation wins: it is a mismatch between what the pack claimed
1212
- // and what its own signed body carries, and a failing check always wins.
1213
- // With no deviation and a content-dependent finding, the pack lines up with
1214
- // its claim everywhere a signature reaches and does not line up somewhere
1215
- // only unsigned bytes could settle, which is UNDETERMINED: `partial` is
1216
- // 3's word for that, and it adds none.
842
+ // A deviation always wins. Otherwise a content finding is undetermined:
843
+ // `incomplete`.
1217
844
  const verdict: DeclarationVerdict =
1218
845
  deviations.length > 0
1219
846
  ? "failed"
1220
847
  : contentFindings.length > 0 || unread
1221
- ? "partial"
848
+ ? "incomplete"
1222
849
  : "verified";
1223
850
 
1224
851
  return {
@@ -1236,22 +863,17 @@ export function checkDeclaration(
1236
863
  }
1237
864
 
1238
865
  /**
1239
- * Rule 11 over every claimed `chain_key`, in `chains[]` order. The twin of
1240
- * Python's `_chain_claim_findings`, which states the rule in full.
866
+ * Rule 11 over every claimed `chain_key`, in `chains[]` order. Python's twin is
867
+ * `_chain_claim_findings`.
1241
868
  *
1242
- * A claim covers every record whose OWN signed linkage names the key
1243
- * (`source === "record"`), under every producing key; an adapter's metadata
1244
- * linkage is never read for a claim. Under a claim, in bundle order: every
1245
- * record after the first that begins the chain is a `chain_second_root`; a
1246
- * record whose predecessor already has an earlier successor in its chain is a
1247
- * `chain_fork`; a record whose pointer resolves to no record of its own chain
1248
- * (one producing key, one `chain_key`) is a `chain_link_dangling`, with no
1249
- * window allowance; a claimed key with no such record is
1250
- * `chain_claimed_absent`. Links are read from the record each envelope binds,
1251
- * `event_bytes` or an inline `payload`. A dangling link is the note
1252
- * `chain_claim_withheld` instead, once per key, only where its pointer equals
1253
- * the signed `payload_hash` of a same-key record whose binding is
1254
- * undetermined: exactly the missing link. Nothing else is excused.
869
+ * A claim covers every record whose own signed linkage names the key, under
870
+ * every producing key; adapter metadata linkage is never read. In bundle order:
871
+ * a record after the first root is `chain_second_root`; a second successor of
872
+ * one predecessor is `chain_fork`; a pointer to no record of its own chain is
873
+ * `chain_link_dangling`; a claimed key with no record is `chain_claimed_absent`.
874
+ * A dangling link is excused as the note `chain_claim_withheld` only where its
875
+ * pointer equals the `payload_hash` of a same-key record with an undetermined
876
+ * binding.
1255
877
  */
1256
878
  function chainClaimFindings(
1257
879
  claimed: string[],
@@ -1300,43 +922,10 @@ function chainClaimFindings(
1300
922
  return { deviations, notes };
1301
923
  }
1302
924
 
1303
- // ---- Presentation (optional) -------------------------------------------------
1304
-
1305
- /**
1306
- * The house wording for one deviation.
1307
- *
1308
- * Offered, not imposed, exactly as `renderCause` is: what must not vary
1309
- * between surfaces is the verdict and the set of deviations, and those come
1310
- * from `checkDeclaration`. Both kernels return this same text for the same
1311
- * deviation, and `cross-kernel/run_cli.sh` holds the two commands to it.
1312
- */
1313
925
  /**
1314
- * One line a surface can print beside the pack verdict: what the pack claimed
1315
- * to hold, and whether it does.
1316
- *
1317
- * Both kernels' commands print this sentence, so a reader who runs
1318
- * `sanning-proof verify` and `npx @sanning/proof verify` over one pack reads
1319
- * one sentence rather than two that differ by a word.
1320
- *
1321
- * `partial` is spelled out rather than printed as a word, because "partial" on
1322
- * its own beside a green pack reads as a hedge. What it means is that nobody
1323
- * said what the pack was meant to contain, and a reader deciding whether this
1324
- * is a full hand-over needs that in words.
1325
- */
1326
- /**
1327
- * What a pack's declaration CLAIMS, for a reader one layer out.
1328
- *
1329
- * `checkDeclaration` answers "does this pack agree with its own claim". A case
1330
- * reader (`./case.ts`, anchor#198) needs a different thing from the same
1331
- * object: which records the holder claimed as a hand-off side at all, so that
1332
- * a conformant hand-off record the holder claimed NOWHERE is a named finding
1333
- * rather than a silence. That is the door proof#137 ratified open at this
1334
- * layer: `handoffs[]` MAY be empty, so rule 2 cannot reach a declared record
1335
- * and rule 7 cannot reach an unclaimed one.
1336
- *
1337
- * It is read HERE, from the same two functions `checkDeclaration` reads,
1338
- * rather than restated in the case layer. A second reader of one object drifts
1339
- * from the first, and a looser copy blesses a claim the real one rejects.
926
+ * What a pack's declaration claims, for `./case.ts`: which records the holder
927
+ * claimed as a hand-off side, so a hand-off record claimed nowhere is a named
928
+ * finding. Read from the same functions `checkDeclaration` uses.
1340
929
  */
1341
930
  export interface DeclarationClaims {
1342
931
  /**
@@ -1345,37 +934,23 @@ export interface DeclarationClaims {
1345
934
  */
1346
935
  present: boolean;
1347
936
  /**
1348
- * Whether this reader could read the claim as one of `DECLARATION_VERSIONS` AND the
1349
- * object satisfies 5.4.1. `false` covers an absent declaration, an unknown
1350
- * version and a malformed object, and a case reader must not read any of the
1351
- * three as "the holder claimed nothing to check".
937
+ * Whether the claim reads as a known version and satisfies §5.4.1. `false`
938
+ * never means "the holder claimed nothing to check".
1352
939
  */
1353
940
  readable: boolean;
1354
941
  /** The case the declaration names, where it names one. */
1355
942
  caseId: string | null;
1356
- /**
1357
- * Every `event_id` a `handoffs[]` entry names on either side, in declaration
1358
- * order, with no duplicates. Empty where the holder claimed no pair, which
1359
- * is legal and is exactly the shape a case reader has to name.
1360
- */
943
+ /** Every `event_id` a `handoffs[]` entry names, in order, without duplicates. */
1361
944
  claimedSides: string[];
1362
- /**
1363
- * The version the claim was read as, where `readable`; else `null`. The case
1364
- * reader's claim check is v1's alone (5.5.1 v1.20): a v2 pack's claim check
1365
- * is rule 9, which the declaration check already ran.
1366
- */
945
+ /** The version the claim was read as, where `readable`; else `null`. */
1367
946
  version: string | null;
1368
- /** Every record a READABLE v2 declaration signs a role for. Empty for v1. */
947
+ /** Every record a readable v2 or v3 declaration signs a role for. */
1369
948
  roles: { eventId: string; role: DeclarationRole }[];
1370
949
  }
1371
950
 
1372
951
  /**
1373
- * Read the claim a pack's declaration makes, without grading the pack.
1374
- *
1375
- * Deliberately total: a bundle this reader cannot read comes back with
1376
- * `readable: false` rather than throwing, because a case reader grades what it
1377
- * was handed and a reader that throws on a hostile pack cannot report that the
1378
- * pack is hostile.
952
+ * Read the claim a pack's declaration makes, without grading the pack. Total:
953
+ * never throws, so a hostile pack is reported rather than crashing the reader.
1379
954
  */
1380
955
  export function declarationClaims(bundle: unknown): DeclarationClaims {
1381
956
  const none: DeclarationClaims = {
@@ -1413,55 +988,47 @@ export function declarationClaims(bundle: unknown): DeclarationClaims {
1413
988
  };
1414
989
  }
1415
990
 
991
+ /**
992
+ * One line a surface can print beside the pack verdict. Both kernels print the
993
+ * same sentence.
994
+ */
1416
995
  export function renderDeclarationSummary(check: DeclarationCheck): string {
1417
996
  if (!check.present) {
1418
- return "this pack does not say what it holds, so whether it is complete is UNKNOWN";
997
+ return "this pack does not say what it holds, so whether it is complete is unknown";
1419
998
  }
1420
999
  if (check.verdict === "failed") {
1421
- // READ BEFORE THE CONTENT-FINDINGS BRANCH BELOW. A failing check always
1422
- // wins (section 5.4.3: a pack with one deviation and one content-dependent
1423
- // finding is `failed`, and the finding is still named), so a pack that
1424
- // deviates gets the deviating sentence even when it also carries findings.
1425
- // Ordered the other way round, a pack holding both printed "this pack
1426
- // agrees with its own declaration everywhere its signature reaches" with
1427
- // its own named deviations listed underneath, which is the reading this
1428
- // section exists to prevent: a deviation surfacing as something a reader
1429
- // is left to interpret.
1000
+ // Before the content-findings branch: a deviation always wins.
1430
1001
  if (check.contentFindings.length > 0) {
1431
- // BOTH counts, because the two facts are about different records and a
1432
- // reader shown one of them would look in the wrong place.
1002
+ // Both counts: the two facts are about different records.
1433
1003
  return (
1434
- `this pack DEVIATES from its own declaration, in ${check.deviations.length} way(s), ` +
1004
+ `this pack deviates from its own declaration, in ${check.deviations.length} way(s), ` +
1435
1005
  `and in ${check.contentFindings.length} further way(s) it does not line up with ` +
1436
1006
  "the unsigned content handed in beside it, which anyone could have supplied"
1437
1007
  );
1438
1008
  }
1439
- return `this pack DEVIATES from its own declaration, in ${check.deviations.length} way(s)`;
1009
+ return `this pack deviates from its own declaration, in ${check.deviations.length} way(s)`;
1440
1010
  }
1441
1011
  if (check.contentFindings.length > 0) {
1442
- // Read before the `partial` branch below, because both reach this verdict
1443
- // and they are two different sentences. This one says what the reader
1444
- // could not establish; the one below says the reader could not read the
1445
- // claim at all.
1012
+ // Before the `incomplete` branch below: this says what the reader could not
1013
+ // establish, that one says the claim could not be read.
1446
1014
  return (
1447
1015
  "this pack agrees with its own declaration everywhere its signature reaches, " +
1448
1016
  `and in ${check.contentFindings.length} way(s) it does not line up with the ` +
1449
1017
  "unsigned content handed in beside it, which anyone could have supplied"
1450
1018
  );
1451
1019
  }
1452
- if (check.verdict === "partial" && (check.chains ?? []).length > 0) {
1453
- // Read before the branch below: the declaration WAS read, and its chain
1454
- // claim was not checked because the caller gave no reading.
1020
+ if (check.verdict === "incomplete" && (check.chains ?? []).length > 0) {
1021
+ // The declaration was read; its chain claim was not checked.
1455
1022
  return (
1456
1023
  "this pack holds what it declares, and says its chains are complete " +
1457
1024
  "and in order, which this verifier did not check, so whether they " +
1458
- "are is UNKNOWN"
1025
+ "are is unknown"
1459
1026
  );
1460
1027
  }
1461
- if (check.verdict === "partial") {
1028
+ if (check.verdict === "incomplete") {
1462
1029
  return (
1463
1030
  "this pack says what it holds in a declaration this verifier cannot read, " +
1464
- "so whether it is complete is UNKNOWN"
1031
+ "so whether it is complete is unknown"
1465
1032
  );
1466
1033
  }
1467
1034
  return (
@@ -1470,27 +1037,24 @@ export function renderDeclarationSummary(check: DeclarationCheck): string {
1470
1037
  );
1471
1038
  }
1472
1039
 
1473
- /**
1474
- * The house wording for one note. Offered, not imposed, as `renderDeviation`
1475
- * is.
1476
- */
1040
+ /** The house wording for one note. */
1477
1041
  export function renderNote(note: DeclarationNote): string {
1478
1042
  switch (note.kind) {
1479
1043
  case "disclosure_did_not_travel":
1480
1044
  return (
1481
- `this pack declares ${note.count} record(s) disclosed and supplies no bytes for ` +
1482
- "any of them: the bytes did not travel with the pack, so their content is UNKNOWN"
1045
+ `this pack declares ${note.count} ${note.count === 1 ? "record" : "records"} disclosed and supplies no bytes for ` +
1046
+ "any of them: the bytes did not travel with the pack, so their content is unknown"
1483
1047
  );
1484
1048
  case "pair_record_withheld":
1485
1049
  return (
1486
1050
  `the claimed hand-off at position ${note.pairIndex} names ${note.eventId} on its ` +
1487
1051
  `${note.side} side, whose record bytes this pack withholds, so what that record ` +
1488
- "IS is UNKNOWN"
1052
+ "is, is unknown"
1489
1053
  );
1490
1054
  case "role_step_withheld":
1491
1055
  return (
1492
1056
  `${note.eventId} is signed as a step and this pack withholds its ` +
1493
- "record bytes, so whether it is one is UNKNOWN"
1057
+ "record bytes, so whether it is one is unknown"
1494
1058
  );
1495
1059
  case "other_declaration_held":
1496
1060
  return (
@@ -1502,7 +1066,7 @@ export function renderNote(note: DeclarationNote): string {
1502
1066
  return (
1503
1067
  `${note.eventId} is signed as a declaration and no content for it ` +
1504
1068
  "reached this verifier, so whether it is a declaration other than " +
1505
- "this pack's own is UNKNOWN"
1069
+ "this pack's own is unknown"
1506
1070
  );
1507
1071
  case "chain_claim_withheld":
1508
1072
  return (
@@ -1515,12 +1079,8 @@ export function renderNote(note: DeclarationNote): string {
1515
1079
  }
1516
1080
 
1517
1081
  /**
1518
- * The house wording for one content-dependent finding.
1519
- *
1520
- * EVERY SENTENCE HERE NAMES THE CONTENT AND NOT THE HOLDER. That is the whole
1521
- * point of the kind: the bytes are unsigned and whoever assembled the case
1522
- * chose them, so a sentence that reads as an accusation would put the weaker
1523
- * word beside the stronger claim and an auditor would read the claim.
1082
+ * The house wording for one content finding. Names the content, never the
1083
+ * holder: the bytes are unsigned.
1524
1084
  */
1525
1085
  export function renderContentFinding(finding: DeclarationContentFinding): string {
1526
1086
  switch (finding.kind) {
@@ -1528,17 +1088,20 @@ export function renderContentFinding(finding: DeclarationContentFinding): string
1528
1088
  return (
1529
1089
  `${finding.eventId} is declared disclosed and no bytes for it reached this ` +
1530
1090
  "verifier, from the pack or from beside it, so whether the holder supplied " +
1531
- "them is UNKNOWN"
1091
+ "them is unknown"
1532
1092
  );
1533
1093
  case "content_dependent_disclosure":
1534
1094
  return (
1535
1095
  `${finding.eventId} is declared withheld and bytes for it were handed in ` +
1536
1096
  "beside this pack, which the pack does not carry and nobody signed, so " +
1537
- "whether the holder disclosed them is UNKNOWN"
1097
+ "whether the holder disclosed them is unknown"
1538
1098
  );
1539
1099
  }
1540
1100
  }
1541
1101
 
1102
+ /**
1103
+ * The house wording for one deviation. Both kernels return the same text.
1104
+ */
1542
1105
  export function renderDeviation(deviation: DeclarationDeviation): string {
1543
1106
  switch (deviation.kind) {
1544
1107
  case "declaration_malformed":
@@ -1547,9 +1110,9 @@ export function renderDeviation(deviation: DeclarationDeviation): string {
1547
1110
  `${deviation.version || DECLARATION_VERSION}: ${deviation.reason}`
1548
1111
  );
1549
1112
  case "declared_absent":
1550
- return `${deviation.eventId} is declared and is NOT in this pack`;
1113
+ return `${deviation.eventId} is declared and is not in this pack`;
1551
1114
  case "undeclared_present":
1552
- return `${deviation.eventId} is in this pack and is NOT declared`;
1115
+ return `${deviation.eventId} is in this pack and is not declared`;
1553
1116
  case "withheld_but_disclosed":
1554
1117
  return (
1555
1118
  `${deviation.eventId} is declared withheld and this pack carries its bytes ` +