Skip to content
Merged
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,40 @@ Published-release endpoints accept `country=us|uk|be` (default `us`).
| `GET /api/microcosm/staging/runs` | List staging build runs |
| `GET /api/microcosm/staging/run?id=<run_id>` | One staging run's progress and its candidate diagnostics: an uploaded `calibration_diagnostics` telemetry artifact, or the `calibration_diagnostics.json` of the dataset bundle the run staged under `staged/<run_id>/` in the country's release repository (read at the commit the run recorded, digest-verified) |
| `GET /api/microcosm/staging/target-diagnostics?id=<run_id>&...` | Faceted diagnostics for a staging candidate once diagnostics exist |
| `GET /api/microcosm/staging/compare?run=<run_id>&release=latest` | Diff staging candidate against a published release |
| `GET /api/microcosm/staging/compare?run=<run_id>&release=latest` | Diff a staging candidate against a published release: by default the current release, or the newest published local-area build for a local-area candidate (see below) |
| `GET /api/microcosm/staging/target-change-tree?run=<run_id>&release=<resolved_id>&mode=reported\|shared&...` | One hierarchy level of weighted target-error changes for a staging candidate and an explicit current release |

`/api/microcosm/staging/compare` resolves `release=latest`, its default, for
each candidate:

- **Local-area candidates.** A run is a local-area candidate when the
`pipeline.id` its `run_manifest.json` records is one of the country's
`staging.local_area_pipelines` in the country registry. The UK lists
`uk-local-candidate`; no other country lists any, so their run manifests are
not read for this. A local-area candidate is compared with the most recently
published local-area build: among release builds in the tree manifest whose
release manifest's `dataset_role` names a local area (such as
`non_default_local_area`), the one with the latest recorded `createdAt`, else
`updatedAt`. The local-area release has no pointer file and is never
promoted, so it has no current build.
- **Every other candidate,** and a local-area candidate when the tree manifest
lists no local-area release build, is compared with the country's current
release. That includes a local-area release published on Hugging Face whose
calibration tree the dashboard has not built yet.

Only a local-area candidate's response adds `comparison_release`, which names
the release it was compared with:

| Field | Meaning |
|---|---|
| `release_id` | The release compared with, the same as `a.release_id` |
| `basis` | `newest_local_area_release`, or `current_release` when the tree manifest lists no local-area release build |
| `local_area_candidate` | Always `true` when the field is present |

An explicit `release=<id>` is compared as requested and adds no
`comparison_release`. Responses for US, Belgian and UK national candidates are
unchanged.

The staging target-change route supports two comparison modes. `reported` uses
each release's actual target weights and complete target surface, including
added and removed targets. `shared` restricts the calculation to shared targets,
Expand Down Expand Up @@ -203,6 +234,15 @@ backward-compatible `POPULACE_STAGING_HF_REPO` and
with `POPULACE_UK_STAGING_HF_REPO` or `POPULACE_UK_STAGING_HF_REVISION`. Never
use a `NEXT_PUBLIC_` variable for a private-repository credential.

A staging registration may also list `local_area_pipelines`: the
`pipeline.id` values, as run manifests record them, of pipelines whose
candidates are local-area builds. The Staging page and
`/api/microcosm/staging/compare` compare those candidates with the newest
published local-area build instead of the current release, and the page names
that release in its validation table and calibration map. The UK lists
`uk-local-candidate`, Microcosm's local-area candidate pipeline; its national
attempts run `uk-frs-calibration`. A country that lists none is unaffected.

The staging contract fixtures under
`frontend/packages/calibration-tree/src/fixtures/staging-contract/` are byte-identical copies
of Microcosm's canonical producer fixtures. The consumer tests pin each
Expand Down
87 changes: 87 additions & 0 deletions frontend/app/api/microcosm/staging/compare/route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { expect, test } from "bun:test";

import type {
StagingComparison,
StagingComparisonRelease,
} from "@policyengine/calibration-tree/staging-artifact";
import { createStagingCompareHandler } from "./route";

const DENSE_NEWEST = "microcosm-uk-2024-25-dense-20261019T000432Z-ee5c2dbe";
const LOCAL_CANDIDATE_RUN = "uk-local-candidate-f100-s42-20261019T000432Z-ee5c2dbe";

// The route passes a loaded comparison through unchanged, so the stub
// carries only the field the Staging page reads to name the compared
// release, not a whole comparison.
function loadedComparison(runId: string, releaseId: string): StagingComparison {
return {
available: true,
run_id: runId,
a: { release_id: releaseId },
} as unknown as StagingComparison;
}

function handler(resolved: StagingComparisonRelease) {
const calls: { resolved: string[]; loaded: Array<[string, string, string]> } = {
resolved: [],
loaded: [],
};
const get = createStagingCompareHandler({
resolveComparisonRelease: async (runId, country) => {
calls.resolved.push(`${country}:${runId}`);
return resolved;
},
loadComparison: async (runId, releaseId, _revalidate, country) => {
calls.loaded.push([country, runId, releaseId]);
return loadedComparison(runId, releaseId);
},
});
return { get, calls };
}

