@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.
- package/README.md +30 -29
- package/dist/agent-record.d.ts +62 -0
- package/dist/agent-record.js +527 -0
- package/dist/agent-schemas.generated.d.ts +15 -0
- package/dist/agent-schemas.generated.js +5759 -0
- package/dist/case-file.d.ts +32 -89
- package/dist/case-file.js +41 -93
- package/dist/case.d.ts +343 -810
- package/dist/case.js +693 -1151
- package/dist/chain.d.ts +19 -47
- package/dist/chain.js +78 -205
- package/dist/claims.d.ts +211 -0
- package/dist/claims.js +1081 -0
- package/dist/cli.d.ts +45 -68
- package/dist/cli.js +618 -1022
- package/dist/compose.js +23 -65
- package/dist/crypto.d.ts +7 -8
- package/dist/crypto.js +60 -147
- package/dist/declaration.d.ts +68 -194
- package/dist/declaration.js +175 -518
- package/dist/environment.d.ts +9 -30
- package/dist/environment.js +17 -66
- package/dist/evidence.d.ts +119 -160
- package/dist/evidence.js +744 -1103
- package/dist/exit-codes.d.ts +11 -27
- package/dist/exit-codes.js +24 -59
- package/dist/file-match.d.ts +33 -79
- package/dist/file-match.js +33 -69
- package/dist/grading.d.ts +98 -135
- package/dist/grading.js +224 -380
- package/dist/handoff.d.ts +62 -160
- package/dist/handoff.js +153 -393
- package/dist/index.d.ts +14 -10
- package/dist/index.js +60 -167
- package/dist/json-schema.d.ts +29 -0
- package/dist/json-schema.js +298 -0
- package/dist/merkle.js +16 -28
- package/dist/message-parts.d.ts +56 -212
- package/dist/message-parts.js +142 -502
- package/dist/side-content.d.ts +11 -22
- package/dist/side-content.js +22 -65
- package/dist/types.d.ts +17 -0
- package/dist/types.js +19 -5
- package/dist/verifier.d.ts +40 -114
- package/dist/verifier.js +196 -535
- package/dist/witness-cache.d.ts +24 -45
- package/dist/witness-cache.js +38 -81
- package/dist/witness-lookup.d.ts +36 -57
- package/dist/witness-lookup.js +89 -139
- package/dist/zip-reader.d.ts +18 -33
- package/dist/zip-reader.js +19 -34
- package/package.json +11 -3
- package/src/agent-record.ts +608 -0
- package/src/agent-schemas.generated.ts +5764 -0
- package/src/case-file.ts +45 -109
- package/src/case.ts +848 -1322
- package/src/chain.ts +85 -224
- package/src/claims.ts +1253 -0
- package/src/cli.ts +714 -1176
- package/src/compose.ts +33 -79
- package/src/crypto.ts +60 -147
- package/src/declaration.ts +219 -656
- package/src/environment.ts +17 -65
- package/src/evidence.ts +958 -1381
- package/src/exit-codes.ts +24 -63
- package/src/file-match.ts +40 -89
- package/src/grading.ts +287 -421
- package/src/handoff.ts +175 -427
- package/src/index.ts +132 -177
- package/src/json-schema.ts +295 -0
- package/src/merkle.ts +16 -28
- package/src/message-parts.ts +155 -533
- package/src/side-content.ts +25 -68
- package/src/types.ts +55 -36
- package/src/verifier.ts +202 -526
- package/src/witness-cache.ts +45 -94
- package/src/witness-lookup.ts +107 -177
- package/src/zip-reader.ts +20 -38
- package/vectors/README.md +52 -34
- package/vectors/agent-record.json +2175 -0
- package/vectors/case-file-index.json +1 -1
- package/vectors/case-split.json +2 -2
- package/vectors/chain-reading.json +3 -3
- package/vectors/file-match.json +285 -148
- package/vectors/jcs-reader.json +6 -6
- package/vectors/pack-grading.json +102 -25
- package/vectors/sandbox-evidence.json +597 -580
- package/vectors/stamp-on-chain.json +18 -18
- package/vectors/witness-cache.json +49 -49
- package/dist/agent-proof.d.ts +0 -28
- package/dist/agent-proof.js +0 -226
- package/dist/run-adapters.d.ts +0 -10
- package/dist/run-adapters.js +0 -35
- package/dist/run.d.ts +0 -209
- package/dist/run.js +0 -1405
- package/src/agent-proof.ts +0 -264
- package/src/run-adapters.ts +0 -44
- package/src/run.ts +0 -1598
package/src/exit-codes.ts
CHANGED
|
@@ -1,82 +1,43 @@
|
|
|
1
|
-
// One exit-code table for every verb (
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// an exit code through this one table, for `verify`, `case` and `export` alike,
|
|
5
|
-
// and every verb's `--help` prints `EXIT_CODES_HELP` byte for byte. The twin is
|
|
6
|
-
// `src/sanning_proof/exit_codes.py`; the specification is `evidence-export.md`
|
|
7
|
-
// §5.
|
|
8
|
-
//
|
|
9
|
-
// Until 0.13.0 `verify` exited 0 on `partial` unless a gateway was unreachable,
|
|
10
|
-
// while `case` exited 3 on it, so a CI gate reading `verify`'s exit code read a
|
|
11
|
-
// stamp witnessed by nobody as verified. Every word that means "could not be
|
|
12
|
-
// settled" now exits 3, which is the code's name: undetermined.
|
|
1
|
+
// One exit-code table for every verb (`verify`, `case`, `export`), shared by both
|
|
2
|
+
// commands. Every verb's `--help` prints `EXIT_CODES_HELP` byte for byte
|
|
3
|
+
// (evidence-export.md section 5).
|
|
13
4
|
|
|
14
5
|
export const EXIT_VERIFIED = 0;
|
|
15
6
|
export const EXIT_FAILED = 1;
|
|
16
|
-
export const
|
|
17
|
-
export const EXIT_UNDETERMINED = 3;
|
|
18
|
-
export const EXIT_MIXED = 4;
|
|
7
|
+
export const EXIT_INCOMPLETE = 2;
|
|
19
8
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* that are ALL signed `dev` whose only unsettled witnesses are simulated.
|
|
23
|
-
* Not a verdict word: the verdict stays what the checks established
|
|
24
|
-
* (`sandboxExit`). No flag turns 5 into 0.
|
|
9
|
+
* Not a verdict: the verdict is `verified` and a record is signed `environment: "dev"`
|
|
10
|
+
* (envelope-spec section 2). A failed or incomplete sandbox run keeps 1 or 2.
|
|
25
11
|
*/
|
|
26
|
-
export const EXIT_SANDBOX =
|
|
12
|
+
export const EXIT_SANDBOX = 3;
|
|
13
|
+
/** A bad flag, a missing file, or an input that is not a pack or a case at all. */
|
|
14
|
+
export const EXIT_COULD_NOT_RUN = 4;
|
|
27
15
|
|
|
28
|
-
/**
|
|
29
|
-
* The exit code of each of the eight verdict words (`evidence-bundle.md` §3).
|
|
30
|
-
* `undetermined` is the name of 3, and never a word here.
|
|
31
|
-
*/
|
|
16
|
+
/** The exit code of each verdict (`evidence-bundle.md` section 3). */
|
|
32
17
|
export const EXIT_CODES: Readonly<Record<string, number>> = Object.freeze({
|
|
33
18
|
verified: EXIT_VERIFIED,
|
|
34
19
|
failed: EXIT_FAILED,
|
|
35
|
-
|
|
36
|
-
partial: EXIT_UNDETERMINED,
|
|
37
|
-
pending: EXIT_UNDETERMINED,
|
|
38
|
-
incomplete: EXIT_UNDETERMINED,
|
|
39
|
-
not_found: EXIT_UNDETERMINED,
|
|
40
|
-
mixed: EXIT_MIXED,
|
|
20
|
+
incomplete: EXIT_INCOMPLETE,
|
|
41
21
|
});
|
|
42
22
|
|
|
43
|
-
/** What every verb's help prints
|
|
23
|
+
/** What every verb's help prints. */
|
|
44
24
|
export const EXIT_CODES_HELP = [
|
|
45
25
|
"Exit codes, one table for every verb:",
|
|
46
|
-
" 0 verified
|
|
47
|
-
" 1 failed",
|
|
48
|
-
" 2
|
|
49
|
-
" 3
|
|
50
|
-
" 4
|
|
51
|
-
" 5 sandbox: a record is signed environment dev; would be 0, or only a simulated witness is unchecked",
|
|
26
|
+
" 0 verified: every claim in scope is proven; export: the export was written",
|
|
27
|
+
" 1 failed: a claim is disproven",
|
|
28
|
+
" 2 incomplete: nothing is disproven and a claim is not proven",
|
|
29
|
+
" 3 sandbox: verified, and a record is signed environment dev",
|
|
30
|
+
" 4 could not run: a bad flag, a missing file, or an input that is not a pack or case",
|
|
52
31
|
].join("\n");
|
|
53
32
|
|
|
54
33
|
/**
|
|
55
|
-
* The exit code of a verdict
|
|
56
|
-
*
|
|
34
|
+
* The exit code of a verdict. `verified` over a record signed `dev` is 3. A value outside
|
|
35
|
+
* the three verdicts is 4, never 0.
|
|
57
36
|
*/
|
|
58
|
-
export function exitCodeFor(verdict: unknown): number {
|
|
59
|
-
if (typeof verdict
|
|
60
|
-
return
|
|
37
|
+
export function exitCodeFor(verdict: unknown, environmentMarker: string | null = null): number {
|
|
38
|
+
if (typeof verdict !== "string" || !Object.prototype.hasOwnProperty.call(EXIT_CODES, verdict)) {
|
|
39
|
+
return EXIT_COULD_NOT_RUN;
|
|
61
40
|
}
|
|
62
|
-
return
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* The code a verb exits with once the environment marker is read. 5 where the
|
|
67
|
-
* run would otherwise exit 0 and the pack or case holds a record signed `dev`;
|
|
68
|
-
* 5 where the run is undetermined only because a simulated witness could not
|
|
69
|
-
* be looked up (`onlySimulatedUnsettled`, which the caller works out) and
|
|
70
|
-
* EVERY record is signed `dev`, so a production or mixed pack carrying a
|
|
71
|
-
* simulated witness keeps 3; the code unchanged everywhere else. A failure
|
|
72
|
-
* stays 1, malformed 2 and mixed 4.
|
|
73
|
-
*/
|
|
74
|
-
export function sandboxExit(
|
|
75
|
-
code: number,
|
|
76
|
-
environmentMarker: string | null,
|
|
77
|
-
onlySimulatedUnsettled = false,
|
|
78
|
-
): number {
|
|
79
|
-
if (code === EXIT_VERIFIED && environmentMarker !== null) return EXIT_SANDBOX;
|
|
80
|
-
if (code === EXIT_UNDETERMINED && environmentMarker === "dev" && onlySimulatedUnsettled) return EXIT_SANDBOX;
|
|
81
|
-
return code;
|
|
41
|
+
if (verdict === "verified" && environmentMarker !== null) return EXIT_SANDBOX;
|
|
42
|
+
return EXIT_CODES[verdict]!;
|
|
82
43
|
}
|
package/src/file-match.ts
CHANGED
|
@@ -1,45 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Matches files handed in beside a pack against the commitments its signed
|
|
3
|
+
* records make (evidence-bundle.md §5.7). Conformance: `vectors/file-match.json`.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* - Matching is by SHA-256 of the exact bytes only. A file's name is a label
|
|
6
|
+
* and is never compared with an `event_id`, a part name or a path.
|
|
7
|
+
* - Unmatched is reported, never guessed.
|
|
8
|
+
* - Only what a record's own key signed counts: a verified envelope whose
|
|
9
|
+
* `event_bytes` bind to its `payload_hash`, or a hand-off message whose bytes
|
|
10
|
+
* bind to that record's `message_hash`. No wrapper field is read.
|
|
11
|
+
* - The verdict does not move (`FILE_MATCH_SCOPE`).
|
|
8
12
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* commits to (naming the record, its signer and its `signed_at`), which match
|
|
15
|
-
* no record, and which commitments had no file offered.
|
|
16
|
-
*
|
|
17
|
-
* THE RULES THAT CARRY THE WEIGHT:
|
|
18
|
-
*
|
|
19
|
-
* - By hash, and by nothing else. A file's name is carried through as the
|
|
20
|
-
* caller's label and is never compared with an `event_id`, a part's name or
|
|
21
|
-
* a `logs/` path. There is no layout convention.
|
|
22
|
-
* - Unmatched is reported, never guessed.
|
|
23
|
-
* - Only what a record's own key signed: a verified envelope whose
|
|
24
|
-
* `event_bytes` bind to its `payload_hash`, or a hand-off message whose
|
|
25
|
-
* bytes bind to that record's `message_hash`. No wrapper field is read.
|
|
26
|
-
* - The verdict does not move. A match proves that a signed record in the
|
|
27
|
-
* pack committed to those exact bytes, and nothing more (`FILE_MATCH_SCOPE`).
|
|
28
|
-
*
|
|
29
|
-
* WHAT COUNTS AS A COMMITMENT, in record order and then part order:
|
|
30
|
-
*
|
|
31
|
-
* 1. `record`: a record outside envelope-spec.md 4.1 commits through
|
|
32
|
-
* `event.content_hash`, 64 hex characters in either case.
|
|
33
|
-
* 2. `file_part`: a record 4.1 governs commits to its MESSAGE through
|
|
34
|
-
* `event.message_hash` (5.1, A20), never through a `content_hash` it also
|
|
35
|
-
* writes. Where the message's JCS bytes are in hand (in the body, or in
|
|
36
|
-
* the side content) and bind to it, each file part of that message (A18)
|
|
37
|
-
* commits by its `content_hash`.
|
|
38
|
-
*
|
|
39
|
-
* Side content is read for ONE thing, to find a hand-off message that binds to
|
|
40
|
-
* a signed `message_hash`. It is never an offered file.
|
|
41
|
-
*
|
|
42
|
-
* Pure: a bundle, the files and the optional side content in, a reading out.
|
|
13
|
+
* A record outside envelope-spec.md §4.1 commits through `event.content_hash`.
|
|
14
|
+
* A hand-off record commits to its message through `event.message_hash`, and,
|
|
15
|
+
* where the message bytes are in hand and bind, each file part of that message
|
|
16
|
+
* commits by its `content_hash`. Side content is read only to find such a
|
|
17
|
+
* message; it is never an offered file.
|
|
43
18
|
*/
|
|
44
19
|
|
|
45
20
|
import { hexToBytes, isHexField, sha256Hex } from "./crypto.js";
|
|
@@ -52,7 +27,7 @@ import type { SideContentMap } from "./side-content.js";
|
|
|
52
27
|
import type { Envelope } from "./types.js";
|
|
53
28
|
import { jcsParse, verifyEnvelope } from "./verifier.js";
|
|
54
29
|
|
|
55
|
-
/** The
|
|
30
|
+
/** The scope sentence every surface prints with the reading (§5.7). */
|
|
56
31
|
export const FILE_MATCH_SCOPE =
|
|
57
32
|
"A match proves that a signed record in the pack committed to these exact " +
|
|
58
33
|
"bytes, and nothing more.";
|
|
@@ -71,9 +46,8 @@ export const NOT_READ_REASONS = [
|
|
|
71
46
|
export type NotReadReason = (typeof NOT_READ_REASONS)[number];
|
|
72
47
|
|
|
73
48
|
/**
|
|
74
|
-
* Why a file was
|
|
75
|
-
*
|
|
76
|
-
* skipped, so "no match" is never read as "no file was there".
|
|
49
|
+
* Why a found file was not hashed (§5.7.5). A skipped file is listed, never
|
|
50
|
+
* dropped, so "no match" is never read as "no file was there".
|
|
77
51
|
*/
|
|
78
52
|
export const SKIPPED_REASONS = [
|
|
79
53
|
"symbolic_link",
|
|
@@ -90,7 +64,6 @@ const SKIPPED_TEXT: Record<SkippedReason, string> = {
|
|
|
90
64
|
not_regular: "not a regular file: not read",
|
|
91
65
|
unreadable: "could not be read",
|
|
92
66
|
too_large: "past the 1 GiB read from folders in one run: not read",
|
|
93
|
-
// Followed by the reader's own words, `renderSkipped`.
|
|
94
67
|
refused: "a .zip entry the reader refused",
|
|
95
68
|
};
|
|
96
69
|
|
|
@@ -105,9 +78,8 @@ const REASON_TEXT: Record<NotReadReason, string> = {
|
|
|
105
78
|
message_withheld: "its message is not in hand, so its file parts cannot be read",
|
|
106
79
|
};
|
|
107
80
|
|
|
108
|
-
// The
|
|
109
|
-
|
|
110
|
-
const RECORD_PROFILES = ["sanning.agent/v1", "sanning.events/v2"] as const;
|
|
81
|
+
// The accepted profiles minus the quarantined `sanning.mlflow/v1`.
|
|
82
|
+
const RECORD_PROFILES = ["sanning.events/v2"] as const;
|
|
111
83
|
|
|
112
84
|
/** One commitment a signed record makes to a file's bytes. */
|
|
113
85
|
export interface FileCommitment {
|
|
@@ -118,13 +90,9 @@ export interface FileCommitment {
|
|
|
118
90
|
contentHash: string;
|
|
119
91
|
signer: string;
|
|
120
92
|
signedAt: string | null;
|
|
121
|
-
/**
|
|
122
|
-
* The pack, by its position in the packs `matchCaseFiles` was handed (the
|
|
123
|
-
* same position the case reading's `packIndex` names), or `null` from
|
|
124
|
-
* `matchFiles`, which reads one pack (A43).
|
|
125
|
-
*/
|
|
93
|
+
/** Position in the packs `matchCaseFiles` was handed, or `null` from `matchFiles`. */
|
|
126
94
|
packIndex: number | null;
|
|
127
|
-
/**
|
|
95
|
+
/** Position in the events the pack presents. */
|
|
128
96
|
recordIndex: number;
|
|
129
97
|
}
|
|
130
98
|
|
|
@@ -143,7 +111,7 @@ export interface UnmatchedFile {
|
|
|
143
111
|
export interface SkippedFile {
|
|
144
112
|
name: string;
|
|
145
113
|
reason: SkippedReason;
|
|
146
|
-
/** The reader's own words, for a `.zip` entry
|
|
114
|
+
/** The reader's own words, for a refused `.zip` entry. */
|
|
147
115
|
detail?: string;
|
|
148
116
|
}
|
|
149
117
|
|
|
@@ -151,12 +119,8 @@ export interface NotRead {
|
|
|
151
119
|
packRef: string | null;
|
|
152
120
|
eventId: string | null;
|
|
153
121
|
reason: NotReadReason;
|
|
154
|
-
/** As on `FileCommitment` (A43). */
|
|
155
122
|
packIndex: number | null;
|
|
156
|
-
/**
|
|
157
|
-
* As on `FileCommitment`, or `null` where the pack presents no events at all
|
|
158
|
-
* (`no_records`).
|
|
159
|
-
*/
|
|
123
|
+
/** `null` where the pack presents no events (`no_records`). */
|
|
160
124
|
recordIndex: number | null;
|
|
161
125
|
}
|
|
162
126
|
|
|
@@ -167,18 +131,13 @@ export interface FileMatchReading {
|
|
|
167
131
|
notOffered: FileCommitment[];
|
|
168
132
|
notRead: NotRead[];
|
|
169
133
|
scope: string;
|
|
170
|
-
/**
|
|
171
|
-
* Files found and not hashed. The matching functions leave it empty: it is
|
|
172
|
-
* the caller's, because only a caller that reads a folder knows a link.
|
|
173
|
-
*/
|
|
134
|
+
/** Files found and not hashed. Filled by the caller, which alone reads folders. */
|
|
174
135
|
skipped?: SkippedFile[];
|
|
175
136
|
}
|
|
176
137
|
|
|
177
138
|
/**
|
|
178
|
-
* A file handed in beside a pack. `name` is a label
|
|
179
|
-
*
|
|
180
|
-
* caller has already hashed it: a command streams a file of any size rather
|
|
181
|
-
* than hold it.
|
|
139
|
+
* A file handed in beside a pack. `name` is a label, never compared. Give
|
|
140
|
+
* `bytes`, or `sha256` (64 lowercase hex) when the caller streamed the file.
|
|
182
141
|
*/
|
|
183
142
|
export type OfferedFile =
|
|
184
143
|
| { name: string; bytes: Uint8Array; sha256?: undefined }
|
|
@@ -203,8 +162,8 @@ function own(o: Record<string, unknown>, key: string): unknown {
|
|
|
203
162
|
return Object.prototype.hasOwnProperty.call(o, key) ? o[key] : undefined;
|
|
204
163
|
}
|
|
205
164
|
|
|
206
|
-
// The
|
|
207
|
-
//
|
|
165
|
+
// The key and signature must be hex at their lengths, as every body verifier
|
|
166
|
+
// requires inside a bundle.
|
|
208
167
|
function envelopeSpelled(envelope: Record<string, unknown>): boolean {
|
|
209
168
|
for (const [name, nibbles] of [
|
|
210
169
|
["public_key", 64],
|
|
@@ -369,8 +328,8 @@ async function readPack(
|
|
|
369
328
|
return { commitments, notRead };
|
|
370
329
|
}
|
|
371
330
|
|
|
372
|
-
// A malformed
|
|
373
|
-
//
|
|
331
|
+
// A malformed entry is the caller's mistake, not the evidence's, so it throws
|
|
332
|
+
// rather than reading as an unmatched file.
|
|
374
333
|
async function offeredDigests(files: readonly OfferedFile[]): Promise<[string, string][]> {
|
|
375
334
|
const out: [string, string][] = [];
|
|
376
335
|
for (const entry of files) {
|
|
@@ -427,10 +386,8 @@ async function match(
|
|
|
427
386
|
}
|
|
428
387
|
|
|
429
388
|
/**
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
* find a hand-off message that binds to its record's `message_hash`; it is
|
|
433
|
-
* never an offered file.
|
|
389
|
+
* Matches each offered file against one pack's signed commitments. `content`
|
|
390
|
+
* is the pack's side content, read only to bind hand-off messages.
|
|
434
391
|
*/
|
|
435
392
|
export async function matchFiles(
|
|
436
393
|
bundle: unknown,
|
|
@@ -441,7 +398,7 @@ export async function matchFiles(
|
|
|
441
398
|
return match(commitments, notRead, files);
|
|
442
399
|
}
|
|
443
400
|
|
|
444
|
-
/**
|
|
401
|
+
/** `matchFiles` over every pack of a case. */
|
|
445
402
|
export async function matchCaseFiles(
|
|
446
403
|
packs: readonly FileMatchPack[],
|
|
447
404
|
files: readonly OfferedFile[],
|
|
@@ -472,7 +429,7 @@ function commitmentJson(c: FileCommitment): Record<string, unknown> {
|
|
|
472
429
|
};
|
|
473
430
|
}
|
|
474
431
|
|
|
475
|
-
/** The reading as the `files` member of `--json
|
|
432
|
+
/** The reading as the `files` member of `--json`. */
|
|
476
433
|
export function fileMatchToJson(reading: FileMatchReading): Record<string, unknown> {
|
|
477
434
|
const skipped = reading.skipped ?? [];
|
|
478
435
|
return {
|
|
@@ -492,8 +449,7 @@ export function fileMatchToJson(reading: FileMatchReading): Record<string, unkno
|
|
|
492
449
|
reason: n.reason,
|
|
493
450
|
rendered: renderNotRead(n),
|
|
494
451
|
})),
|
|
495
|
-
//
|
|
496
|
-
// file is the object it was before a folder could be offered (A40).
|
|
452
|
+
// Omitted when empty, so a reading with nothing skipped keeps its shape.
|
|
497
453
|
...(skipped.length > 0
|
|
498
454
|
? {
|
|
499
455
|
skipped: skipped.map((k) => ({
|
|
@@ -535,11 +491,9 @@ function renderNotRead(n: NotRead): string {
|
|
|
535
491
|
|
|
536
492
|
/**
|
|
537
493
|
* The commitments no offered file matched and no disclosed log answers.
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
* and is not returned. Only the text report's count reads this; the reading, and
|
|
542
|
-
* `--json`, still list every commitment no file matched.
|
|
494
|
+
* A record whose own pack discloses bytes hashing to its `content_hash` is
|
|
495
|
+
* accounted for. Only the text report's count reads this; `--json` still lists
|
|
496
|
+
* every unmatched commitment.
|
|
543
497
|
*/
|
|
544
498
|
export async function notAccountedFor(
|
|
545
499
|
reading: FileMatchReading,
|
|
@@ -559,11 +513,8 @@ export async function notAccountedFor(
|
|
|
559
513
|
}
|
|
560
514
|
|
|
561
515
|
/**
|
|
562
|
-
* The text report's lines for the reading
|
|
563
|
-
*
|
|
564
|
-
* By default every commitment no file matched is a line. `notAmong` is the case
|
|
565
|
-
* command's report when no `--files` was given: one count line for the recorded
|
|
566
|
-
* files no loose file matched, and no line for each (A41).
|
|
516
|
+
* The text report's lines for the reading. With `notAmong` (the case command
|
|
517
|
+
* with no `--files`), unmatched commitments collapse to one count line.
|
|
567
518
|
*/
|
|
568
519
|
export function renderFileMatch(reading: FileMatchReading, notAmong?: number): string[] {
|
|
569
520
|
const skipped = reading.skipped ?? [];
|