@policyengine/calibration-tree 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +58 -2
- package/package.json +71 -4
- package/src/calibration-build-comparison.ts +77 -0
- package/src/calibration-comparison-blob.ts +367 -0
- package/src/calibration-comparison-bundle.ts +149 -0
- package/src/calibration-explorer.ts +202 -0
- package/src/calibration-release-locator.ts +353 -0
- package/src/calibration-tree-artifact.ts +1974 -0
- package/src/calibration-tree-blob.ts +403 -0
- package/src/calibration-tree-bundle.ts +569 -0
- package/src/calibration-tree-manifest.ts +349 -0
- package/src/calibration-tree-worker.ts +445 -0
- package/src/calibration-tree.ts +1284 -0
- package/src/chronicle-metadata.ts +42 -0
- package/src/countries.ts +355 -0
- package/src/geography-labels.ts +59 -0
- package/src/hierarchy-catalog-reader.ts +297 -0
- package/src/hierarchy-target-reader.ts +243 -0
- package/src/historical-weight-summary.ts +53 -0
- package/src/latest-artifact.ts +3767 -0
- package/src/legacy-target-reader.ts +337 -0
- package/src/program-label.ts +64 -0
- package/src/publish-calibration-tree.ts +1984 -0
- package/src/reforms.ts +224 -0
- package/src/release-cut.ts +32 -0
- package/src/release-record.ts +277 -0
- package/src/source-label.ts +5 -0
- package/src/source-labels.ts +71 -0
- package/src/staging-artifact.ts +1411 -0
- package/src/staging-contract.ts +916 -0
- package/src/structured-dimension-reader.ts +137 -0
- package/src/structured-target-reader.ts +76 -0
- package/src/target-change.ts +421 -0
- package/src/target-loss-attribution-manifest.ts +436 -0
- package/src/target-loss-attribution.ts +1012 -0
- package/src/target-representation.ts +69 -0
- package/src/target-surface-matcher.ts +266 -0
- package/types/next-fetch-cache.d.ts +8 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export type TargetRepresentation =
|
|
2
|
+
| "legacy"
|
|
3
|
+
| "structured"
|
|
4
|
+
| "hierarchy"
|
|
5
|
+
| "mixed"
|
|
6
|
+
| "unknown";
|
|
7
|
+
|
|
8
|
+
export type TargetRowRepresentation = "legacy" | "structured" | "hierarchy";
|
|
9
|
+
|
|
10
|
+
export class UnsupportedCalibrationDiagnosticsSchemaError extends Error {}
|
|
11
|
+
|
|
12
|
+
const KNOWN_UNVERSIONED_LEGACY_DIAGNOSTICS = new Map([
|
|
13
|
+
[
|
|
14
|
+
"populace-us-2024-buildo-acs-local-77e2061-20260724T110908Z",
|
|
15
|
+
"54251f2209c2e71bb1cce9f5626a3e59879847747156e873de51011c8e8b508f",
|
|
16
|
+
],
|
|
17
|
+
[
|
|
18
|
+
"populace-us-2024-buildp-acs-local-592ae5d6-20260819T020303Z",
|
|
19
|
+
"b6f05b652049f88c044f1907eeed13c378aea47aec9c6dd6518faa90d1a0dcd0",
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
"populace-us-2024-buildo-acs-local-767312d60-20260923T074941Z",
|
|
23
|
+
"f39d10a72415eb85ac0faa231f7970bffcc408bd53ef0f05985ea8dd0ac94903",
|
|
24
|
+
],
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Select the only target reader permitted for a diagnostics artifact.
|
|
29
|
+
*
|
|
30
|
+
* Row shape is deliberately not inspected: the top-level schema version is
|
|
31
|
+
* the compatibility contract between Microcosm and this dashboard.
|
|
32
|
+
*/
|
|
33
|
+
export function targetRepresentationForSchema(
|
|
34
|
+
schemaVersion: unknown,
|
|
35
|
+
): TargetRowRepresentation {
|
|
36
|
+
if (
|
|
37
|
+
typeof schemaVersion !== "number" ||
|
|
38
|
+
!Number.isInteger(schemaVersion)
|
|
39
|
+
) {
|
|
40
|
+
throw new UnsupportedCalibrationDiagnosticsSchemaError(
|
|
41
|
+
"Calibration diagnostics must declare an integer schema_version.",
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (schemaVersion >= 1 && schemaVersion <= 6) return "legacy";
|
|
45
|
+
if (schemaVersion === 7) return "structured";
|
|
46
|
+
// Schema 9 declares the schema 8 hierarchy's labels in catalogs;
|
|
47
|
+
// `expandHierarchyCatalogs` turns its rows back into schema 8 rows.
|
|
48
|
+
if (schemaVersion === 8 || schemaVersion === 9) return "hierarchy";
|
|
49
|
+
throw new UnsupportedCalibrationDiagnosticsSchemaError(
|
|
50
|
+
`Unsupported calibration diagnostics schema_version ${schemaVersion}; ` +
|
|
51
|
+
"this dashboard reads versions 1 through 9.",
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function targetRepresentationForDiagnosticsArtifact(
|
|
56
|
+
schemaVersion: unknown,
|
|
57
|
+
identity: { releaseId: string; sha256: string | null },
|
|
58
|
+
): TargetRowRepresentation {
|
|
59
|
+
if (schemaVersion != null) {
|
|
60
|
+
return targetRepresentationForSchema(schemaVersion);
|
|
61
|
+
}
|
|
62
|
+
const expectedSha256 = KNOWN_UNVERSIONED_LEGACY_DIAGNOSTICS.get(
|
|
63
|
+
identity.releaseId,
|
|
64
|
+
);
|
|
65
|
+
if (expectedSha256 && identity.sha256 === expectedSha256) {
|
|
66
|
+
return "legacy";
|
|
67
|
+
}
|
|
68
|
+
return targetRepresentationForSchema(schemaVersion);
|
|
69
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import type { Calibration } from "./latest-artifact";
|
|
2
|
+
import type {
|
|
3
|
+
TargetRepresentation,
|
|
4
|
+
TargetRowRepresentation,
|
|
5
|
+
} from "./target-representation";
|
|
6
|
+
|
|
7
|
+
type TargetRow = Calibration["rows"][number];
|
|
8
|
+
|
|
9
|
+
export type TargetMatchKind =
|
|
10
|
+
| "base_name"
|
|
11
|
+
| "chronicle_fact_key"
|
|
12
|
+
| "structured_identity";
|
|
13
|
+
export type TargetSurfaceStatus = "shared" | "added" | "removed";
|
|
14
|
+
|
|
15
|
+
export interface TargetMatchingCounts {
|
|
16
|
+
base_name: number;
|
|
17
|
+
chronicle_fact_key: number;
|
|
18
|
+
structured_identity: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface TargetMatchingSummary {
|
|
22
|
+
current_representation: TargetRepresentation;
|
|
23
|
+
candidate_representation: TargetRepresentation;
|
|
24
|
+
matched_by: TargetMatchingCounts;
|
|
25
|
+
ambiguous_key_groups: TargetMatchingCounts;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TargetSurfaceMatch {
|
|
29
|
+
comparison_id: string;
|
|
30
|
+
comparison_status: TargetSurfaceStatus;
|
|
31
|
+
match_kind: TargetMatchKind | null;
|
|
32
|
+
current: TargetRow | null;
|
|
33
|
+
candidate: TargetRow | null;
|
|
34
|
+
current_name: string | null;
|
|
35
|
+
candidate_name: string | null;
|
|
36
|
+
current_representation: TargetRowRepresentation | null;
|
|
37
|
+
candidate_representation: TargetRowRepresentation | null;
|
|
38
|
+
current_target_ordinal: number | null;
|
|
39
|
+
candidate_target_ordinal: number | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface TargetSurfaceMatchResult {
|
|
43
|
+
matches: TargetSurfaceMatch[];
|
|
44
|
+
matching: TargetMatchingSummary;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface IndexedTarget {
|
|
48
|
+
index: number;
|
|
49
|
+
row: TargetRow;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface MatchStrategy {
|
|
53
|
+
kind: TargetMatchKind;
|
|
54
|
+
key: (row: TargetRow) => string | null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function asObject(value: unknown): Record<string, unknown> {
|
|
58
|
+
return value != null && typeof value === "object" && !Array.isArray(value)
|
|
59
|
+
? value as Record<string, unknown>
|
|
60
|
+
: {};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function nonEmptyString(value: unknown): string | null {
|
|
64
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function normalizedTargetName(row: TargetRow): string | null {
|
|
68
|
+
return nonEmptyString(row.base_name) ?? nonEmptyString(row.name);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function originalTargetName(row: TargetRow): string | null {
|
|
72
|
+
return nonEmptyString(row.name) ?? nonEmptyString(row.base_name);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function targetRowRepresentation(row: TargetRow): TargetRowRepresentation {
|
|
76
|
+
if (row.target_representation === "hierarchy") return "hierarchy";
|
|
77
|
+
if (row.target_representation === "structured") return "structured";
|
|
78
|
+
if (row.target_representation === "legacy") return "legacy";
|
|
79
|
+
return row.dimension_adapter === "structured" ? "structured" : "legacy";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function collectionRepresentation(calibration: Calibration): TargetRepresentation {
|
|
83
|
+
const published = calibration.target_schema?.target_representation;
|
|
84
|
+
if (
|
|
85
|
+
published === "legacy" ||
|
|
86
|
+
published === "structured" ||
|
|
87
|
+
published === "hierarchy" ||
|
|
88
|
+
published === "mixed" ||
|
|
89
|
+
published === "unknown"
|
|
90
|
+
) {
|
|
91
|
+
return published;
|
|
92
|
+
}
|
|
93
|
+
if (!calibration.rows.length) return "unknown";
|
|
94
|
+
const representations = new Set(calibration.rows.map(targetRowRepresentation));
|
|
95
|
+
if (representations.size > 1) return "mixed";
|
|
96
|
+
if (representations.has("hierarchy")) return "hierarchy";
|
|
97
|
+
return representations.has("structured") ? "structured" : "legacy";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function chronicleFactKey(row: TargetRow): string | null {
|
|
101
|
+
return nonEmptyString(asObject(row.chronicle).fact_key);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function structuredIdentity(row: TargetRow): string | null {
|
|
105
|
+
const representation = targetRowRepresentation(row);
|
|
106
|
+
if (representation !== "structured" && representation !== "hierarchy") {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const source = nonEmptyString(row.source);
|
|
110
|
+
const variable = nonEmptyString(row.variable);
|
|
111
|
+
if (!source || !variable) return null;
|
|
112
|
+
const dimensions = representation === "hierarchy"
|
|
113
|
+
? (Array.isArray(row.target_dimensions) ? row.target_dimensions : [])
|
|
114
|
+
.flatMap((dimension) => {
|
|
115
|
+
const id = nonEmptyString(dimension.source_key ?? dimension.key);
|
|
116
|
+
const rawValue = nonEmptyString(
|
|
117
|
+
dimension.raw_value ?? dimension.value_id ?? dimension.value,
|
|
118
|
+
);
|
|
119
|
+
return id && rawValue ? [[id, rawValue] as const] : [];
|
|
120
|
+
})
|
|
121
|
+
: Object.entries(asObject(row.dimensions))
|
|
122
|
+
.flatMap(([id, value]) => {
|
|
123
|
+
const rawValue = nonEmptyString(value);
|
|
124
|
+
return id && rawValue ? [[id, rawValue] as const] : [];
|
|
125
|
+
})
|
|
126
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
127
|
+
return JSON.stringify({
|
|
128
|
+
source,
|
|
129
|
+
variable,
|
|
130
|
+
measure: nonEmptyString(row.measure),
|
|
131
|
+
dimensions,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function groupsByKey(
|
|
136
|
+
rows: Map<number, IndexedTarget>,
|
|
137
|
+
keyOf: MatchStrategy["key"],
|
|
138
|
+
): Map<string, IndexedTarget[]> {
|
|
139
|
+
const groups = new Map<string, IndexedTarget[]>();
|
|
140
|
+
for (const indexed of rows.values()) {
|
|
141
|
+
const key = keyOf(indexed.row);
|
|
142
|
+
if (!key) continue;
|
|
143
|
+
const group = groups.get(key) ?? [];
|
|
144
|
+
group.push(indexed);
|
|
145
|
+
groups.set(key, group);
|
|
146
|
+
}
|
|
147
|
+
return groups;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function encoded(value: string): string {
|
|
151
|
+
return encodeURIComponent(value);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function sharedMatch(
|
|
155
|
+
kind: TargetMatchKind,
|
|
156
|
+
key: string,
|
|
157
|
+
current: IndexedTarget,
|
|
158
|
+
candidate: IndexedTarget,
|
|
159
|
+
): TargetSurfaceMatch {
|
|
160
|
+
return {
|
|
161
|
+
comparison_id: `shared:${kind}:${encoded(key)}`,
|
|
162
|
+
comparison_status: "shared",
|
|
163
|
+
match_kind: kind,
|
|
164
|
+
current: current.row,
|
|
165
|
+
candidate: candidate.row,
|
|
166
|
+
current_name: originalTargetName(current.row),
|
|
167
|
+
candidate_name: originalTargetName(candidate.row),
|
|
168
|
+
current_representation: targetRowRepresentation(current.row),
|
|
169
|
+
candidate_representation: targetRowRepresentation(candidate.row),
|
|
170
|
+
current_target_ordinal: current.index,
|
|
171
|
+
candidate_target_ordinal: candidate.index,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function sideOnlyMatch(
|
|
176
|
+
side: "current" | "candidate",
|
|
177
|
+
indexed: IndexedTarget,
|
|
178
|
+
): TargetSurfaceMatch {
|
|
179
|
+
const row = indexed.row;
|
|
180
|
+
const name = originalTargetName(row);
|
|
181
|
+
return {
|
|
182
|
+
comparison_id: `${side}:${indexed.index}:${encoded(normalizedTargetName(row) ?? name ?? "")}`,
|
|
183
|
+
comparison_status: side === "current" ? "removed" : "added",
|
|
184
|
+
match_kind: null,
|
|
185
|
+
current: side === "current" ? row : null,
|
|
186
|
+
candidate: side === "candidate" ? row : null,
|
|
187
|
+
current_name: side === "current" ? name : null,
|
|
188
|
+
candidate_name: side === "candidate" ? name : null,
|
|
189
|
+
current_representation:
|
|
190
|
+
side === "current" ? targetRowRepresentation(row) : null,
|
|
191
|
+
candidate_representation:
|
|
192
|
+
side === "candidate" ? targetRowRepresentation(row) : null,
|
|
193
|
+
current_target_ordinal: side === "current" ? indexed.index : null,
|
|
194
|
+
candidate_target_ordinal: side === "candidate" ? indexed.index : null,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const MATCH_STRATEGIES: MatchStrategy[] = [
|
|
199
|
+
{ kind: "base_name", key: normalizedTargetName },
|
|
200
|
+
{ kind: "chronicle_fact_key", key: chronicleFactKey },
|
|
201
|
+
{ kind: "structured_identity", key: structuredIdentity },
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
function zeroCounts(): TargetMatchingCounts {
|
|
205
|
+
return {
|
|
206
|
+
base_name: 0,
|
|
207
|
+
chronicle_fact_key: 0,
|
|
208
|
+
structured_identity: 0,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function matchTargetSurfaces(
|
|
213
|
+
currentCalibration: Calibration,
|
|
214
|
+
candidateCalibration: Calibration,
|
|
215
|
+
): TargetSurfaceMatchResult {
|
|
216
|
+
const current = new Map(
|
|
217
|
+
currentCalibration.rows.map((row, index) => [index, { index, row }]),
|
|
218
|
+
);
|
|
219
|
+
const candidate = new Map(
|
|
220
|
+
candidateCalibration.rows.map((row, index) => [index, { index, row }]),
|
|
221
|
+
);
|
|
222
|
+
const matchedBy = zeroCounts();
|
|
223
|
+
const ambiguousKeyGroups = zeroCounts();
|
|
224
|
+
const matches: TargetSurfaceMatch[] = [];
|
|
225
|
+
|
|
226
|
+
for (const strategy of MATCH_STRATEGIES) {
|
|
227
|
+
const currentGroups = groupsByKey(current, strategy.key);
|
|
228
|
+
const candidateGroups = groupsByKey(candidate, strategy.key);
|
|
229
|
+
const keys = [...new Set([...currentGroups.keys(), ...candidateGroups.keys()])]
|
|
230
|
+
.sort((left, right) => left.localeCompare(right));
|
|
231
|
+
for (const key of keys) {
|
|
232
|
+
const currentGroup = currentGroups.get(key) ?? [];
|
|
233
|
+
const candidateGroup = candidateGroups.get(key) ?? [];
|
|
234
|
+
if (!currentGroup.length || !candidateGroup.length) continue;
|
|
235
|
+
if (currentGroup.length !== 1 || candidateGroup.length !== 1) {
|
|
236
|
+
ambiguousKeyGroups[strategy.kind] += 1;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const currentTarget = currentGroup[0];
|
|
240
|
+
const candidateTarget = candidateGroup[0];
|
|
241
|
+
matches.push(sharedMatch(strategy.kind, key, currentTarget, candidateTarget));
|
|
242
|
+
matchedBy[strategy.kind] += 1;
|
|
243
|
+
current.delete(currentTarget.index);
|
|
244
|
+
candidate.delete(candidateTarget.index);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
matches.push(
|
|
249
|
+
...[...current.values()]
|
|
250
|
+
.sort((left, right) => left.index - right.index)
|
|
251
|
+
.map((row) => sideOnlyMatch("current", row)),
|
|
252
|
+
...[...candidate.values()]
|
|
253
|
+
.sort((left, right) => left.index - right.index)
|
|
254
|
+
.map((row) => sideOnlyMatch("candidate", row)),
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
matches,
|
|
259
|
+
matching: {
|
|
260
|
+
current_representation: collectionRepresentation(currentCalibration),
|
|
261
|
+
candidate_representation: collectionRepresentation(candidateCalibration),
|
|
262
|
+
matched_by: matchedBy,
|
|
263
|
+
ambiguous_key_groups: ambiguousKeyGroups,
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// The dashboard calls the release and staging readers inside Next.js, whose
|
|
2
|
+
// fetch reads a `next.revalidate` hint to cache Hugging Face responses; Bun
|
|
3
|
+
// ignores the field. Next.js declares it on RequestInit for the dashboard.
|
|
4
|
+
// This declaration mirrors Next's NextFetchRequestConfig and is seen only by
|
|
5
|
+
// the package's own typecheck, so the package typechecks without Next.js.
|
|
6
|
+
interface RequestInit {
|
|
7
|
+
next?: { revalidate?: number | false; tags?: string[] } | undefined;
|
|
8
|
+
}
|