@actuarial-ts/compliance 0.7.1 → 0.8.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.
@@ -0,0 +1,524 @@
1
+ import {
2
+ BOUNDED_REPLAY_CONTRACT_VERSION,
3
+ canonicalJson,
4
+ diagnosticJsonPreflight,
5
+ fnv1a64,
6
+ isDiagnosticPlainRecord,
7
+ isDiagnosticToken,
8
+ snapshotDiagnosticJson,
9
+ type AnalysisStatisticDefinition,
10
+ type CustomizationResourceLimits,
11
+ type JsonValue,
12
+ } from "@actuarial-ts/core";
13
+ import {
14
+ parseCustomizationResourceLimits,
15
+ type CustomizationExternalRecord,
16
+ } from "@actuarial-ts/data";
17
+ import { z } from "zod";
18
+ import { ComplianceError } from "./errors.js";
19
+ import {
20
+ assertComputedDiagnosticArtifactDigest,
21
+ type ComputedDiagnosticArtifactDigest,
22
+ } from "./diagnosticArtifactStream.js";
23
+ import { createSha256 } from "./sha256Stream.js";
24
+ import type { DiagnosticArtifactDigest } from "./diagnosticRun.js";
25
+
26
+ export type BoundedReplayStatistic = Extract<
27
+ AnalysisStatisticDefinition,
28
+ { readonly kind: "sum" | "ratio" | "weighted-mean" | "minimum" | "maximum" | "distinct-count" | "quantile" }
29
+ >;
30
+
31
+ export interface BoundedReplayMeasure {
32
+ readonly id: string;
33
+ readonly unit: string;
34
+ readonly statistic: BoundedReplayStatistic;
35
+ /** Required for exact externally ordered quantiles; counts finite values. */
36
+ readonly orderedPopulationSize?: number;
37
+ }
38
+
39
+ export interface BoundedReplayValue {
40
+ readonly measureId: string;
41
+ readonly value: number | null;
42
+ readonly numerator: number | null;
43
+ readonly denominator: number | null;
44
+ }
45
+
46
+ export interface BoundedReplayVerification {
47
+ readonly contractVersion: typeof BOUNDED_REPLAY_CONTRACT_VERSION;
48
+ readonly runId: string;
49
+ readonly sourceArtifact: DiagnosticArtifactDigest;
50
+ readonly calculationContext?: JsonValue;
51
+ readonly recordCount: number;
52
+ readonly recordDigest: string;
53
+ readonly calculationIdentity: string;
54
+ readonly values: readonly BoundedReplayValue[];
55
+ }
56
+
57
+ declare const verifiedBoundedReplayBrand: unique symbol;
58
+ export type VerifiedBoundedReplayReceipt = BoundedReplayVerification & {
59
+ readonly [verifiedBoundedReplayBrand]: true;
60
+ };
61
+ const verifiedBoundedReplayReceipts = new WeakSet<object>();
62
+
63
+ /** Refuses structurally similar JSON that did not come from independent replay. */
64
+ export function assertVerifiedBoundedReplayReceipt(
65
+ value: unknown,
66
+ ): asserts value is VerifiedBoundedReplayReceipt {
67
+ if (
68
+ value === null ||
69
+ typeof value !== "object" ||
70
+ !verifiedBoundedReplayReceipts.has(value)
71
+ )
72
+ invalid("Value is not an authentic independently verified bounded replay receipt");
73
+ }
74
+
75
+ type MeasureRecord = {
76
+ readonly measures: Readonly<Record<string, number | null>>;
77
+ /** Namespaced stable keys used by externally ordered exact distinct counts. */
78
+ readonly entityKeys?: Readonly<Record<string, string | null>>;
79
+ };
80
+ const encoder = new TextEncoder();
81
+ const tokenSchema = z.string().refine(isDiagnosticToken, "Expected a valid token");
82
+ const finiteSchema = z.number().finite();
83
+ const jsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>
84
+ z.union([
85
+ z.null(),
86
+ z.boolean(),
87
+ finiteSchema,
88
+ z.string(),
89
+ z.array(jsonValueSchema),
90
+ z.record(jsonValueSchema),
91
+ ]),
92
+ );
93
+ const sourceArtifactSchema = z.object({
94
+ id: tokenSchema,
95
+ scope: z.enum(["input", "preparation"]),
96
+ assurance: z.literal("sdk-computed"),
97
+ algorithm: z.literal("sha256"),
98
+ value: z.string().regex(/^[0-9a-f]{64}$/),
99
+ byteLength: z.number().int().nonnegative().safe(),
100
+ }).strict();
101
+ const statisticSchema = z.discriminatedUnion("kind", [
102
+ z.object({ kind: z.literal("sum"), measureId: tokenSchema }).strict(),
103
+ z.object({
104
+ kind: z.literal("ratio"),
105
+ numeratorMeasureId: tokenSchema,
106
+ denominatorMeasureId: tokenSchema,
107
+ scale: finiteSchema,
108
+ denominatorRule: z.enum(["positive", "nonzero"]),
109
+ }).strict(),
110
+ z.object({ kind: z.literal("weighted-mean"), measureId: tokenSchema, weightMeasureId: tokenSchema }).strict(),
111
+ z.object({ kind: z.literal("minimum"), measureId: tokenSchema }).strict(),
112
+ z.object({ kind: z.literal("maximum"), measureId: tokenSchema }).strict(),
113
+ z.object({ kind: z.literal("distinct-count"), entity: z.enum(["claim", "claimant", "occurrence", "policy", "coverage"]) }).strict(),
114
+ z.object({ kind: z.literal("quantile"), measureId: tokenSchema, probability: finiteSchema.min(0).max(1), method: z.literal("hf-type-7") }).strict(),
115
+ ]);
116
+ const boundedReplayMeasureSchema: z.ZodType<BoundedReplayMeasure> = z.object({
117
+ id: tokenSchema,
118
+ unit: tokenSchema,
119
+ statistic: statisticSchema,
120
+ orderedPopulationSize: z.number().int().positive().safe().optional(),
121
+ }).strict();
122
+ const replayValueSchema: z.ZodType<BoundedReplayValue> = z.object({
123
+ measureId: tokenSchema,
124
+ value: finiteSchema.nullable(),
125
+ numerator: finiteSchema.nullable(),
126
+ denominator: finiteSchema.nullable(),
127
+ }).strict();
128
+ const headerSchema = z.object({
129
+ type: z.literal("header"),
130
+ contractVersion: z.literal(BOUNDED_REPLAY_CONTRACT_VERSION),
131
+ runId: tokenSchema,
132
+ sourceArtifact: sourceArtifactSchema,
133
+ measures: z.array(boundedReplayMeasureSchema),
134
+ calculationContext: jsonValueSchema.optional(),
135
+ }).strict();
136
+ const measureRecordSchema = z.object({
137
+ measures: z.record(tokenSchema, finiteSchema.nullable()),
138
+ entityKeys: z.record(tokenSchema, z.string().nullable()).optional(),
139
+ }).strict();
140
+ const recordFrameSchema = z.object({
141
+ type: z.literal("record"),
142
+ record: z.object({
143
+ id: tokenSchema,
144
+ partitionKey: z.string(),
145
+ orderKey: z.string(),
146
+ value: measureRecordSchema,
147
+ }).strict(),
148
+ }).strict();
149
+ const trailerSchema = z.object({
150
+ type: z.literal("trailer"),
151
+ recordCount: z.number().int().nonnegative().safe(),
152
+ recordDigest: z.string().regex(/^[0-9a-f]{64}$/),
153
+ calculationIdentity: z.string().regex(/^fnv1a64-jcs-v1:[0-9a-f]{16}$/),
154
+ values: z.array(replayValueSchema),
155
+ }).strict();
156
+
157
+ function invalid(message: string): never {
158
+ throw new ComplianceError("BAD_DIAGNOSTIC_RUN", message, "$.boundedReplay");
159
+ }
160
+
161
+ function validateLimits(limits: unknown): CustomizationResourceLimits {
162
+ try {
163
+ return parseCustomizationResourceLimits(limits);
164
+ } catch (error) {
165
+ invalid(error instanceof Error ? error.message : "Invalid customization resource limits");
166
+ }
167
+ }
168
+
169
+ /** Validates and owns an untrusted bounded replay measure list. */
170
+ export function parseBoundedReplayMeasures(
171
+ value: unknown,
172
+ limits: CustomizationResourceLimits,
173
+ ): readonly BoundedReplayMeasure[] {
174
+ const parsed = z.array(boundedReplayMeasureSchema).safeParse(value);
175
+ if (!parsed.success) invalid(`Bounded replay measures are invalid: ${parsed.error.issues[0]?.message ?? "schema mismatch"}`);
176
+ const measures = parsed.data;
177
+ if (measures.length === 0 || measures.length > limits.maximumOutputValues)
178
+ invalid("Bounded replay measure count is outside maximumOutputValues");
179
+ if (new Set(measures.map((measure) => measure.id)).size !== measures.length)
180
+ invalid("Bounded replay measure IDs must be unique");
181
+ for (const measure of measures) {
182
+ const statistic = measure.statistic;
183
+ if (statistic.kind === "quantile" && (!Number.isSafeInteger(measure.orderedPopulationSize) || measure.orderedPopulationSize! < 1))
184
+ invalid("Bounded replay exact quantile requires positive orderedPopulationSize");
185
+ if (statistic.kind !== "quantile" && measure.orderedPopulationSize !== undefined)
186
+ invalid("orderedPopulationSize applies only to bounded exact quantiles");
187
+ }
188
+ const distinctEntities = new Set(measures.flatMap((measure) => measure.statistic.kind === "distinct-count" ? [measure.statistic.entity] : []));
189
+ if (distinctEntities.size > 1) invalid("One bounded replay may externally order one distinct entity kind");
190
+ const quantileMeasures = new Set(measures.flatMap((measure) => measure.statistic.kind === "quantile" ? [measure.statistic.measureId] : []));
191
+ if (quantileMeasures.size > 1) invalid("One bounded replay may externally order one quantile source measure");
192
+ return snapshotDiagnosticJson(measures);
193
+ }
194
+
195
+ function checkedAdd(left: number, right: number, description: string): number {
196
+ const result = left + right;
197
+ if (!Number.isFinite(result)) invalid(`Bounded replay ${description} overflowed the finite number range`);
198
+ return Object.is(result, -0) ? 0 : result;
199
+ }
200
+
201
+ function checkedProduct(left: number, right: number, description: string): number {
202
+ const result = left * right;
203
+ if (!Number.isFinite(result)) invalid(`Bounded replay ${description} overflowed the finite number range`);
204
+ return Object.is(result, -0) ? 0 : result;
205
+ }
206
+
207
+ function checkedResult(value: number, description: string): number {
208
+ if (!Number.isFinite(value)) invalid(`Bounded replay ${description} overflowed the finite number range`);
209
+ return Object.is(value, -0) ? 0 : value;
210
+ }
211
+
212
+ interface Aggregate {
213
+ readonly definition: BoundedReplayMeasure;
214
+ sum: number;
215
+ numerator: number;
216
+ denominator: number;
217
+ pairs: number;
218
+ minimum: number;
219
+ maximum: number;
220
+ lastDistinctKey: string | null;
221
+ distinctCount: number;
222
+ orderedCount: number;
223
+ lastOrderedValue: number | null;
224
+ lowerOrderedValue: number | null;
225
+ upperOrderedValue: number | null;
226
+ }
227
+
228
+ function aggregates(measures: readonly BoundedReplayMeasure[]): Aggregate[] {
229
+ return measures.map((definition) => ({ definition, sum: 0, numerator: 0, denominator: 0, pairs: 0, minimum: Number.POSITIVE_INFINITY, maximum: Number.NEGATIVE_INFINITY, lastDistinctKey: null, distinctCount: 0, orderedCount: 0, lastOrderedValue: null, lowerOrderedValue: null, upperOrderedValue: null }));
230
+ }
231
+
232
+ function acceptRecord(state: Aggregate[], record: MeasureRecord): void {
233
+ for (const aggregate of state) {
234
+ const statistic = aggregate.definition.statistic;
235
+ if (statistic.kind === "sum") {
236
+ const value = record.measures[statistic.measureId];
237
+ if (typeof value === "number" && Number.isFinite(value)) aggregate.sum = checkedAdd(aggregate.sum, value, "sum");
238
+ } else if (statistic.kind === "ratio") {
239
+ const numerator = record.measures[statistic.numeratorMeasureId];
240
+ const denominator = record.measures[statistic.denominatorMeasureId];
241
+ if (typeof numerator === "number" && Number.isFinite(numerator) && typeof denominator === "number" && Number.isFinite(denominator)) {
242
+ aggregate.numerator = checkedAdd(aggregate.numerator, numerator, "ratio numerator");
243
+ aggregate.denominator = checkedAdd(aggregate.denominator, denominator, "ratio denominator");
244
+ aggregate.pairs += 1;
245
+ }
246
+ } else if (statistic.kind === "weighted-mean") {
247
+ const value = record.measures[statistic.measureId];
248
+ const weight = record.measures[statistic.weightMeasureId];
249
+ if (typeof value === "number" && Number.isFinite(value) && typeof weight === "number" && Number.isFinite(weight)) {
250
+ if (weight < 0) invalid("Bounded weighted mean encountered a negative weight");
251
+ if (weight > 0) {
252
+ aggregate.numerator = checkedAdd(aggregate.numerator, checkedProduct(value, weight, "weighted product"), "weighted numerator");
253
+ aggregate.denominator = checkedAdd(aggregate.denominator, weight, "weight denominator");
254
+ aggregate.pairs += 1;
255
+ }
256
+ }
257
+ } else if (statistic.kind === "minimum" || statistic.kind === "maximum") {
258
+ const value = record.measures[statistic.measureId];
259
+ if (typeof value === "number" && Number.isFinite(value)) {
260
+ aggregate.minimum = Math.min(aggregate.minimum, value);
261
+ aggregate.maximum = Math.max(aggregate.maximum, value);
262
+ aggregate.pairs += 1;
263
+ }
264
+ } else if (statistic.kind === "distinct-count") {
265
+ const distinctKey = record.entityKeys?.[statistic.entity];
266
+ if (distinctKey === undefined) invalid(`Bounded distinct count requires entityKeys.${statistic.entity}`);
267
+ if (distinctKey === null) continue;
268
+ if (aggregate.lastDistinctKey !== null && distinctKey < aggregate.lastDistinctKey)
269
+ invalid("Bounded distinct keys are not in external ascending order");
270
+ if (distinctKey !== aggregate.lastDistinctKey) {
271
+ if (aggregate.distinctCount === Number.MAX_SAFE_INTEGER) invalid("Bounded distinct count exceeds the safe integer range");
272
+ aggregate.distinctCount += 1;
273
+ }
274
+ aggregate.lastDistinctKey = distinctKey;
275
+ } else {
276
+ if (statistic.kind !== "quantile") invalid("Unsupported bounded replay statistic");
277
+ const value = record.measures[statistic.measureId];
278
+ if (typeof value !== "number" || !Number.isFinite(value)) continue;
279
+ if (aggregate.lastOrderedValue !== null && value < aggregate.lastOrderedValue)
280
+ invalid("Bounded quantile values are not in external ascending order");
281
+ const populationSize = aggregate.definition.orderedPopulationSize!;
282
+ const h = (populationSize - 1) * statistic.probability;
283
+ const lower = Math.floor(h);
284
+ const upper = Math.ceil(h);
285
+ if (aggregate.orderedCount === lower) aggregate.lowerOrderedValue = value;
286
+ if (aggregate.orderedCount === upper) aggregate.upperOrderedValue = value;
287
+ if (aggregate.orderedCount === Number.MAX_SAFE_INTEGER) invalid("Bounded quantile count exceeds the safe integer range");
288
+ aggregate.orderedCount += 1;
289
+ aggregate.lastOrderedValue = value;
290
+ }
291
+ }
292
+ }
293
+
294
+ function values(state: readonly Aggregate[]): readonly BoundedReplayValue[] {
295
+ return state.map((aggregate) => {
296
+ const statistic = aggregate.definition.statistic;
297
+ if (statistic.kind === "sum")
298
+ return { measureId: aggregate.definition.id, value: Object.is(aggregate.sum, -0) ? 0 : aggregate.sum, numerator: null, denominator: null };
299
+ if (statistic.kind === "weighted-mean")
300
+ return { measureId: aggregate.definition.id, value: aggregate.denominator > 0 ? checkedResult(aggregate.numerator / aggregate.denominator, "weighted mean") : null, numerator: aggregate.pairs > 0 ? aggregate.numerator : null, denominator: aggregate.pairs > 0 ? aggregate.denominator : null };
301
+ if (statistic.kind === "minimum" || statistic.kind === "maximum") {
302
+ const selected = statistic.kind === "minimum" ? aggregate.minimum : aggregate.maximum;
303
+ return { measureId: aggregate.definition.id, value: aggregate.pairs === 0 ? null : Object.is(selected, -0) ? 0 : selected, numerator: null, denominator: null };
304
+ }
305
+ if (statistic.kind === "distinct-count")
306
+ return { measureId: aggregate.definition.id, value: aggregate.distinctCount, numerator: null, denominator: null };
307
+ if (statistic.kind === "quantile") {
308
+ if (aggregate.orderedCount !== aggregate.definition.orderedPopulationSize)
309
+ invalid(`Bounded quantile received ${aggregate.orderedCount} finite values but orderedPopulationSize is ${aggregate.definition.orderedPopulationSize}`);
310
+ const h = (aggregate.orderedCount - 1) * statistic.probability;
311
+ const fraction = h - Math.floor(h);
312
+ const lower = aggregate.lowerOrderedValue!;
313
+ const upper = aggregate.upperOrderedValue!;
314
+ const interpolated = fraction === 0
315
+ ? lower
316
+ : checkedAdd(
317
+ checkedProduct(lower, 1 - fraction, "quantile interpolation"),
318
+ checkedProduct(upper, fraction, "quantile interpolation"),
319
+ "quantile interpolation",
320
+ );
321
+ return { measureId: aggregate.definition.id, value: interpolated, numerator: null, denominator: null };
322
+ }
323
+ if (statistic.kind !== "ratio") invalid("Unsupported bounded replay statistic");
324
+ const allowed = statistic.denominatorRule === "positive" ? aggregate.denominator > 0 : aggregate.denominator !== 0;
325
+ return {
326
+ measureId: aggregate.definition.id,
327
+ value: aggregate.pairs > 0 && allowed ? checkedResult(checkedResult(aggregate.numerator / aggregate.denominator, "ratio") * statistic.scale, "scaled ratio") : null,
328
+ numerator: aggregate.pairs > 0 ? aggregate.numerator : null,
329
+ denominator: aggregate.pairs > 0 ? aggregate.denominator : null,
330
+ };
331
+ });
332
+ }
333
+
334
+ function line(value: unknown, maximumBytes: number, limitName = "maximumInMemoryBytes"): Uint8Array {
335
+ const bytes = encoder.encode(`${JSON.stringify(value)}\n`);
336
+ if (bytes.byteLength > maximumBytes) invalid(`Bounded replay frame exceeds ${limitName}`);
337
+ return bytes;
338
+ }
339
+
340
+ function throwIfCancelled(signal: AbortSignal | undefined): void {
341
+ if (signal?.aborted) throw new ComplianceError("BAD_DIAGNOSTIC_RUN", "Bounded replay was cancelled", "$.boundedReplay");
342
+ }
343
+
344
+ /** Writes a replay whose retained SDK state is bounded by measures and one record frame. */
345
+ export async function* writeBoundedAnalysisReplay(input: {
346
+ readonly runId: string;
347
+ readonly sourceArtifact: ComputedDiagnosticArtifactDigest;
348
+ readonly measures: readonly BoundedReplayMeasure[];
349
+ /** Optional closed calculation/selection context bound into the receipt identity. */
350
+ readonly calculationContext?: JsonValue;
351
+ readonly records: AsyncIterable<CustomizationExternalRecord<MeasureRecord>> | Iterable<CustomizationExternalRecord<MeasureRecord>>;
352
+ readonly limits: CustomizationResourceLimits;
353
+ readonly signal?: AbortSignal;
354
+ }): AsyncGenerator<Uint8Array> {
355
+ const limits = validateLimits(input.limits);
356
+ const runId = input.runId;
357
+ const sourceArtifactHandle = input.sourceArtifact;
358
+ const records = input.records;
359
+ const signal = input.signal;
360
+ const suppliedContext = input.calculationContext;
361
+ if (!isDiagnosticToken(runId)) invalid("Bounded replay runId is invalid");
362
+ assertComputedDiagnosticArtifactDigest(sourceArtifactHandle);
363
+ const sourceArtifact = snapshotDiagnosticJson(sourceArtifactHandle);
364
+ const measures = parseBoundedReplayMeasures(input.measures, limits);
365
+ if (suppliedContext !== undefined) {
366
+ const issues = diagnosticJsonPreflight(suppliedContext, "input");
367
+ if (issues.length > 0) invalid(issues[0]!.message);
368
+ }
369
+ const calculationContext = suppliedContext === undefined
370
+ ? undefined
371
+ : snapshotDiagnosticJson(suppliedContext);
372
+ const header = snapshotDiagnosticJson({
373
+ type: "header",
374
+ contractVersion: BOUNDED_REPLAY_CONTRACT_VERSION,
375
+ runId,
376
+ sourceArtifact,
377
+ measures,
378
+ ...(calculationContext === undefined ? {} : { calculationContext }),
379
+ });
380
+ yield line(header, limits.maximumInMemoryBytes);
381
+ const state = aggregates(measures);
382
+ const digest = createSha256();
383
+ let recordCount = 0;
384
+ for await (const external of records) {
385
+ throwIfCancelled(signal);
386
+ if (recordCount >= limits.maximumInputRecords) invalid("Bounded replay exceeds maximumInputRecords");
387
+ recordCount += 1;
388
+ const preflight = diagnosticJsonPreflight(external, "input");
389
+ if (preflight.length > 0) invalid(preflight[0]!.message);
390
+ if (!isDiagnosticPlainRecord(external.value) || !isDiagnosticPlainRecord(external.value.measures))
391
+ invalid("Bounded replay records require a measures object");
392
+ const owned = snapshotDiagnosticJson({ type: "record", record: external });
393
+ const parsedRecord = recordFrameSchema.safeParse(owned);
394
+ if (!parsedRecord.success) invalid(`Bounded replay record is invalid: ${parsedRecord.error.issues[0]?.message ?? "schema mismatch"}`);
395
+ const bytes = line(parsedRecord.data, limits.maximumRecordBytes, "maximumRecordBytes");
396
+ digest.update(bytes);
397
+ acceptRecord(state, parsedRecord.data.record.value);
398
+ yield bytes;
399
+ }
400
+ const calculatedValues = values(state);
401
+ const recordDigest = digest.digest();
402
+ const identityValue = {
403
+ contractVersion: BOUNDED_REPLAY_CONTRACT_VERSION,
404
+ runId,
405
+ sourceArtifact,
406
+ measures,
407
+ ...(calculationContext === undefined ? {} : { calculationContext }),
408
+ recordCount,
409
+ recordDigest,
410
+ values: calculatedValues,
411
+ };
412
+ const trailer = snapshotDiagnosticJson({
413
+ type: "trailer",
414
+ recordCount,
415
+ recordDigest,
416
+ calculationIdentity: `fnv1a64-jcs-v1:${fnv1a64(canonicalJson(identityValue))}`,
417
+ values: calculatedValues,
418
+ });
419
+ yield line(trailer, limits.maximumInMemoryBytes);
420
+ }
421
+
422
+ async function* decodedLines(
423
+ chunks: AsyncIterable<Uint8Array> | Iterable<Uint8Array>,
424
+ maximumBytes: number,
425
+ ): AsyncGenerator<{ readonly text: string; readonly bytes: Uint8Array }> {
426
+ const decoder = new TextDecoder("utf-8", { fatal: true });
427
+ let pending = "";
428
+ for await (const chunk of chunks) {
429
+ if (!(chunk instanceof Uint8Array) || chunk.byteLength > maximumBytes) invalid("Invalid or oversized bounded replay chunk");
430
+ pending += decoder.decode(chunk, { stream: true });
431
+ if (encoder.encode(pending).byteLength > maximumBytes) invalid("Bounded replay line exceeds maximumInMemoryBytes");
432
+ let boundary;
433
+ while ((boundary = pending.indexOf("\n")) >= 0) {
434
+ const text = pending.slice(0, boundary);
435
+ pending = pending.slice(boundary + 1);
436
+ yield { text, bytes: encoder.encode(`${text}\n`) };
437
+ }
438
+ }
439
+ pending += decoder.decode();
440
+ if (pending !== "") invalid("Bounded replay ends with an incomplete frame");
441
+ }
442
+
443
+ /** Independently recomputes every bounded result and identity while reading one frame at a time. */
444
+ export async function verifyBoundedAnalysisReplay(input: {
445
+ readonly chunks: AsyncIterable<Uint8Array> | Iterable<Uint8Array>;
446
+ readonly limits: CustomizationResourceLimits;
447
+ /** Authentic handle recomputed from the source bytes being verified. */
448
+ readonly expectedSourceArtifact: ComputedDiagnosticArtifactDigest;
449
+ readonly signal?: AbortSignal;
450
+ }): Promise<VerifiedBoundedReplayReceipt> {
451
+ const limits = validateLimits(input.limits);
452
+ const chunks = input.chunks;
453
+ const signal = input.signal;
454
+ const expectedSourceArtifactHandle = input.expectedSourceArtifact;
455
+ assertComputedDiagnosticArtifactDigest(expectedSourceArtifactHandle);
456
+ const expectedSourceArtifact = snapshotDiagnosticJson(expectedSourceArtifactHandle);
457
+ let header: z.infer<typeof headerSchema> | undefined;
458
+ let trailer: z.infer<typeof trailerSchema> | undefined;
459
+ let state: Aggregate[] = [];
460
+ let recordCount = 0;
461
+ const digest = createSha256();
462
+ for await (const frame of decodedLines(chunks, limits.maximumInMemoryBytes)) {
463
+ throwIfCancelled(signal);
464
+ let parsed: unknown;
465
+ try { parsed = JSON.parse(frame.text); } catch { invalid("Bounded replay frame is not valid JSON"); }
466
+ const preflight = diagnosticJsonPreflight(parsed, "input");
467
+ if (preflight.length > 0 || !isDiagnosticPlainRecord(parsed)) invalid("Bounded replay frame is not plain JSON data");
468
+ if (parsed.type === "header") {
469
+ if (header !== undefined || recordCount > 0 || trailer !== undefined) invalid("Bounded replay header is out of order");
470
+ const result = headerSchema.safeParse(parsed);
471
+ if (!result.success) invalid(`Bounded replay header is invalid: ${result.error.issues[0]?.message ?? "schema mismatch"}`);
472
+ const measures = parseBoundedReplayMeasures(result.data.measures, limits);
473
+ header = snapshotDiagnosticJson(result.data);
474
+ state = aggregates(measures);
475
+ if (canonicalJson(header.sourceArtifact) !== canonicalJson(expectedSourceArtifact)) invalid("Bounded replay source artifact differs from the expected immutable source");
476
+ } else if (parsed.type === "record") {
477
+ if (header === undefined || trailer !== undefined) invalid("Bounded replay record is out of order");
478
+ if (frame.bytes.byteLength > limits.maximumRecordBytes) invalid("Bounded replay record exceeds maximumRecordBytes");
479
+ const result = recordFrameSchema.safeParse(parsed);
480
+ if (!result.success) invalid(`Bounded replay record is invalid: ${result.error.issues[0]?.message ?? "schema mismatch"}`);
481
+ if (recordCount >= limits.maximumInputRecords) invalid("Bounded replay exceeds maximumInputRecords");
482
+ recordCount += 1;
483
+ digest.update(frame.bytes);
484
+ acceptRecord(state, result.data.record.value);
485
+ } else if (parsed.type === "trailer") {
486
+ if (header === undefined || trailer !== undefined) invalid("Bounded replay trailer is out of order");
487
+ const result = trailerSchema.safeParse(parsed);
488
+ if (!result.success) invalid(`Bounded replay trailer is invalid: ${result.error.issues[0]?.message ?? "schema mismatch"}`);
489
+ trailer = snapshotDiagnosticJson(result.data);
490
+ } else invalid("Unknown bounded replay frame type");
491
+ }
492
+ if (header === undefined || trailer === undefined) invalid("Bounded replay is truncated");
493
+ const calculatedValues = values(state);
494
+ const recordDigest = digest.digest();
495
+ const identityValue = {
496
+ contractVersion: BOUNDED_REPLAY_CONTRACT_VERSION,
497
+ runId: header.runId,
498
+ sourceArtifact: header.sourceArtifact,
499
+ measures: header.measures,
500
+ ...(header.calculationContext === undefined
501
+ ? {}
502
+ : { calculationContext: header.calculationContext }),
503
+ recordCount,
504
+ recordDigest,
505
+ values: calculatedValues,
506
+ };
507
+ const calculationIdentity = `fnv1a64-jcs-v1:${fnv1a64(canonicalJson(identityValue))}`;
508
+ if (trailer.recordCount !== recordCount || trailer.recordDigest !== recordDigest || trailer.calculationIdentity !== calculationIdentity || canonicalJson(trailer.values) !== canonicalJson(calculatedValues))
509
+ invalid("Bounded replay verification differs from its recorded trailer");
510
+ const receipt = snapshotDiagnosticJson({
511
+ contractVersion: BOUNDED_REPLAY_CONTRACT_VERSION,
512
+ runId: header.runId,
513
+ sourceArtifact: header.sourceArtifact,
514
+ ...(header.calculationContext === undefined
515
+ ? {}
516
+ : { calculationContext: header.calculationContext }),
517
+ recordCount,
518
+ recordDigest,
519
+ calculationIdentity,
520
+ values: calculatedValues,
521
+ }) as VerifiedBoundedReplayReceipt;
522
+ verifiedBoundedReplayReceipts.add(receipt);
523
+ return receipt;
524
+ }