@sanning/proof 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +30 -29
  2. package/dist/agent-record.d.ts +62 -0
  3. package/dist/agent-record.js +527 -0
  4. package/dist/agent-schemas.generated.d.ts +15 -0
  5. package/dist/agent-schemas.generated.js +5759 -0
  6. package/dist/case-file.d.ts +32 -89
  7. package/dist/case-file.js +41 -93
  8. package/dist/case.d.ts +343 -810
  9. package/dist/case.js +693 -1151
  10. package/dist/chain.d.ts +19 -47
  11. package/dist/chain.js +78 -205
  12. package/dist/claims.d.ts +211 -0
  13. package/dist/claims.js +1081 -0
  14. package/dist/cli.d.ts +45 -68
  15. package/dist/cli.js +618 -1022
  16. package/dist/compose.js +23 -65
  17. package/dist/crypto.d.ts +7 -8
  18. package/dist/crypto.js +60 -147
  19. package/dist/declaration.d.ts +68 -194
  20. package/dist/declaration.js +175 -518
  21. package/dist/environment.d.ts +9 -30
  22. package/dist/environment.js +17 -66
  23. package/dist/evidence.d.ts +119 -160
  24. package/dist/evidence.js +744 -1103
  25. package/dist/exit-codes.d.ts +11 -27
  26. package/dist/exit-codes.js +24 -59
  27. package/dist/file-match.d.ts +33 -79
  28. package/dist/file-match.js +33 -69
  29. package/dist/grading.d.ts +98 -135
  30. package/dist/grading.js +224 -380
  31. package/dist/handoff.d.ts +62 -160
  32. package/dist/handoff.js +153 -393
  33. package/dist/index.d.ts +14 -10
  34. package/dist/index.js +60 -167
  35. package/dist/json-schema.d.ts +29 -0
  36. package/dist/json-schema.js +298 -0
  37. package/dist/merkle.js +16 -28
  38. package/dist/message-parts.d.ts +56 -212
  39. package/dist/message-parts.js +142 -502
  40. package/dist/side-content.d.ts +11 -22
  41. package/dist/side-content.js +22 -65
  42. package/dist/types.d.ts +17 -0
  43. package/dist/types.js +19 -5
  44. package/dist/verifier.d.ts +40 -114
  45. package/dist/verifier.js +196 -535
  46. package/dist/witness-cache.d.ts +24 -45
  47. package/dist/witness-cache.js +38 -81
  48. package/dist/witness-lookup.d.ts +36 -57
  49. package/dist/witness-lookup.js +89 -139
  50. package/dist/zip-reader.d.ts +18 -33
  51. package/dist/zip-reader.js +19 -34
  52. package/package.json +11 -3
  53. package/src/agent-record.ts +608 -0
  54. package/src/agent-schemas.generated.ts +5764 -0
  55. package/src/case-file.ts +45 -109
  56. package/src/case.ts +848 -1322
  57. package/src/chain.ts +85 -224
  58. package/src/claims.ts +1253 -0
  59. package/src/cli.ts +714 -1176
  60. package/src/compose.ts +33 -79
  61. package/src/crypto.ts +60 -147
  62. package/src/declaration.ts +219 -656
  63. package/src/environment.ts +17 -65
  64. package/src/evidence.ts +958 -1381
  65. package/src/exit-codes.ts +24 -63
  66. package/src/file-match.ts +40 -89
  67. package/src/grading.ts +287 -421
  68. package/src/handoff.ts +175 -427
  69. package/src/index.ts +132 -177
  70. package/src/json-schema.ts +295 -0
  71. package/src/merkle.ts +16 -28
  72. package/src/message-parts.ts +155 -533
  73. package/src/side-content.ts +25 -68
  74. package/src/types.ts +55 -36
  75. package/src/verifier.ts +202 -526
  76. package/src/witness-cache.ts +45 -94
  77. package/src/witness-lookup.ts +107 -177
  78. package/src/zip-reader.ts +20 -38
  79. package/vectors/README.md +52 -34
  80. package/vectors/agent-record.json +2175 -0
  81. package/vectors/case-file-index.json +1 -1
  82. package/vectors/case-split.json +2 -2
  83. package/vectors/chain-reading.json +3 -3
  84. package/vectors/file-match.json +285 -148
  85. package/vectors/jcs-reader.json +6 -6
  86. package/vectors/pack-grading.json +102 -25
  87. package/vectors/sandbox-evidence.json +597 -580
  88. package/vectors/stamp-on-chain.json +18 -18
  89. package/vectors/witness-cache.json +49 -49
  90. package/dist/agent-proof.d.ts +0 -28
  91. package/dist/agent-proof.js +0 -226
  92. package/dist/run-adapters.d.ts +0 -10
  93. package/dist/run-adapters.js +0 -35
  94. package/dist/run.d.ts +0 -209
  95. package/dist/run.js +0 -1405
  96. package/src/agent-proof.ts +0 -264
  97. package/src/run-adapters.ts +0 -44
  98. package/src/run.ts +0 -1598