test("a US candidate's comparison response is unchanged", async () => {
const { get, calls } = handler({
release_id: "populace-us-2024-spm-receipts-20260923",
basis: "current_release",
local_area_candidate: false,
});
const response = await get(new Request("http://dashboard.test/api/microcosm/staging/compare?country=us&run=us-run"));
expect(await response.json()).toEqual({
available: true,
run_id: "us-run",
a: { release_id: "populace-us-2024-spm-receipts-20260923" },
});
expect(calls.loaded).toEqual([["us", "us-run", "populace-us-2024-spm-receipts-20260923"]]);
});

test("a UK local-area candidate's comparison names the local-area release it uses", async () => {
const resolved: StagingComparisonRelease = {
release_id: DENSE_NEWEST,
basis: "newest_local_area_release",
local_area_candidate: true,
};
const { get, calls } = handler(resolved);
const response = await get(new Request(
`http://dashboard.test/api/microcosm/staging/compare?country=uk&run=${LOCAL_CANDIDATE_RUN}&release=latest`,
));
expect(await response.json()).toEqual({
available: true,
run_id: LOCAL_CANDIDATE_RUN,
a: { release_id: DENSE_NEWEST },
comparison_release: resolved,
});
expect(calls.resolved).toEqual([`uk:${LOCAL_CANDIDATE_RUN}`]);
expect(calls.loaded).toEqual([["uk", LOCAL_CANDIDATE_RUN, DENSE_NEWEST]]);
});

test("an explicitly requested release is compared as requested", async () => {
const { get, calls } = handler({
release_id: DENSE_NEWEST,
basis: "newest_local_area_release",
local_area_candidate: true,
});
await get(new Request(
`http://dashboard.test/api/microcosm/staging/compare?country=uk&run=${LOCAL_CANDIDATE_RUN}&release=some-release`,
));
expect(calls.resolved).toEqual([]);
expect(calls.loaded).toEqual([["uk", LOCAL_CANDIDATE_RUN, "some-release"]]);
});
85 changes: 61 additions & 24 deletions frontend/app/api/microcosm/staging/compare/route.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,75 @@
import { NextResponse } from "next/server";

import { hasCapability } from "@policyengine/calibration-tree/countries";
import { hasCapability, type MicrocosmCountry } from "@policyengine/calibration-tree/countries";
import {
loadPointerReleaseId,
parseCountry,
scrub,
} from "@policyengine/calibration-tree/latest-artifact";
import { loadStagingComparison } from "@policyengine/calibration-tree/staging-artifact";
import {
loadStagingComparison,
resolveStagingComparisonRelease,
type StagingComparison,
type StagingComparisonRelease,
} from "@policyengine/calibration-tree/staging-artifact";

export const dynamic = "force-dynamic";
export const revalidate = 0;
export const runtime = "nodejs";
export const maxDuration = 300;

