@policyengine/calibration-tree 0.0.0-stage → 0.1.1

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 (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -2
  3. package/package.json +73 -4
  4. package/src/calibration-build-comparison.ts +77 -0
  5. package/src/calibration-comparison-blob.ts +367 -0
  6. package/src/calibration-comparison-bundle.ts +149 -0
  7. package/src/calibration-explorer.ts +202 -0
  8. package/src/calibration-release-locator.ts +353 -0
  9. package/src/calibration-tree-artifact.ts +1974 -0
  10. package/src/calibration-tree-blob.ts +403 -0
  11. package/src/calibration-tree-bundle.ts +569 -0
  12. package/src/calibration-tree-manifest.ts +349 -0
  13. package/src/calibration-tree-publication.ts +399 -0
  14. package/src/calibration-tree-worker.ts +637 -0
  15. package/src/calibration-tree.ts +1284 -0
  16. package/src/chronicle-metadata.ts +42 -0
  17. package/src/countries.ts +355 -0
  18. package/src/geography-labels.ts +59 -0
  19. package/src/hierarchy-catalog-reader.ts +297 -0
  20. package/src/hierarchy-target-reader.ts +243 -0
  21. package/src/historical-weight-summary.ts +53 -0
  22. package/src/latest-artifact.ts +3767 -0
  23. package/src/legacy-target-reader.ts +337 -0
  24. package/src/program-label.ts +64 -0
  25. package/src/publish-calibration-tree.ts +1984 -0
  26. package/src/reforms.ts +224 -0
  27. package/src/release-cut.ts +32 -0
  28. package/src/release-record.ts +277 -0
  29. package/src/source-label.ts +5 -0
  30. package/src/source-labels.ts +71 -0
  31. package/src/staging-artifact.ts +1411 -0
  32. package/src/staging-contract.ts +916 -0
  33. package/src/structured-dimension-reader.ts +137 -0
  34. package/src/structured-target-reader.ts +76 -0
  35. package/src/target-change.ts +421 -0
  36. package/src/target-loss-attribution-manifest.ts +436 -0
  37. package/src/target-loss-attribution.ts +1012 -0
  38. package/src/target-representation.ts +69 -0
  39. package/src/target-surface-matcher.ts +266 -0
  40. package/types/next-fetch-cache.d.ts +8 -0
@@ -0,0 +1,916 @@
1
+ /** Explicit version dispatch and UI normalization for Microcosm staging data. */
2
+
3
+ export type JsonObject = Record<string, unknown>;
4
+
5
+ export class IncompatibleStagingDataError extends Error {
6
+ constructor(message: string) {
7
+ super(`Incompatible staging data: ${message}`);
8
+ this.name = "IncompatibleStagingDataError";
9
+ }
10
+ }
11
+
12
+ // Versions 2 and 3 share these names. Version 3 adds the `blocked` run status
13
+ // with its `block` details, and `failure_class` on failures.
14
+ const STRUCTURED_SCHEMAS = {
15
+ manifest: "microcosm.staging.run-manifest",
16
+ progress: "microcosm.staging.progress",
17
+ calibration: "microcosm.staging.calibration-progress",
18
+ event: "microcosm.staging.event",
19
+ } as const;
20
+
21
+ function objectValue(value: unknown, label: string): JsonObject {
22
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
23
+ throw new IncompatibleStagingDataError(`${label} must be an object.`);
24
+ }
25
+ return value as JsonObject;
26
+ }
27
+
28
+ function stringValue(value: unknown, label: string): string {
29
+ if (typeof value !== "string" || !value.trim()) {
30
+ throw new IncompatibleStagingDataError(`${label} must be a non-empty string.`);
31
+ }
32
+ return value;
33
+ }
34
+
35
+ const RFC_3339_DATE_TIME =
36
+ /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/;
37
+
38
+ function dateTimeValue(value: unknown, label: string): string {
39
+ const dateTime = stringValue(value, label);
40
+ const match = RFC_3339_DATE_TIME.exec(dateTime);
41
+ if (!match) {
42
+ throw new IncompatibleStagingDataError(
43
+ `${label} must be a valid RFC 3339 date-time string.`,
44
+ );
45
+ }
46
+ const year = Number(match[1]);
47
+ const month = Number(match[2]);
48
+ const day = Number(match[3]);
49
+ const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
50
+ const daysInMonth = [
51
+ 31,
52
+ leapYear ? 29 : 28,
53
+ 31,
54
+ 30,
55
+ 31,
56
+ 30,
57
+ 31,
58
+ 31,
59
+ 30,
60
+ 31,
61
+ 30,
62
+ 31,
63
+ ];
64
+
65
+ if (
66
+ day > daysInMonth[month - 1] ||
67
+ !Number.isFinite(Date.parse(dateTime))
68
+ ) {
69
+ throw new IncompatibleStagingDataError(
70
+ `${label} must be a valid RFC 3339 date-time string.`,
71
+ );
72
+ }
73
+ return dateTime;
74
+ }
75
+
76
+ function nullableString(value: unknown, label: string): string | null {
77
+ return value == null ? null : stringValue(value, label);
78
+ }
79
+
80
+ const SAFE_IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
81
+ const SHA_256 = /^[0-9a-f]{64}$/;
82
+
83
+ function assertExactKeys(
84
+ value: JsonObject,
85
+ label: string,
86
+ expectedKeys: readonly string[],
87
+ ): void {
88
+ const expected = new Set(expectedKeys);
89
+ for (const key of expected) {
90
+ if (!Object.prototype.hasOwnProperty.call(value, key)) {
91
+ throw new IncompatibleStagingDataError(`${label} is missing ${key}.`);
92
+ }
93
+ }
94
+ for (const key of Object.keys(value)) {
95
+ if (!expected.has(key)) {
96
+ throw new IncompatibleStagingDataError(`${label} contains unexpected field ${key}.`);
97
+ }
98
+ }
99
+ }
100
+
101
+ function safeIdentifier(value: unknown, label: string): string {
102
+ if (typeof value !== "string" || !SAFE_IDENTIFIER.test(value)) {
103
+ throw new IncompatibleStagingDataError(`${label} must be a safe identifier.`);
104
+ }
105
+ return value;
106
+ }
107
+
108
+ function nullableSafeIdentifier(value: unknown, label: string): string | null {
109
+ return value === null ? null : safeIdentifier(value, label);
110
+ }
111
+
112
+ function enumValue<const T extends string>(
113
+ value: unknown,
114
+ label: string,
115
+ choices: readonly T[],
116
+ ): T {
117
+ if (typeof value !== "string" || !choices.includes(value as T)) {
118
+ throw new IncompatibleStagingDataError(`${label} is unsupported.`);
119
+ }
120
+ return value as T;
121
+ }
122
+
123
+ function booleanValue(value: unknown, label: string): boolean {
124
+ if (typeof value !== "boolean") {
125
+ throw new IncompatibleStagingDataError(`${label} must be boolean.`);
126
+ }
127
+ return value;
128
+ }
129
+
130
+ function integerValue(value: unknown, label: string, minimum = 0): number {
131
+ if (!Number.isInteger(value) || Number(value) < minimum) {
132
+ throw new IncompatibleStagingDataError(
133
+ `${label} must be an integer greater than or equal to ${minimum}.`,
134
+ );
135
+ }
136
+ return Number(value);
137
+ }
138
+
139
+ function nullableInteger(value: unknown, label: string): number | null {
140
+ return value === null ? null : integerValue(value, label);
141
+ }
142
+
143
+ function nullableNumber(value: unknown, label: string): number | null {
144
+ if (value === null) return null;
145
+ if (typeof value !== "number" || !Number.isFinite(value)) {
146
+ throw new IncompatibleStagingDataError(`${label} must be a finite number or null.`);
147
+ }
148
+ return value;
149
+ }
150
+
151
+ function boundedString(
152
+ value: unknown,
153
+ label: string,
154
+ options: { minimum?: number; maximum?: number } = {},
155
+ ): string {
156
+ const minimum = options.minimum ?? 0;
157
+ const maximum = options.maximum ?? Number.POSITIVE_INFINITY;
158
+ if (
159
+ typeof value !== "string" ||
160
+ value.length < minimum ||
161
+ value.length > maximum
162
+ ) {
163
+ throw new IncompatibleStagingDataError(
164
+ `${label} must be a string between ${minimum} and ${maximum} characters.`,
165
+ );
166
+ }
167
+ return value;
168
+ }
169
+
170
+ function nullableBoundedString(
171
+ value: unknown,
172
+ label: string,
173
+ options: { minimum?: number; maximum?: number } = {},
174
+ ): string | null {
175
+ return value === null ? null : boundedString(value, label, options);
176
+ }
177
+
178
+ export type StagingSchemaVersion = 1 | 2 | 3;
179
+
180
+ // Versions 2 and later are the structured contract; version 1 is the US
181
+ // transition layout.
182
+ export function isStructuredStagingDocument(
183
+ document: JsonObject | null | undefined,
184
+ ): boolean {
185
+ return typeof document?.schema_version === "number" && document.schema_version >= 2;
186
+ }
187
+
188
+ function version(
189
+ payload: JsonObject,
190
+ kind: keyof typeof STRUCTURED_SCHEMAS,
191
+ options: { allowUnversionedV1?: boolean } = {},
192
+ ): StagingSchemaVersion {
193
+ if (payload.schema_version === 1) {
194
+ if (payload.schema_name != null) {
195
+ throw new IncompatibleStagingDataError(
196
+ `version 1 ${kind} data must not declare schema_name.`,
197
+ );
198
+ }
199
+ return 1;
200
+ }
201
+ if (payload.schema_version === 2 || payload.schema_version === 3) {
202
+ if (payload.schema_name !== STRUCTURED_SCHEMAS[kind]) {
203
+ throw new IncompatibleStagingDataError(
204
+ `expected ${STRUCTURED_SCHEMAS[kind]} version ${payload.schema_version}, received ${String(payload.schema_name)}.`,
205
+ );
206
+ }
207
+ return payload.schema_version;
208
+ }
209
+ if (options.allowUnversionedV1 && payload.schema_version == null && payload.schema_name == null) {
210
+ return 1;
211
+ }
212
+ throw new IncompatibleStagingDataError(
213
+ `unsupported ${kind} schema version ${String(payload.schema_version)}.`,
214
+ );
215
+ }
216
+
217
+ export interface ParsedStagingRunSummary {
218
+ run_id: string;
219
+ candidate_release_id: string | null;
220
+ release_id: string | null;
221
+ country_code: string | null;
222
+ run_kind: string | null;
223
+ non_release: boolean | null;
224
+ status: string | null;
225
+ stage: string | null;
226
+ started_at: string | null;
227
+ updated_at: string | null;
228
+ progress_path: string;
229
+ run_manifest_path: string;
230
+ schema_version: 1;
231
+ }
232
+
233
+ function optionalString(value: unknown): string | null {
234
+ return typeof value === "string" && value.trim() ? value : null;
235
+ }
236
+
237
+ export function parseStagingRunIndex(value: unknown): ParsedStagingRunSummary[] {
238
+ const payload = objectValue(value, "run index");
239
+ if (payload.schema_version !== 1 || payload.schema_name != null) {
240
+ throw new IncompatibleStagingDataError(
241
+ `run index must use schema version 1, received ${String(payload.schema_version)}.`,
242
+ );
243
+ }
244
+ if (!Array.isArray(payload.runs)) {
245
+ throw new IncompatibleStagingDataError("run index runs must be an array.");
246
+ }
247
+ return payload.runs.map((entry, index) => {
248
+ const row = objectValue(entry, `run index row ${index}`);
249
+ const runId = stringValue(row.run_id, `run index row ${index} run_id`);
250
+ const candidate = nullableString(
251
+ row.candidate_release_id,
252
+ `run index row ${index} candidate_release_id`,
253
+ );
254
+ return {
255
+ run_id: runId,
256
+ candidate_release_id: candidate,
257
+ release_id: null,
258
+ country_code: null,
259
+ run_kind: null,
260
+ non_release: null,
261
+ status: optionalString(row.status),
262
+ stage: optionalString(row.stage),
263
+ started_at: optionalString(row.started_at),
264
+ updated_at: optionalString(row.updated_at),
265
+ progress_path:
266
+ optionalString(row.progress_path) ?? `runs/${runId}/progress.json`,
267
+ run_manifest_path:
268
+ optionalString(row.run_manifest_path) ?? `runs/${runId}/run_manifest.json`,
269
+ schema_version: 1,
270
+ };
271
+ });
272
+ }
273
+
274
+ const ARTIFACT_KINDS = new Set([
275
+ "aggregate_diagnostics",
276
+ "build_metadata",
277
+ "sampling_receipt",
278
+ "validation_summary",
279
+ ]);
280
+
281
+ const RUN_FIELDS = [
282
+ "run_id",
283
+ "country_code",
284
+ "operation_id",
285
+ "pipeline",
286
+ "candidate_id",
287
+ "release_id",
288
+ "run_kind",
289
+ "non_release",
290
+ "started_at",
291
+ "updated_at",
292
+ "status",
293
+ "current_stage",
294
+ ] as const;
295
+
296
+ const DELIVERY_FIELDS = [
297
+ "contract_version",
298
+ "enabled",
299
+ "mode",
300
+ "run_id",
301
+ "configured_repository",
302
+ "upload_attempts",
303
+ "upload_successes",
304
+ "read_back",
305
+ "last_error_code",
306
+ "opt_out_reason",
307
+ ] as const;
308
+
309
+ function parsePipeline(value: unknown): JsonObject {
310
+ const pipeline = objectValue(value, "version 2 pipeline");
311
+ assertExactKeys(pipeline, "version 2 pipeline", ["id", "version"]);
312
+ return {
313
+ id: safeIdentifier(pipeline.id, "pipeline id"),
314
+ version: boundedString(pipeline.version, "pipeline version", { minimum: 1 }),
315
+ };
316
+ }
317
+
318
+ function parseSample(value: unknown): JsonObject | null {
319
+ if (value === null) return null;
320
+ const sample = objectValue(value, "version 2 sample");
321
+ assertExactKeys(sample, "version 2 sample", ["mode"]);
322
+ if (sample.mode !== "full") {
323
+ throw new IncompatibleStagingDataError("version 2 sample mode must be full.");
324
+ }
325
+ return sample;
326
+ }
327
+
328
+ function parseDelivery(value: unknown, envelopeRunId: string): JsonObject {
329
+ const delivery = objectValue(value, "version 2 delivery");
330
+ assertExactKeys(delivery, "version 2 delivery", DELIVERY_FIELDS);
331
+ if (delivery.contract_version !== 2) {
332
+ throw new IncompatibleStagingDataError("delivery contract_version must be 2.");
333
+ }
334
+ const enabled = booleanValue(delivery.enabled, "delivery enabled");
335
+ const mode = enumValue(delivery.mode, "delivery mode", [
336
+ "local_and_remote",
337
+ "local_only",
338
+ "disabled",
339
+ ] as const);
340
+ const runId = nullableSafeIdentifier(delivery.run_id, "delivery run_id");
341
+ const repository = nullableBoundedString(
342
+ delivery.configured_repository,
343
+ "delivery configured_repository",
344
+ { minimum: 1 },
345
+ );
346
+ const attempts = integerValue(delivery.upload_attempts, "delivery upload_attempts");
347
+ const successes = integerValue(
348
+ delivery.upload_successes,
349
+ "delivery upload_successes",
350
+ );
351
+ enumValue(delivery.read_back, "delivery read_back", [
352
+ "not_requested",
353
+ "passed",
354
+ "failed",
355
+ ] as const);
356
+ if (
357
+ delivery.last_error_code !== null &&
358
+ (typeof delivery.last_error_code !== "string" ||
359
+ !/^[A-Z0-9_]+$/.test(delivery.last_error_code))
360
+ ) {
361
+ throw new IncompatibleStagingDataError("delivery last_error_code is invalid.");
362
+ }
363
+ const reason = nullableBoundedString(
364
+ delivery.opt_out_reason,
365
+ "delivery opt_out_reason",
366
+ { minimum: 1 },
367
+ );
368
+ if (successes > attempts) {
369
+ throw new IncompatibleStagingDataError(
370
+ "delivery upload_successes cannot exceed upload_attempts.",
371
+ );
372
+ }
373
+ if (enabled) {
374
+ if (mode === "disabled" || runId == null || reason != null) {
375
+ throw new IncompatibleStagingDataError(
376
+ "enabled staging has contradictory delivery fields.",
377
+ );
378
+ }
379
+ if (runId !== envelopeRunId) {
380
+ throw new IncompatibleStagingDataError(
381
+ "delivery run_id does not match its enclosing document.",
382
+ );
383
+ }
384
+ if (mode === "local_and_remote" && repository == null) {
385
+ throw new IncompatibleStagingDataError(
386
+ "remote staging requires a configured repository.",
387
+ );
388
+ }
389
+ if (mode === "local_only" && repository != null) {
390
+ throw new IncompatibleStagingDataError(
391
+ "local-only staging cannot name a repository.",
392
+ );
393
+ }
394
+ } else if (
395
+ mode !== "disabled" ||
396
+ runId != null ||
397
+ repository != null ||
398
+ reason == null ||
399
+ attempts !== 0 ||
400
+ successes !== 0
401
+ ) {
402
+ throw new IncompatibleStagingDataError(
403
+ "disabled staging has contradictory delivery fields.",
404
+ );
405
+ }
406
+ return delivery;
407
+ }
408
+
409
+ const FAILURE_FIELDS = [
410
+ "error_code",
411
+ "error_type",
412
+ "message",
413
+ "local_diagnostic_reference",
414
+ ] as const;
415
+
416
+ function parseFailure(value: unknown, schemaVersion: 2 | 3): JsonObject | null {
417
+ if (value === null) return null;
418
+ const label = `version ${schemaVersion} failure`;
419
+ const failure = objectValue(value, label);
420
+ assertExactKeys(
421
+ failure,
422
+ label,
423
+ schemaVersion === 3 ? [...FAILURE_FIELDS, "failure_class"] : FAILURE_FIELDS,
424
+ );
425
+ if (typeof failure.error_code !== "string" || !/^[A-Z0-9_]+$/.test(failure.error_code)) {
426
+ throw new IncompatibleStagingDataError("failure error_code is invalid.");
427
+ }
428
+ if (
429
+ typeof failure.error_type !== "string" ||
430
+ !/^[A-Za-z][A-Za-z0-9_]*$/.test(failure.error_type)
431
+ ) {
432
+ throw new IncompatibleStagingDataError("failure error_type is invalid.");
433
+ }
434
+ boundedString(failure.message, "failure message", { minimum: 1, maximum: 500 });
435
+ nullableBoundedString(
436
+ failure.local_diagnostic_reference,
437
+ "failure local_diagnostic_reference",
438
+ { maximum: 200 },
439
+ );
440
+ if (
441
+ schemaVersion === 3 &&
442
+ failure.failure_class !== null &&
443
+ (typeof failure.failure_class !== "string" ||
444
+ !/^[a-z][a-z0-9_]*$/.test(failure.failure_class))
445
+ ) {
446
+ throw new IncompatibleStagingDataError("failure failure_class is invalid.");
447
+ }
448
+ return failure;
449
+ }
450
+
451
+ // Version 3: which gate phase refused the candidate, and which gates. The gate
452
+ // ids may be empty when the refusal named no gate; the count is at least one.
453
+ function parseBlock(value: unknown): JsonObject | null {
454
+ if (value === null) return null;
455
+ const block = objectValue(value, "version 3 block");
456
+ assertExactKeys(block, "version 3 block", [
457
+ "phase",
458
+ "blocking_failure_count",
459
+ "blocking_gate_ids",
460
+ ]);
461
+ safeIdentifier(block.phase, "block phase");
462
+ integerValue(block.blocking_failure_count, "block blocking_failure_count", 1);
463
+ if (!Array.isArray(block.blocking_gate_ids)) {
464
+ throw new IncompatibleStagingDataError("block blocking_gate_ids must be an array.");
465
+ }
466
+ for (const [index, gateId] of block.blocking_gate_ids.entries()) {
467
+ boundedString(gateId, `block blocking_gate_ids ${index}`, {
468
+ minimum: 1,
469
+ maximum: 200,
470
+ });
471
+ }
472
+ return block;
473
+ }
474
+
475
+ function parseRunEnvelope(
476
+ payload: JsonObject,
477
+ label: string,
478
+ schemaVersion: 2 | 3,
479
+ ): {
480
+ runId: string;
481
+ candidateId: string;
482
+ releaseId: string | null;
483
+ pipeline: JsonObject;
484
+ failure: JsonObject | null;
485
+ block: JsonObject | null;
486
+ delivery: JsonObject;
487
+ } {
488
+ const runId = safeIdentifier(payload.run_id, `${label} run_id`);
489
+ if (typeof payload.country_code !== "string" || !/^[A-Z]{2}$/.test(payload.country_code)) {
490
+ throw new IncompatibleStagingDataError(`${label} country_code must contain two uppercase letters.`);
491
+ }
492
+ safeIdentifier(payload.operation_id, `${label} operation_id`);
493
+ const pipeline = parsePipeline(payload.pipeline);
494
+ const candidateId = safeIdentifier(payload.candidate_id, `${label} candidate_id`);
495
+ const releaseId = nullableSafeIdentifier(payload.release_id, `${label} release_id`);
496
+ safeIdentifier(payload.run_kind, `${label} run_kind`);
497
+ const nonRelease = booleanValue(payload.non_release, `${label} non_release`);
498
+ if (nonRelease !== (releaseId == null)) {
499
+ throw new IncompatibleStagingDataError(
500
+ `${label} non_release must be true exactly when release_id is absent.`,
501
+ );
502
+ }
503
+ dateTimeValue(payload.started_at, `${label} started_at`);
504
+ dateTimeValue(payload.updated_at, `${label} updated_at`);
505
+ const status = enumValue(
506
+ payload.status,
507
+ `${label} status`,
508
+ schemaVersion === 3
509
+ ? (["running", "completed", "blocked", "failed"] as const)
510
+ : (["running", "completed", "failed"] as const),
511
+ );
512
+ safeIdentifier(payload.current_stage, `${label} current_stage`);
513
+ const delivery = parseDelivery(payload.delivery, runId);
514
+ const failure = parseFailure(payload.failure, schemaVersion);
515
+ if (status === "failed" && failure == null) {
516
+ throw new IncompatibleStagingDataError(`${label} failed status requires failure data.`);
517
+ }
518
+ if (status !== "failed" && failure != null) {
519
+ throw new IncompatibleStagingDataError(`${label} non-failed status cannot contain failure data.`);
520
+ }
521
+ const block = schemaVersion === 3 ? parseBlock(payload.block) : null;
522
+ if (status === "blocked" && block == null) {
523
+ throw new IncompatibleStagingDataError(`${label} blocked status requires block data.`);
524
+ }
525
+ if (status !== "blocked" && block != null) {
526
+ throw new IncompatibleStagingDataError(`${label} non-blocked status cannot contain block data.`);
527
+ }
528
+ return { runId, candidateId, releaseId, pipeline, failure, block, delivery };
529
+ }
530
+
531
+ // Version 1 telemetry has no content policy. Its failure path records the
532
+ // Python traceback in `details` (microcosm staging.py `fail()`), which can carry
533
+ // build-machine paths; the dashboard serves staging documents publicly, so the
534
+ // traceback is dropped here. The error type and message are kept.
535
+ function withoutTraceback(payload: JsonObject): JsonObject {
536
+ const details = payload.details;
537
+ if (!details || typeof details !== "object" || Array.isArray(details)) return payload;
538
+ if (!("traceback" in details)) return payload;
539
+ const { traceback: _traceback, ...rest } = details as JsonObject;
540
+ return { ...payload, details: rest };
541
+ }
542
+
543
+ export function parseStagingProgress(value: unknown): JsonObject {
544
+ const payload = objectValue(value, "progress");
545
+ const schemaVersion = version(payload, "progress");
546
+ if (schemaVersion === 1) {
547
+ const runId = stringValue(payload.run_id, "progress run_id");
548
+ return { ...withoutTraceback(payload), run_id: runId };
549
+ }
550
+ assertExactKeys(payload, `version ${schemaVersion} progress`, [
551
+ "schema_name",
552
+ "schema_version",
553
+ ...RUN_FIELDS,
554
+ "sample",
555
+ "delivery",
556
+ "message",
557
+ "details",
558
+ "failure",
559
+ ...(schemaVersion === 3 ? ["block"] : []),
560
+ ]);
561
+ const envelope = parseRunEnvelope(payload, "progress", schemaVersion);
562
+ const sample = parseSample(payload.sample);
563
+ nullableBoundedString(payload.message, "progress message", { maximum: 500 });
564
+ objectValue(payload.details, "progress details");
565
+ return {
566
+ ...payload,
567
+ run_id: envelope.runId,
568
+ candidate_release_id: envelope.candidateId,
569
+ release_id: envelope.releaseId,
570
+ pipeline: envelope.pipeline,
571
+ sample,
572
+ failure: envelope.failure,
573
+ block: envelope.block,
574
+ stage: payload.current_stage,
575
+ delivery: envelope.delivery,
576
+ };
577
+ }
578
+
579
+ function parseArtifacts(value: unknown, runId: string): Record<string, JsonObject> {
580
+ if (!Array.isArray(value)) {
581
+ throw new IncompatibleStagingDataError("version 2 artifacts must be an array.");
582
+ }
583
+ const artifacts = new Map<string, JsonObject>();
584
+ for (const [index, entry] of value.entries()) {
585
+ const artifact = objectValue(entry, `artifact ${index}`);
586
+ assertExactKeys(artifact, `artifact ${index}`, [
587
+ "logical_name",
588
+ "artifact_kind",
589
+ "contract_relative_path",
590
+ "media_type",
591
+ "sha256",
592
+ "classification",
593
+ ]);
594
+ const name = safeIdentifier(artifact.logical_name, `artifact ${index} logical_name`);
595
+ const path = stringValue(
596
+ artifact.contract_relative_path,
597
+ `artifact ${index} contract_relative_path`,
598
+ );
599
+ if (path !== `artifacts/${name}.json` || path.includes("..") || path.startsWith("/")) {
600
+ throw new IncompatibleStagingDataError(`artifact ${name} has an unsafe path.`);
601
+ }
602
+ if (!ARTIFACT_KINDS.has(String(artifact.artifact_kind))) {
603
+ throw new IncompatibleStagingDataError(`artifact ${name} has an unsupported kind.`);
604
+ }
605
+ if (typeof artifact.sha256 !== "string" || !SHA_256.test(artifact.sha256)) {
606
+ throw new IncompatibleStagingDataError(`artifact ${name} has an invalid digest.`);
607
+ }
608
+ if (
609
+ artifact.media_type !== "application/json" ||
610
+ !new Set(["aggregate", "non_row_level"]).has(String(artifact.classification))
611
+ ) {
612
+ throw new IncompatibleStagingDataError(`artifact ${name} is not reviewed JSON.`);
613
+ }
614
+ if (artifacts.has(name)) {
615
+ throw new IncompatibleStagingDataError(`artifact ${name} appears more than once.`);
616
+ }
617
+ artifacts.set(name, {
618
+ ...artifact,
619
+ path,
620
+ staging_path: `runs/${runId}/${path}`,
621
+ });
622
+ }
623
+ return Object.fromEntries(artifacts);
624
+ }
625
+
626
+ export function parseStagingManifest(value: unknown): JsonObject {
627
+ const payload = objectValue(value, "run manifest");
628
+ const schemaVersion = version(payload, "manifest");
629
+ if (schemaVersion === 1) {
630
+ const runId = stringValue(payload.run_id, "run manifest run_id");
631
+ return { ...payload, run_id: runId };
632
+ }
633
+ assertExactKeys(payload, `version ${schemaVersion} run manifest`, [
634
+ "schema_name",
635
+ "schema_version",
636
+ ...RUN_FIELDS,
637
+ "sample",
638
+ "delivery",
639
+ "artifacts",
640
+ "failure",
641
+ ...(schemaVersion === 3 ? ["block"] : []),
642
+ "paths",
643
+ ]);
644
+ const envelope = parseRunEnvelope(payload, "run manifest", schemaVersion);
645
+ const sample = parseSample(payload.sample);
646
+ const paths = objectValue(payload.paths, "run manifest paths");
647
+ assertExactKeys(paths, "run manifest paths", [
648
+ "progress",
649
+ "events",
650
+ "calibration_progress",
651
+ ]);
652
+ if (typeof paths.progress !== "string" || typeof paths.events !== "string") {
653
+ throw new IncompatibleStagingDataError(
654
+ "run manifest progress and events paths must be strings.",
655
+ );
656
+ }
657
+ if (
658
+ paths.calibration_progress !== null &&
659
+ typeof paths.calibration_progress !== "string"
660
+ ) {
661
+ throw new IncompatibleStagingDataError(
662
+ "run manifest calibration_progress path must be a string or null.",
663
+ );
664
+ }
665
+ return {
666
+ ...payload,
667
+ run_id: envelope.runId,
668
+ candidate_release_id: envelope.candidateId,
669
+ release_id: envelope.releaseId,
670
+ pipeline: envelope.pipeline,
671
+ sample,
672
+ failure: envelope.failure,
673
+ block: envelope.block,
674
+ stage: payload.current_stage,
675
+ delivery: envelope.delivery,
676
+ artifacts: parseArtifacts(payload.artifacts, envelope.runId),
677
+ paths,
678
+ };
679
+ }
680
+
681
+ export function parseStagingCalibrationProgress(value: unknown): JsonObject {
682
+ const payload = objectValue(value, "calibration progress");
683
+ const schemaVersion = version(payload, "calibration");
684
+ if (!Array.isArray(payload.events)) {
685
+ throw new IncompatibleStagingDataError("calibration events must be an array.");
686
+ }
687
+ if (schemaVersion === 1) return payload;
688
+ assertExactKeys(payload, `version ${schemaVersion} calibration progress`, [
689
+ "schema_name",
690
+ "schema_version",
691
+ "run_id",
692
+ "candidate_id",
693
+ "updated_at",
694
+ "events",
695
+ ]);
696
+ const runId = safeIdentifier(payload.run_id, "calibration progress run_id");
697
+ const candidateId = safeIdentifier(
698
+ payload.candidate_id,
699
+ "calibration progress candidate_id",
700
+ );
701
+ dateTimeValue(payload.updated_at, "calibration progress updated_at");
702
+ return {
703
+ ...payload,
704
+ run_id: runId,
705
+ candidate_release_id: candidateId,
706
+ events: payload.events.map((entry, index) => {
707
+ const event = objectValue(entry, `calibration event ${index}`);
708
+ assertExactKeys(event, `calibration event ${index}`, [
709
+ "timestamp",
710
+ "epoch",
711
+ "epochs",
712
+ "phase",
713
+ "loss",
714
+ "iteration",
715
+ "budget_search",
716
+ "budget_iteration",
717
+ "budget_iters",
718
+ "l0_lambda",
719
+ ]);
720
+ const timestamp = dateTimeValue(
721
+ event.timestamp,
722
+ `calibration event ${index} timestamp`,
723
+ );
724
+ nullableInteger(event.epoch, `calibration event ${index} epoch`);
725
+ nullableInteger(event.epochs, `calibration event ${index} epochs`);
726
+ nullableBoundedString(event.phase, `calibration event ${index} phase`);
727
+ nullableNumber(event.loss, `calibration event ${index} loss`);
728
+ nullableInteger(event.iteration, `calibration event ${index} iteration`);
729
+ nullableInteger(event.budget_search, `calibration event ${index} budget_search`);
730
+ nullableInteger(
731
+ event.budget_iteration,
732
+ `calibration event ${index} budget_iteration`,
733
+ );
734
+ nullableInteger(event.budget_iters, `calibration event ${index} budget_iters`);
735
+ nullableNumber(event.l0_lambda, `calibration event ${index} l0_lambda`);
736
+ return {
737
+ ...event,
738
+ time: timestamp,
739
+ };
740
+ }),
741
+ };
742
+ }
743
+
744
+ export function parseStagingEvents(text: string | null): JsonObject[] {
745
+ if (!text) return [];
746
+ const events = text
747
+ .split("\n")
748
+ .map((line) => line.trim())
749
+ .filter(Boolean)
750
+ .map((line, index) => {
751
+ let payload: JsonObject;
752
+ try {
753
+ payload = objectValue(JSON.parse(line), `event ${index}`);
754
+ } catch (error) {
755
+ if (error instanceof IncompatibleStagingDataError) throw error;
756
+ throw new IncompatibleStagingDataError(`event ${index} is not valid JSON.`);
757
+ }
758
+ const schemaVersion = version(payload, "event", { allowUnversionedV1: true });
759
+ if (schemaVersion === 1) return withoutTraceback(payload);
760
+ assertExactKeys(payload, `event ${index}`, [
761
+ "schema_name",
762
+ "schema_version",
763
+ "sequence",
764
+ "timestamp",
765
+ "event_type",
766
+ "run_id",
767
+ "stage_id",
768
+ "status",
769
+ "message",
770
+ "details",
771
+ ]);
772
+ integerValue(payload.sequence, `event ${index} sequence`, 1);
773
+ const timestamp = dateTimeValue(payload.timestamp, `event ${index} timestamp`);
774
+ enumValue(payload.event_type, `event ${index} event_type`, [
775
+ "stage",
776
+ "calibration",
777
+ ] as const);
778
+ safeIdentifier(payload.run_id, `event ${index} run_id`);
779
+ const stageId = safeIdentifier(payload.stage_id, `event ${index} stage_id`);
780
+ enumValue(
781
+ payload.status,
782
+ `event ${index} status`,
783
+ schemaVersion === 3
784
+ ? (["started", "completed", "failed", "progress", "blocked"] as const)
785
+ : (["started", "completed", "failed", "progress"] as const),
786
+ );
787
+ nullableBoundedString(payload.message, `event ${index} message`, { maximum: 500 });
788
+ objectValue(payload.details, `event ${index} details`);
789
+ return {
790
+ ...payload,
791
+ stage: stageId,
792
+ time: timestamp,
793
+ };
794
+ });
795
+ const structured = events.filter(isStructuredStagingDocument);
796
+ if (
797
+ structured.length &&
798
+ structured.some((event, index) => event.sequence !== index + 1)
799
+ ) {
800
+ throw new IncompatibleStagingDataError(
801
+ `version ${String(structured[0].schema_version)} event sequence is not contiguous.`,
802
+ );
803
+ }
804
+ return events;
805
+ }
806
+
807
+ export interface StagingRunDocuments {
808
+ progress?: JsonObject | null;
809
+ runManifest?: JsonObject | null;
810
+ calibrationProgress?: JsonObject | null;
811
+ events?: JsonObject[];
812
+ }
813
+
814
+ function samePipeline(left: unknown, right: unknown): boolean {
815
+ const leftPipeline = objectValue(left, "left pipeline");
816
+ const rightPipeline = objectValue(right, "right pipeline");
817
+ return (
818
+ leftPipeline.id === rightPipeline.id &&
819
+ leftPipeline.version === rightPipeline.version
820
+ );
821
+ }
822
+
823
+ export function validateStagingRunConsistency(
824
+ requestedRunId: string,
825
+ documents: StagingRunDocuments,
826
+ ): void {
827
+ const runId = safeIdentifier(requestedRunId, "requested run_id");
828
+ const progress = documents.progress ?? null;
829
+ const manifest = documents.runManifest ?? null;
830
+ const calibration = documents.calibrationProgress ?? null;
831
+ const events = documents.events ?? [];
832
+ const allDocuments = [progress, manifest, calibration, ...events].filter(
833
+ (document): document is JsonObject => document != null,
834
+ );
835
+ if (!allDocuments.length) return;
836
+
837
+ const versions = new Set(
838
+ allDocuments.map((document) => document.schema_version ?? 1),
839
+ );
840
+ if (versions.size !== 1) {
841
+ throw new IncompatibleStagingDataError(
842
+ `a run cannot mix document versions ${[...versions].sort().join(" and ")}.`,
843
+ );
844
+ }
845
+ if (versions.has(1)) return;
846
+
847
+ for (const [label, document] of [
848
+ ["progress", progress],
849
+ ["run manifest", manifest],
850
+ ["calibration progress", calibration],
851
+ ] as const) {
852
+ if (document && document.run_id !== runId) {
853
+ throw new IncompatibleStagingDataError(
854
+ `${label} identifies run ${String(document.run_id)}, not ${runId}.`,
855
+ );
856
+ }
857
+ }
858
+ for (const [index, event] of events.entries()) {
859
+ if (event.run_id !== runId) {
860
+ throw new IncompatibleStagingDataError(
861
+ `event ${index} identifies run ${String(event.run_id)}, not ${runId}.`,
862
+ );
863
+ }
864
+ }
865
+
866
+ if (manifest) {
867
+ const paths = objectValue(manifest.paths, "run manifest paths");
868
+ const expectedProgress = `runs/${runId}/progress.json`;
869
+ const expectedEvents = `runs/${runId}/events.ndjson`;
870
+ if (paths.progress !== expectedProgress || paths.events !== expectedEvents) {
871
+ throw new IncompatibleStagingDataError(
872
+ "run manifest paths do not identify the requested run.",
873
+ );
874
+ }
875
+ if (
876
+ paths.calibration_progress !== null &&
877
+ paths.calibration_progress !== `runs/${runId}/calibration_progress.json`
878
+ ) {
879
+ throw new IncompatibleStagingDataError(
880
+ "run manifest calibration progress path does not identify the requested run.",
881
+ );
882
+ }
883
+ }
884
+
885
+ if (progress && manifest) {
886
+ const stableFields = [
887
+ "run_id",
888
+ "country_code",
889
+ "operation_id",
890
+ "candidate_id",
891
+ "release_id",
892
+ "run_kind",
893
+ "non_release",
894
+ "started_at",
895
+ ] as const;
896
+ for (const field of stableFields) {
897
+ if (progress[field] !== manifest[field]) {
898
+ throw new IncompatibleStagingDataError(
899
+ `progress and run manifest disagree on stable field ${field}.`,
900
+ );
901
+ }
902
+ }
903
+ if (!samePipeline(progress.pipeline, manifest.pipeline)) {
904
+ throw new IncompatibleStagingDataError(
905
+ "progress and run manifest disagree on stable field pipeline.",
906
+ );
907
+ }
908
+ }
909
+
910
+ const identity = manifest ?? progress;
911
+ if (calibration && identity && calibration.candidate_id !== identity.candidate_id) {
912
+ throw new IncompatibleStagingDataError(
913
+ "calibration progress and run documents disagree on candidate_id.",
914
+ );
915
+ }
916
+ }