@@ -1,30 +1,18 @@
1
1
  /**
2
- * The case file's index, compared with the drop it describes (A25).
2
+ * A case file's index compared with the drop it describes (evidence-bundle §5.6).
3
3
  *
4
- * The TypeScript half of `src/sanning_proof/case_file.py`. Both kernels are
5
- * held to one table, `vectors/case-file-index.json`, so neither can pass by
6
- * agreeing with the other, and `cross-kernel/run_case_cli.sh` holds the two
7
- * commands to the same `case_file` output.
4
+ * A case file is a ZIP of packs, each under `packs/<ref>/`, plus an index
5
+ * (`contents.json`), `HOW-TO-VERIFY.txt` and an optional `report/` of files no
6
+ * record commits to. Nobody signs it: each pack is byte for byte what its signer
7
+ * produced, and a reader reads the packs, never the file.
8
8
  *
9
- * WHAT A CASE FILE IS (evidence-bundle.md 5.6 v1.20). A ZIP archive holding one
10
- * case's packs, each unpacked under `packs/<ref>/`, an index (`contents.json`),
11
- * the one command that checks them (`HOW-TO-VERIFY.txt`) and an optional
12
- * `report/` of files no record commits to. **Nobody signs it.** Each pack in it
13
- * is byte for byte the pack its signer produced, and a reader reads the packs,
14
- * never the file.
9
+ * Trust boundary: the index is never an input to the case reading (`readCase`).
10
+ * Adding, removing or editing it moves no verdict, exception, note or exit code.
11
+ * A lie in the index is a finding here and nothing more.
15
12
  *
16
- * WHAT THIS MODULE DOES, AND WHAT IT MUST NEVER DO. It compares the index with
17
- * the files the reader was handed and returns findings with NO verdict
18
- * contribution. **The index is never an input to the case reading**
19
- * (`readCase`): adding, removing or editing `contents.json` moves no verdict,
20
- * no exception, no note, no contribution and no exit code. An assembler can lie
21
- * in every member of the index, and each lie is a finding here and nothing
22
- * more; it can leave the index out, which is no finding at all.
23
- *
24
- * Pure: bytes and a file list in, a reading out. No filesystem, no zip, no
25
- * network. How the files were listed and hashed is the caller's.
13
+ * Pure: bytes and a file list in, a reading out. No filesystem, zip or network.
26
14
  */
27
- /** The index's dispatch key (5.6.2). An absent or unknown value makes the index unreadable. */
15
+ /** The index's dispatch key (5.6.2); an absent or unknown value makes the index unreadable. */
28
16
  export declare const CASE_FILE_VERSION = "sanning.case-file/v1";
29
17
  /** The index, at the root of the case file. */
30
18
  export declare const CASE_FILE_INDEX_NAME = "contents.json";
@@ -32,32 +20,16 @@ export declare const CASE_FILE_INDEX_NAME = "contents.json";
32
20
  export declare const HOW_TO_VERIFY_NAME = "HOW-TO-VERIFY.txt";
33
21
  /** The first line of `report/README.txt`, exactly (5.6.1). */
34
22
  export declare const REPORT_README_FIRST_LINE = "Not evidence: no record in this case file commits to the files in this folder.";
35
- /**
36
- * How deep `[` and `{` may nest in the index before it is refused unread. The
37
- * same limit `bundle.json` is read under, so a hostile index cannot make one
38
- * kernel's parser recurse where the other's does not.
39
- */
23
+ /** How deep `[` and `{` may nest in the index before it is refused unread (the limit `bundle.json` is read under). */
40
24
  export declare const MAX_INDEX_DEPTH = 512;
