@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,1012 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ import {
4
+ HISTORICAL_ATTRIBUTION_SUPPORT_BY_RELEASE,
5
+ type AttributionVerificationTolerance,
6
+ type HistoricalAttributionRecipe,
7
+ type HistoricalAttributionSupport,
8
+ } from "./target-loss-attribution-manifest";
9
+
10
+ type JsonObject = Record<string, unknown>;
11
+ export type TargetLossAttributionStatus =
12
+ | "reported"
13
+ | "exact_reconstructed"
14
+ | "derived"
15
+ | "unavailable";
16
+
17
+ export interface TargetLossDiagnosticWarning {
18
+ code: string;
19
+ severity: string | null;
20
+ message: string;
21
+ }
22
+
23
+ export interface FinalTargetLossAttributionTarget {
24
+ index: number;
25
+ name: string;
26
+ target_loss_weight: number;
27
+ target_loss_weight_share: number;
28
+ target_loss_scale: number;
29
+ final_capped_scaled_error: number;
30
+ final_loss_contribution: number;
31
+ }
32
+
33
+ export interface TargetLossAttributionVerification {
34
+ valid: boolean;
35
+ difference: number | null;
36
+ tolerance: AttributionVerificationTolerance;
37
+ }
38
+
39
+ export interface FinalTargetLossAttribution {
40
+ status: TargetLossAttributionStatus;
41
+ aggregate: number | null;
42
+ historical_final_loss: number | null;
43
+ cap: number | null;
44
+ basis_identifier: string | null;
45
+ basis_hash: string | null;
46
+ verification: TargetLossAttributionVerification | null;
47
+ producer_warnings: TargetLossDiagnosticWarning[];
48
+ reason: string | null;
49
+ targets: FinalTargetLossAttributionTarget[];
50
+ }
51
+
52
+ export interface NormalizeTargetLossAttributionInput {
53
+ diagnostics: JsonObject;
54
+ rows: JsonObject[];
55
+ releaseId: string;
56
+ buildManifest: JsonObject;
57
+ releaseFamily: "national" | "local_area";
58
+ diagnosticsSha256?: string | null;
59
+ }
60
+
61
+ export interface NormalizedTargetLossAttributionResult {
62
+ attribution: FinalTargetLossAttribution;
63
+ provenance: CalibrationProvenance;
64
+ rows: JsonObject[];
65
+ }
66
+
67
+ export interface CalibrationProvenance {
68
+ mode: "direct" | "inherited";
69
+ dataset_release_id: string;
70
+ calibration_source_id: string | null;
71
+ diagnostics_sha256: string | null;
72
+ target_surface_sha256: string | null;
73
+ validation: {
74
+ status: "not_applicable" | "verified" | "failed";
75
+ reason: string | null;
76
+ };
77
+ }
78
+
79
+ export interface HistoricalAttributionEvidence {
80
+ releaseId: string;
81
+ buildId: string | null;
82
+ buildSha: string | null;
83
+ producerCommit: string | null;
84
+ releaseFamily: "national" | "local_area";
85
+ diagnosticsSchema: number | null;
86
+ targetCount: number;
87
+ orderedTargetNamesSha256: string;
88
+ producerTargetSurfaceSha256: string | null;
89
+ weightingIdentifier: string | null;
90
+ lossCap: number | null;
91
+ }
92
+
93
+ export interface InheritedAttributionEvidence {
94
+ parentBuildId: string | null;
95
+ declaredDiagnosticsSchema: number | null;
96
+ declaredDiagnosticsSha256: string | null;
97
+ observedDiagnosticsSha256: string | null;
98
+ releaseFamily: "national" | "local_area";
99
+ diagnosticsSchema: number | null;
100
+ targetCount: number;
101
+ orderedTargetNamesSha256: string;
102
+ producerTargetSurfaceSha256: string | null;
103
+ weightingIdentifier: string | null;
104
+ lossCap: number | null;
105
+ }
106
+
107
+ export interface HistoricalAttributionClassification {
108
+ status: "exact_reconstructed" | "derived" | "unavailable";
109
+ recipe: HistoricalAttributionRecipe | null;
110
+ reason: string | null;
111
+ }
112
+
113
+ export const TARGET_LOSS_ATTRIBUTION_ABS_TOLERANCE = 1e-12;
114
+ export const TARGET_LOSS_ATTRIBUTION_REL_TOLERANCE = 1e-12;
115
+ export const TARGET_LOSS_BASIS_HASH_ALGORITHM = "sha256_utf8len32_f64be_v1";
116
+ export const TARGET_LOSS_FORMULA =
117
+ "weighted_mean(min(abs((estimate - target) / scale), cap))";
118
+
119
+ const TARGET_LOSS_WARNING_PREFIX = "target_loss_attribution_";
120
+ const SHA256_RE = /^[0-9a-f]{64}$/i;
121
+ const ATTRIBUTION_FIELDS = [
122
+ "target_loss_weight",
123
+ "target_loss_weight_share",
124
+ "target_loss_scale",
125
+ "final_capped_scaled_error",
126
+ "final_loss_contribution",
127
+ ] as const;
128
+ const NOT_APPLICABLE_TOLERANCE: AttributionVerificationTolerance = {
129
+ kind: "not_applicable",
130
+ absolute: 0,
131
+ relative: 0,
132
+ };
133
+
134
+ const CONCEPT_METADATA_EXCLUSIONS = new Set([
135
+ "congressional_district_geoid",
136
+ "geography_scope",
137
+ "hierarchy_child_ids",
138
+ "hierarchy_child_sum_raw",
139
+ "hierarchy_coverage_ratio",
140
+ "hierarchy_expected_child_count",
141
+ "hierarchy_observed_child_count",
142
+ "hierarchy_parent_geography_id",
143
+ "hierarchy_parent_geography_level",
144
+ "hierarchy_parent_key",
145
+ "hierarchy_parent_target_name",
146
+ "hierarchy_parent_target_period",
147
+ "hierarchy_parent_value",
148
+ "hierarchy_raw_value",
149
+ "hierarchy_reconciliation_factor",
150
+ "hierarchy_reconciliation_method",
151
+ "hierarchy_reconciliation_rule",
152
+ "ledger_aggregate_fact_key",
153
+ "ledger_dimension_set_key",
154
+ "ledger_fact_key",
155
+ "ledger_geography_id",
156
+ "ledger_geography_level",
157
+ "ledger_geography_name",
158
+ "ledger_geography_vintage",
159
+ "ledger_legacy_fact_key",
160
+ "ledger_layout_groupby_dimension",
161
+ "ledger_layout_groupby_value_id",
162
+ "ledger_layout_record_set_id",
163
+ "ledger_observed_measure_key",
164
+ "ledger_semantic_fact_key",
165
+ "ledger_source_record_id",
166
+ "state_fips",
167
+ ]);
168
+
169
+ function asObject(value: unknown): JsonObject {
170
+ return value && typeof value === "object" && !Array.isArray(value)
171
+ ? (value as JsonObject)
172
+ : {};
173
+ }
174
+
175
+ function finiteNumber(value: unknown): number | null {
176
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
177
+ }
178
+
179
+ function optionalString(value: unknown): string | null {
180
+ return typeof value === "string" && value.length ? value : null;
181
+ }
182
+
183
+ function normalizedSha256(value: unknown): string | null {
184
+ const candidate = optionalString(value);
185
+ return candidate && SHA256_RE.test(candidate) ? candidate.toLowerCase() : null;
186
+ }
187
+
188
+ function targetName(row: JsonObject): string {
189
+ return String(row.name ?? row.target_name ?? "");
190
+ }
191
+
192
+ function targetValue(row: JsonObject): number | null {
193
+ return finiteNumber(row.target) ?? finiteNumber(row.value);
194
+ }
195
+
196
+ function finalEstimate(row: JsonObject): number | null {
197
+ return finiteNumber(row.final_estimate) ?? finiteNumber(row.estimate);
198
+ }
199
+
200
+ function targetMetadata(row: JsonObject): JsonObject {
201
+ return asObject(row.metadata);
202
+ }
203
+
204
+ function targetFamily(row: JsonObject): string {
205
+ const registryFamily = optionalString(asObject(row.registry).family);
206
+ return registryFamily ?? optionalString(targetMetadata(row).target_role) ?? "unknown";
207
+ }
208
+
209
+ function isClose(
210
+ left: number,
211
+ right: number,
212
+ tolerance: Pick<AttributionVerificationTolerance, "absolute" | "relative">,
213
+ ): boolean {
214
+ return Math.abs(left - right) <= Math.max(
215
+ tolerance.absolute,
216
+ tolerance.relative * Math.max(Math.abs(left), Math.abs(right)),
217
+ );
218
+ }
219
+
220
+ function unavailable(
221
+ finalLoss: number | null,
222
+ reason: string,
223
+ warnings: TargetLossDiagnosticWarning[],
224
+ ): FinalTargetLossAttribution {
225
+ return {
226
+ status: "unavailable",
227
+ aggregate: null,
228
+ historical_final_loss: finalLoss,
229
+ cap: null,
230
+ basis_identifier: null,
231
+ basis_hash: null,
232
+ verification: null,
233
+ producer_warnings: warnings,
234
+ reason,
235
+ targets: [],
236
+ };
237
+ }
238
+
239
+ function stripAttributionFields(row: JsonObject): JsonObject {
240
+ const clean = { ...row };
241
+ for (const field of ATTRIBUTION_FIELDS) delete clean[field];
242
+ return clean;
243
+ }
244
+
245
+ function rowsWithAttribution(
246
+ rows: JsonObject[],
247
+ targets: FinalTargetLossAttributionTarget[],
248
+ ): JsonObject[] {
249
+ return rows.map((row, index) => {
250
+ const clean = stripAttributionFields(row);
251
+ const target = targets[index];
252
+ if (!target) return clean;
253
+ return {
254
+ ...clean,
255
+ target_loss_weight: target.target_loss_weight,
256
+ target_loss_weight_share: target.target_loss_weight_share,
257
+ target_loss_scale: target.target_loss_scale,
258
+ final_capped_scaled_error: target.final_capped_scaled_error,
259
+ final_loss_contribution: target.final_loss_contribution,
260
+ };
261
+ });
262
+ }
263
+
264
+ export function targetLossBasisHash(
265
+ rows: Array<Pick<FinalTargetLossAttributionTarget, "name" | "target_loss_weight" | "target_loss_scale">>,
266
+ ): string {
267
+ const hash = createHash("sha256");
268
+ hash.update(Buffer.from("microcosm-target-loss-basis-v1\0", "utf8"));
269
+ for (const row of rows) {
270
+ const name = Buffer.from(row.name, "utf8");
271
+ const nameLength = Buffer.allocUnsafe(4);
272
+ nameLength.writeUInt32BE(name.length);
273
+ const weight = Buffer.allocUnsafe(8);
274
+ weight.writeDoubleBE(row.target_loss_weight);
275
+ const scale = Buffer.allocUnsafe(8);
276
+ scale.writeDoubleBE(row.target_loss_scale);
277
+ hash.update(nameLength);
278
+ hash.update(name);
279
+ hash.update(weight);
280
+ hash.update(scale);
281
+ }
282
+ return hash.digest("hex");
283
+ }
284
+
285
+ export function orderedTargetNamesHash(rows: JsonObject[]): string {
286
+ const hash = createHash("sha256");
287
+ hash.update(Buffer.from("microcosm-target-name-surface-v1\0", "utf8"));
288
+ for (const row of rows) {
289
+ const name = Buffer.from(targetName(row), "utf8");
290
+ const nameLength = Buffer.allocUnsafe(4);
291
+ nameLength.writeUInt32BE(name.length);
292
+ hash.update(nameLength);
293
+ hash.update(name);
294
+ }
295
+ return hash.digest("hex");
296
+ }
297
+
298
+ function diagnosticWarnings(diagnostics: JsonObject): TargetLossDiagnosticWarning[] {
299
+ if (!Array.isArray(diagnostics.diagnostic_warnings)) return [];
300
+ return diagnostics.diagnostic_warnings.flatMap((value) => {
301
+ const warning = asObject(value);
302
+ const code = optionalString(warning.code);
303
+ const message = optionalString(warning.message);
304
+ if (!code || !message) return [];
305
+ return [{ code, severity: optionalString(warning.severity), message }];
306
+ });
307
+ }
308
+
309
+ function reportedAttribution(
310
+ diagnostics: JsonObject,
311
+ rows: JsonObject[],
312
+ finalLoss: number | null,
313
+ warnings: TargetLossDiagnosticWarning[],
314
+ ): FinalTargetLossAttribution {
315
+ const producerWarning = warnings.find((warning) =>
316
+ warning.code.startsWith(TARGET_LOSS_WARNING_PREFIX),
317
+ );
318
+ if (producerWarning) {
319
+ return unavailable(
320
+ finalLoss,
321
+ `Microcosm withheld target-loss attribution: ${producerWarning.message}`,
322
+ warnings,
323
+ );
324
+ }
325
+ if (finalLoss == null) {
326
+ return unavailable(finalLoss, "Schema-version-6 diagnostics have no finite final_loss.", warnings);
327
+ }
328
+ const basis = asObject(diagnostics.target_loss_basis);
329
+ const cap = finiteNumber(basis.cap);
330
+ const targetCount = finiteNumber(basis.target_count);
331
+ const totalWeight = finiteNumber(basis.total_target_weight);
332
+ const formula = optionalString(basis.formula);
333
+ const weightKind = optionalString(basis.weight_kind);
334
+ const scaleKind = optionalString(basis.scale_kind);
335
+ const hashAlgorithm = optionalString(basis.hash_algorithm);
336
+ const expectedHash = optionalString(basis.sha256);
337
+ if (
338
+ cap == null || cap <= 0 || targetCount !== rows.length || totalWeight == null ||
339
+ totalWeight <= 0 || formula !== TARGET_LOSS_FORMULA || !weightKind || !scaleKind ||
340
+ hashAlgorithm !== TARGET_LOSS_BASIS_HASH_ALGORITHM ||
341
+ !expectedHash || !/^[0-9a-f]{64}$/.test(expectedHash)
342
+ ) {
343
+ return unavailable(
344
+ finalLoss,
345
+ "Schema-version-6 target_loss_basis is missing or inconsistent.",
346
+ warnings,
347
+ );
348
+ }
349
+
350
+ const targets: FinalTargetLossAttributionTarget[] = [];
351
+ for (const [index, row] of rows.entries()) {
352
+ const name = targetName(row);
353
+ const target = targetValue(row);
354
+ const estimate = finalEstimate(row);
355
+ const weight = finiteNumber(row.target_loss_weight);
356
+ const share = finiteNumber(row.target_loss_weight_share);
357
+ const scale = finiteNumber(row.target_loss_scale);
358
+ const cappedError = finiteNumber(row.final_capped_scaled_error);
359
+ const contribution = finiteNumber(row.final_loss_contribution);
360
+ if (
361
+ !name || target == null || estimate == null || weight == null || weight < 0 ||
362
+ share == null || share < 0 || scale == null || scale <= 0 ||
363
+ cappedError == null || cappedError < 0 || cappedError > cap ||
364
+ contribution == null || contribution < 0
365
+ ) {
366
+ return unavailable(
367
+ finalLoss,
368
+ `Schema-version-6 target attribution is partial or invalid at row ${index}.`,
369
+ warnings,
370
+ );
371
+ }
372
+ const recomputedError = Math.min(Math.abs(estimate - target) / scale, cap);
373
+ if (
374
+ !isClose(cappedError, recomputedError, {
375
+ absolute: TARGET_LOSS_ATTRIBUTION_ABS_TOLERANCE,
376
+ relative: TARGET_LOSS_ATTRIBUTION_REL_TOLERANCE,
377
+ }) ||
378
+ !isClose(contribution, share * cappedError, {
379
+ absolute: TARGET_LOSS_ATTRIBUTION_ABS_TOLERANCE,
380
+ relative: TARGET_LOSS_ATTRIBUTION_REL_TOLERANCE,
381
+ })
382
+ ) {
383
+ return unavailable(
384
+ finalLoss,
385
+ `Schema-version-6 target attribution values disagree at row ${index}.`,
386
+ warnings,
387
+ );
388
+ }
389
+ targets.push({
390
+ index,
391
+ name,
392
+ target_loss_weight: weight,
393
+ target_loss_weight_share: share,
394
+ target_loss_scale: scale,
395
+ final_capped_scaled_error: cappedError,
396
+ final_loss_contribution: contribution,
397
+ });
398
+ }
399
+
400
+ const summedWeight = targets.reduce((sum, target) => sum + target.target_loss_weight, 0);
401
+ const summedShares = targets.reduce((sum, target) => sum + target.target_loss_weight_share, 0);
402
+ const aggregate = targets.reduce((sum, target) => sum + target.final_loss_contribution, 0);
403
+ const tolerance = {
404
+ absolute: TARGET_LOSS_ATTRIBUTION_ABS_TOLERANCE,
405
+ relative: TARGET_LOSS_ATTRIBUTION_REL_TOLERANCE,
406
+ };
407
+ const rowsConsistent = targets.every((target) =>
408
+ isClose(target.target_loss_weight_share, target.target_loss_weight / summedWeight, tolerance),
409
+ );
410
+ const actualHash = targetLossBasisHash(targets);
411
+ if (
412
+ !isClose(summedWeight, totalWeight, tolerance) ||
413
+ !isClose(summedShares, 1, tolerance) ||
414
+ !rowsConsistent ||
415
+ !isClose(aggregate, finalLoss, tolerance) ||
416
+ actualHash !== expectedHash
417
+ ) {
418
+ return unavailable(
419
+ finalLoss,
420
+ "Schema-version-6 target attribution fails its aggregate or basis-hash invariant.",
421
+ warnings,
422
+ );
423
+ }
424
+
425
+ return {
426
+ status: "reported",
427
+ aggregate,
428
+ historical_final_loss: finalLoss,
429
+ cap,
430
+ basis_identifier: `${weightKind}:${scaleKind}`,
431
+ basis_hash: actualHash,
432
+ verification: {
433
+ valid: true,
434
+ difference: aggregate - finalLoss,
435
+ tolerance: {
436
+ kind: "floating_point",
437
+ absolute: TARGET_LOSS_ATTRIBUTION_ABS_TOLERANCE,
438
+ relative: TARGET_LOSS_ATTRIBUTION_REL_TOLERANCE,
439
+ },
440
+ },
441
+ producer_warnings: warnings,
442
+ reason: null,
443
+ targets,
444
+ };
445
+ }
446
+
447
+ type BasisClassifier = (row: JsonObject) => "amount" | "count";
448
+
449
+ function historicalBasis(row: JsonObject): "amount" | "count" {
450
+ const metadata = targetMetadata(row);
451
+ const mode = String(metadata.measure_mode ?? "");
452
+ const sourceMeasure = String(metadata.source_measure_id ?? "");
453
+ if (metadata.count === "true") return "count";
454
+ if (["count", "positive_count", "less_than_count"].includes(mode)) return "count";
455
+ if (sourceMeasure.includes("enrollment") || sourceMeasure.includes("recipients")) {
456
+ return "count";
457
+ }
458
+ if (sourceMeasure.includes("return") && sourceMeasure.includes("count")) return "count";
459
+ return "amount";
460
+ }
461
+
462
+ function newerBasis(row: JsonObject): "amount" | "count" {
463
+ const metadata = targetMetadata(row);
464
+ const mode = String(metadata.measure_mode ?? "");
465
+ const sourceMeasure = String(metadata.source_measure_id ?? "");
466
+ if (["indicator_sum", "less_than_indicator_sum"].includes(mode)) return "count";
467
+ if (sourceMeasure.includes("enrollment") || sourceMeasure.includes("recipients")) {
468
+ return "count";
469
+ }
470
+ if (sourceMeasure.includes("return") && sourceMeasure.includes("count")) return "count";
471
+ return "amount";
472
+ }
473
+
474
+ // Versioned fallback for schema-v1/v2 slash-delimited rows after the artifact
475
+ // adapter has parsed their measure. It is deliberately confined to the six
476
+ // manifest-pinned legacy releases.
477
+ function legacyCurrentDoctrineBasis(row: JsonObject): "amount" | "count" {
478
+ const modern = newerBasis(row);
479
+ if (modern === "count") return modern;
480
+ const measure = String(row.measure ?? "").toLowerCase();
481
+ if (["count", "filers", "nonfilers"].includes(measure)) return "count";
482
+ const nameTokens = targetName(row).toLowerCase().split(/[./]/);
483
+ if (
484
+ nameTokens.some((token) => ["count", "filers", "nonfilers"].includes(token)) ||
485
+ nameTokens.some((token) => /_(returns|claims|count)$/.test(token))
486
+ ) {
487
+ return "count";
488
+ }
489
+ return "amount";
490
+ }
491
+
492
+ function squareRootWeights(rows: JsonObject[], basisOf: BasisClassifier): number[] | null {
493
+ const weights = rows.map(() => 1);
494
+ const bases = [...new Set(rows.map(basisOf))].sort();
495
+ if (!bases.length) return null;
496
+ for (const basis of bases) {
497
+ const indices = rows.flatMap((row, index) => basisOf(row) === basis ? [index] : []);
498
+ const raw = indices.map((index) => {
499
+ const target = targetValue(rows[index]);
500
+ return target == null ? Number.NaN : Math.sqrt(Math.max(Math.abs(target), 1));
501
+ });
502
+ if (!raw.every(Number.isFinite)) return null;
503
+ const mean = raw.reduce((sum, value) => sum + value, 0) / raw.length;
504
+ indices.forEach((index, offset) => { weights[index] = raw[offset] / mean; });
505
+ }
506
+ return weights;
507
+ }
508
+
509
+ function conceptKey(row: JsonObject, basisOf: BasisClassifier): string {
510
+ const metadata = targetMetadata(row);
511
+ if (metadata.ledger_geography_level !== "congressional_district") {
512
+ return JSON.stringify([
513
+ basisOf(row),
514
+ row.entity,
515
+ row.period,
516
+ targetFamily(row),
517
+ targetName(row),
518
+ ]);
519
+ }
520
+ const semanticMetadata = Object.entries(metadata)
521
+ .filter(([key]) => !CONCEPT_METADATA_EXCLUSIONS.has(key))
522
+ .sort(([left], [right]) => left.localeCompare(right));
523
+ return JSON.stringify([
524
+ basisOf(row),
525
+ row.entity,
526
+ row.period,
527
+ targetFamily(row),
528
+ row.filter ?? "",
529
+ metadata.state_fips ?? "",
530
+ semanticMetadata,
531
+ ]);
532
+ }
533
+
534
+ function reconstructedWeights(
535
+ rows: JsonObject[],
536
+ basisOf: BasisClassifier,
537
+ conceptBudget: boolean,
538
+ familyMultipliers: JsonObject | null,
539
+ ): number[] | null {
540
+ const weights = squareRootWeights(rows, basisOf);
541
+ if (!weights) return null;
542
+ if (conceptBudget) {
543
+ const groups = new Map<string, number[]>();
544
+ rows.forEach((row, index) => {
545
+ const key = conceptKey(row, basisOf);
546
+ const indices = groups.get(key) ?? [];
547
+ indices.push(index);
548
+ groups.set(key, indices);
549
+ });
550
+ for (const indices of groups.values()) {
551
+ const total = indices.reduce((sum, index) => sum + weights[index], 0);
552
+ const budget = Math.max(...indices.map((index) => weights[index]), 0);
553
+ if (total > 0 && budget > 0) {
554
+ for (const index of indices) weights[index] *= budget / total;
555
+ }
556
+ }
557
+ }
558
+
559
+ const bases = [...new Set(rows.map(basisOf))].sort();
560
+ const basisTotal = rows.length / bases.length;
561
+ for (const basis of bases) {
562
+ const indices = rows.flatMap((row, index) => basisOf(row) === basis ? [index] : []);
563
+ const total = indices.reduce((sum, index) => sum + weights[index], 0);
564
+ if (total <= 0) return null;
565
+ for (const index of indices) weights[index] *= basisTotal / total;
566
+ }
567
+ let mean = weights.reduce((sum, weight) => sum + weight, 0) / weights.length;
568
+ if (!Number.isFinite(mean) || mean <= 0) return null;
569
+ for (let index = 0; index < weights.length; index += 1) weights[index] /= mean;
570
+
571
+ if (familyMultipliers) {
572
+ const matchedFamilies = new Set<string>();
573
+ for (let index = 0; index < rows.length; index += 1) {
574
+ const family = targetFamily(rows[index]);
575
+ const multiplier = finiteNumber(familyMultipliers[family]);
576
+ if (multiplier != null) {
577
+ if (multiplier <= 0) return null;
578
+ weights[index] *= multiplier;
579
+ matchedFamilies.add(family);
580
+ }
581
+ }
582
+ if (Object.keys(familyMultipliers).some((family) => !matchedFamilies.has(family))) {
583
+ return null;
584
+ }
585
+ mean = weights.reduce((sum, weight) => sum + weight, 0) / weights.length;
586
+ if (!Number.isFinite(mean) || mean <= 0) return null;
587
+ for (let index = 0; index < weights.length; index += 1) weights[index] /= mean;
588
+ }
589
+ return weights;
590
+ }
591
+
592
+ function lossCap(diagnostics: JsonObject): number | null {
593
+ const options = asObject(diagnostics.options);
594
+ const producer = asObject(diagnostics.build);
595
+ return finiteNumber(asObject(options.target_loss_scales).cap)
596
+ ?? finiteNumber(options.target_loss_cap)
597
+ ?? finiteNumber(producer.target_loss_cap);
598
+ }
599
+
600
+ function supportContentFailure(
601
+ support: HistoricalAttributionSupport,
602
+ evidence: Pick<
603
+ HistoricalAttributionEvidence,
604
+ | "releaseFamily"
605
+ | "diagnosticsSchema"
606
+ | "targetCount"
607
+ | "orderedTargetNamesSha256"
608
+ | "producerTargetSurfaceSha256"
609
+ | "weightingIdentifier"
610
+ | "lossCap"
611
+ >,
612
+ ): string | null {
613
+ if (support.diagnosticsSchema !== evidence.diagnosticsSchema) return "diagnostics schema mismatch";
614
+ if (support.releaseFamily !== evidence.releaseFamily) return "release family mismatch";
615
+ if (support.targetCount !== evidence.targetCount) return "target count mismatch";
616
+ if (support.orderedTargetNamesSha256 !== evidence.orderedTargetNamesSha256) {
617
+ return "ordered target surface mismatch";
618
+ }
619
+ if (
620
+ support.producerTargetSurfaceSha256 &&
621
+ evidence.producerTargetSurfaceSha256 !== support.producerTargetSurfaceSha256
622
+ ) {
623
+ return "diagnostics target-surface digest does not match the pinned parent digest";
624
+ }
625
+ if (support.weightingIdentifier !== evidence.weightingIdentifier) {
626
+ return "weighting identifier mismatch";
627
+ }
628
+ if (support.lossCap != null && support.lossCap !== evidence.lossCap) {
629
+ return "target-loss cap mismatch";
630
+ }
631
+ return null;
632
+ }
633
+
634
+ function supportEvidenceFailure(
635
+ support: HistoricalAttributionSupport,
636
+ evidence: HistoricalAttributionEvidence,
637
+ ): string | null {
638
+ if (support.buildId !== evidence.buildId) return "build identifier mismatch";
639
+ if (support.buildSha && support.buildSha !== evidence.buildSha) {
640
+ return "build SHA mismatch";
641
+ }
642
+ if (support.producerCommit && support.producerCommit !== evidence.producerCommit) {
643
+ return "producer commit mismatch";
644
+ }
645
+ return supportContentFailure(support, evidence);
646
+ }
647
+
648
+ export function classifyHistoricalAttributionEvidence(
649
+ evidence: HistoricalAttributionEvidence,
650
+ ): HistoricalAttributionClassification {
651
+ const support = HISTORICAL_ATTRIBUTION_SUPPORT_BY_RELEASE.get(evidence.releaseId);
652
+ if (!support) {
653
+ return {
654
+ status: "unavailable",
655
+ recipe: null,
656
+ reason: "No pinned historical attribution recipe matches this release.",
657
+ };
658
+ }
659
+ const failure = supportEvidenceFailure(support, evidence);
660
+ return failure
661
+ ? { status: "unavailable", recipe: null, reason: failure }
662
+ : { status: support.expectedStatus, recipe: support.recipe, reason: null };
663
+ }
664
+
665
+ export function classifyInheritedAttributionEvidence(
666
+ evidence: InheritedAttributionEvidence,
667
+ ): HistoricalAttributionClassification {
668
+ if (!evidence.parentBuildId) {
669
+ return {
670
+ status: "unavailable",
671
+ recipe: null,
672
+ reason: "inherited calibration does not declare a parent build identifier",
673
+ };
674
+ }
675
+ const support = HISTORICAL_ATTRIBUTION_SUPPORT_BY_RELEASE.get(evidence.parentBuildId);
676
+ if (!support || support.buildId !== evidence.parentBuildId) {
677
+ return {
678
+ status: "unavailable",
679
+ recipe: null,
680
+ reason: "no pinned historical attribution recipe matches the declared parent build",
681
+ };
682
+ }
683
+ if (
684
+ evidence.declaredDiagnosticsSchema == null ||
685
+ !Number.isInteger(evidence.declaredDiagnosticsSchema)
686
+ ) {
687
+ return {
688
+ status: "unavailable",
689
+ recipe: null,
690
+ reason: "inherited calibration does not declare an integer diagnostics schema version",
691
+ };
692
+ }
693
+ if (evidence.declaredDiagnosticsSchema !== evidence.diagnosticsSchema) {
694
+ return {
695
+ status: "unavailable",
696
+ recipe: null,
697
+ reason: "declared diagnostics schema does not match the downloaded diagnostics",
698
+ };
699
+ }
700
+ const declaredDigest = normalizedSha256(evidence.declaredDiagnosticsSha256);
701
+ if (!declaredDigest) {
702
+ return {
703
+ status: "unavailable",
704
+ recipe: null,
705
+ reason: "inherited calibration does not declare a valid diagnostics SHA-256 digest",
706
+ };
707
+ }
708
+ const observedDigest = normalizedSha256(evidence.observedDiagnosticsSha256);
709
+ if (!observedDigest) {
710
+ return {
711
+ status: "unavailable",
712
+ recipe: null,
713
+ reason: "the downloaded diagnostics SHA-256 digest is unavailable",
714
+ };
715
+ }
716
+ if (observedDigest !== declaredDigest) {
717
+ return {
718
+ status: "unavailable",
719
+ recipe: null,
720
+ reason: "downloaded diagnostics do not match the digest declared by the release",
721
+ };
722
+ }
723
+ const parentDigest = normalizedSha256(support.diagnosticsSha256);
724
+ if (!parentDigest) {
725
+ return {
726
+ status: "unavailable",
727
+ recipe: null,
728
+ reason: "the declared parent has no pinned diagnostics digest",
729
+ };
730
+ }
731
+ if (observedDigest !== parentDigest) {
732
+ return {
733
+ status: "unavailable",
734
+ recipe: null,
735
+ reason: "downloaded diagnostics do not match the pinned parent artifact",
736
+ };
737
+ }
738
+ if (!support.producerTargetSurfaceSha256) {
739
+ return {
740
+ status: "unavailable",
741
+ recipe: null,
742
+ reason: "the declared parent has no pinned target-surface digest",
743
+ };
744
+ }
745
+ if (!normalizedSha256(evidence.producerTargetSurfaceSha256)) {
746
+ return {
747
+ status: "unavailable",
748
+ recipe: null,
749
+ reason: "downloaded diagnostics do not declare a valid target-surface digest",
750
+ };
751
+ }
752
+ const failure = supportContentFailure(support, evidence);
753
+ return failure
754
+ ? { status: "unavailable", recipe: null, reason: failure }
755
+ : { status: support.expectedStatus, recipe: support.recipe, reason: null };
756
+ }
757
+
758
+ function targetSurfaceSha256(
759
+ diagnostics: JsonObject,
760
+ buildManifest: JsonObject,
761
+ ): string | null {
762
+ return optionalString(asObject(diagnostics.target_surface).sha256)
763
+ ?? optionalString(asObject(asObject(buildManifest.calibration).target_surface).sha256);
764
+ }
765
+
766
+ function historicalEvidence(
767
+ releaseId: string,
768
+ diagnostics: JsonObject,
769
+ rows: JsonObject[],
770
+ buildManifest: JsonObject,
771
+ releaseFamily: "national" | "local_area",
772
+ ): HistoricalAttributionEvidence {
773
+ return {
774
+ releaseId,
775
+ buildId: optionalString(buildManifest.build_id),
776
+ buildSha: optionalString(buildManifest.build_sha),
777
+ producerCommit: optionalString(asObject(buildManifest.code).git_commit),
778
+ releaseFamily,
779
+ diagnosticsSchema: finiteNumber(diagnostics.schema_version),
780
+ targetCount: rows.length,
781
+ orderedTargetNamesSha256: orderedTargetNamesHash(rows),
782
+ producerTargetSurfaceSha256: targetSurfaceSha256(diagnostics, buildManifest),
783
+ weightingIdentifier: optionalString(asObject(diagnostics.build).target_loss_weighting),
784
+ lossCap: lossCap(diagnostics),
785
+ };
786
+ }
787
+
788
+ export function reconstructTargetLossAttribution(
789
+ support: HistoricalAttributionSupport,
790
+ diagnostics: JsonObject,
791
+ rows: JsonObject[],
792
+ finalLoss: number | null,
793
+ warnings: TargetLossDiagnosticWarning[],
794
+ ): FinalTargetLossAttribution {
795
+ if (finalLoss == null) return unavailable(finalLoss, "Historical diagnostics have no finite final_loss.", warnings);
796
+
797
+ const recipe: HistoricalAttributionRecipe = support.recipe;
798
+ const isDerived = recipe === "legacy_current_doctrine_v1";
799
+ const cap = isDerived || recipe === "uniform_target_scale_cap_100pct_v1"
800
+ ? 1
801
+ : lossCap(diagnostics);
802
+ if (cap == null || !Number.isFinite(cap) || cap <= 0) {
803
+ return unavailable(finalLoss, "The supported recipe has no valid loss cap.", warnings);
804
+ }
805
+ const scaleSummary = asObject(asObject(diagnostics.options).target_loss_scales);
806
+ const scaleKind = optionalString(scaleSummary.kind);
807
+ if (!isDerived && recipe !== "uniform_target_scale_cap_100pct_v1" && scaleKind !== "default_target") {
808
+ return unavailable(finalLoss, "The historical recipe requires default target-derived scales.", warnings);
809
+ }
810
+ const producer = asObject(diagnostics.build);
811
+ const rawMultipliers = producer.target_loss_family_multipliers;
812
+ const familyMultipliers = rawMultipliers == null ? null : asObject(rawMultipliers);
813
+ if (rawMultipliers != null && familyMultipliers && !Object.keys(familyMultipliers).length) {
814
+ return unavailable(finalLoss, "Declared target-family multipliers are incomplete.", warnings);
815
+ }
816
+
817
+ let weights: number[] | null;
818
+ if (recipe === "uniform_target_scale_cap_100pct_v1") {
819
+ weights = rows.map(() => 1);
820
+ } else if (recipe === "historical_sqrt_value_50_50_v1") {
821
+ weights = reconstructedWeights(rows, historicalBasis, false, familyMultipliers);
822
+ } else if (recipe === "newer_sqrt_value_50_50_v2") {
823
+ weights = reconstructedWeights(rows, newerBasis, false, familyMultipliers);
824
+ } else if (recipe === "concept_budget_sqrt_value_50_50_v3") {
825
+ weights = reconstructedWeights(rows, newerBasis, true, familyMultipliers);
826
+ } else {
827
+ weights = reconstructedWeights(rows, legacyCurrentDoctrineBasis, true, null);
828
+ }
829
+ if (!weights || weights.length !== rows.length) {
830
+ return unavailable(finalLoss, "The supported weighting recipe could not reconstruct every target.", warnings);
831
+ }
832
+ const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
833
+ if (!Number.isFinite(totalWeight) || totalWeight <= 0) {
834
+ return unavailable(finalLoss, "The reconstructed target weights have no positive total.", warnings);
835
+ }
836
+
837
+ const targets: FinalTargetLossAttributionTarget[] = [];
838
+ for (const [index, row] of rows.entries()) {
839
+ const name = targetName(row);
840
+ const target = targetValue(row);
841
+ const estimate = finalEstimate(row);
842
+ if (!name || target == null || estimate == null) {
843
+ return unavailable(finalLoss, `Target row ${index} lacks a finite target or final estimate.`, warnings);
844
+ }
845
+ const scale = Math.max(Math.abs(target), 1);
846
+ const weight = weights[index];
847
+ const share = weight / totalWeight;
848
+ const cappedError = Math.min(Math.abs(estimate - target) / scale, cap);
849
+ targets.push({
850
+ index,
851
+ name,
852
+ target_loss_weight: weight,
853
+ target_loss_weight_share: share,
854
+ target_loss_scale: scale,
855
+ final_capped_scaled_error: cappedError,
856
+ final_loss_contribution: share * cappedError,
857
+ });
858
+ }
859
+ const aggregate = targets.reduce((sum, target) => sum + target.final_loss_contribution, 0);
860
+ const difference = aggregate - finalLoss;
861
+ if (!isDerived && !isClose(aggregate, finalLoss, support.tolerance)) {
862
+ return unavailable(
863
+ finalLoss,
864
+ "The supported historical recipe does not reproduce final_loss within its pinned tolerance.",
865
+ warnings,
866
+ );
867
+ }
868
+ return {
869
+ status: isDerived ? "derived" : "exact_reconstructed",
870
+ aggregate,
871
+ historical_final_loss: finalLoss,
872
+ cap,
873
+ basis_identifier: recipe,
874
+ basis_hash: targetLossBasisHash(targets),
875
+ verification: {
876
+ valid: isDerived ? true : isClose(aggregate, finalLoss, support.tolerance),
877
+ difference: isDerived ? null : difference,
878
+ tolerance: isDerived ? NOT_APPLICABLE_TOLERANCE : support.tolerance,
879
+ },
880
+ producer_warnings: warnings,
881
+ reason: isDerived
882
+ ? "Derived under the pinned current-doctrine recipe; historical final_loss remains separate."
883
+ : null,
884
+ targets,
885
+ };
886
+ }
887
+
888
+ export function normalizeTargetLossAttribution(
889
+ input: NormalizeTargetLossAttributionInput,
890
+ ): NormalizedTargetLossAttributionResult {
891
+ const {
892
+ diagnostics,
893
+ releaseId,
894
+ buildManifest,
895
+ releaseFamily,
896
+ diagnosticsSha256 = null,
897
+ } = input;
898
+ const rows = input.rows.map(stripAttributionFields);
899
+ const finalLoss = finiteNumber(diagnostics.final_loss);
900
+ const warnings = diagnosticWarnings(diagnostics);
901
+ const schemaVersion = finiteNumber(diagnostics.schema_version);
902
+ const calibration = asObject(buildManifest.calibration);
903
+ const inherited = calibration.mode === "inherited";
904
+ const parentBuildId = inherited ? optionalString(calibration.parent_build_id) : null;
905
+ const inheritedClassification = inherited
906
+ ? classifyInheritedAttributionEvidence({
907
+ parentBuildId,
908
+ declaredDiagnosticsSchema: finiteNumber(calibration.diagnostics_schema_version),
909
+ declaredDiagnosticsSha256: optionalString(calibration.diagnostics_sha256),
910
+ observedDiagnosticsSha256: diagnosticsSha256,
911
+ releaseFamily,
912
+ diagnosticsSchema: schemaVersion,
913
+ targetCount: input.rows.length,
914
+ orderedTargetNamesSha256: orderedTargetNamesHash(input.rows),
915
+ producerTargetSurfaceSha256: targetSurfaceSha256(diagnostics, buildManifest),
916
+ weightingIdentifier: optionalString(asObject(diagnostics.build).target_loss_weighting),
917
+ lossCap: lossCap(diagnostics),
918
+ })
919
+ : null;
920
+
921
+ let attribution: FinalTargetLossAttribution;
922
+ if (inheritedClassification?.status === "unavailable") {
923
+ attribution = unavailable(
924
+ finalLoss,
925
+ `Inherited calibration diagnostics rejected: ${inheritedClassification.reason}.`,
926
+ warnings,
927
+ );
928
+ } else if (schemaVersion != null && schemaVersion >= 6) {
929
+ attribution = reportedAttribution(diagnostics, input.rows, finalLoss, warnings);
930
+ } else {
931
+ const supportReleaseId = inherited ? parentBuildId : releaseId;
932
+ const support = supportReleaseId
933
+ ? HISTORICAL_ATTRIBUTION_SUPPORT_BY_RELEASE.get(supportReleaseId)
934
+ : undefined;
935
+ if (!support) {
936
+ attribution = unavailable(finalLoss, "No pinned historical attribution recipe matches this release.", warnings);
937
+ } else if (inherited) {
938
+ attribution = reconstructTargetLossAttribution(
939
+ support,
940
+ diagnostics,
941
+ input.rows,
942
+ finalLoss,
943
+ warnings,
944
+ );
945
+ } else {
946
+ const classification = classifyHistoricalAttributionEvidence(
947
+ historicalEvidence(releaseId, diagnostics, input.rows, buildManifest, releaseFamily),
948
+ );
949
+ attribution = classification.status === "unavailable"
950
+ ? unavailable(
951
+ finalLoss,
952
+ `Historical attribution support rejected: ${classification.reason}.`,
953
+ warnings,
954
+ )
955
+ : reconstructTargetLossAttribution(
956
+ support,
957
+ diagnostics,
958
+ input.rows,
959
+ finalLoss,
960
+ warnings,
961
+ );
962
+ }
963
+ }
964
+ const inheritedValidationReason = inheritedClassification?.reason
965
+ ?? (inherited && attribution.status === "unavailable" ? attribution.reason : null);
966
+ const provenance: CalibrationProvenance = inherited
967
+ ? {
968
+ mode: "inherited",
969
+ dataset_release_id: releaseId,
970
+ calibration_source_id: parentBuildId,
971
+ diagnostics_sha256: normalizedSha256(diagnosticsSha256),
972
+ target_surface_sha256: normalizedSha256(
973
+ targetSurfaceSha256(diagnostics, buildManifest),
974
+ ),
975
+ validation: {
976
+ status: inheritedValidationReason ? "failed" : "verified",
977
+ reason: inheritedValidationReason,
978
+ },
979
+ }
980
+ : {
981
+ mode: "direct",
982
+ dataset_release_id: releaseId,
983
+ calibration_source_id: releaseId,
984
+ diagnostics_sha256: normalizedSha256(diagnosticsSha256),
985
+ target_surface_sha256: normalizedSha256(
986
+ targetSurfaceSha256(diagnostics, buildManifest),
987
+ ),
988
+ validation: { status: "not_applicable", reason: null },
989
+ };
990
+ return {
991
+ attribution,
992
+ provenance,
993
+ rows: attribution.status === "unavailable"
994
+ ? rows
995
+ : rowsWithAttribution(rows, attribution.targets),
996
+ };
997
+ }
998
+
999
+ export function targetLossAttributionSummary(attribution: FinalTargetLossAttribution) {
1000
+ return {
1001
+ status: attribution.status,
1002
+ aggregate: attribution.aggregate,
1003
+ historical_final_loss: attribution.historical_final_loss,
1004
+ cap: attribution.cap,
1005
+ basis_identifier: attribution.basis_identifier,
1006
+ basis_hash: attribution.basis_hash,
1007
+ verification: attribution.verification,
1008
+ producer_warnings: attribution.producer_warnings,
1009
+ reason: attribution.reason,
1010
+ target_count: attribution.targets.length,
1011
+ };
1012
+ }