export async function GET(request: Request) {
const url = new URL(request.url);
const country = parseCountry(url.searchParams.get("country"));
const runId = url.searchParams.get("run")?.trim();
let release = url.searchParams.get("release")?.trim() || "latest";
if (!runId && hasCapability(country, "staging")) {
return NextResponse.json({ detail: "Provide a staging run id via ?run=." }, { status: 400 });
}
try {
if (release === "latest" && hasCapability(country, "staging")) {
release = (await loadPointerReleaseId(300, country)).release_id;
export interface StagingCompareRouteDependencies {
resolveComparisonRelease(
runId: string,
country: MicrocosmCountry,
): Promise<StagingComparisonRelease>;
loadComparison(
runId: string,
releaseId: string,
revalidate: number,
country: MicrocosmCountry,
): Promise<StagingComparison>;
}

export function createStagingCompareHandler(
dependencies: StagingCompareRouteDependencies = {
resolveComparisonRelease: (runId, country) =>
resolveStagingComparisonRelease(runId, country),
loadComparison: loadStagingComparison,
},
) {
return async function GET(request: Request) {
const url = new URL(request.url);
const country = parseCountry(url.searchParams.get("country"));
const runId = url.searchParams.get("run")?.trim();
let release = url.searchParams.get("release")?.trim() || "latest";
if (!runId && hasCapability(country, "staging")) {
return NextResponse.json({ detail: "Provide a staging run id via ?run=." }, { status: 400 });
}
return NextResponse.json(
scrub(await loadStagingComparison(runId ?? "", release, revalidate, country)),
{ headers: { "Cache-Control": "no-store" } },
);
} catch (error) {
return NextResponse.json(
{ detail: error instanceof Error ? error.message : String(error) },
{ status: 502 },
);
}
try {
// "latest" is the current release for a national candidate and the
// newest published local-area release for a local-area candidate; the
// response names the local-area choice so the page can say so.
let comparisonRelease: StagingComparisonRelease | null = null;
if (release === "latest" && hasCapability(country, "staging")) {
comparisonRelease = await dependencies.resolveComparisonRelease(runId ?? "", country);
release = comparisonRelease.release_id;
}
const comparison = await dependencies.loadComparison(runId ?? "", release, revalidate, country);
return NextResponse.json(
scrub(
comparisonRelease?.local_area_candidate
? { ...comparison, comparison_release: comparisonRelease }
: comparison,
),
{ headers: { "Cache-Control": "no-store" } },
);
} catch (error) {
return NextResponse.json(
{ detail: error instanceof Error ? error.message : String(error) },
{ status: 502 },
);
}
};
}

export const GET = createStagingCompareHandler();
3 changes: 3 additions & 0 deletions frontend/components/microcosm/calibration-explorer-map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,8 @@ interface CalibrationExplorerMapProps {
stagingRunId?: string;
stagingComparison?: {
releaseId: string;
/** Names that release, e.g. "Local-area release"; defaults to "Current release". */
releaseLabel?: string;
rows: MicrocosmComparisonRow[];
weightedTargetErrorChange: number | null;
};
Expand Down Expand Up @@ -506,6 +508,7 @@ function CalibrationExplorerMapForSource({
selectedTargetChange,
stagingComparison.weightedTargetErrorChange,
selectedTarget,
stagingComparison.releaseLabel,
)
: null;

Expand Down
12 changes: 9 additions & 3 deletions frontend/components/microcosm/microcosm-staging-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
formatStagingStatus,
} from "@/lib/microcosm/staging-status";
import { formatWeightedTargetError } from "@/lib/microcosm/target-change-visualization";
import { stagingComparisonBaseline } from "@/lib/microcosm/calibration-selection";

type LossKind = "normalized_target_loss" | "raw_optimizer_objective" | undefined;

Expand Down Expand Up @@ -694,7 +695,7 @@ const VALIDATION_METHOD_HELP = {
reformWithin10:
"Measures the share of scored out-of-sample reforms whose estimated effect is within 10% of the external benchmark. Out-of-sample means that the reform includes values that are not used as calibration targets; higher is better.",
targetCoverage:
"Counts the complete set of calibration targets available in each release, including targets that are not shared. The verdict reports how many targets the candidate adds and removes relative to the current release.",
"Counts the complete set of calibration targets available in each release, including targets that are not shared. The verdict reports how many targets the candidate adds and removes relative to the release it is compared with.",
} as const;

function sideStats(errors: number[]): SideStats {
Expand Down Expand Up @@ -845,6 +846,9 @@ function MicrocosmStagingRunsView({
runData?.has_calibration ? selectedRun : undefined,
"latest",
);
// A local-area candidate is compared with the local-area release, not the
// current (national) release; the page names which.
const comparisonBaseline = stagingComparisonBaseline(compareData?.comparison_release);
// Fit stats on the targets both sides share — the honest better-or-worse basis.
const commonStats = useMemo(() => {
const rows = compareData?.rows ?? [];
Expand Down Expand Up @@ -1029,7 +1033,7 @@ function MicrocosmStagingRunsView({
title="Candidate validation"
>
<LoadingBlock
label="Comparing candidate vs current release (loads both calibration packages)…"
label="Comparing the candidate with its comparison release (loads both calibration packages)…"
height="h-24"
/>
</SectionCard>
Expand All @@ -1038,14 +1042,15 @@ function MicrocosmStagingRunsView({
{hasCandidateValidation && (
<SectionCard
title="Candidate validation"
description={comparisonBaseline.note ?? undefined}
padded={false}
>
<div className="overflow-x-auto px-5">
<table className="grid w-full grid-cols-[max-content_max-content_max-content_max-content_max-content] justify-between gap-x-2 text-left text-xs">
<thead className="col-span-full grid grid-cols-subgrid">
<tr className="col-span-full grid grid-cols-subgrid border-b border-border text-[10px] uppercase tracking-wider text-muted-foreground">
<th className="whitespace-nowrap py-2 font-semibold">Validation point</th>
<th className="whitespace-nowrap py-2 text-right font-semibold">Current release</th>
<th className="whitespace-nowrap py-2 text-right font-semibold">{comparisonBaseline.releaseLabel}</th>
<th className="whitespace-nowrap py-2 text-right font-semibold">Candidate</th>
<th className="whitespace-nowrap py-2 text-right font-semibold">Verdict</th>
<th className="whitespace-nowrap py-2 text-center font-semibold">About</th>
Expand Down Expand Up @@ -1183,6 +1188,7 @@ function MicrocosmStagingRunsView({
comparisonReleaseId={
compareData?.summary ? compareData.a.release_id : undefined
}
comparisonRelease={compareData?.comparison_release}
comparisonRows={compareData?.rows}
weightedTargetErrorChange={weightedTargetErrorChange}
pageIntroHeight={pageIntroHeight}
Expand Down
Loading
Loading