41
- /**
42
- * Every finding this comparison can report, in the order it reports them
43
- * (5.6.3). None of them is a verdict word and none contributes to one.
44
- */
25
+ /** Every finding this comparison can report, in report order (5.6.3). None is a verdict word. */
45
26
  export declare const CASE_FILE_FINDING_KINDS: readonly ["case_index_unreadable", "case_index_pack_missing", "case_index_pack_unlisted", "case_index_file_missing", "case_index_file_unlisted", "case_index_file_differs", "case_index_file_too_large", "case_index_names_absent"];
46
27
  export type CaseFileFindingKind = (typeof CASE_FILE_FINDING_KINDS)[number];
47
- /**
48
- * Whether `ref` is a legal pack folder name in a case file. For a producer; a
49
- * reader takes whatever folders it finds and names them.
50
- */
28
+ /** Whether `ref` is a legal pack folder name. For producers; a reader takes whatever folders it finds. */
51
29
  export declare function isPackRef(ref: unknown): boolean;
52
- /**
53
- * `HOW-TO-VERIFY.txt`, exactly (5.6.4): ASCII, LF line endings, with `name` the
54
- * case file's name without `.zip`.
55
- */
30
+ /** `HOW-TO-VERIFY.txt`, exactly (5.6.4): ASCII, LF endings; `name` is the case file's name without `.zip`. */
56
31
  export declare function howToVerifyText(name: string): string;
57
- /**
58
- * One way the index and the drop disagree, as data. What the index SAYS
59
- * against what the folder HOLDS, and never a statement about any party.
60
- */
32
+ /** One way the index and the folder disagree: what the index says against what the folder holds, never a statement about a party. */
61
33
  export interface CaseFileFinding {
62
34
  kind: CaseFileFindingKind;
63
35
  /** The pack folder name, for the two pack kinds. */
@@ -68,79 +40,50 @@ export interface CaseFileFinding {
68
40
  listedSha256?: string;
69
41
  /** What the file in the folder hashes to, for `case_index_file_differs`. */
70
42
  sha256?: string;
71
- /**
72
- * Why the index is unreadable, or the assembler's reason a pack is absent,
73
- * for `case_index_unreadable` and `case_index_names_absent`.
74
- */
43
+ /** Why the index is unreadable, or the assembler's reason a pack is absent. */
75
44
  reason?: string;
76
- /**
77
- * The key the assembler says the absent pack would be signed by, as the index
78
- * writes it, for `case_index_names_absent`.
79
- */
45
+ /** The key the assembler says the absent pack is signed by, for `case_index_names_absent`. */
80
46
  publicKey?: string;
81
47
  }
82
48
  export interface CaseFileIndexReading {
83
49
  /** `absent` (no index was handed in), `unreadable` or `read`. */
84
50
  index: "absent" | "unreadable" | "read";
85
- /**
86
- * The index's `case_id`, a label the assembler wrote and nothing is compared
87
- * with, or `null` where the index was not read.
88
- */
51
+ /** The index's `case_id`, a label nothing is compared with; `null` where the index was not read. */
89
52
  caseId: string | null;
90
53
  findings: CaseFileFinding[];
91
54
  }
92
55
  /**
93
56
  * Whether `[` and `{` outside strings ever nest deeper than `limit`. One pass,
94
- * no recursion; the same scan as `json_depth_exceeds` in the Python twin, so
95
- * both kernels refuse the same text. Given bytes, it reads each byte as the
96
- * character of that code, which is the same answer for UTF-8: the six
97
- * characters it reads are single bytes and no multi-byte sequence holds one.
98
- * The CLI's `bundle.json` reader uses it too.
57
+ * no recursion. Bytes are read as character codes, which is exact for UTF-8
58
+ * because the six characters it reads never occur inside a multi-byte sequence.
99
59
  */
100
60
  export declare function jsonDepthExceeds(text: string | Uint8Array, limit: number): boolean;
101
- /**
102
- * The reading of an index the caller could not hold at all (it is over the
103
- * run's whole-read total, or it cannot be read): one `case_index_unreadable`
104
- * finding with the caller's `reason`, and no comparison. The index is never an
105
- * input to the verdict, so a file that cannot be read keeps nothing from it.
106
- */
61
+ /** The reading of an index the caller could not hold (over the run's read total, or unreadable): one `case_index_unreadable` finding, no comparison. */
107
62
  export declare function unreadableCaseFileIndex(reason: string): CaseFileIndexReading;
108
63
  /**
109
64
  * Every path the index lists (each pack's files and `report`), or `null` where
110
- * the index is unreadable. A caller that charges a budget for hashing charges
111
- * these files first, so a file the index does not list, which needs no digest,
112
- * cannot turn a real mismatch into `too_large`.
65
+ * the index is unreadable. A caller with a hashing budget charges these first,
66
+ * so an unlisted file cannot turn a real mismatch into `too_large`.
113
67
  */
114
68
  export declare function caseFileListedPaths(index: Uint8Array): ReadonlySet<string> | null;
115
69
  /**
116
70
  * Compare a case file's index with the folder it sits at the root of.
117
71
  *
118
72
  * `index` is the bytes of `contents.json`, or `null` where the folder holds
119
- * none. `files` is every regular file in the folder, as `{ path, sha256 }`
120
- * with `path` from the folder's root, `/`-separated, or `{ path, too_large: true }`
121
- * for a file the caller did not hash because it is over its cap (reported as
122
- * `case_index_file_too_large` where the index lists it, never as a match or as
123
- * absent); only the files under
124
- * `packs/` and `report/` are compared, so a caller may list the rest or not. A
125
- * folder with no index is a folder of packs, with no finding.
126
- *
127
- * Findings come in the order of `CASE_FILE_FINDING_KINDS`, and within a kind in
128
- * the order of their `ref` or `path` by UTF-8 bytes (the order a caller charges
129
- * its hashing budget in is another, UTF-16 code units: evidence-bundle 5.6.3);
130
- * `case_index_names_absent`
131
- * is in the index's own order, one per `absent[]` entry. An unreadable index is
132
- * one finding and no other comparison runs.
73
+ * none (a folder of packs, no finding). `files` is every regular file, as
74
+ * `{ path, sha256 }` with a `/`-separated path from the root, or
75
+ * `{ path, too_large: true }` for a file left unhashed over the caller's cap
76
+ * (reported as `case_index_file_too_large` where listed). Only files under
77
+ * `packs/` and `report/` are compared.
133
78
  *
134
- * Nothing here is a verdict, and nothing here is ever an input to `readCase`.
79
+ * Findings follow `CASE_FILE_FINDING_KINDS`, and within a kind sort by UTF-8
80
+ * bytes of `ref` or `path` (5.6.3); `case_index_names_absent` keeps the index's
81
+ * order. An unreadable index is one finding and no other comparison runs.
135
82
  */
136
83
  export declare function readCaseFileIndex(index: Uint8Array | null, files: readonly {
137
84
  path: string;
138
85
  sha256?: string;
139
86
  too_large?: boolean;
140
87
  }[]): CaseFileIndexReading;
141
- /**
142
- * The house wording for one finding: what the index says against what the
143
- * folder holds, and the assembler's own statement for an absent pack. Never a
144
- * sentence about a party. Both kernels return this text.
145
- */
88
+ /** The house wording for one finding. Never a sentence about a party. */
146
89
  export declare function renderCaseFileFinding(finding: CaseFileFinding): string;
package/dist/case-file.js CHANGED
@@ -1,31 +1,19 @@
1
1
  /**
2
- * The case file's index, compared with the drop it describes (A25).
2
+ * A case file's index compared with the drop it describes (evidence-bundle §5.6).
3
3
  *
4
- * The TypeScript half of `src/sanning_proof/case_file.py`. Both kernels are
5
- * held to one table, `vectors/case-file-index.json`, so neither can pass by
6
- * agreeing with the other, and `cross-kernel/run_case_cli.sh` holds the two
7
- * commands to the same `case_file` output.
4
+ * A case file is a ZIP of packs, each under `packs/<ref>/`, plus an index
5
+ * (`contents.json`), `HOW-TO-VERIFY.txt` and an optional `report/` of files no
6
+ * record commits to. Nobody signs it: each pack is byte for byte what its signer
7
+ * produced, and a reader reads the packs, never the file.
8
8
  *
9
- * WHAT A CASE FILE IS (evidence-bundle.md 5.6 v1.20). A ZIP archive holding one
10
- * case's packs, each unpacked under `packs/<ref>/`, an index (`contents.json`),
11
- * the one command that checks them (`HOW-TO-VERIFY.txt`) and an optional
12
- * `report/` of files no record commits to. **Nobody signs it.** Each pack in it
13
- * is byte for byte the pack its signer produced, and a reader reads the packs,
14
- * never the file.
9
+ * Trust boundary: the index is never an input to the case reading (`readCase`).
10
+ * Adding, removing or editing it moves no verdict, exception, note or exit code.
11
+ * A lie in the index is a finding here and nothing more.
15
12
  *
16
- * WHAT THIS MODULE DOES, AND WHAT IT MUST NEVER DO. It compares the index with
17
- * the files the reader was handed and returns findings with NO verdict
18
- * contribution. **The index is never an input to the case reading**
19
- * (`readCase`): adding, removing or editing `contents.json` moves no verdict,
20
- * no exception, no note, no contribution and no exit code. An assembler can lie
21
- * in every member of the index, and each lie is a finding here and nothing
22
- * more; it can leave the index out, which is no finding at all.
23
- *
24
- * Pure: bytes and a file list in, a reading out. No filesystem, no zip, no
25
- * network. How the files were listed and hashed is the caller's.
13
+ * Pure: bytes and a file list in, a reading out. No filesystem, zip or network.
26
14
  */
27
15
  import { DuplicateKeyError, jcsParse } from "./verifier.js";
28
- /** The index's dispatch key (5.6.2). An absent or unknown value makes the index unreadable. */
16
+ /** The index's dispatch key (5.6.2); an absent or unknown value makes the index unreadable. */
29
17
  export const CASE_FILE_VERSION = "sanning.case-file/v1";
30
18
  /** The index, at the root of the case file. */
31
19
  export const CASE_FILE_INDEX_NAME = "contents.json";
@@ -33,16 +21,9 @@ export const CASE_FILE_INDEX_NAME = "contents.json";
33
21
  export const HOW_TO_VERIFY_NAME = "HOW-TO-VERIFY.txt";
34
22
  /** The first line of `report/README.txt`, exactly (5.6.1). */
35
23
  export const REPORT_README_FIRST_LINE = "Not evidence: no record in this case file commits to the files in this folder.";
36
- /**
37
- * How deep `[` and `{` may nest in the index before it is refused unread. The
38
- * same limit `bundle.json` is read under, so a hostile index cannot make one
39
- * kernel's parser recurse where the other's does not.
40
- */
24
+ /** How deep `[` and `{` may nest in the index before it is refused unread (the limit `bundle.json` is read under). */
41
25
  export const MAX_INDEX_DEPTH = 512;
42
- /**
43
- * Every finding this comparison can report, in the order it reports them
44
- * (5.6.3). None of them is a verdict word and none contributes to one.
45
- */
26
+ /** Every finding this comparison can report, in report order (5.6.3). None is a verdict word. */
46
27
  export const CASE_FILE_FINDING_KINDS = [
47
28
  "case_index_unreadable",
48
29
  "case_index_pack_missing",
@@ -53,21 +34,13 @@ export const CASE_FILE_FINDING_KINDS = [
53
34
  "case_index_file_too_large",
54
35
  "case_index_names_absent",
55
36
  ];
56
- // `<ref>`: 1 to 128 characters from A-Z, a-z, 0-9, `.`, `_` and `-`, and not
57
- // `.` or `..` (5.6.1). The class holds no newline, so `$` cannot match before
58
- // one here.
37
+ // 1 to 128 of A-Z a-z 0-9 `.` `_` `-`, not `.` or `..` (5.6.1). No newline in the class, so `$` is safe.
59
38
  const REF = /^[A-Za-z0-9._-]{1,128}$/;
60
- /**
61
- * Whether `ref` is a legal pack folder name in a case file. For a producer; a
62
- * reader takes whatever folders it finds and names them.
63
- */
39
+ /** Whether `ref` is a legal pack folder name. For producers; a reader takes whatever folders it finds. */
64
40
  export function isPackRef(ref) {
65
41
  return typeof ref === "string" && REF.test(ref) && ref !== "." && ref !== "..";
66
42
  }
67
- /**
68
- * `HOW-TO-VERIFY.txt`, exactly (5.6.4): ASCII, LF line endings, with `name` the
69
- * case file's name without `.zip`.
70
- */
43
+ /** `HOW-TO-VERIFY.txt`, exactly (5.6.4): ASCII, LF endings; `name` is the case file's name without `.zip`. */
71
44
  export function howToVerifyText(name) {
72
45
  return ("This case file holds evidence packs. Each pack is signed by its own key.\n" +
73
46
  "Nobody signed this file, contents.json, or anything under report/.\n" +
@@ -81,21 +54,18 @@ export function howToVerifyText(name) {
81
54
  "\n" +
82
55
  ` sanning-proof case ${name}\n` +
83
56
  "\n" +
84
- "Exit codes: 0 verified, 1 failed, 2 not readable or a usage error,\n" +
85
- "3 could not be settled (partial, pending, incomplete, not found), 4 mixed,\n" +
86
- "5 sandbox (records signed environment dev, not production evidence).\n" +
57
+ "Exit codes: 0 verified, 1 failed, 2 incomplete (not everything could be\n" +
58
+ "checked), 3 verified sandbox evidence (records signed environment dev),\n" +
59
+ "4 could not run (a bad flag, or an input that is not a case).\n" +
87
60
  "\n" +
88
61
  "To also look each witness up on the public record, add --gateways <url,url>.\n" +
89
- "The lookup is reported on its own line and never changes the verdict word.\n" +
90
- "With it, a witness that does not match exits 1.\n");
62
+ "The lookup is reported on the witness line. A lookup that does not settle\n" +
63
+ "changes nothing; a witness that does not match fails the case, exit 1.\n");
91
64
  }
92
65
  /**
93
66
  * Whether `[` and `{` outside strings ever nest deeper than `limit`. One pass,
94
- * no recursion; the same scan as `json_depth_exceeds` in the Python twin, so
95
- * both kernels refuse the same text. Given bytes, it reads each byte as the
96
- * character of that code, which is the same answer for UTF-8: the six
97
- * characters it reads are single bytes and no multi-byte sequence holds one.
98
- * The CLI's `bundle.json` reader uses it too.
67
+ * no recursion. Bytes are read as character codes, which is exact for UTF-8
68
+ * because the six characters it reads never occur inside a multi-byte sequence.
99
69
  */
100
70
  export function jsonDepthExceeds(text, limit) {
101
71
  let depth = 0;
@@ -164,13 +134,12 @@ function fileList(value, where) {
164
134
  return out;
165
135
  }
166
136
  /**
167
- * The index, read under the JSON rule 5.4.2 rule 7 pins, and checked for every
168
- * REQUIRED member and its type. JCS is not required and an unknown member is
169
- * ignored (5.6.2). The FIRST problem is the reason.
137
+ * The index, read under the JSON rule of 5.4.2 and checked for every required
138
+ * member and its type. JCS is not required and unknown members are ignored
139
+ * (5.6.2). The first problem found is the reason.
170
140
  */
171
141
  function readIndex(raw) {
172
- // Scanned as BYTES, before any decode, so both kernels refuse the same index
173
- // in the same order whatever it holds.
142
+ // Scanned as bytes, before any decode.
174
143
  if (jsonDepthExceeds(raw, MAX_INDEX_DEPTH)) {
175
144
  throw new Unreadable(`nests deeper than ${MAX_INDEX_DEPTH} levels`);
176
145
  }
@@ -179,8 +148,7 @@ function readIndex(raw) {
179
148
  value = jcsParse(raw);
180
149
  }
181
150
  catch (e) {
182
- // Named, because an index that lists one pack twice under one member name
183
- // reads as whichever copy came last (I-JSON, proof#144).
151
+ // Named: a duplicated member name would otherwise read as whichever copy came last (I-JSON).
184
152
  if (e instanceof DuplicateKeyError)
185
153
  throw new Unreadable(e.message);
186
154
  throw new Unreadable("not readable as JSON");
@@ -220,12 +188,7 @@ function readIndex(raw) {
220
188
  const report = fileList(own(value, "report"), "report");
221
189
  return { caseId, packs: listedPacks, absent: listedAbsent, report };
222
190
  }
223
- /**
224
- * The reading of an index the caller could not hold at all (it is over the
225
- * run's whole-read total, or it cannot be read): one `case_index_unreadable`
226
- * finding with the caller's `reason`, and no comparison. The index is never an
227
- * input to the verdict, so a file that cannot be read keeps nothing from it.
228
- */
191
+ /** The reading of an index the caller could not hold (over the run's read total, or unreadable): one `case_index_unreadable` finding, no comparison. */
229
192
  export function unreadableCaseFileIndex(reason) {
230
193
  return {
231
194
  index: "unreadable",
@@ -235,9 +198,8 @@ export function unreadableCaseFileIndex(reason) {
235
198
  }
236
199
  /**
237
200
  * Every path the index lists (each pack's files and `report`), or `null` where
238
- * the index is unreadable. A caller that charges a budget for hashing charges
239
- * these files first, so a file the index does not list, which needs no digest,
240
- * cannot turn a real mismatch into `too_large`.
201
+ * the index is unreadable. A caller with a hashing budget charges these first,
202
+ * so an unlisted file cannot turn a real mismatch into `too_large`.
241
203
  */
242
204
  export function caseFileListedPaths(index) {
243
205
  let read;
@@ -261,22 +223,15 @@ export function caseFileListedPaths(index) {
261
223
  * Compare a case file's index with the folder it sits at the root of.
262
224
  *
263
225
  * `index` is the bytes of `contents.json`, or `null` where the folder holds
264
- * none. `files` is every regular file in the folder, as `{ path, sha256 }`
265
- * with `path` from the folder's root, `/`-separated, or `{ path, too_large: true }`
266
- * for a file the caller did not hash because it is over its cap (reported as
267
- * `case_index_file_too_large` where the index lists it, never as a match or as
268
- * absent); only the files under
269
- * `packs/` and `report/` are compared, so a caller may list the rest or not. A
270
- * folder with no index is a folder of packs, with no finding.
226
+ * none (a folder of packs, no finding). `files` is every regular file, as
227
+ * `{ path, sha256 }` with a `/`-separated path from the root, or
228
+ * `{ path, too_large: true }` for a file left unhashed over the caller's cap
229
+ * (reported as `case_index_file_too_large` where listed). Only files under
230
+ * `packs/` and `report/` are compared.
271
231
  *
272
- * Findings come in the order of `CASE_FILE_FINDING_KINDS`, and within a kind in
273
- * the order of their `ref` or `path` by UTF-8 bytes (the order a caller charges
274
- * its hashing budget in is another, UTF-16 code units: evidence-bundle 5.6.3);
275
- * `case_index_names_absent`
276
- * is in the index's own order, one per `absent[]` entry. An unreadable index is
277
- * one finding and no other comparison runs.
278
- *
279
- * Nothing here is a verdict, and nothing here is ever an input to `readCase`.
232
+ * Findings follow `CASE_FILE_FINDING_KINDS`, and within a kind sort by UTF-8
233
+ * bytes of `ref` or `path` (5.6.3); `case_index_names_absent` keeps the index's
234
+ * order. An unreadable index is one finding and no other comparison runs.
280
235
  */
281
236
  export function readCaseFileIndex(index, files) {
282
237
  if (index === null || index === undefined) {
@@ -295,9 +250,7 @@ export function readCaseFileIndex(index, files) {
295
250
  findings: [{ kind: "case_index_unreadable", reason: e.reason }],
296
251
  };
297
252
  }
298
- // `null` is a file that is here and was too large to hash (a file entry of
299
- // `{ path, too_large: true }`): neither a match nor absent, reported as
300
- // neither.
253
+ // `null`: present but too large to hash; neither a match nor absent.
301
254
  const here = new Map();
302
255
  for (const entry of files) {
303
256
  const path = isObject(entry) ? own(entry, "path") : undefined;
@@ -311,8 +264,7 @@ export function readCaseFileIndex(index, files) {
311
264
  here.set(path, null);
312
265
  }
313
266
  const listedRefs = new Set(read.packs.map(([ref]) => ref));
314
- // The first listing of a path wins, in index order: packs in order, each
315
- // pack's files in order, then report/. A path listed twice is compared once.
267
+ // The first listing of a path wins (packs in order, then report/).
316
268
  const listed = new Map();
317
269
  for (const [, packFiles] of read.packs) {
318
270
  for (const [path, digest] of packFiles)
@@ -376,11 +328,7 @@ export function readCaseFileIndex(index, files) {
376
328
  }
377
329
  return { index: "read", caseId: read.caseId, findings };
378
330
  }
379
- /**
380
- * The house wording for one finding: what the index says against what the
381
- * folder holds, and the assembler's own statement for an absent pack. Never a
382
- * sentence about a party. Both kernels return this text.
383
- */
331
+ /** The house wording for one finding. Never a sentence about a party. */
384
332
  export function renderCaseFileFinding(finding) {
385
333
  switch (finding.kind) {
386
334
  case "case_index_unreadable":