From d730443dde1ee6735266be8d9e9efa4d84dad5d7 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 23 Oct 2026 13:18:01 -0400 Subject: [PATCH 1/8] Document Budget same-line national recut exception --- docs/engineering/skills/data-certification.md | 30 +++++ pr-body.md | 119 ++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 pr-body.md diff --git a/docs/engineering/skills/data-certification.md b/docs/engineering/skills/data-certification.md index 23c5d813..b90de0af 100644 --- a/docs/engineering/skills/data-certification.md +++ b/docs/engineering/skills/data-certification.md @@ -245,6 +245,36 @@ differencing. withholds a standard reform's figures, so the gate cannot recompute that reform's verdict. Accepted by María Juaristi in #685. +### Same-line re-cut exception + +The Budget coordinator's **23 October 2026 decision under d1682 and d1344** +permits a same-line re-cut exception for the Budget week: the data must stay on +the same producer line, and the model must be pinned to the cut's build-time +version. For Saturday's national cut +`microcosm-uk-2024-25-national-20261023T075039Z-a133f37a`, that is +`policyengine-uk==2.147.2` (Microcosm build `829c1bc26`, on main via +[microcosm#1338](https://github.com/PolicyEngine/microcosm/pull/1338)). +Certify national-only with `--drop-regional-release`, stacked on +policyengine.py#704 (`budget-local-data-manifest`, `da2ae8fb` or a reviewed later +head). Constituency and local-authority outputs are absent until Monday's cut; +María Juaristi (`juaristi22`) can override this routing decision. + +The exception's evidence package is: all release-cut gates; the UC floor; +snapshots; the #574 reform contract and Budget-inputs gates on the exact cut, +with the coverage used by #685; the strict check; and a same-line 2×2 +attribution against the previously certified `6cba5d71` national cut on pe-uk +2.132.6. For FY2026-27 through FY2030-31, compare both data cuts under both +model versions, separating model effects on fixed data from data effects on a +fixed model. Map moves above the aggregate-impact report's thresholds to +pe-uk PRs or Microcosm changes, label unmapped moves **unattributed**, and list +limitations. Record incompatible cells as **not computable** with their error +and use the computable cells. Record scorecard replay as **unavailable** until +the Microcosm replay fix lands; unavailable is not a pass. + +This scoped decision does not inherit #685's cross-data-line comparison +exception or accept a new UC-floor gap, missing input or failed gate. Any such +acceptance needs an explicit decision and evidence in the certification PR. + ## Release runbooks Concrete, fill-in-the-id runbooks that replay a specific certification live diff --git a/pr-body.md b/pr-body.md new file mode 100644 index 00000000..974c89a3 --- /dev/null +++ b/pr-body.md @@ -0,0 +1,119 @@ +Fixes # + +Certify the Saturday 24 October national Microcosm cut +`microcosm-uk-2024-25-national-20261023T075039Z-a133f37a` with +`policyengine-uk==2.147.2`, its build-time model version. The Microcosm build is +`829c1bc26`, now on main through [microcosm#1338](https://github.com/PolicyEngine/microcosm/pull/1338). +That PR reports all 19 release-cut gates passing. This cut predates the final +DfE age-keying change; that change belongs to the next main build. + +This is a preparation draft: the exact-pair evidence below is pending. Replace +the issue placeholder with a verified certification issue and add its Towncrier +fragment before opening the PR. Do not treat the rehearsal's results as this +branch's certification receipts. + +### Base and national-only scope + +Stack on [policyengine.py#704](https://github.com/PolicyEngine/policyengine.py/pull/704), +`budget-local-data-manifest`, pinned for this preparation at +`da2ae8fb8e97145093e35366686df99c1e38449e` (or a reviewed later head). The +certification uses `--drop-regional-release` and the exact national cut tag in +its manifest URI. The certification command, receipts and resulting bundle +must agree on that tag and model pin. #699 is not the base. + +This is **national-only**: constituency and local-authority outputs are absent +until Monday's 26 October cut. María Juaristi +([juaristi22](https://github.com/juaristi22)) can override that routing decision. +An override needs its own explicit routing decision and evidence; this draft +does not certify the dense cut for the newer model. + +The rehearsal's metadata-only dense-schema inspection found **16 absent new +inputs and 0 newly lost build-model variables** for the older dense cut +`microcosm-uk-2024-25-dense-20261019T000432Z-ee5c2dbe`. The 83 ignored stored +columns were already unknown to the build model; the full target-input absent +count was 143. These are distinct measures. The source receipt is +`~/reviews/uk-hub/jobs/budget-certify-rehearsal/evidence/dense-schema.json` +(dense H5 SHA-256 +`8e10e130d0260e0b52e2e29902f0502ebdff76a14a3e57d2c876bf4cd8db5872`); +it read no microdata values. Attach the metadata receipt with the final evidence. + +### Same-line re-cut exception + +**Same-line re-cut exception (coordinator, 23 Oct, under d1682 and d1344).** +The Budget coordinator's 23 October 15:10Z decision sets the evidence package +below for this Saturday cut. Its scope is a re-cut of the same producer line, +using the model pinned at that cut's build version, during Budget week. It does +not inherit #685's cross-data-line comparison exception or automatically accept +a new UC-floor gap, input gap or failed gate. Record any acceptance decision +explicitly with its owner and evidence. + +### Evidence package + +- [ ] Attach and verify **all release-cut gate** receipts for the exact + `a133f37a` cut, including the producer's 19-gate report from microcosm#1338. +- [ ] Run the **UC floor** on this cut and attach its receipt. If the #685 + caseload benchmark gap persists, obtain and record fresh explicit acceptance; + #685's acceptance is not a new-cut pass. +- [ ] Refresh and review **household snapshots** on the pinned pair. +- [ ] Run the **#574 reform contract and Budget-inputs gates** on the cut with + the complete coverage used by policyengine.py#685; attach the receipts and + explain every needs-review, absent or unusable input. +- [ ] Run and attach the **strict bundle check**, generated release lock and + TRO evidence for the pinned model/data pair. +- [ ] Run the **same-line 2×2 attribution** for FY2026-27 through FY2030-31: + `{6cba5d71 data, a133f37a data} × {pe-uk 2.132.6, pe-uk 2.147.2}`. Report + weighted national `gov_balance`, every tax and benefit head, and poverty. + Separate the model effect on fixed data from the data effect on a fixed + model. Use the aggregate-impact report's `report_spec.yaml` flag thresholds; + map each flagged move to pe-uk PRs between 2.132.6 and 2.147.2 and/or Microcosm + changes between the two cuts' build commits. Label every unmapped move + **unattributed**. Record a cross-model cell that cannot load as + **not computable**, preserve its error, attribute through the computable + cells, and list all limitations. +- [ ] Record **scorecard replay: unavailable until the Microcosm replay fix + lands**. Attach the unavailable status now; replace it with an exact-pair + replay receipt if the fix becomes available. Unavailable is not a pass. +- [ ] Complete and review the remaining mechanical certification checks and + normal PR CI; attach the final commit and receipts. All evidence must identify + the resulting candidate commit and exact model/data pair. + +The previous certified pair is national cut +`microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71` with pe-uk 2.132.6. +Only weighted national aggregates may be shared, with UKDS small-cell rules +applied; retain private inputs and temporary environments outside Git. + +### Release and adoption timeline + +1. **Friday 23 October preparation:** stage the patched microcosm#1348 tool, + sequential guarded `run-heavy.sh`, attribution script and this decision + record. This preparation runs no simulations, suites, snapshots, UC floor or + national data loads, and pushes nothing. +2. **Saturday 24 October heavy phase:** start only after the other hub's + `route-a-release-rerun-d1811-2026-10-23` reservation ends at **00:30Z** and + `reservation.py allowed uk-hub` permits work. Every step checks load1 ≤110 + and free disk ≥55,000,000,000 bytes. Run one step at a time, review evidence + and limitations, then push and open a same-repository draft PR. The .py + 330-minute automatic qualify job is not a pull-request trigger; the required + evidence remains the checklist above. +3. **Review, merge and release:** finish normal PR CI and review before merge; + main tests, Versioning and Publish then produce PyPI and the GitHub Release. + #685 observed about **1h15 PR CI plus 3h10 merge-to-PyPI**, including roughly + two hours of queueing. These are observations, not a release-time guarantee. +4. **Dashboard adoption:** W5 monitors PyPI and consumer dispatch (or performs + the authorized manual fallback); the adoption workflow validates the wheel + identity and opens a draft upgrade PR. Vahid regenerates every published + measure and combined run on that branch; María reconciles results and + approves exceptions. Complete review/CI, merge, explicitly deploy the + household API, smoke-check it, and prepare the green draft revert PR. The + rehearsal's release-path research estimates about **1h20 plus regeneration + beyond 20m** with quiet runners, or **7h15 plus regeneration beyond 1h45** + under the observed busy queues. The dashboard runbook's **06:00Z completion** + includes deployment, household checks and the revert PR; its **07:00Z + go/no-go** must reflect actual completion. A three-hour local certification + budget alone cannot guarantee adoption by those times. +5. **Monday 26 October:** assess the next cut for constituency and + local-authority outputs, subject to María's override and new evidence. + +Timeline sources: rehearsal `evidence/timings.csv` and +`evidence/research-release.md`, the [dashboard Budget-day runbook](https://github.com/PolicyEngine/uk-autumn-budget-dashboard-2026/blob/main/docs/budget-day-runbook.md), +and the [#685 final-head PR CI run](https://github.com/PolicyEngine/policyengine.py/actions/runs/39588831360). From 58e0536cb00ca960beaeec990381c780fa9d248a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 23 Oct 2026 13:25:19 -0400 Subject: [PATCH 2/8] Prepare portable certifier with twelve rehearsal patches --- .job/certify_uk_cut_in_policyengine_py.py | 1110 +++++++++++++++++ .job/prepare-tool.py | 143 +++ .job/tool-patches.json | 185 +++ .../0000-tool-trailing-newline-compat.patch | 18 + .../0001-certifier-base-timing-safety-NOTE.md | 9 + .../0001-certifier-base-timing-safety.patch | 562 +++++++++ ...0002-certifier-preflight-streaming-NOTE.md | 7 + .../0002-certifier-preflight-streaming.patch | 723 +++++++++++ ...03-certifier-qualification-handoff-NOTE.md | 7 + ...0003-certifier-qualification-handoff.patch | 104 ++ .../0004-certifier-exact-pair-surface-NOTE.md | 7 + .../0004-certifier-exact-pair-surface.patch | 182 +++ ...05-certifier-scope-country-imports-NOTE.md | 9 + ...0005-certifier-scope-country-imports.patch | 78 ++ ...0006-certifier-measured-uc-summary-NOTE.md | 7 + .../0006-certifier-measured-uc-summary.patch | 195 +++ ...tifier-upstream-test-compatibility-NOTE.md | 9 + ...ertifier-upstream-test-compatibility.patch | 148 +++ ...08-certifier-live-test-diagnostics-NOTE.md | 7 + ...0008-certifier-live-test-diagnostics.patch | 28 + .../0009-certifier-stall-tracebacks-NOTE.md | 11 + .../0009-certifier-stall-tracebacks.patch | 53 + ...-certifier-temp-boundary-preflight-NOTE.md | 11 + ...10-certifier-temp-boundary-preflight.patch | 152 +++ .../0011-certifier-resume-worktree-NOTE.md | 11 + .../0011-certifier-resume-worktree.patch | 412 ++++++ ...rtifier-pytest-startup-diagnostics-NOTE.md | 13 + ...certifier-pytest-startup-diagnostics.patch | 148 +++ .../0013-reservation-branch-provenance.patch | 108 ++ .job/tool-patches/README.md | 39 + 30 files changed, 4496 insertions(+) create mode 100755 .job/certify_uk_cut_in_policyengine_py.py create mode 100644 .job/prepare-tool.py create mode 100644 .job/tool-patches.json create mode 100644 .job/tool-patches/0000-tool-trailing-newline-compat.patch create mode 100644 .job/tool-patches/0001-certifier-base-timing-safety-NOTE.md create mode 100644 .job/tool-patches/0001-certifier-base-timing-safety.patch create mode 100644 .job/tool-patches/0002-certifier-preflight-streaming-NOTE.md create mode 100644 .job/tool-patches/0002-certifier-preflight-streaming.patch create mode 100644 .job/tool-patches/0003-certifier-qualification-handoff-NOTE.md create mode 100644 .job/tool-patches/0003-certifier-qualification-handoff.patch create mode 100644 .job/tool-patches/0004-certifier-exact-pair-surface-NOTE.md create mode 100644 .job/tool-patches/0004-certifier-exact-pair-surface.patch create mode 100644 .job/tool-patches/0005-certifier-scope-country-imports-NOTE.md create mode 100644 .job/tool-patches/0005-certifier-scope-country-imports.patch create mode 100644 .job/tool-patches/0006-certifier-measured-uc-summary-NOTE.md create mode 100644 .job/tool-patches/0006-certifier-measured-uc-summary.patch create mode 100644 .job/tool-patches/0007-certifier-upstream-test-compatibility-NOTE.md create mode 100644 .job/tool-patches/0007-certifier-upstream-test-compatibility.patch create mode 100644 .job/tool-patches/0008-certifier-live-test-diagnostics-NOTE.md create mode 100644 .job/tool-patches/0008-certifier-live-test-diagnostics.patch create mode 100644 .job/tool-patches/0009-certifier-stall-tracebacks-NOTE.md create mode 100644 .job/tool-patches/0009-certifier-stall-tracebacks.patch create mode 100644 .job/tool-patches/0010-certifier-temp-boundary-preflight-NOTE.md create mode 100644 .job/tool-patches/0010-certifier-temp-boundary-preflight.patch create mode 100644 .job/tool-patches/0011-certifier-resume-worktree-NOTE.md create mode 100644 .job/tool-patches/0011-certifier-resume-worktree.patch create mode 100644 .job/tool-patches/0012-certifier-pytest-startup-diagnostics-NOTE.md create mode 100644 .job/tool-patches/0012-certifier-pytest-startup-diagnostics.patch create mode 100644 .job/tool-patches/0013-reservation-branch-provenance.patch create mode 100644 .job/tool-patches/README.md diff --git a/.job/certify_uk_cut_in_policyengine_py.py b/.job/certify_uk_cut_in_policyengine_py.py new file mode 100755 index 00000000..c6c51f34 --- /dev/null +++ b/.job/certify_uk_cut_in_policyengine_py.py @@ -0,0 +1,1110 @@ +"""Certify a published Microcosm UK cut into policyengine.py, on a fresh branch. + +policyengine.py serves the UK cut its bundle manifest certifies; moving that +pin is a policyengine.py pull request and release. This script does the +mechanical part of that pull request the way policyengine.py#685 did it by +hand (``docs/engineering/skills/data-certification.md`` there), in a new +worktree branched from policyengine.py's ``origin/main``: + +1. refuse if someone is already certifying this cut (one writer per cut); +2. read the national (and regional) release manifests at their tags and + check, before anything is written, that the regional route can certify; +3. ``scripts/bundle.py certify-data`` with the national tag and the chosen + regional route, at the cut's own policyengine-uk and core; +4. ``uv lock`` and ``scripts/release_lock.py``; +5. ``bundle.py generate --include-tros``, the certified-constant rewrite of + policyengine.py's own qualification library, the household snapshots, + then ``bundle.py check --published-spm --include-tros --strict-tros``; +6. the UK Universal Credit floor (weighted national totals only) and the + test suite; +7. a commit, and a pull request body with every judgment left to a person + (the UC floor acceptance, dropped dataset overlays, failing tests). + +It pushes and opens a draft pull request only with ``--push`` and +``--open-pr``. It never merges and never releases. + +Regional routes (pick one): + +``--dense-tag TAG`` a Microcosm local-area cut (ship together) +``--keep-regional-release`` the bundle's current regional release +``--area-coded-national-uri`` an enhanced-FRS release that claims the version +``--drop-regional-release`` no area routing (sim-api's UK default then + lacks constituency and local-authority runs) + +A regional release built on another policyengine-uk is refused by +certification unless policyengine.py accepts a recorded mismatch; pass +``--accept-regional-model-mismatch REASON`` only where that option exists +(see ``docs/uk-cut-anyone.md``). + +Needs ``HF_TOKEN`` (read ``policyengine/populace-uk-private``), PyPI, and +``gh`` for the writer check and the pull request. +""" + +from __future__ import annotations + +import argparse +import json +import math +import os +import re +import shlex +import shutil +import subprocess +import sys +import tempfile +import time +from contextlib import contextmanager +from dataclasses import dataclass +from datetime import UTC, datetime +from pathlib import Path +from typing import Any + +PEPY_REPO = "PolicyEngine/policyengine.py" +CUT_REPO = "policyengine/populace-uk-private" +NATIONAL_RELEASE = "microcosm-uk-2024-25-national" +DENSE_RELEASE = "microcosm-uk-2024-25-dense" +PEPY_PYTHON = "3.13" # certify-uk-release.yaml's interpreter +TAG_PATTERN = re.compile( + r"^microcosm-uk-2024-25-(national|dense)-\d{8}T\d{6}Z-[0-9a-f]{8}$" +) +CONSTANT_PATTERN = r'(?P
["@/-]){old}(?P["/])'
+CONSTANT_FILES = (
+    "tests/test_models.py",
+    "tests/test_release_manifests.py",
+    "tests/test_uk_regions.py",
+)
+
+
+class CertifyError(RuntimeError):
+    """A refusal the operator must act on."""
+
+
+def manifest_uri(tag: str, release: str) -> str:
+    return f"hf://dataset/{CUT_REPO}@{tag}/releases/{release}/release_manifest.json"
+
+
+def check_tag(tag: str, kind: str) -> str:
+    match = TAG_PATTERN.match(tag)
+    if not match or match.group(1) != kind:
+        raise CertifyError(
+            f"{tag!r} is not a {kind} cut tag; certify cut tags only, never @main."
+        )
+    return tag
+
+
+@dataclass(frozen=True)
+class CutVersions:
+    tag: str
+    model: str
+    core: str
+    claims: tuple[str, ...]
+
+
+def read_cut(tag: str, release: str) -> CutVersions:
+    # Set before importing the hub: its HTTP timeout constants are import-time.
+    os.environ.setdefault("HF_HUB_DOWNLOAD_TIMEOUT", "120")
+    os.environ.setdefault("HF_HUB_ETAG_TIMEOUT", "60")
+    from huggingface_hub import hf_hub_download
+
+    path = hf_hub_download(
+        CUT_REPO,
+        f"releases/{release}/release_manifest.json",
+        repo_type="dataset",
+        revision=tag,
+        token=os.environ.get("HUGGING_FACE_TOKEN") or os.environ.get("HF_TOKEN"),
+        etag_timeout=60,
+    )
+    manifest = json.loads(Path(path).read_text())
+    build = manifest.get("build") or {}
+    if build.get("cut_tag") != tag:
+        raise CertifyError(f"{tag}: the manifest names cut {build.get('cut_tag')!r}.")
+    return CutVersions(
+        tag=tag,
+        model=build["built_with_model_package"]["version"],
+        core=build["built_with_core_package"]["version"],
+        claims=tuple(
+            claim["specifier"]
+            for claim in manifest.get("compatible_model_packages", ())
+            if claim.get("name") == "policyengine-uk"
+        ),
+    )
+
+
+def covers(cut: CutVersions, version: str) -> bool:
+    """policyengine.py's rule: built with the version, or an exact claim of it."""
+
+    return cut.model == version or f"=={version}" in cut.claims
+
+
+def regional_flags(args: argparse.Namespace, bundle_record: dict) -> list[str]:
+    if args.dense_tag:
+        return [
+            "--regional-manifest-uri",
+            manifest_uri(check_tag(args.dense_tag, "dense"), DENSE_RELEASE),
+        ]
+    if args.keep_regional_release:
+        uri = bundle_record.get("regional_source_manifest_uri")
+        if not uri:
+            raise CertifyError("the bundle routes no regional release to keep.")
+        return ["--regional-manifest-uri", uri]
+    if args.area_coded_national_uri:
+        return [
+            "--regional-manifest-uri",
+            args.area_coded_national_uri,
+            "--regional-release-kind",
+            "area_coded_national",
+        ]
+    return ["--drop-regional-release"]
+
+
+def regional_tag(flags: list[str]) -> str | None:
+    if "--regional-manifest-uri" not in flags:
+        return None
+    uri = flags[flags.index("--regional-manifest-uri") + 1]
+    match = re.search(r"@([^/]+)/", uri)
+    return (
+        match.group(1) if match and match.group(1).startswith(DENSE_RELEASE) else None
+    )
+
+
+def resource_guard(root: Path) -> dict[str, float | int]:
+    """Refuse work before spawning a process on an overloaded or full host."""
+
+    reservation = os.environ.get("PE_CERTIFY_RESERVATION_SCRIPT")
+    if reservation:
+        result = subprocess.run(
+            [sys.executable, reservation, "allowed", "uk-hub"],
+            capture_output=True,
+            text=True,
+            timeout=30,
+            check=False,
+        )
+        if result.returncode:
+            raise CertifyError(
+                "UK hub reservation refused this step: "
+                + (result.stdout + result.stderr).strip()[-1000:]
+            )
+    load1 = os.getloadavg()[0]
+    while not root.exists():
+        root = root.parent
+    free = shutil.disk_usage(root).free
+    if load1 > 110 or free < 55_000_000_000:
+        raise CertifyError(
+            f"resource guard: load1={load1:.2f} (maximum 110), "
+            f"free={free / 1e9:.2f} GB (minimum 55 decimal GB)."
+        )
+    return {"load1": load1, "free_bytes": free}
+
+
+def check_test_temp_directory(directory: Path | None = None) -> Path:
+    """Refuse a full suite whose temporary fixtures inherit an ancestor Git repo."""
+    directory = Path(directory or tempfile.gettempdir()).expanduser().resolve()
+    for ancestor in (directory, *directory.parents):
+        if (ancestor / ".git").exists():
+            raise CertifyError(
+                f"Full-suite temporary directory {directory} is inside Git "
+                f"repository {ancestor}. Version fixtures would inherit its tags "
+                "and private disclosure audit fixtures correctly refuse Git paths. "
+                "Set TMPDIR to a private writable directory outside every Git "
+                "repository before certification. If workspace-only execution "
+                "prevents that, report full-suite validation blocked; --skip-tests "
+                "records a mechanical handoff only and does not qualify the cut."
+            )
+    return directory
+
+
+def certification_path(relative: str, national_tag: str) -> bool:
+    """Files generated by this handoff, excluding local data and operator files."""
+    snapshots = {
+        "uk_couple_two_kids",
+        "uk_model_surface",
+        "uk_single_adult_employment_income",
+        "uk_single_adult_no_income",
+        "uk_single_parent_one_child",
+        "us_married_two_kids_high_income",
+        "us_model_surface",
+        "us_single_adult_employment_income",
+        "us_single_adult_no_income",
+        "us_single_parent_one_child",
+    }
+    return relative in {
+        "pyproject.toml",
+        "uv.lock",
+        *CONSTANT_FILES,
+        f"changelog.d/certify-uk-{national_tag}.changed.md",
+        "src/policyengine/data/bundle/manifest.json",
+        "src/policyengine/data/bundle/uk.trace.tro.jsonld",
+        "src/policyengine/data/bundle/us.trace.tro.jsonld",
+        *(
+            f"tests/fixtures/household_calculator_snapshots/{name}.json"
+            for name in snapshots
+        ),
+    }
+
+
+def certification_changes(worktree: Path, national_tag: str) -> list[str]:
+    """Preserve operator files and refuse unrelated tracked/index changes."""
+    tracked = subprocess.check_output(
+        ["git", "diff", "--name-only", "-z", "HEAD"], cwd=worktree, text=True
+    ).split("\0")
+    unexpected = [
+        path for path in tracked if path and not certification_path(path, national_tag)
+    ]
+    if unexpected:
+        raise CertifyError(
+            "unrelated tracked changes are preserved; refusing to replay or commit: "
+            + ", ".join(unexpected)
+        )
+    untracked = subprocess.check_output(
+        ["git", "ls-files", "--others", "--exclude-standard", "-z"],
+        cwd=worktree,
+        text=True,
+    ).split("\0")
+    changelog = f"changelog.d/certify-uk-{national_tag}.changed.md"
+    return sorted(
+        {path for path in tracked if path}
+        | {path for path in untracked if path == changelog}
+    )
+
+
+def validate_resume_worktree(
+    repo: Path, worktree: Path, branch: str, base_commit: str, national_tag: str
+) -> list[str]:
+    """Adopt only an unfinished, registered handoff at the exact resolved base."""
+    if not worktree.is_dir():
+        raise CertifyError(f"resume worktree {worktree} does not exist.")
+    records, record = [], {}
+    output = subprocess.check_output(
+        ["git", "worktree", "list", "--porcelain", "-z"], cwd=repo, text=True
+    )
+    for field in output.split("\0"):
+        if not field:
+            if record:
+                records.append(record)
+                record = {}
+        else:
+            key, _, value = field.partition(" ")
+            record[key] = value
+    if record:
+        records.append(record)
+    entry = next(
+        (
+            item
+            for item in records
+            if "worktree" in item
+            and Path(item["worktree"]).resolve() == worktree.resolve()
+        ),
+        None,
+    )
+    if entry is None or entry.get("branch") != f"refs/heads/{branch}":
+        raise CertifyError(
+            "resume worktree is not registered on the expected certification branch."
+        )
+    common_dirs = []
+    for directory in (repo, worktree):
+        common = Path(
+            subprocess.check_output(
+                ["git", "rev-parse", "--git-common-dir"],
+                cwd=directory,
+                text=True,
+            ).strip()
+        )
+        common_dirs.append((directory / common).resolve())
+    if common_dirs[0] != common_dirs[1]:
+        raise CertifyError("resume worktree belongs to another Git repository.")
+    head = subprocess.check_output(
+        ["git", "rev-parse", "HEAD"],
+        cwd=worktree,
+        text=True,
+    ).strip()
+    if head != base_commit or entry.get("HEAD") != base_commit:
+        raise CertifyError(
+            "resume requires HEAD at the exact resolved base commit; committed "
+            "descendants/history are preserved. Use a fresh worktree for another run."
+        )
+    if subprocess.check_output(
+        ["git", "ls-files", "--unmerged", "-z"],
+        cwd=worktree,
+        text=True,
+    ):
+        raise CertifyError(
+            "resume worktree has unresolved merges; no files were changed."
+        )
+    paths = certification_changes(worktree, national_tag)
+    manifest_path = "src/policyengine/data/bundle/manifest.json"
+    if manifest_path in paths:
+        record = json.loads((worktree / manifest_path).read_text())["data_releases"][
+            "uk"
+        ]
+        revision = (record.get("data_package") or {}).get("release_manifest_revision")
+        if revision != national_tag:
+            raise CertifyError("resume worktree's modified manifest names another cut.")
+    return paths
+
+
+def record_timing(path: Path, record: dict[str, Any]) -> None:
+    path.parent.mkdir(parents=True, exist_ok=True)
+    with path.open("a") as handle:
+        handle.write(json.dumps(record) + "\n")
+
+
+def pytest_command(
+    python: str, arguments: list[str], *, startup_timeout: float = 300.0
+) -> list[str]:
+    """Diagnose imports before pytest's session/per-test clocks can start."""
+    if not math.isfinite(startup_timeout) or startup_timeout <= 0:
+        raise ValueError("pytest startup timeout must be positive and finite")
+    bootstrap = (
+        "import faulthandler, os, sys\n"
+        "diagnostic_stream = os.fdopen(os.dup(2), 'w')\n"
+        f"faulthandler.dump_traceback_later({startup_timeout!r}, "
+        "repeat=True, file=diagnostic_stream)\n"
+        "try:\n"
+        "    import pytest\n"
+        "    raise SystemExit(pytest.main(sys.argv[1:]))\n"
+        "finally:\n"
+        "    faulthandler.cancel_dump_traceback_later()\n"
+        "    diagnostic_stream.close()\n"
+    )
+    return [python, "-c", bootstrap, *arguments]
+
+
+@contextmanager
+def timed_step(name: str, path: Path):
+    """Make network preflight and local Python work visible, including failures."""
+    started = time.monotonic()
+    record = {"step": name, "started_at": datetime.now(UTC).isoformat()}
+    print(f"[start] {name}", flush=True)
+    try:
+        record.update(resource_guard(path.parent))
+        yield record
+        record.setdefault("returncode", 0)
+    except Exception as error:
+        record["returncode"] = getattr(error, "returncode", None)
+        record["error"] = str(error)
+        raise
+    finally:
+        record["elapsed_seconds"] = time.monotonic() - started
+        record_timing(path, record)
+        print(
+            f"[done] {name}: {record['elapsed_seconds']:.3f}s "
+            f"(exit {record.get('returncode')})",
+            flush=True,
+        )
+
+
+def run_logged(argv: list[str], *, cwd: Path, env: dict[str, str], handle):
+    """Stream child output into the diagnostic log while retaining its result."""
+    output = []
+    with subprocess.Popen(
+        argv,
+        cwd=cwd,
+        env=env,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.STDOUT,
+        text=True,
+        bufsize=1,
+    ) as process:
+        for line in process.stdout:
+            handle.write(line)
+            handle.flush()
+            output.append(line)
+        returncode = process.wait()
+    return subprocess.CompletedProcess(argv, returncode, "".join(output), "")
+
+
+class Shell:
+    """Runs commands in the policyengine.py worktree and records each one."""
+
+    def __init__(
+        self,
+        cwd: Path,
+        log: Path,
+        *,
+        dry_run: bool = False,
+        timings: Path | None = None,
+    ) -> None:
+        self.cwd = cwd
+        self.log = log
+        self.dry_run = dry_run
+        self.timings = timings or log.with_suffix(".timings.jsonl")
+        self.commands: list[str] = []
+
+    def __call__(
+        self,
+        argv: list[str],
+        *,
+        env: dict[str, str] | None = None,
+        check=True,
+        step: str | None = None,
+    ) -> subprocess.CompletedProcess:
+        line = shlex.join(argv)
+        self.commands.append(line)
+        print(f"$ {line}", flush=True)
+        if self.dry_run:
+            return subprocess.CompletedProcess(argv, 0, "", "")
+        started = time.monotonic()
+        timing = {
+            "step": step or line,
+            "command": line,
+            "started_at": datetime.now(UTC).isoformat(),
+            "returncode": None,
+        }
+        try:
+            timing.update(resource_guard(self.cwd))
+            with self.log.open("a") as handle:
+                handle.write(f"\n# {datetime.now(UTC):%Y-%m-%dT%H:%M:%SZ} $ {line}\n")
+                handle.flush()
+                result = run_logged(
+                    argv,
+                    cwd=self.cwd,
+                    env={**os.environ, **(env or {})},
+                    handle=handle,
+                )
+            timing["returncode"] = result.returncode
+        except Exception as error:
+            timing["error"] = str(error)
+            raise
+        finally:
+            timing["elapsed_seconds"] = time.monotonic() - started
+            record_timing(self.timings, timing)
+            print(
+                f"[done] {step or line}: {timing['elapsed_seconds']:.3f}s "
+                f"(exit {timing.get('returncode')})",
+                flush=True,
+            )
+        if check and result.returncode != 0:
+            tail = "\n".join(result.stdout.splitlines()[-25:])
+            raise CertifyError(f"{line} exited {result.returncode}:\n{tail}")
+        return result
+
+
+def writer_check(
+    national_tag: str, pepy_repo: Path, branch: str | None = None
+) -> list[str]:
+    """Open pull requests or branches already certifying this cut."""
+
+    found = []
+    result = subprocess.run(
+        [
+            "gh", "pr", "list", "--repo", PEPY_REPO, "--state", "open",
+            "--search", national_tag, "--json", "number,title,headRefName",
+        ],
+        capture_output=True,
+        text=True,
+        check=False,
+        timeout=120,
+    )  # fmt: skip
+    if result.returncode != 0:
+        raise CertifyError(
+            "could not list policyengine.py pull requests to check for another "
+            f"writer ({result.stderr.strip()[:200]}); retry, or ask the UK hub."
+        )
+    for pr in json.loads(result.stdout or "[]"):
+        found.append(f"#{pr['number']} {pr['title']} ({pr['headRefName']})")
+    branch = branch or f"certify-uk-{national_tag.rsplit('-', 1)[-1]}"
+    remote = subprocess.run(
+        ["git", "ls-remote", "--heads", "origin", branch],
+        cwd=pepy_repo,
+        capture_output=True,
+        text=True,
+        check=False,
+        timeout=120,
+    )
+    if remote.stdout.strip():
+        found.append(f"branch {branch} exists on origin")
+    return found
+
+
+def rewrite_regional_constants(
+    root: Path, old: str | None, new: str | None
+) -> list[str]:
+    """Replace the old local-area cut tag in the certified-constant files."""
+
+    if not old or not new or old == new:
+        return []
+    pattern = re.compile(CONSTANT_PATTERN.format(old=re.escape(old)))
+    changed = []
+    for relative in CONSTANT_FILES:
+        path = root / relative
+        text = path.read_text()
+        updated = pattern.sub(lambda m: m.group("pre") + new + m.group("post"), text)
+        if updated != text:
+            path.write_text(updated)
+            changed.append(relative)
+    return changed
+
+
+_SURFACE_SCRIPT = (
+    "import json; from policyengine_uk import variables; "
+    "print(json.dumps({'all': sorted(variables), 'inputs': sorted("
+    "n for n, v in variables.items() if v.is_input_variable())}))"
+)
+
+
+def model_surface(
+    version: str,
+    core_version: str | None = None,
+    *,
+    timings: Path | None = None,
+    resource_root: Path | None = None,
+) -> dict[str, list[str]]:
+    """Read the country's existing variable registry at the requested model/core."""
+
+    packages = ["--with", f"policyengine-uk=={version}"]
+    if core_version:
+        packages += ["--with", f"policyengine-core=={core_version}"]
+    started = time.monotonic()
+    timing = {
+        "step": f"model_surface:{version}",
+        "model_version": version,
+        "core_version": core_version,
+        "started_at": datetime.now(UTC).isoformat(),
+        "returncode": None,
+    }
+    print(f"[start] model_surface:{version}", flush=True)
+    try:
+        timing.update(resource_guard(resource_root or Path.cwd()))
+        result = subprocess.run(
+        [
+            "uv", "run", "--no-project", "--python", PEPY_PYTHON,
+            *packages, "python", "-c", _SURFACE_SCRIPT,
+        ],
+        capture_output=True,
+        text=True,
+        check=True,
+        env={k: v for k, v in os.environ.items() if k not in {"UV_FROZEN", "VIRTUAL_ENV"}},
+        )  # fmt: skip
+        timing["returncode"] = result.returncode
+    except Exception as error:
+        timing["returncode"] = getattr(error, "returncode", None)
+        timing["error"] = str(error)
+        raise
+    finally:
+        timing["elapsed_seconds"] = time.monotonic() - started
+        if timings:
+            record_timing(timings, timing)
+        print(
+            f"[done] model_surface:{version}: {timing['elapsed_seconds']:.3f}s "
+            f"(exit {timing.get('returncode')})",
+            flush=True,
+        )
+    return json.loads(result.stdout.strip().splitlines()[-1])
+
+
+def input_surface_change(
+    built: dict[str, list[str]], certified: dict[str, list[str]]
+) -> dict[str, list[str]]:
+    """What area runs lose when a file built on one model runs under another.
+
+    ``dropped``: the file's input columns the certified model no longer
+    defines; policyengine-core ignores them on load. ``defaulted``: inputs
+    of the certified model the file cannot carry (new variables, and
+    variables that were formulas when it was built), which take defaults.
+    A built input that became a formula is in neither: its stored values
+    still load.
+    """
+
+    built_inputs = set(built["inputs"])
+    return {
+        "dropped": sorted(built_inputs - set(certified["all"])),
+        "defaulted": sorted(set(certified["inputs"]) - built_inputs),
+    }
+
+
+def overlay_diff(old_bundle: dict, new_bundle: dict) -> list[str]:
+    old = set((old_bundle.get("dataset_overlays") or {}).get("uk", {}) or {})
+    new = set((new_bundle.get("dataset_overlays") or {}).get("uk", {}) or {})
+    return sorted(old - new)
+
+
+def uc_floor_summary(receipt: dict[str, Any]) -> str:
+    """Summarize a complete measured floor without truncating its JSON receipt."""
+    measures = receipt.get("measures") or []
+    if (
+        receipt.get("check") != "uk_uc_floor"
+        or not isinstance(receipt.get("passed"), bool)
+        or not {"benefit_units", "spend"}.issubset(
+            {measure.get("name") for measure in measures}
+        )
+        or any(
+            not isinstance(measure.get("passed"), bool)
+            or not isinstance(measure.get("ratio"), (int, float))
+            or not math.isfinite(measure["ratio"])
+            for measure in measures
+        )
+        or receipt["passed"] != all(measure["passed"] for measure in measures)
+    ):
+        raise CertifyError(
+            "UC floor failed operationally: incomplete measured receipt."
+        )
+    status = "passed" if receipt["passed"] else "FAILED"
+    ratios = ", ".join(
+        f"{measure['name']}={measure['ratio']:.6f}" for measure in measures
+    )
+    return f"{status}; measured/benchmark ratios: {ratios}"
+
+
+def pr_body(context: dict[str, Any]) -> str:
+    lines = [
+        f"Certifies Microcosm UK national cut `{context['national_tag']}` "
+        f"(policyengine-uk {context['model']}, core {context['core']}).",
+        "",
+        "**Mechanical handoff; wrapper qualification pending** unless reviewed "
+        "receipts cover this exact candidate pair, commit and bundle. Microcosm's "
+        "19 producer release-cut gates and passing pytest do not establish "
+        "wrapper qualification.",
+        "",
+        f"- **Regional route:** {context['regional_summary']}",
+        f"- **Compatibility basis:** `{context.get('basis')}`",
+        f"- **Made by** `tools/certify_uk_cut_in_policyengine_py.py` in "
+        f"PolicyEngine/microcosm at `{context['microcosm_commit'][:9]}`.",
+        "",
+        "## How it was made",
+        "",
+        "```bash",
+        *context["commands"],
+        "```",
+        "",
+        "## Receipts",
+        "",
+        f"- UC floor (weighted national totals, `scripts/check_uk_uc_floor.py`): "
+        f"{context.get('uc_floor', 'not run')}",
+        f"- Tests: {context.get('tests', 'not run')}",
+        f"- Certified-constant files rewritten: {context.get('constants') or 'none'}",
+        "",
+        "## Needs a person before this leaves draft",
+        "",
+        "- [ ] **Wrapper qualification.** Attach exact-pair receipts for every "
+        "required candidate gate, including the full reform contract, metrics "
+        "review and reform responsiveness; attach and review scorecard replay "
+        "evidence. Keep qualification pending until the evidence is reviewed. "
+        "Record any explicit reviewer-approved exception; this script waives "
+        "no gate.",
+    ]
+    if (context.get("uc_floor_receipt") or {}).get("passed") is False:
+        lines.append(
+            "- [ ] **UC floor acceptance.** This cut has a measured floor failure "
+            "and needs explicit acceptance in its pull request "
+            "(data-certification.md, recorded exceptions). Accepted by: ___"
+        )
+    if context.get("dropped_overlays"):
+        lines.append(
+            "- [ ] **Dataset overlays dropped by certification:** "
+            f"{', '.join(context['dropped_overlays'])}. Re-add any that must stay "
+            "loadable from the previous bundle (as #685 did)."
+        )
+    surface = context.get("input_surface")
+    if surface is not None:
+        lines.append(
+            "- [ ] **Area runs on the older local-area file** (accepted mismatch, "
+            f"built on {context.get('regional_model')}): columns ignored on load: "
+            f"{', '.join(surface['dropped']) or 'none'}; inputs that take defaults: "
+            f"{', '.join(surface['defaulted']) or 'none'}. Confirm none matters "
+            "for a reform the Budget dashboard shows by area."
+        )
+    if context.get("warnings"):
+        lines.append(
+            "- [ ] **Certification warnings** (from `certify-data`):\n  "
+            + "\n  ".join(f"- {w}" for w in context["warnings"])
+        )
+    if context.get("tests_failed"):
+        lines.append(
+            "- [ ] **Failing tests** to update for the new pair: "
+            + ", ".join(context["tests_failed"][:20])
+        )
+    lines += [
+        "- [ ] One writer: the UK hub and María know this PR exists.",
+        "",
+        "🤖 Generated with [Claude Code](https://claude.com/claude-code)",
+    ]
+    return "\n".join(lines) + "\n"
+
+
+def parse(argv: list[str] | None) -> argparse.Namespace:
+    parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
+    parser.add_argument("--national-tag", required=True)
+    route = parser.add_mutually_exclusive_group(required=True)
+    route.add_argument("--dense-tag")
+    route.add_argument("--keep-regional-release", action="store_true")
+    route.add_argument("--area-coded-national-uri")
+    route.add_argument("--drop-regional-release", action="store_true")
+    parser.add_argument(
+        "--accept-regional-model-mismatch",
+        metavar="REASON",
+        help="forwarded to certify-data where policyengine.py supports it",
+    )
+    parser.add_argument(
+        "--allow-dropped-columns",
+        metavar="REASON",
+        help=(
+            "with an accepted mismatch, proceed although the newer model no "
+            "longer defines some of the regional file's input columns"
+        ),
+    )
+    parser.add_argument("--model-version", help="default: the cut's build-time model")
+    parser.add_argument("--core-version", help="default: the cut's build-time core")
+    parser.add_argument(
+        "--pepy-repo",
+        type=Path,
+        default=Path("~/PolicyEngine/policyengine.py").expanduser(),
+    )
+    parser.add_argument("--worktree", type=Path, help="default: beside the repo")
+    parser.add_argument("--branch", help="default: certify-uk-")
+    parser.add_argument(
+        "--resume-worktree",
+        action="store_true",
+        help="replay stages in an unfinished registered worktree at the exact base",
+    )
+    parser.add_argument(
+        "--base-ref",
+        default="origin/main",
+        help="base branch/ref for both the old bundle and the new worktree",
+    )
+    parser.add_argument("--skip-writer-check", metavar="WHY")
+    parser.add_argument("--skip-tests", action="store_true")
+    parser.add_argument("--test-workers", type=int, default=4)
+    parser.add_argument("--push", action="store_true")
+    parser.add_argument(
+        "--pr-base",
+        default="main",
+        help="pull-request target branch (default: main), independent of --base-ref",
+    )
+    parser.add_argument("--open-pr", action="store_true", help="draft; needs --push")
+    parser.add_argument("--dry-run", action="store_true", help="print the plan only")
+    args = parser.parse_args(argv)
+    if args.open_pr and not args.push:
+        parser.error("--open-pr needs --push.")
+    if not 1 <= args.test_workers <= 4:
+        parser.error("--test-workers must be between 1 and 4.")
+    check_tag(args.national_tag, "national")
+    return args
+
+
+def main(argv: list[str] | None = None) -> int:
+    args = parse(argv)
+    try:
+        return certify(args)
+    except CertifyError as error:
+        print(f"REFUSED: {error}", file=sys.stderr)
+        return 2
+
+
+def certify(args: argparse.Namespace) -> int:
+    repo = args.pepy_repo
+    resource_guard(repo)
+    microcosm_commit = os.environ.get("PE_CERTIFY_TOOL_SOURCE_COMMIT")
+    if microcosm_commit:
+        if not re.fullmatch(r"[0-9a-f]{40}", microcosm_commit):
+            raise CertifyError("PE_CERTIFY_TOOL_SOURCE_COMMIT must be a full Git SHA.")
+    else:
+        microcosm_commit = subprocess.check_output(
+            ["git", "rev-parse", "HEAD"],
+            cwd=Path(__file__).resolve().parents[1],
+            text=True,
+        ).strip()
+    suffix = args.national_tag.rsplit("-", 1)[-1]
+    branch = args.branch or f"certify-uk-{suffix}"
+    worktree = args.worktree or repo.parent / "_worktrees" / f"pepy-{branch}"
+    log = worktree.parent / f"{worktree.name}.certify.log"
+    timings = log.with_suffix(".timings.jsonl")
+    with timed_step("branch-preflight", timings):
+        result = subprocess.run(
+            ["git", "check-ref-format", "--branch", branch],
+            cwd=repo,
+            capture_output=True,
+            text=True,
+            check=False,
+        )
+        if result.returncode or branch.startswith("-"):
+            raise CertifyError(f"Invalid certification branch: {branch!r}.")
+    if not args.skip_tests and not args.dry_run:
+        with timed_step("test-temp-preflight", timings) as timing:
+            timing["temporary_directory"] = str(check_test_temp_directory())
+    base_ref = args.base_ref
+    if base_ref.startswith("origin/"):
+        remote_branch = base_ref.removeprefix("origin/")
+        with timed_step("git-fetch", timings):
+            subprocess.run(
+                [
+                    "git",
+                    "fetch",
+                    "--quiet",
+                    "origin",
+                    f"+refs/heads/{remote_branch}:refs/remotes/origin/{remote_branch}",
+                ],
+                cwd=repo,
+                check=True,
+                timeout=300,
+            )
+    with timed_step("resolve-base", timings):
+        base_commit = subprocess.check_output(
+            ["git", "rev-parse", "--verify", f"{base_ref}^{{commit}}"],
+            cwd=repo,
+            text=True,
+        ).strip()
+
+    replay_paths = []
+    if args.resume_worktree:
+        with timed_step("resume-worktree-validation", timings):
+            replay_paths = validate_resume_worktree(
+                repo, worktree, branch, base_commit, args.national_tag
+            )
+    elif worktree.exists():
+        raise CertifyError(
+            f"{worktree} exists; use --resume-worktree for an unfinished handoff "
+            "or choose a fresh --worktree."
+        )
+
+    if args.skip_writer_check:
+        print(f"writer check skipped: {args.skip_writer_check}")
+    else:
+        with timed_step("writer-check", timings):
+            others = writer_check(args.national_tag, repo, branch)
+        if others:
+            raise CertifyError(
+                "this cut is already being certified: "
+                + "; ".join(others)
+                + ". Coordinate with its owner instead of opening a second PR."
+            )
+
+    with timed_step("national-manifest", timings):
+        national = read_cut(args.national_tag, NATIONAL_RELEASE)
+    model = args.model_version or national.model
+    core = args.core_version or national.core
+    if not covers(national, model):
+        raise CertifyError(
+            f"the national cut was built with policyengine-uk {national.model} and "
+            f"claims {list(national.claims)}; it cannot be certified for {model}."
+        )
+    with timed_step("old-bundle", timings):
+        old_bundle = json.loads(
+            subprocess.check_output(
+                [
+                    "git",
+                    "show",
+                    f"{base_commit}:src/policyengine/data/bundle/manifest.json",
+                ],
+                cwd=repo,
+                text=True,
+            )
+        )
+    record = old_bundle["data_releases"]["uk"]
+    flags = regional_flags(args, record)
+    new_regional = regional_tag(flags)
+    regional_summary = shlex.join(flags)
+    dense, surface = None, None
+    if new_regional is not None:
+        with timed_step("regional-manifest", timings):
+            dense = read_cut(new_regional, DENSE_RELEASE)
+        if not covers(dense, model):
+            if not args.accept_regional_model_mismatch:
+                raise CertifyError(
+                    f"the regional cut {new_regional} was built with policyengine-uk "
+                    f"{dense.model} and claims {list(dense.claims)}, so certification "
+                    f"for {model} will refuse it. Routes: a dense cut on {model} "
+                    "(--dense-tag), --drop-regional-release, an enhanced-FRS release "
+                    "that claims the version (--area-coded-national-uri), or an "
+                    "accepted, recorded mismatch (--accept-regional-model-mismatch) "
+                    "where policyengine.py supports one."
+                )
+            surface = input_surface_change(
+                model_surface(
+                    dense.model, dense.core, timings=timings, resource_root=repo
+                ),
+                model_surface(model, core, timings=timings, resource_root=repo),
+            )
+            if surface["dropped"] and not args.allow_dropped_columns:
+                raise CertifyError(
+                    f"policyengine-uk {model} no longer defines these input columns "
+                    f"of the {dense.model} local-area file, so area runs would lose "
+                    f"them: {', '.join(surface['dropped'])}. Build a dense cut on "
+                    f"{model}, or pass --allow-dropped-columns with a reason."
+                )
+            print(json.dumps({"input_surface_change": surface}, indent=2))
+            flags += [
+                "--accept-regional-model-mismatch",
+                args.accept_regional_model_mismatch,
+            ]
+            regional_summary += f" (regional built on {dense.model}; mismatch accepted)"
+    old_regional = regional_tag(
+        ["--regional-manifest-uri", record.get("regional_source_manifest_uri") or ""]
+    )
+
+    plan = {
+        "base_ref": base_ref,
+        "base_commit": base_commit,
+        "national_tag": args.national_tag,
+        "model": model,
+        "core": core,
+        "regional": regional_summary,
+        "branch": branch,
+        "worktree": str(worktree),
+        "resume_worktree": args.resume_worktree,
+        "replayed_generated_paths": replay_paths,
+    }
+    print(json.dumps(plan, indent=2))
+    if args.dry_run:
+        return 0
+    if not args.resume_worktree:
+        with timed_step("git-worktree", timings):
+            subprocess.run(
+                [
+                    "git",
+                    "worktree",
+                    "add",
+                    "--quiet",
+                    "-b",
+                    branch,
+                    str(worktree),
+                    base_commit,
+                ],
+                cwd=repo,
+                check=True,
+            )
+    run = Shell(worktree, log, timings=timings)
+    python = str(worktree / ".venv/bin/python")
+    sync = ["uv", "sync", "--frozen", "--extra", "uk", "--extra", "dev",
+            "--python", PEPY_PYTHON]  # fmt: skip
+    run(sync, step="initial-sync")
+    certify_result = run(
+        [
+            python, "scripts/bundle.py", "certify-data", "--country", "uk",
+            "--data-producer", "populace",
+            "--manifest-uri", manifest_uri(args.national_tag, NATIONAL_RELEASE),
+            *flags,
+            "--model-version", model, "--core-version", core,
+        ], step="certify-data",
+    )  # fmt: skip
+    warnings = [
+        line.strip()
+        for line in certify_result.stdout.splitlines()
+        if "warning" in line.lower()
+    ]
+    run(
+        ["uv", "lock", "--no-config", "--no-sources", "--default-index",
+         "https://pypi.org/simple", "--python", python], step="lock",
+    )  # fmt: skip
+    run(sync, step="certified-sync")
+    run([python, "scripts/release_lock.py"], step="release-lock")
+    run([python, "scripts/bundle.py", "generate", "--include-tros"], step="TROs")
+    new_bundle = json.loads(
+        (worktree / "src/policyengine/data/bundle/manifest.json").read_text()
+    )
+    new_record = new_bundle["data_releases"]["uk"]
+    rewrite = run(
+        [
+            python, "-c",
+            "import json, sys; from pathlib import Path; "
+            "from policyengine.provenance.uk_qualification import "
+            "certified_constant_replacements as r, rewrite_certified_constants as w; "
+            "old, new = json.loads(sys.argv[1]), json.loads(sys.argv[2]); "
+            "print(json.dumps(w(Path('.'), r(old, new))))",
+            json.dumps(record), json.dumps(new_record),
+        ], step="constant-rewrite",
+        env={"POLICYENGINE_SKIP_COUNTRY_IMPORTS": "1"},
+    )  # fmt: skip
+    constants = json.loads(rewrite.stdout.strip().splitlines()[-1] or "[]")
+    with timed_step("regional-constant-rewrite", timings):
+        constants += rewrite_regional_constants(worktree, old_regional, new_regional)
+    run(
+        pytest_command(python, ["tests/test_household_calculator_snapshot.py", "-q"]),
+        env={"PE_UPDATE_SNAPSHOTS": "1"},
+        step="snapshots",
+    )
+    run(
+        [python, "scripts/bundle.py", "check", "--published-spm", "--include-tros",
+         "--strict-tros"], step="strict-check",
+    )  # fmt: skip
+    uc_path = worktree.parent / f"{worktree.name}.uc_floor.json"
+    # An earlier attempt's receipt must not mask an operational failure.
+    uc_path.unlink(missing_ok=True)
+    uc = run(
+        [python, "scripts/check_uk_uc_floor.py", "--dataset",
+         new_record["default_dataset"], "--year", "2025", "--output", str(uc_path)],
+        check=False,
+        step="UC-floor",
+        env={"POLICYENGINE_SKIP_COUNTRY_IMPORTS": "1"},
+    )  # fmt: skip
+    if not uc_path.exists():
+        raise CertifyError(
+            f"UC floor failed operationally (exit {uc.returncode}); no measured "
+            f"receipt was produced. See {log}."
+        )
+    try:
+        receipt = json.loads(uc_path.read_text())
+        uc_summary = uc_floor_summary(receipt)
+    except (json.JSONDecodeError, AttributeError, TypeError) as error:
+        raise CertifyError(
+            "UC floor failed operationally: invalid measured receipt."
+        ) from error
+    if uc.returncode != (0 if receipt["passed"] else 1):
+        raise CertifyError(
+            f"UC floor failed operationally (exit {uc.returncode}); "
+            "the measured receipt disagrees with the process exit status."
+        )
+    tests_summary, tests_failed, tests_returncode = "skipped", [], 0
+    if not args.skip_tests:
+        xdist = run(
+            [python, "-c", "import xdist"], check=False, step="check-pytest-xdist"
+        )
+        if xdist.returncode:
+            run(
+                ["uv", "pip", "install", "--python", python, "pytest-xdist"],
+                step="install-pytest-xdist",
+            )
+        tests = run(
+            pytest_command(python, ["-n", str(args.test_workers), "-vv",
+             "--durations=20", "-o", "faulthandler_timeout=300",
+             "-p", "no:cacheprovider"]),
+            check=False,
+            step="tests",
+        )  # fmt: skip
+        tests_returncode = tests.returncode
+        tests_failed = re.findall(r"^FAILED (\S+)", tests.stdout, flags=re.MULTILINE)
+        tests_summary = (tests.stdout.strip().splitlines() or ["?"])[-1]
+        if tests_returncode and not tests_failed:
+            tests_failed = [f"pytest exited {tests_returncode}: {tests_summary}"]
+    context = {
+        "national_tag": args.national_tag,
+        "model": model,
+        "core": core,
+        "regional_summary": f"`{regional_summary}`",
+        "basis": (new_record.get("certification") or {}).get("compatibility_basis"),
+        "microcosm_commit": microcosm_commit,
+        "commands": run.commands,
+        "uc_floor": f"`{uc_summary}`",
+        "uc_floor_receipt": receipt,
+        "tests": tests_summary,
+        "tests_failed": tests_failed,
+        "constants": ", ".join(constants),
+        "dropped_overlays": overlay_diff(old_bundle, new_bundle),
+        "warnings": warnings,
+        "input_surface": surface,
+        "regional_model": dense.model if dense is not None else None,
+    }
+    body = worktree.parent / f"{worktree.name}.pr-body.md"
+    body.write_text(pr_body(context))
+    with timed_step("generated-file-validation", timings):
+        staged_paths = certification_changes(worktree, args.national_tag)
+    if staged_paths:
+        run(["git", "add", "--", *staged_paths], step="stage-certification")
+    run(
+        ["git", "commit", "-q", "-m",
+         f"Certify Microcosm UK cut {args.national_tag}\n\n"
+         "Co-Authored-By: Claude Opus 5.5 "]
+    )  # fmt: skip
+    print(f"\ncommitted on {branch} in {worktree}; PR body draft: {body}")
+    if args.push:
+        run(["git", "push", "-u", "origin", branch])
+    if args.open_pr:
+        run(
+            ["gh", "pr", "create", "--draft", "--repo", PEPY_REPO, "--base", args.pr_base,
+             "--head", branch, "--title",
+             f"Certify Microcosm UK cut {args.national_tag}", "--body-file", str(body)]
+        )  # fmt: skip
+    return 0 if not tests_returncode else 1
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/.job/prepare-tool.py b/.job/prepare-tool.py
new file mode 100644
index 00000000..5627aa93
--- /dev/null
+++ b/.job/prepare-tool.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+"""Restore the locally patched #1348 tool without changing an existing checkout.
+
+This performs Git operations only; it never runs the certification tool, imports
+a country model, installs dependencies, or loads national data. The original
+rehearsal patch files are immutable inputs checked against tool-patches.json.
+"""
+
+from __future__ import annotations
+
+import argparse
+import hashlib
+import json
+import subprocess
+import sys
+from pathlib import Path
+
+UPSTREAM = "61e25de8a673f4187b2d882acab02d94d4a6da99"
+OLD_BUILD = "810315ac2"
+NEW_BUILD = "829c1bc26"
+TOOL = "tools/certify_uk_cut_in_policyengine_py.py"
+
+
+def git(repo: Path, *args: str, capture: bool = False) -> str:
+    result = subprocess.run(
+        ["git", "-C", str(repo), *args],
+        check=True,
+        text=True,
+        stdout=subprocess.PIPE if capture else None,
+    )
+    return result.stdout.strip() if capture else ""
+
+
+def digest(path: Path) -> str:
+    return hashlib.sha256(path.read_bytes()).hexdigest()
+
+
+def parse() -> argparse.Namespace:
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument(
+        "--source-repository",
+        default=str(Path("~/PolicyEngine/microcosm").expanduser()),
+        help="read-only clone source; may be a local repository or Git URL",
+    )
+    parser.add_argument("--destination", type=Path)
+    return parser.parse_args()
+
+
+def main() -> int:
+    args = parse()
+    job = Path(__file__).resolve().parent
+    workspace = job.parent
+    repo = (args.destination or job / "microcosm").resolve()
+    if not repo.is_relative_to(workspace) or repo == workspace:
+        raise ValueError("The tool checkout must be inside the assigned workspace.")
+    receipt = json.loads((job / "tool-patches.json").read_text())
+    patches = job / "tool-patches"
+    for patch in receipt["patches"]:
+        if digest(patches / patch["patch"]) != patch["sha256"]:
+            raise ValueError(f"Patch digest differs: {patch['patch']}")
+    adaptation = receipt["local_adaptation"]
+    if digest(patches / adaptation["patch"]) != adaptation["sha256"]:
+        raise ValueError("Local adaptation patch digest differs.")
+    preparation = receipt["compatibility_preparation"]
+    if digest(patches / preparation["patch"]) != preparation["sha256"]:
+        raise ValueError("EOF compatibility patch digest differs.")
+    expected = receipt["portable_source"]["sha256"]
+    expected_tree = receipt["local_adaptation"]["tree"]
+    if digest(job / "certify_uk_cut_in_policyengine_py.py") != expected:
+        raise ValueError("Portable tool snapshot digest differs.")
+
+    reused = repo.exists()
+    if reused:
+        if not (repo / ".git").is_dir():
+            raise ValueError(f"Existing destination is not this tool's clone: {repo}")
+        if git(repo, "status", "--porcelain", capture=True):
+            raise ValueError("Existing tool checkout is dirty; it was preserved.")
+        if digest(repo / TOOL) != expected:
+            raise ValueError("Existing tool source differs; it was preserved.")
+        if git(repo, "rev-parse", "HEAD^{tree}", capture=True) != expected_tree:
+            raise ValueError("Existing tool tree differs; it was preserved.")
+        git(repo, "merge-base", "--is-ancestor", UPSTREAM, "HEAD")
+    else:
+        source = args.source_repository
+        if source == str(Path("~/PolicyEngine/microcosm").expanduser()):
+            if not Path(source).is_dir():
+                source = "https://github.com/PolicyEngine/microcosm.git"
+        subprocess.run(["git", "clone", "--no-checkout", source, str(repo)], check=True)
+        try:
+            git(repo, "cat-file", "-e", UPSTREAM + "^{commit}")
+        except subprocess.CalledProcessError:
+            git(repo, "fetch", "origin", UPSTREAM)
+        git(repo, "checkout", "-b", "uk-cut-anyone-budget-prepared", UPSTREAM)
+        for name, message in (
+            (preparation["patch"], "Align EOF with rehearsal patch source"),
+            *[(patch["patch"], "") for patch in receipt["patches"]],
+            (adaptation["patch"], "Guard certification steps and preserve provenance"),
+        ):
+            path = patches / name
+            if message:
+                git(repo, "apply", "--check", str(path))
+                git(repo, "apply", "--index", str(path))
+                git(repo, "commit", "-m", message)
+            else:
+                # Never use a three-way fallback: failures require inspection.
+                git(repo, "am", "--no-3way", str(path))
+            if git(repo, "status", "--porcelain", capture=True):
+                raise ValueError(f"Patch left a dirty checkout: {name}")
+        if digest(repo / TOOL) != expected:
+            raise ValueError("Reconstructed tool differs from the reviewed snapshot.")
+        if git(repo, "rev-parse", "HEAD^{tree}", capture=True) != expected_tree:
+            raise ValueError("Reconstructed tool tree differs from the reviewed tree.")
+
+    for build in (OLD_BUILD, NEW_BUILD):
+        try:
+            git(repo, "cat-file", "-e", build + "^{commit}")
+        except subprocess.CalledProcessError:
+            raise ValueError(
+                f"Source checkout lacks build {build}; fetch the build commit "
+                "from microcosm before attribution. No existing files were replaced."
+            ) from None
+    git(repo, "merge-base", "--is-ancestor", OLD_BUILD, NEW_BUILD)
+    result = {
+        "schema_version": 1,
+        "reused_checkout": reused,
+        "checkout": str(repo),
+        "head": git(repo, "rev-parse", "HEAD", capture=True),
+        "tree": git(repo, "rev-parse", "HEAD^{tree}", capture=True),
+        "tool_sha256": digest(repo / TOOL),
+        "old_build": git(repo, "rev-parse", OLD_BUILD, capture=True),
+        "new_build": git(repo, "rev-parse", NEW_BUILD, capture=True),
+        "old_build_is_ancestor": True,
+    }
+    print(json.dumps(result, indent=2))
+    return 0
+
+
+if __name__ == "__main__":
+    try:
+        raise SystemExit(main())
+    except (ValueError, subprocess.CalledProcessError) as error:
+        print(f"REFUSED: {error}", file=sys.stderr)
+        raise SystemExit(2)
diff --git a/.job/tool-patches.json b/.job/tool-patches.json
new file mode 100644
index 00000000..2a6aea6d
--- /dev/null
+++ b/.job/tool-patches.json
@@ -0,0 +1,185 @@
+{
+  "schema_version": 1,
+  "source_pr": "PolicyEngine/microcosm#1348",
+  "branch": "uk-cut-anyone",
+  "base": "61e25de8a673f4187b2d882acab02d94d4a6da99",
+  "compatibility_preparation": {
+    "patch": "0000-tool-trailing-newline-compat.patch",
+    "reason": "Exact upstream tool and upstream test each omit one trailing blank line present in rehearsal patch source; restore only those blank lines before original patch series; no #699 applied",
+    "original_clean_apply_failures": [
+      {
+        "patch": "0001",
+        "exit_code": 128,
+        "hunk": "tool EOF"
+      },
+      {
+        "patch": "0007 second commit",
+        "exit_code": 128,
+        "hunk": "upstream test EOF"
+      }
+    ],
+    "normalized_base": "6aaaa92e4e27066743383e79ec1686b3b1da04d8",
+    "sha256": "ad1ac2cd48a0cb026dce988ada604fc0cc235217a629b8dbf6f3302e26ae10e4"
+  },
+  "patch_application": "git am --no-3way",
+  "patches": [
+    {
+      "patch": "0001-certifier-base-timing-safety.patch",
+      "sha256": "f51322454f591831dc0b9d6ff8d510ab185238ae4ab5ab0416f87ca8a13b4598",
+      "before": "6aaaa92e4e27066743383e79ec1686b3b1da04d8",
+      "exit_code": 0,
+      "output": "Applying: Make UK certification stackable, timed and fail closed on operational errors",
+      "after": "1d77d3efef56ca58348024b1e73674a167860985",
+      "commits": [
+        "1d77d3efef56ca58348024b1e73674a167860985 Make UK certification stackable, timed and fail closed on operational errors"
+      ]
+    },
+    {
+      "patch": "0002-certifier-preflight-streaming.patch",
+      "sha256": "d3e14fa6521cd77d2ace990b755bd6f3d2807ab5f11341ca4c9f256669072742",
+      "before": "1d77d3efef56ca58348024b1e73674a167860985",
+      "exit_code": 0,
+      "output": "Applying: Expose timed preflight and stream long-running certification logs",
+      "after": "022dd40bef32ff6305a140d95b38ae5c3c009c33",
+      "commits": [
+        "022dd40bef32ff6305a140d95b38ae5c3c009c33 Expose timed preflight and stream long-running certification logs"
+      ]
+    },
+    {
+      "patch": "0003-certifier-qualification-handoff.patch",
+      "sha256": "8bc4ca5d7be1518ef81c8c0b7c4910bd4e94297dc944a590066cf26e4a9d9998",
+      "before": "022dd40bef32ff6305a140d95b38ae5c3c009c33",
+      "exit_code": 0,
+      "output": "Applying: Label certification handoff qualification pending and allow stacked PR target",
+      "after": "68f8200a2242a3d43cd587a74eaa6361c0f16137",
+      "commits": [
+        "68f8200a2242a3d43cd587a74eaa6361c0f16137 Label certification handoff qualification pending and allow stacked PR target"
+      ]
+    },
+    {
+      "patch": "0004-certifier-exact-pair-surface.patch",
+      "sha256": "5a08378fcddf909f5e7cc255944b1c579fb31e1a952abd3480e486090ae58bd6",
+      "before": "68f8200a2242a3d43cd587a74eaa6361c0f16137",
+      "exit_code": 0,
+      "output": "Applying: Probe country input registry once at exact model and core versions",
+      "after": "7702685d48a8a841d79940d5b3da698f91145f45",
+      "commits": [
+        "7702685d48a8a841d79940d5b3da698f91145f45 Probe country input registry once at exact model and core versions"
+      ]
+    },
+    {
+      "patch": "0005-certifier-scope-country-imports.patch",
+      "sha256": "4989a6beca6e6b690f84a395e3fa29f242c9fe2d78a8d8834ddd63fef98a51f5",
+      "before": "7702685d48a8a841d79940d5b3da698f91145f45",
+      "exit_code": 0,
+      "output": "Applying: Avoid unrelated country initialization in constant rewrite and UC floor",
+      "after": "9e9f7e107100bed2d6e7ea6af226cdbdaa4226d1",
+      "commits": [
+        "9e9f7e107100bed2d6e7ea6af226cdbdaa4226d1 Avoid unrelated country initialization in constant rewrite and UC floor"
+      ]
+    },
+    {
+      "patch": "0006-certifier-measured-uc-summary.patch",
+      "sha256": "ee98af58428adcd278dd0b16432355a45c22b4373c1f227bbed4ef8179c8d7e2",
+      "before": "9e9f7e107100bed2d6e7ea6af226cdbdaa4226d1",
+      "exit_code": 0,
+      "output": "Applying: Report measured UC floor status and request acceptance only for failures",
+      "after": "4a6ef8c045f3ac071124c340ca463ee467357db0",
+      "commits": [
+        "4a6ef8c045f3ac071124c340ca463ee467357db0 Report measured UC floor status and request acceptance only for failures"
+      ]
+    },
+    {
+      "patch": "0007-certifier-upstream-test-compatibility.patch",
+      "sha256": "9a2a01e1ee52659cd2fa771708644351c532e0188d963e01635647de5ebb81d2",
+      "before": "4a6ef8c045f3ac071124c340ca463ee467357db0",
+      "exit_code": 0,
+      "output": "Applying: Preserve upstream handoff coverage and discover rehearsal regressions in microcosm CI\nApplying: Adapt upstream fixtures to measured UC and immutable exact-pair interfaces",
+      "after": "be14f48a1a9b84f79416e71b38a564941288a07d",
+      "commits": [
+        "45a595c7c4eee09b4c9c282b39609569818f1a47 Preserve upstream handoff coverage and discover rehearsal regressions in microcosm CI",
+        "be14f48a1a9b84f79416e71b38a564941288a07d Adapt upstream fixtures to measured UC and immutable exact-pair interfaces"
+      ]
+    },
+    {
+      "patch": "0008-certifier-live-test-diagnostics.patch",
+      "sha256": "e0154995ab15ae0f6983a3aa85533d17dd3e275840e31140e0007fdb4d2eda12",
+      "before": "be14f48a1a9b84f79416e71b38a564941288a07d",
+      "exit_code": 0,
+      "output": "Applying: Expose full-suite test identities and slowest durations in streamed logs",
+      "after": "ec36db868f684c7d5192807bc5d56eaa0bf84c4c",
+      "commits": [
+        "ec36db868f684c7d5192807bc5d56eaa0bf84c4c Expose full-suite test identities and slowest durations in streamed logs"
+      ]
+    },
+    {
+      "patch": "0009-certifier-stall-tracebacks.patch",
+      "sha256": "985523b2d010aec3dd7d52a883ed15a9c438cb18be8bbbeada1a9f61906f43bb",
+      "before": "ec36db868f684c7d5192807bc5d56eaa0bf84c4c",
+      "exit_code": 0,
+      "output": "Applying: Dump nonfatal worker tracebacks when full-suite tests exceed five minutes",
+      "after": "a15d671af079cf04b8648fc0d262f6013e5eb247",
+      "commits": [
+        "a15d671af079cf04b8648fc0d262f6013e5eb247 Dump nonfatal worker tracebacks when full-suite tests exceed five minutes"
+      ]
+    },
+    {
+      "patch": "0010-certifier-temp-boundary-preflight.patch",
+      "sha256": "0ace1313843afbcfedc1c6cf36244e5e5f95de4b7c4543d9d589b5452e5e297e",
+      "before": "a15d671af079cf04b8648fc0d262f6013e5eb247",
+      "exit_code": 0,
+      "output": "Applying: Refuse full-suite fixtures nested inside a Git repository before heavy work",
+      "after": "144c86540f7a8eef1d99f06afba387eea8d818de",
+      "commits": [
+        "144c86540f7a8eef1d99f06afba387eea8d818de Refuse full-suite fixtures nested inside a Git repository before heavy work"
+      ]
+    },
+    {
+      "patch": "0011-certifier-resume-worktree.patch",
+      "sha256": "13efd2bc8d74dfe2a1e98398046cbd0c90a0fb27abd00e32049f73052bf337af",
+      "before": "144c86540f7a8eef1d99f06afba387eea8d818de",
+      "exit_code": 0,
+      "output": "Applying: Resume unfinished certification worktrees and stage only generated artifacts",
+      "after": "cd8ff6559a90633daa13e003ade9b5529f8c145f",
+      "commits": [
+        "cd8ff6559a90633daa13e003ade9b5529f8c145f Resume unfinished certification worktrees and stage only generated artifacts"
+      ]
+    },
+    {
+      "patch": "0012-certifier-pytest-startup-diagnostics.patch",
+      "sha256": "9760b5b48ba02bbafc947aeb023da3e654f7579c6385d20daeb24ee06eb7c5a4",
+      "before": "cd8ff6559a90633daa13e003ade9b5529f8c145f",
+      "exit_code": 0,
+      "output": "Applying: Report pytest startup stalls through original stderr without terminating tests",
+      "after": "d43b5c6dbd2df86bbe07564ea08fc659cd7cce1a",
+      "commits": [
+        "d43b5c6dbd2df86bbe07564ea08fc659cd7cce1a Report pytest startup stalls through original stderr without terminating tests"
+      ]
+    }
+  ],
+  "patched_head": "d43b5c6dbd2df86bbe07564ea08fc659cd7cce1a",
+  "local_adaptation": {
+    "patch": "0013-reservation-branch-provenance.patch",
+    "sha256": "04f814684975e492b2a35d26236a5984792b946785bdff4a87a8e3c78c3f09aa",
+    "commit": "0788c00c717e3b22562f3f78da208a9a1e2a4591",
+    "changes": [
+      "PE_CERTIFY_RESERVATION_SCRIPT calls reservation.py allowed uk-hub before every timed preflight step, Shell command, and model surface calculation, followed by unchanged load1 <=110 and decimal free bytes >=55000000000 checks",
+      "Optional --branch selects requested branch, validates with git check-ref-format --branch, and checks writer ownership on that branch",
+      "PE_CERTIFY_TOOL_SOURCE_COMMIT preserves source Git identity in portable single-file snapshot"
+    ],
+    "validation": [
+      "git diff --check",
+      "Python ast.parse; no tests, snapshots, UC-floor, simulations or national loads run"
+    ],
+    "tree": "619842d13be4e4881be69b395c64a514cb449488"
+  },
+  "portable_source": {
+    "path": ".job/certify_uk_cut_in_policyengine_py.py",
+    "sha256": "941df515c0609cfcb27af3143ccb3e09fb089975843ea510b4e2022f7733d5ae"
+  },
+  "build_ancestry": {
+    "old": "810315ac22b81c15c008cfb2723ee99e18725fe4",
+    "new": "829c1bc26c0457c668fe1dfc1e866cb4a752654a",
+    "old_is_ancestor": true
+  }
+}
diff --git a/.job/tool-patches/0000-tool-trailing-newline-compat.patch b/.job/tool-patches/0000-tool-trailing-newline-compat.patch
new file mode 100644
index 00000000..9b4d776b
--- /dev/null
+++ b/.job/tool-patches/0000-tool-trailing-newline-compat.patch
@@ -0,0 +1,18 @@
+diff --git a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_in_policyengine_py.py b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_in_policyengine_py.py
+index 4cc57d991..2142351c0 100644
+--- a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_in_policyengine_py.py
++++ b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_in_policyengine_py.py
+@@ -260,3 +260,4 @@ def test_the_pr_body_lists_the_area_input_surface():
+     )
+     assert "built on 2.132.6" in body
+     assert "local_bus_uncapped_single_fare" in body
++
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index 16cdccef0..e185862fe 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -619,3 +619,4 @@ def certify(args: argparse.Namespace) -> int:
+ 
+ if __name__ == "__main__":
+     raise SystemExit(main())
++
diff --git a/.job/tool-patches/0001-certifier-base-timing-safety-NOTE.md b/.job/tool-patches/0001-certifier-base-timing-safety-NOTE.md
new file mode 100644
index 00000000..3c8084bc
--- /dev/null
+++ b/.job/tool-patches/0001-certifier-base-timing-safety-NOTE.md
@@ -0,0 +1,9 @@
+For microcosm #1348's owner: local-only rehearsal patch; nothing pushed.
+
+The original runner always reads and branches from origin/main, so it cannot stack on policyengine.py #704. It also relies on Hugging Face's HF_TOKEN fallback while the operational credential is HUGGING_FACE_TOKEN. This patch accepts --base-ref (default origin/main), fetches only origin/* refs, resolves one immutable commit for both the old manifest and worktree, and passes the in-memory token explicitly.
+
+Every subprocess run through Shell now records elapsed seconds and return code (also on failures) to an adjacent .timings.jsonl file. Model-surface comparisons are guarded and timed too. Before each Shell command and each model-surface calculation, refuse load1 > 110 or free space < 55,000,000,000 bytes. The configured pytest workers must be 1..4. The dev extra currently omits pytest-xdist; install it in the isolated environment only if absent before the parallel suite.
+
+Pytest's actual nonzero return code now fails the run even when no FAILED test lines exist (configuration/collection/xdist failures previously looked green). UC floor exceptions without a fresh measured receipt now stop the run; a measured floor violation still appears in the draft's explicit human-acceptance checklist. The patch does not grant that acceptance.
+
+Validation: `python3 -m unittest discover -s .job/upstream/tests -v`: 10 offline mocked-command tests passed. `python3 -m py_compile .job/upstream/tools/certify_uk_cut_in_policyengine_py.py` passed. Live certification timings and downstream issues belong in the main rehearsal report.
diff --git a/.job/tool-patches/0001-certifier-base-timing-safety.patch b/.job/tool-patches/0001-certifier-base-timing-safety.patch
new file mode 100644
index 00000000..0c7b5b1b
--- /dev/null
+++ b/.job/tool-patches/0001-certifier-base-timing-safety.patch
@@ -0,0 +1,562 @@
+From 2ffb565161a7f584206ced90750c5f39189ea403 Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 07:32:37 -0400
+Subject: [PATCH] Make UK certification stackable, timed and fail closed on
+ operational errors
+
+---
+ .../test_certify_uk_cut_in_policyengine_py.py | 179 +++++++++++++++++
+ tools/certify_uk_cut_in_policyengine_py.py    | 188 ++++++++++++++----
+ 2 files changed, 329 insertions(+), 38 deletions(-)
+ create mode 100644 tests/test_certify_uk_cut_in_policyengine_py.py
+
+diff --git a/tests/test_certify_uk_cut_in_policyengine_py.py b/tests/test_certify_uk_cut_in_policyengine_py.py
+new file mode 100644
+index 0000000..d383721
+--- /dev/null
++++ b/tests/test_certify_uk_cut_in_policyengine_py.py
+@@ -0,0 +1,179 @@
++"""Offline regression tests for the certification orchestration safeguards."""
++
++import argparse
++import importlib.util
++import json
++import os
++from pathlib import Path
++import subprocess
++import sys
++import tempfile
++import types
++import unittest
++from unittest.mock import Mock, patch
++
++
++SPEC = importlib.util.spec_from_file_location(
++    "certify_runner",
++    Path(__file__).resolve().parents[1] / "tools/certify_uk_cut_in_policyengine_py.py",
++)
++runner = importlib.util.module_from_spec(SPEC)
++sys.modules[SPEC.name] = runner
++SPEC.loader.exec_module(runner)
++TAG = "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a"
++
++
++class RunnerTests(unittest.TestCase):
++    def test_hugging_face_token_is_explicit_and_not_logged(self):
++        with tempfile.TemporaryDirectory() as tmp:
++            manifest = Path(tmp) / "manifest.json"
++            manifest.write_text(json.dumps({"build": {
++                "cut_tag": TAG,
++                "built_with_model_package": {"version": "2.147.2"},
++                "built_with_core_package": {"version": "3.0"},
++            }}))
++            download = Mock(return_value=str(manifest))
++            with patch.dict(sys.modules, {"huggingface_hub": types.SimpleNamespace(
++                hf_hub_download=download
++            )}), patch.dict(os.environ, {"HUGGING_FACE_TOKEN": "private-value"}):
++                self.assertEqual(runner.read_cut(TAG, runner.NATIONAL_RELEASE).model,
++                                 "2.147.2")
++            self.assertEqual(download.call_args.kwargs["token"], "private-value")
++
++    def test_shell_records_failed_commands(self):
++        with tempfile.TemporaryDirectory() as tmp:
++            root = Path(tmp)
++            shell = runner.Shell(root, root / "run.log")
++            with patch.object(runner, "resource_guard", return_value={"load1": 1}), \
++                 patch.object(runner.subprocess, "run", return_value=
++                              subprocess.CompletedProcess(["false"], 2, "bad")):
++                with self.assertRaisesRegex(runner.CertifyError, "exited 2"):
++                    shell(["false"], step="failure")
++            record = json.loads(shell.timings.read_text())
++            self.assertEqual(record["step"], "failure")
++            self.assertEqual(record["returncode"], 2)
++            self.assertGreaterEqual(record["elapsed_seconds"], 0)
++
++    def test_guard_prevents_process_and_records_refusal(self):
++        with tempfile.TemporaryDirectory() as tmp:
++            shell = runner.Shell(Path(tmp), Path(tmp) / "run.log")
++            with patch.object(runner, "resource_guard", side_effect=
++                              runner.CertifyError("resource guard")), \
++                 patch.object(runner.subprocess, "run") as spawn:
++                with self.assertRaises(runner.CertifyError):
++                    shell(["heavy"])
++                spawn.assert_not_called()
++            self.assertIn("resource guard", json.loads(shell.timings.read_text())["error"])
++
++    def test_resource_thresholds_are_decimal(self):
++        with patch.object(runner.os, "getloadavg", return_value=(110, 1, 1)), \
++             patch.object(runner.shutil, "disk_usage", return_value=
++                          types.SimpleNamespace(free=55_000_000_000)):
++            runner.resource_guard(Path.cwd())
++        for load, free in ((110.1, 55_000_000_000), (1, 54_999_999_999)):
++            with patch.object(runner.os, "getloadavg", return_value=(load, 1, 1)), \
++                 patch.object(runner.shutil, "disk_usage", return_value=
++                              types.SimpleNamespace(free=free)):
++                with self.assertRaises(runner.CertifyError):
++                    runner.resource_guard(Path.cwd())
++
++    def test_model_surface_is_guarded(self):
++        with patch.object(runner, "resource_guard", side_effect=
++                          runner.CertifyError("resource guard")), \
++             patch.object(runner.subprocess, "run") as spawn:
++            with self.assertRaises(runner.CertifyError):
++                runner.model_surface("2.147.2")
++            spawn.assert_not_called()
++
++    def test_workers_are_limited(self):
++        for workers in (0, 5):
++            with self.assertRaises(SystemExit):
++                runner.parse(["--national-tag", TAG, "--drop-regional-release",
++                              "--test-workers", str(workers)])
++        self.assertEqual(runner.parse([
++            "--national-tag", TAG, "--drop-regional-release",
++            "--test-workers", "4"
++        ]).test_workers, 4)
++
++    def test_local_base_is_resolved_once_and_used_for_old_bundle(self):
++        with tempfile.TemporaryDirectory() as tmp:
++            args = runner.parse([
++                "--national-tag", TAG, "--drop-regional-release", "--dry-run",
++                "--base-ref", "stack-local", "--pepy-repo", tmp,
++                "--skip-writer-check", "offline regression",
++            ])
++            old = {"data_releases": {"uk": {}}}
++            with patch.object(runner.subprocess, "check_output", side_effect=
++                              ["microcosm\n", "immutable-sha\n", json.dumps(old)]) as read, \
++                 patch.object(runner.subprocess, "run") as spawn, \
++                 patch.object(runner, "read_cut", return_value=
++                              runner.CutVersions(TAG, "2.147.2", "3.0", ())):
++                self.assertEqual(runner.certify(args), 0)
++            spawn.assert_not_called()
++            self.assertEqual(read.call_args_list[1].args[0],
++                             ["git", "rev-parse", "--verify", "stack-local^{commit}"])
++            self.assertEqual(read.call_args_list[2].args[0], ["git", "show",
++                "immutable-sha:src/policyengine/data/bundle/manifest.json"])
++
++    def run_mock_pipeline(self, *, pytest_code=0, uc_code=0, receipt=None):
++        tmp = tempfile.TemporaryDirectory()
++        self.addCleanup(tmp.cleanup)
++        root = Path(tmp.name)
++        worktree = root / "certification"
++        bundle = {"data_releases": {"uk": {"default_dataset": "candidate"}}}
++        args = runner.parse([
++            "--national-tag", TAG, "--drop-regional-release", "--base-ref", "local",
++            "--pepy-repo", str(root), "--worktree", str(worktree),
++            "--skip-writer-check", "offline regression",
++        ])
++        calls = []
++        def command(argv, **kwargs):
++            calls.append(argv)
++            if argv[:3] == ["git", "worktree", "add"]:
++                manifest = worktree / "src/policyengine/data/bundle/manifest.json"
++                manifest.parent.mkdir(parents=True)
++                manifest.write_text(json.dumps(bundle))
++            return subprocess.CompletedProcess(argv, 0, "", "")
++        def shell(argv, **kwargs):
++            calls.append(argv)
++            if "constant-rewrite" == kwargs.get("step"):
++                return subprocess.CompletedProcess(argv, 0, "[]", "")
++            if "UC-floor" == kwargs.get("step"):
++                if receipt is not None:
++                    (root / "certification.uc_floor.json").write_text(json.dumps(receipt))
++                return subprocess.CompletedProcess(argv, uc_code, "traceback", "")
++            if "tests" == kwargs.get("step"):
++                return subprocess.CompletedProcess(argv, pytest_code,
++                                                    "ERROR: unrecognized arguments: -n", "")
++            return subprocess.CompletedProcess(argv, 0, "", "")
++        with patch.object(runner.subprocess, "check_output", side_effect=
++                          ["microcosm", "immutable-sha", json.dumps(bundle)]), \
++             patch.object(runner.subprocess, "run", side_effect=command), \
++             patch.object(runner, "read_cut", return_value=
++                          runner.CutVersions(TAG, "2.147.2", "3.0", ())), \
++             patch.object(runner.Shell, "__call__", side_effect=shell):
++            result = runner.certify(args)
++        return result, calls, root
++
++    def test_pytest_configuration_error_is_failure_without_failed_lines(self):
++        result, calls, root = self.run_mock_pipeline(pytest_code=4)
++        self.assertEqual(result, 1)
++        self.assertIn("pytest exited 4", (root / "certification.pr-body.md").read_text())
++        worktree_call = next(c for c in calls if c[:3] == ["git", "worktree", "add"])
++        self.assertEqual(worktree_call[-1], "immutable-sha")
++
++    def test_uc_operational_failure_refuses(self):
++        with self.assertRaisesRegex(runner.CertifyError, "failed operationally"):
++            self.run_mock_pipeline(uc_code=1)
++
++    def test_measured_uc_floor_failure_keeps_human_acceptance(self):
++        result, _, root = self.run_mock_pipeline(uc_code=1, receipt={
++            "check": "uk_uc_floor", "passed": False,
++            "measures": [{"name": "spend", "passed": False}],
++        })
++        self.assertEqual(result, 0)
++        self.assertIn("UC floor acceptance", (root / "certification.pr-body.md").read_text())
++
++
++if __name__ == "__main__":
++    unittest.main()
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index e185862..62689f9 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -47,8 +47,10 @@ import json
+ import os
+ import re
+ import shlex
++import shutil
+ import subprocess
+ import sys
++import time
+ from dataclasses import dataclass
+ from datetime import UTC, datetime
+ from pathlib import Path
+@@ -103,6 +105,7 @@ def read_cut(tag: str, release: str) -> CutVersions:
+         f"releases/{release}/release_manifest.json",
+         repo_type="dataset",
+         revision=tag,
++        token=os.environ.get("HUGGING_FACE_TOKEN") or os.environ.get("HF_TOKEN"),
+     )
+     manifest = json.loads(Path(path).read_text())
+     build = manifest.get("build") or {}
+@@ -157,36 +160,76 @@ def regional_tag(flags: list[str]) -> str | None:
+     )
+ 
+ 
++def resource_guard(root: Path) -> dict[str, float | int]:
++    """Refuse work before spawning a process on an overloaded or full host."""
++
++    load1 = os.getloadavg()[0]
++    while not root.exists():
++        root = root.parent
++    free = shutil.disk_usage(root).free
++    if load1 > 110 or free < 55_000_000_000:
++        raise CertifyError(
++            f"resource guard: load1={load1:.2f} (maximum 110), "
++            f"free={free / 1e9:.2f} GB (minimum 55 decimal GB)."
++        )
++    return {"load1": load1, "free_bytes": free}
++
++
++def record_timing(path: Path, record: dict[str, Any]) -> None:
++    path.parent.mkdir(parents=True, exist_ok=True)
++    with path.open("a") as handle:
++        handle.write(json.dumps(record) + "\n")
++
++
+ class Shell:
+     """Runs commands in the policyengine.py worktree and records each one."""
+ 
+-    def __init__(self, cwd: Path, log: Path, *, dry_run: bool = False) -> None:
++    def __init__(
++        self, cwd: Path, log: Path, *, dry_run: bool = False,
++        timings: Path | None = None,
++    ) -> None:
+         self.cwd = cwd
+         self.log = log
+         self.dry_run = dry_run
++        self.timings = timings or log.with_suffix(".timings.jsonl")
+         self.commands: list[str] = []
+ 
+     def __call__(
+-        self, argv: list[str], *, env: dict[str, str] | None = None, check=True
++        self, argv: list[str], *, env: dict[str, str] | None = None, check=True,
++        step: str | None = None,
+     ) -> subprocess.CompletedProcess:
+         line = shlex.join(argv)
+         self.commands.append(line)
+         print(f"$ {line}", flush=True)
+         if self.dry_run:
+             return subprocess.CompletedProcess(argv, 0, "", "")
+-        with self.log.open("a") as handle:
+-            handle.write(f"\n# {datetime.now(UTC):%Y-%m-%dT%H:%M:%SZ} $ {line}\n")
+-            handle.flush()
+-            result = subprocess.run(
+-                argv,
+-                cwd=self.cwd,
+-                env={**os.environ, **(env or {})},
+-                stdout=subprocess.PIPE,
+-                stderr=subprocess.STDOUT,
+-                text=True,
+-                check=False,
+-            )
+-            handle.write(result.stdout)
++        started = time.monotonic()
++        timing = {
++            "step": step or line, "command": line,
++            "started_at": datetime.now(UTC).isoformat(), "returncode": None,
++        }
++        try:
++            timing.update(resource_guard(self.cwd))
++            with self.log.open("a") as handle:
++                handle.write(f"\n# {datetime.now(UTC):%Y-%m-%dT%H:%M:%SZ} $ {line}\n")
++                handle.flush()
++                result = subprocess.run(
++                    argv,
++                    cwd=self.cwd,
++                    env={**os.environ, **(env or {})},
++                    stdout=subprocess.PIPE,
++                    stderr=subprocess.STDOUT,
++                    text=True,
++                    check=False,
++                )
++                handle.write(result.stdout)
++            timing["returncode"] = result.returncode
++        except Exception as error:
++            timing["error"] = str(error)
++            raise
++        finally:
++            timing["elapsed_seconds"] = time.monotonic() - started
++            record_timing(self.timings, timing)
+         if check and result.returncode != 0:
+             tail = "\n".join(result.stdout.splitlines()[-25:])
+             raise CertifyError(f"{line} exited {result.returncode}:\n{tail}")
+@@ -252,10 +295,19 @@ _SURFACE_SCRIPT = (
+ )
+ 
+ 
+-def model_surface(version: str) -> dict[str, list[str]]:
++def model_surface(
++    version: str, *, timings: Path | None = None, resource_root: Path | None = None
++) -> dict[str, list[str]]:
+     """Every variable and every input variable of one policyengine-uk version."""
+ 
+-    result = subprocess.run(
++    started = time.monotonic()
++    timing = {
++        "step": f"model_surface:{version}",
++        "started_at": datetime.now(UTC).isoformat(), "returncode": None,
++    }
++    try:
++        timing.update(resource_guard(resource_root or Path.cwd()))
++        result = subprocess.run(
+         [
+             "uv", "run", "--no-project", "--python", PEPY_PYTHON,
+             "--with", f"policyengine-uk=={version}", "python", "-c", _SURFACE_SCRIPT,
+@@ -264,7 +316,16 @@ def model_surface(version: str) -> dict[str, list[str]]:
+         text=True,
+         check=True,
+         env={k: v for k, v in os.environ.items() if k not in {"UV_FROZEN", "VIRTUAL_ENV"}},
+-    )  # fmt: skip
++        )  # fmt: skip
++        timing["returncode"] = result.returncode
++    except Exception as error:
++        timing["returncode"] = getattr(error, "returncode", None)
++        timing["error"] = str(error)
++        raise
++    finally:
++        timing["elapsed_seconds"] = time.monotonic() - started
++        if timings:
++            record_timing(timings, timing)
+     return json.loads(result.stdout.strip().splitlines()[-1])
+ 
+ 
+@@ -385,6 +446,10 @@ def parse(argv: list[str] | None) -> argparse.Namespace:
+         default=Path("~/PolicyEngine/policyengine.py").expanduser(),
+     )
+     parser.add_argument("--worktree", type=Path, help="default: beside the repo")
++    parser.add_argument(
++        "--base-ref", default="origin/main",
++        help="base branch/ref for both the old bundle and the new worktree",
++    )
+     parser.add_argument("--skip-writer-check", metavar="WHY")
+     parser.add_argument("--skip-tests", action="store_true")
+     parser.add_argument("--test-workers", type=int, default=4)
+@@ -394,6 +459,8 @@ def parse(argv: list[str] | None) -> argparse.Namespace:
+     args = parser.parse_args(argv)
+     if args.open_pr and not args.push:
+         parser.error("--open-pr needs --push.")
++    if not 1 <= args.test_workers <= 4:
++        parser.error("--test-workers must be between 1 and 4.")
+     check_tag(args.national_tag, "national")
+     return args
+ 
+@@ -412,7 +479,23 @@ def certify(args: argparse.Namespace) -> int:
+         ["git", "rev-parse", "HEAD"], cwd=Path(__file__).resolve().parents[1], text=True
+     ).strip()
+     repo = args.pepy_repo
+-    subprocess.run(["git", "fetch", "--quiet", "origin", "main"], cwd=repo, check=True)
++    suffix = args.national_tag.rsplit("-", 1)[-1]
++    branch = f"certify-uk-{suffix}"
++    worktree = args.worktree or repo.parent / "_worktrees" / f"pepy-{branch}"
++    log = worktree.parent / f"{worktree.name}.certify.log"
++    timings = log.with_suffix(".timings.jsonl")
++    base_ref = args.base_ref
++    if base_ref.startswith("origin/"):
++        remote_branch = base_ref.removeprefix("origin/")
++        subprocess.run(
++            ["git", "fetch", "--quiet", "origin",
++             f"+refs/heads/{remote_branch}:refs/remotes/origin/{remote_branch}"],
++            cwd=repo, check=True,
++        )
++    base_commit = subprocess.check_output(
++        ["git", "rev-parse", "--verify", f"{base_ref}^{{commit}}"],
++        cwd=repo, text=True,
++    ).strip()
+ 
+     if args.skip_writer_check:
+         print(f"writer check skipped: {args.skip_writer_check}")
+@@ -435,7 +518,7 @@ def certify(args: argparse.Namespace) -> int:
+         )
+     old_bundle = json.loads(
+         subprocess.check_output(
+-            ["git", "show", "origin/main:src/policyengine/data/bundle/manifest.json"],
++            ["git", "show", f"{base_commit}:src/policyengine/data/bundle/manifest.json"],
+             cwd=repo,
+             text=True,
+         )
+@@ -459,7 +542,8 @@ def certify(args: argparse.Namespace) -> int:
+                     "where policyengine.py supports one."
+                 )
+             surface = input_surface_change(
+-                model_surface(dense.model), model_surface(model)
++                model_surface(dense.model, timings=timings, resource_root=repo),
++                model_surface(model, timings=timings, resource_root=repo),
+             )
+             if surface["dropped"] and not args.allow_dropped_columns:
+                 raise CertifyError(
+@@ -478,10 +562,9 @@ def certify(args: argparse.Namespace) -> int:
+         ["--regional-manifest-uri", record.get("regional_source_manifest_uri") or ""]
+     )
+ 
+-    suffix = args.national_tag.rsplit("-", 1)[-1]
+-    branch = f"certify-uk-{suffix}"
+-    worktree = args.worktree or repo.parent / "_worktrees" / f"pepy-{branch}"
+     plan = {
++        "base_ref": base_ref,
++        "base_commit": base_commit,
+         "national_tag": args.national_tag,
+         "model": model,
+         "core": core,
+@@ -503,17 +586,16 @@ def certify(args: argparse.Namespace) -> int:
+             "-b",
+             branch,
+             str(worktree),
+-            "origin/main",
++            base_commit,
+         ],
+         cwd=repo,
+         check=True,
+     )
+-    log = worktree.parent / f"{worktree.name}.certify.log"
+-    run = Shell(worktree, log)
++    run = Shell(worktree, log, timings=timings)
+     python = str(worktree / ".venv/bin/python")
+     sync = ["uv", "sync", "--frozen", "--extra", "uk", "--extra", "dev",
+             "--python", PEPY_PYTHON]  # fmt: skip
+-    run(sync)
++    run(sync, step="initial-sync")
+     certify_result = run(
+         [
+             python, "scripts/bundle.py", "certify-data", "--country", "uk",
+@@ -521,7 +603,7 @@ def certify(args: argparse.Namespace) -> int:
+             "--manifest-uri", manifest_uri(args.national_tag, NATIONAL_RELEASE),
+             *flags,
+             "--model-version", model, "--core-version", core,
+-        ]
++        ], step="certify-data",
+     )  # fmt: skip
+     warnings = [
+         line.strip()
+@@ -530,11 +612,11 @@ def certify(args: argparse.Namespace) -> int:
+     ]
+     run(
+         ["uv", "lock", "--no-config", "--no-sources", "--default-index",
+-         "https://pypi.org/simple", "--python", python]
++         "https://pypi.org/simple", "--python", python], step="lock",
+     )  # fmt: skip
+-    run(sync)
+-    run([python, "scripts/release_lock.py"])
+-    run([python, "scripts/bundle.py", "generate", "--include-tros"])
++    run(sync, step="certified-sync")
++    run([python, "scripts/release_lock.py"], step="release-lock")
++    run([python, "scripts/bundle.py", "generate", "--include-tros"], step="TROs")
+     new_bundle = json.loads(
+         (worktree / "src/policyengine/data/bundle/manifest.json").read_text()
+     )
+@@ -548,38 +630,69 @@ def certify(args: argparse.Namespace) -> int:
+             "old, new = json.loads(sys.argv[1]), json.loads(sys.argv[2]); "
+             "print(json.dumps(w(Path('.'), r(old, new))))",
+             json.dumps(record), json.dumps(new_record),
+-        ]
++        ], step="constant-rewrite",
+     )  # fmt: skip
+     constants = json.loads(rewrite.stdout.strip().splitlines()[-1] or "[]")
+     constants += rewrite_regional_constants(worktree, old_regional, new_regional)
+     run(
+         [python, "-m", "pytest", "tests/test_household_calculator_snapshot.py", "-q"],
+         env={"PE_UPDATE_SNAPSHOTS": "1"},
++        step="snapshots",
+     )
+     run(
+         [python, "scripts/bundle.py", "check", "--published-spm", "--include-tros",
+-         "--strict-tros"]
++         "--strict-tros"], step="strict-check",
+     )  # fmt: skip
+     uc_path = worktree.parent / f"{worktree.name}.uc_floor.json"
++    # An earlier attempt's receipt must not mask an operational failure.
++    uc_path.unlink(missing_ok=True)
+     uc = run(
+         [python, "scripts/check_uk_uc_floor.py", "--dataset",
+          new_record["default_dataset"], "--year", "2025", "--output", str(uc_path)],
+         check=False,
++        step="UC-floor",
+     )  # fmt: skip
++    if uc.returncode and not uc_path.exists():
++        raise CertifyError(
++            f"UC floor failed operationally (exit {uc.returncode}); no measured "
++            f"receipt was produced. See {log}."
++        )
++    if uc_path.exists():
++        receipt = json.loads(uc_path.read_text())
++        if uc.returncode and not (
++            uc.returncode == 1 and receipt.get("check") == "uk_uc_floor"
++            and receipt.get("passed") is False and receipt.get("measures")
++        ):
++            raise CertifyError(
++                f"UC floor failed operationally (exit {uc.returncode}); "
++                "the receipt is not a measured floor violation."
++            )
+     uc_summary = (
+         uc_path.read_text().strip()[:600]
+         if uc_path.exists()
+         else f"exit {uc.returncode}"
+     )
+-    tests_summary, tests_failed = "skipped", []
++    tests_summary, tests_failed, tests_returncode = "skipped", [], 0
+     if not args.skip_tests:
++        xdist = run(
++            [python, "-c", "import xdist"], check=False, step="check-pytest-xdist"
++        )
++        if xdist.returncode:
++            run(
++                ["uv", "pip", "install", "--python", python, "pytest-xdist"],
++                step="install-pytest-xdist",
++            )
+         tests = run(
+             [python, "-m", "pytest", "-n", str(args.test_workers), "-q",
+              "-p", "no:cacheprovider"],
+             check=False,
++            step="tests",
+         )  # fmt: skip
++        tests_returncode = tests.returncode
+         tests_failed = re.findall(r"^FAILED (\S+)", tests.stdout, flags=re.MULTILINE)
+         tests_summary = (tests.stdout.strip().splitlines() or ["?"])[-1]
++        if tests_returncode and not tests_failed:
++            tests_failed = [f"pytest exited {tests_returncode}: {tests_summary}"]
+     context = {
+         "national_tag": args.national_tag,
+         "model": model,
+@@ -614,9 +727,8 @@ def certify(args: argparse.Namespace) -> int:
+              "--head", branch, "--title",
+              f"Certify Microcosm UK cut {args.national_tag}", "--body-file", str(body)]
+         )  # fmt: skip
+-    return 0 if not tests_failed else 1
++    return 0 if not tests_returncode else 1
+ 
+ 
+ if __name__ == "__main__":
+     raise SystemExit(main())
+-
+-- 
+2.55.0
+
diff --git a/.job/tool-patches/0002-certifier-preflight-streaming-NOTE.md b/.job/tool-patches/0002-certifier-preflight-streaming-NOTE.md
new file mode 100644
index 00000000..b4d26665
--- /dev/null
+++ b/.job/tool-patches/0002-certifier-preflight-streaming-NOTE.md
@@ -0,0 +1,7 @@
+For microcosm #1348's owner: follow-up local-only rehearsal patch; apply after 0001.
+
+The first live run silently waited during uninstrumented preflight before reaching Shell. This patch records writer-check, national/regional manifest downloads, git fetch/base resolution/worktree creation, the old-bundle read and the direct regional-constant rewrite in the same JSONL timings. Each preflight/model-surface step prints start and completion messages. Shell child output now streams immediately into the .certify.log file, so operators can tail package installs, TRO generation and pytest while they run.
+
+Git fetch has a 300-second subprocess timeout and the two writer-check network calls each have a 120-second timeout. Hugging Face metadata has a 60-second per-request timeout; downloads use a default 120-second per-request timeout, preserving an operator's existing HF_HUB_DOWNLOAD_TIMEOUT. Hub retries can extend total wall time; these values bound individual HTTP operations, not the whole manifest step.
+
+Validation: 11 offline stdlib unittest regressions passed in 0.118 seconds; Ruff check and Ruff format --check passed for the runner and its test file. Formatting only also cleans the initial patch's tests.
diff --git a/.job/tool-patches/0002-certifier-preflight-streaming.patch b/.job/tool-patches/0002-certifier-preflight-streaming.patch
new file mode 100644
index 00000000..28c10cba
--- /dev/null
+++ b/.job/tool-patches/0002-certifier-preflight-streaming.patch
@@ -0,0 +1,723 @@
+From bc25a773d5c77bc0407dfd029ca2f324bef5f471 Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 07:35:12 -0400
+Subject: [PATCH] Expose timed preflight and stream long-running certification
+ logs
+
+---
+ .../test_certify_uk_cut_in_policyengine_py.py | 265 +++++++++++++-----
+ tools/certify_uk_cut_in_policyengine_py.py    | 185 ++++++++----
+ 2 files changed, 334 insertions(+), 116 deletions(-)
+
+diff --git a/tests/test_certify_uk_cut_in_policyengine_py.py b/tests/test_certify_uk_cut_in_policyengine_py.py
+index d383721..358f88f 100644
+--- a/tests/test_certify_uk_cut_in_policyengine_py.py
++++ b/tests/test_certify_uk_cut_in_policyengine_py.py
+@@ -1,18 +1,17 @@
+ """Offline regression tests for the certification orchestration safeguards."""
+ 
+-import argparse
+ import importlib.util
++import io
+ import json
+ import os
+-from pathlib import Path
+ import subprocess
+ import sys
+ import tempfile
+ import types
+ import unittest
++from pathlib import Path
+ from unittest.mock import Mock, patch
+ 
+-
+ SPEC = importlib.util.spec_from_file_location(
+     "certify_runner",
+     Path(__file__).resolve().parents[1] / "tools/certify_uk_cut_in_policyengine_py.py",
+@@ -27,26 +26,46 @@ class RunnerTests(unittest.TestCase):
+     def test_hugging_face_token_is_explicit_and_not_logged(self):
+         with tempfile.TemporaryDirectory() as tmp:
+             manifest = Path(tmp) / "manifest.json"
+-            manifest.write_text(json.dumps({"build": {
+-                "cut_tag": TAG,
+-                "built_with_model_package": {"version": "2.147.2"},
+-                "built_with_core_package": {"version": "3.0"},
+-            }}))
++            manifest.write_text(
++                json.dumps(
++                    {
++                        "build": {
++                            "cut_tag": TAG,
++                            "built_with_model_package": {"version": "2.147.2"},
++                            "built_with_core_package": {"version": "3.0"},
++                        }
++                    }
++                )
++            )
+             download = Mock(return_value=str(manifest))
+-            with patch.dict(sys.modules, {"huggingface_hub": types.SimpleNamespace(
+-                hf_hub_download=download
+-            )}), patch.dict(os.environ, {"HUGGING_FACE_TOKEN": "private-value"}):
+-                self.assertEqual(runner.read_cut(TAG, runner.NATIONAL_RELEASE).model,
+-                                 "2.147.2")
++            with (
++                patch.dict(
++                    sys.modules,
++                    {
++                        "huggingface_hub": types.SimpleNamespace(
++                            hf_hub_download=download
++                        )
++                    },
++                ),
++                patch.dict(os.environ, {"HUGGING_FACE_TOKEN": "private-value"}),
++            ):
++                self.assertEqual(
++                    runner.read_cut(TAG, runner.NATIONAL_RELEASE).model, "2.147.2"
++                )
+             self.assertEqual(download.call_args.kwargs["token"], "private-value")
+ 
+     def test_shell_records_failed_commands(self):
+         with tempfile.TemporaryDirectory() as tmp:
+             root = Path(tmp)
+             shell = runner.Shell(root, root / "run.log")
+-            with patch.object(runner, "resource_guard", return_value={"load1": 1}), \
+-                 patch.object(runner.subprocess, "run", return_value=
+-                              subprocess.CompletedProcess(["false"], 2, "bad")):
++            with (
++                patch.object(runner, "resource_guard", return_value={"load1": 1}),
++                patch.object(
++                    runner,
++                    "run_logged",
++                    return_value=subprocess.CompletedProcess(["false"], 2, "bad"),
++                ),
++            ):
+                 with self.assertRaisesRegex(runner.CertifyError, "exited 2"):
+                     shell(["false"], step="failure")
+             record = json.loads(shell.timings.read_text())
+@@ -57,30 +76,66 @@ class RunnerTests(unittest.TestCase):
+     def test_guard_prevents_process_and_records_refusal(self):
+         with tempfile.TemporaryDirectory() as tmp:
+             shell = runner.Shell(Path(tmp), Path(tmp) / "run.log")
+-            with patch.object(runner, "resource_guard", side_effect=
+-                              runner.CertifyError("resource guard")), \
+-                 patch.object(runner.subprocess, "run") as spawn:
++            with (
++                patch.object(
++                    runner,
++                    "resource_guard",
++                    side_effect=runner.CertifyError("resource guard"),
++                ),
++                patch.object(runner, "run_logged") as spawn,
++            ):
+                 with self.assertRaises(runner.CertifyError):
+                     shell(["heavy"])
+                 spawn.assert_not_called()
+-            self.assertIn("resource guard", json.loads(shell.timings.read_text())["error"])
++            self.assertIn(
++                "resource guard", json.loads(shell.timings.read_text())["error"]
++            )
++
++    def test_output_streams_and_is_retained(self):
++        handle = io.StringIO()
++        process = Mock()
++        process.stdout = iter(["first\n", "second\n"])
++        process.wait.return_value = 3
++        spawn = Mock()
++        spawn.return_value.__enter__ = Mock(return_value=process)
++        spawn.return_value.__exit__ = Mock(return_value=False)
++        with patch.object(runner.subprocess, "Popen", spawn):
++            result = runner.run_logged(["work"], cwd=Path.cwd(), env={}, handle=handle)
++        self.assertEqual(handle.getvalue(), "first\nsecond\n")
++        self.assertEqual(result.stdout, handle.getvalue())
++        self.assertEqual(result.returncode, 3)
+ 
+     def test_resource_thresholds_are_decimal(self):
+-        with patch.object(runner.os, "getloadavg", return_value=(110, 1, 1)), \
+-             patch.object(runner.shutil, "disk_usage", return_value=
+-                          types.SimpleNamespace(free=55_000_000_000)):
++        with (
++            patch.object(runner.os, "getloadavg", return_value=(110, 1, 1)),
++            patch.object(
++                runner.shutil,
++                "disk_usage",
++                return_value=types.SimpleNamespace(free=55_000_000_000),
++            ),
++        ):
+             runner.resource_guard(Path.cwd())
+         for load, free in ((110.1, 55_000_000_000), (1, 54_999_999_999)):
+-            with patch.object(runner.os, "getloadavg", return_value=(load, 1, 1)), \
+-                 patch.object(runner.shutil, "disk_usage", return_value=
+-                              types.SimpleNamespace(free=free)):
++            with (
++                patch.object(runner.os, "getloadavg", return_value=(load, 1, 1)),
++                patch.object(
++                    runner.shutil,
++                    "disk_usage",
++                    return_value=types.SimpleNamespace(free=free),
++                ),
++            ):
+                 with self.assertRaises(runner.CertifyError):
+                     runner.resource_guard(Path.cwd())
+ 
+     def test_model_surface_is_guarded(self):
+-        with patch.object(runner, "resource_guard", side_effect=
+-                          runner.CertifyError("resource guard")), \
+-             patch.object(runner.subprocess, "run") as spawn:
++        with (
++            patch.object(
++                runner,
++                "resource_guard",
++                side_effect=runner.CertifyError("resource guard"),
++            ),
++            patch.object(runner.subprocess, "run") as spawn,
++        ):
+             with self.assertRaises(runner.CertifyError):
+                 runner.model_surface("2.147.2")
+             spawn.assert_not_called()
+@@ -88,32 +143,72 @@ class RunnerTests(unittest.TestCase):
+     def test_workers_are_limited(self):
+         for workers in (0, 5):
+             with self.assertRaises(SystemExit):
+-                runner.parse(["--national-tag", TAG, "--drop-regional-release",
+-                              "--test-workers", str(workers)])
+-        self.assertEqual(runner.parse([
+-            "--national-tag", TAG, "--drop-regional-release",
+-            "--test-workers", "4"
+-        ]).test_workers, 4)
++                runner.parse(
++                    [
++                        "--national-tag",
++                        TAG,
++                        "--drop-regional-release",
++                        "--test-workers",
++                        str(workers),
++                    ]
++                )
++        self.assertEqual(
++            runner.parse(
++                [
++                    "--national-tag",
++                    TAG,
++                    "--drop-regional-release",
++                    "--test-workers",
++                    "4",
++                ]
++            ).test_workers,
++            4,
++        )
+ 
+     def test_local_base_is_resolved_once_and_used_for_old_bundle(self):
+         with tempfile.TemporaryDirectory() as tmp:
+-            args = runner.parse([
+-                "--national-tag", TAG, "--drop-regional-release", "--dry-run",
+-                "--base-ref", "stack-local", "--pepy-repo", tmp,
+-                "--skip-writer-check", "offline regression",
+-            ])
++            args = runner.parse(
++                [
++                    "--national-tag",
++                    TAG,
++                    "--drop-regional-release",
++                    "--dry-run",
++                    "--base-ref",
++                    "stack-local",
++                    "--pepy-repo",
++                    tmp,
++                    "--skip-writer-check",
++                    "offline regression",
++                ]
++            )
+             old = {"data_releases": {"uk": {}}}
+-            with patch.object(runner.subprocess, "check_output", side_effect=
+-                              ["microcosm\n", "immutable-sha\n", json.dumps(old)]) as read, \
+-                 patch.object(runner.subprocess, "run") as spawn, \
+-                 patch.object(runner, "read_cut", return_value=
+-                              runner.CutVersions(TAG, "2.147.2", "3.0", ())):
++            with (
++                patch.object(
++                    runner.subprocess,
++                    "check_output",
++                    side_effect=["microcosm\n", "immutable-sha\n", json.dumps(old)],
++                ) as read,
++                patch.object(runner.subprocess, "run") as spawn,
++                patch.object(
++                    runner,
++                    "read_cut",
++                    return_value=runner.CutVersions(TAG, "2.147.2", "3.0", ()),
++                ),
++            ):
+                 self.assertEqual(runner.certify(args), 0)
+             spawn.assert_not_called()
+-            self.assertEqual(read.call_args_list[1].args[0],
+-                             ["git", "rev-parse", "--verify", "stack-local^{commit}"])
+-            self.assertEqual(read.call_args_list[2].args[0], ["git", "show",
+-                "immutable-sha:src/policyengine/data/bundle/manifest.json"])
++            self.assertEqual(
++                read.call_args_list[1].args[0],
++                ["git", "rev-parse", "--verify", "stack-local^{commit}"],
++            )
++            self.assertEqual(
++                read.call_args_list[2].args[0],
++                [
++                    "git",
++                    "show",
++                    "immutable-sha:src/policyengine/data/bundle/manifest.json",
++                ],
++            )
+ 
+     def run_mock_pipeline(self, *, pytest_code=0, uc_code=0, receipt=None):
+         tmp = tempfile.TemporaryDirectory()
+@@ -121,12 +216,23 @@ class RunnerTests(unittest.TestCase):
+         root = Path(tmp.name)
+         worktree = root / "certification"
+         bundle = {"data_releases": {"uk": {"default_dataset": "candidate"}}}
+-        args = runner.parse([
+-            "--national-tag", TAG, "--drop-regional-release", "--base-ref", "local",
+-            "--pepy-repo", str(root), "--worktree", str(worktree),
+-            "--skip-writer-check", "offline regression",
+-        ])
++        args = runner.parse(
++            [
++                "--national-tag",
++                TAG,
++                "--drop-regional-release",
++                "--base-ref",
++                "local",
++                "--pepy-repo",
++                str(root),
++                "--worktree",
++                str(worktree),
++                "--skip-writer-check",
++                "offline regression",
++            ]
++        )
+         calls = []
++
+         def command(argv, **kwargs):
+             calls.append(argv)
+             if argv[:3] == ["git", "worktree", "add"]:
+@@ -134,31 +240,46 @@ class RunnerTests(unittest.TestCase):
+                 manifest.parent.mkdir(parents=True)
+                 manifest.write_text(json.dumps(bundle))
+             return subprocess.CompletedProcess(argv, 0, "", "")
++
+         def shell(argv, **kwargs):
+             calls.append(argv)
+             if "constant-rewrite" == kwargs.get("step"):
+                 return subprocess.CompletedProcess(argv, 0, "[]", "")
+             if "UC-floor" == kwargs.get("step"):
+                 if receipt is not None:
+-                    (root / "certification.uc_floor.json").write_text(json.dumps(receipt))
++                    (root / "certification.uc_floor.json").write_text(
++                        json.dumps(receipt)
++                    )
+                 return subprocess.CompletedProcess(argv, uc_code, "traceback", "")
+             if "tests" == kwargs.get("step"):
+-                return subprocess.CompletedProcess(argv, pytest_code,
+-                                                    "ERROR: unrecognized arguments: -n", "")
++                return subprocess.CompletedProcess(
++                    argv, pytest_code, "ERROR: unrecognized arguments: -n", ""
++                )
+             return subprocess.CompletedProcess(argv, 0, "", "")
+-        with patch.object(runner.subprocess, "check_output", side_effect=
+-                          ["microcosm", "immutable-sha", json.dumps(bundle)]), \
+-             patch.object(runner.subprocess, "run", side_effect=command), \
+-             patch.object(runner, "read_cut", return_value=
+-                          runner.CutVersions(TAG, "2.147.2", "3.0", ())), \
+-             patch.object(runner.Shell, "__call__", side_effect=shell):
++
++        with (
++            patch.object(
++                runner.subprocess,
++                "check_output",
++                side_effect=["microcosm", "immutable-sha", json.dumps(bundle)],
++            ),
++            patch.object(runner.subprocess, "run", side_effect=command),
++            patch.object(
++                runner,
++                "read_cut",
++                return_value=runner.CutVersions(TAG, "2.147.2", "3.0", ()),
++            ),
++            patch.object(runner.Shell, "__call__", side_effect=shell),
++        ):
+             result = runner.certify(args)
+         return result, calls, root
+ 
+     def test_pytest_configuration_error_is_failure_without_failed_lines(self):
+         result, calls, root = self.run_mock_pipeline(pytest_code=4)
+         self.assertEqual(result, 1)
+-        self.assertIn("pytest exited 4", (root / "certification.pr-body.md").read_text())
++        self.assertIn(
++            "pytest exited 4", (root / "certification.pr-body.md").read_text()
++        )
+         worktree_call = next(c for c in calls if c[:3] == ["git", "worktree", "add"])
+         self.assertEqual(worktree_call[-1], "immutable-sha")
+ 
+@@ -167,12 +288,18 @@ class RunnerTests(unittest.TestCase):
+             self.run_mock_pipeline(uc_code=1)
+ 
+     def test_measured_uc_floor_failure_keeps_human_acceptance(self):
+-        result, _, root = self.run_mock_pipeline(uc_code=1, receipt={
+-            "check": "uk_uc_floor", "passed": False,
+-            "measures": [{"name": "spend", "passed": False}],
+-        })
++        result, _, root = self.run_mock_pipeline(
++            uc_code=1,
++            receipt={
++                "check": "uk_uc_floor",
++                "passed": False,
++                "measures": [{"name": "spend", "passed": False}],
++            },
++        )
+         self.assertEqual(result, 0)
+-        self.assertIn("UC floor acceptance", (root / "certification.pr-body.md").read_text())
++        self.assertIn(
++            "UC floor acceptance", (root / "certification.pr-body.md").read_text()
++        )
+ 
+ 
+ if __name__ == "__main__":
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index 62689f9..76e819a 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -51,6 +51,7 @@ import shutil
+ import subprocess
+ import sys
+ import time
++from contextlib import contextmanager
+ from dataclasses import dataclass
+ from datetime import UTC, datetime
+ from pathlib import Path
+@@ -98,6 +99,9 @@ class CutVersions:
+ 
+ 
+ def read_cut(tag: str, release: str) -> CutVersions:
++    # Set before importing the hub: its HTTP timeout constants are import-time.
++    os.environ.setdefault("HF_HUB_DOWNLOAD_TIMEOUT", "120")
++    os.environ.setdefault("HF_HUB_ETAG_TIMEOUT", "60")
+     from huggingface_hub import hf_hub_download
+ 
+     path = hf_hub_download(
+@@ -106,6 +110,7 @@ def read_cut(tag: str, release: str) -> CutVersions:
+         repo_type="dataset",
+         revision=tag,
+         token=os.environ.get("HUGGING_FACE_TOKEN") or os.environ.get("HF_TOKEN"),
++        etag_timeout=60,
+     )
+     manifest = json.loads(Path(path).read_text())
+     build = manifest.get("build") or {}
+@@ -181,11 +186,58 @@ def record_timing(path: Path, record: dict[str, Any]) -> None:
+         handle.write(json.dumps(record) + "\n")
+ 
+ 
++@contextmanager
++def timed_step(name: str, path: Path):
++    """Make network preflight and local Python work visible, including failures."""
++    started = time.monotonic()
++    record = {"step": name, "started_at": datetime.now(UTC).isoformat()}
++    print(f"[start] {name}", flush=True)
++    try:
++        yield record
++        record.setdefault("returncode", 0)
++    except Exception as error:
++        record["returncode"] = getattr(error, "returncode", None)
++        record["error"] = str(error)
++        raise
++    finally:
++        record["elapsed_seconds"] = time.monotonic() - started
++        record_timing(path, record)
++        print(
++            f"[done] {name}: {record['elapsed_seconds']:.3f}s "
++            f"(exit {record.get('returncode')})",
++            flush=True,
++        )
++
++
++def run_logged(argv: list[str], *, cwd: Path, env: dict[str, str], handle):
++    """Stream child output into the diagnostic log while retaining its result."""
++    output = []
++    with subprocess.Popen(
++        argv,
++        cwd=cwd,
++        env=env,
++        stdout=subprocess.PIPE,
++        stderr=subprocess.STDOUT,
++        text=True,
++        bufsize=1,
++    ) as process:
++        for line in process.stdout:
++            handle.write(line)
++            handle.flush()
++            output.append(line)
++        returncode = process.wait()
++    return subprocess.CompletedProcess(argv, returncode, "".join(output), "")
++
++
+ class Shell:
+     """Runs commands in the policyengine.py worktree and records each one."""
+ 
+     def __init__(
+-        self, cwd: Path, log: Path, *, dry_run: bool = False,
++        self,
++        cwd: Path,
++        log: Path,
++        *,
++        dry_run: bool = False,
+         timings: Path | None = None,
+     ) -> None:
+         self.cwd = cwd
+@@ -195,7 +247,11 @@ class Shell:
+         self.commands: list[str] = []
+ 
+     def __call__(
+-        self, argv: list[str], *, env: dict[str, str] | None = None, check=True,
++        self,
++        argv: list[str],
++        *,
++        env: dict[str, str] | None = None,
++        check=True,
+         step: str | None = None,
+     ) -> subprocess.CompletedProcess:
+         line = shlex.join(argv)
+@@ -205,24 +261,22 @@ class Shell:
+             return subprocess.CompletedProcess(argv, 0, "", "")
+         started = time.monotonic()
+         timing = {
+-            "step": step or line, "command": line,
+-            "started_at": datetime.now(UTC).isoformat(), "returncode": None,
++            "step": step or line,
++            "command": line,
++            "started_at": datetime.now(UTC).isoformat(),
++            "returncode": None,
+         }
+         try:
+             timing.update(resource_guard(self.cwd))
+             with self.log.open("a") as handle:
+                 handle.write(f"\n# {datetime.now(UTC):%Y-%m-%dT%H:%M:%SZ} $ {line}\n")
+                 handle.flush()
+-                result = subprocess.run(
++                result = run_logged(
+                     argv,
+                     cwd=self.cwd,
+                     env={**os.environ, **(env or {})},
+-                    stdout=subprocess.PIPE,
+-                    stderr=subprocess.STDOUT,
+-                    text=True,
+-                    check=False,
++                    handle=handle,
+                 )
+-                handle.write(result.stdout)
+             timing["returncode"] = result.returncode
+         except Exception as error:
+             timing["error"] = str(error)
+@@ -230,6 +284,11 @@ class Shell:
+         finally:
+             timing["elapsed_seconds"] = time.monotonic() - started
+             record_timing(self.timings, timing)
++            print(
++                f"[done] {step or line}: {timing['elapsed_seconds']:.3f}s "
++                f"(exit {timing.get('returncode')})",
++                flush=True,
++            )
+         if check and result.returncode != 0:
+             tail = "\n".join(result.stdout.splitlines()[-25:])
+             raise CertifyError(f"{line} exited {result.returncode}:\n{tail}")
+@@ -248,6 +307,7 @@ def writer_check(national_tag: str, pepy_repo: Path) -> list[str]:
+         capture_output=True,
+         text=True,
+         check=False,
++        timeout=120,
+     )  # fmt: skip
+     if result.returncode != 0:
+         raise CertifyError(
+@@ -263,6 +323,7 @@ def writer_check(national_tag: str, pepy_repo: Path) -> list[str]:
+         capture_output=True,
+         text=True,
+         check=False,
++        timeout=120,
+     )
+     if remote.stdout.strip():
+         found.append(f"branch {branch} exists on origin")
+@@ -303,8 +364,10 @@ def model_surface(
+     started = time.monotonic()
+     timing = {
+         "step": f"model_surface:{version}",
+-        "started_at": datetime.now(UTC).isoformat(), "returncode": None,
++        "started_at": datetime.now(UTC).isoformat(),
++        "returncode": None,
+     }
++    print(f"[start] model_surface:{version}", flush=True)
+     try:
+         timing.update(resource_guard(resource_root or Path.cwd()))
+         result = subprocess.run(
+@@ -326,6 +389,11 @@ def model_surface(
+         timing["elapsed_seconds"] = time.monotonic() - started
+         if timings:
+             record_timing(timings, timing)
++        print(
++            f"[done] model_surface:{version}: {timing['elapsed_seconds']:.3f}s "
++            f"(exit {timing.get('returncode')})",
++            flush=True,
++        )
+     return json.loads(result.stdout.strip().splitlines()[-1])
+ 
+ 
+@@ -447,7 +515,8 @@ def parse(argv: list[str] | None) -> argparse.Namespace:
+     )
+     parser.add_argument("--worktree", type=Path, help="default: beside the repo")
+     parser.add_argument(
+-        "--base-ref", default="origin/main",
++        "--base-ref",
++        default="origin/main",
+         help="base branch/ref for both the old bundle and the new worktree",
+     )
+     parser.add_argument("--skip-writer-check", metavar="WHY")
+@@ -487,20 +556,31 @@ def certify(args: argparse.Namespace) -> int:
+     base_ref = args.base_ref
+     if base_ref.startswith("origin/"):
+         remote_branch = base_ref.removeprefix("origin/")
+-        subprocess.run(
+-            ["git", "fetch", "--quiet", "origin",
+-             f"+refs/heads/{remote_branch}:refs/remotes/origin/{remote_branch}"],
+-            cwd=repo, check=True,
+-        )
+-    base_commit = subprocess.check_output(
+-        ["git", "rev-parse", "--verify", f"{base_ref}^{{commit}}"],
+-        cwd=repo, text=True,
+-    ).strip()
++        with timed_step("git-fetch", timings):
++            subprocess.run(
++                [
++                    "git",
++                    "fetch",
++                    "--quiet",
++                    "origin",
++                    f"+refs/heads/{remote_branch}:refs/remotes/origin/{remote_branch}",
++                ],
++                cwd=repo,
++                check=True,
++                timeout=300,
++            )
++    with timed_step("resolve-base", timings):
++        base_commit = subprocess.check_output(
++            ["git", "rev-parse", "--verify", f"{base_ref}^{{commit}}"],
++            cwd=repo,
++            text=True,
++        ).strip()
+ 
+     if args.skip_writer_check:
+         print(f"writer check skipped: {args.skip_writer_check}")
+     else:
+-        others = writer_check(args.national_tag, repo)
++        with timed_step("writer-check", timings):
++            others = writer_check(args.national_tag, repo)
+         if others:
+             raise CertifyError(
+                 "this cut is already being certified: "
+@@ -508,7 +588,8 @@ def certify(args: argparse.Namespace) -> int:
+                 + ". Coordinate with its owner instead of opening a second PR."
+             )
+ 
+-    national = read_cut(args.national_tag, NATIONAL_RELEASE)
++    with timed_step("national-manifest", timings):
++        national = read_cut(args.national_tag, NATIONAL_RELEASE)
+     model = args.model_version or national.model
+     core = args.core_version or national.core
+     if not covers(national, model):
+@@ -516,20 +597,26 @@ def certify(args: argparse.Namespace) -> int:
+             f"the national cut was built with policyengine-uk {national.model} and "
+             f"claims {list(national.claims)}; it cannot be certified for {model}."
+         )
+-    old_bundle = json.loads(
+-        subprocess.check_output(
+-            ["git", "show", f"{base_commit}:src/policyengine/data/bundle/manifest.json"],
+-            cwd=repo,
+-            text=True,
++    with timed_step("old-bundle", timings):
++        old_bundle = json.loads(
++            subprocess.check_output(
++                [
++                    "git",
++                    "show",
++                    f"{base_commit}:src/policyengine/data/bundle/manifest.json",
++                ],
++                cwd=repo,
++                text=True,
++            )
+         )
+-    )
+     record = old_bundle["data_releases"]["uk"]
+     flags = regional_flags(args, record)
+     new_regional = regional_tag(flags)
+     regional_summary = shlex.join(flags)
+     dense, surface = None, None
+     if new_regional is not None:
+-        dense = read_cut(new_regional, DENSE_RELEASE)
++        with timed_step("regional-manifest", timings):
++            dense = read_cut(new_regional, DENSE_RELEASE)
+         if not covers(dense, model):
+             if not args.accept_regional_model_mismatch:
+                 raise CertifyError(
+@@ -577,20 +664,21 @@ def certify(args: argparse.Namespace) -> int:
+         return 0
+     if worktree.exists():
+         raise CertifyError(f"{worktree} exists; remove it or pass --worktree.")
+-    subprocess.run(
+-        [
+-            "git",
+-            "worktree",
+-            "add",
+-            "--quiet",
+-            "-b",
+-            branch,
+-            str(worktree),
+-            base_commit,
+-        ],
+-        cwd=repo,
+-        check=True,
+-    )
++    with timed_step("git-worktree", timings):
++        subprocess.run(
++            [
++                "git",
++                "worktree",
++                "add",
++                "--quiet",
++                "-b",
++                branch,
++                str(worktree),
++                base_commit,
++            ],
++            cwd=repo,
++            check=True,
++        )
+     run = Shell(worktree, log, timings=timings)
+     python = str(worktree / ".venv/bin/python")
+     sync = ["uv", "sync", "--frozen", "--extra", "uk", "--extra", "dev",
+@@ -633,7 +721,8 @@ def certify(args: argparse.Namespace) -> int:
+         ], step="constant-rewrite",
+     )  # fmt: skip
+     constants = json.loads(rewrite.stdout.strip().splitlines()[-1] or "[]")
+-    constants += rewrite_regional_constants(worktree, old_regional, new_regional)
++    with timed_step("regional-constant-rewrite", timings):
++        constants += rewrite_regional_constants(worktree, old_regional, new_regional)
+     run(
+         [python, "-m", "pytest", "tests/test_household_calculator_snapshot.py", "-q"],
+         env={"PE_UPDATE_SNAPSHOTS": "1"},
+@@ -660,8 +749,10 @@ def certify(args: argparse.Namespace) -> int:
+     if uc_path.exists():
+         receipt = json.loads(uc_path.read_text())
+         if uc.returncode and not (
+-            uc.returncode == 1 and receipt.get("check") == "uk_uc_floor"
+-            and receipt.get("passed") is False and receipt.get("measures")
++            uc.returncode == 1
++            and receipt.get("check") == "uk_uc_floor"
++            and receipt.get("passed") is False
++            and receipt.get("measures")
+         ):
+             raise CertifyError(
+                 f"UC floor failed operationally (exit {uc.returncode}); "
+-- 
+2.55.0
+
diff --git a/.job/tool-patches/0003-certifier-qualification-handoff-NOTE.md b/.job/tool-patches/0003-certifier-qualification-handoff-NOTE.md
new file mode 100644
index 00000000..75cd4582
--- /dev/null
+++ b/.job/tool-patches/0003-certifier-qualification-handoff-NOTE.md
@@ -0,0 +1,7 @@
+For microcosm #1348's owner: third local-only rehearsal patch; apply after 0001 and 0002. No push, PR or dispatch was executed.
+
+The mechanical handoff previously named only the UC exception and other manual judgments before leaving draft. Repository certification guidance also requires exact-pair wrapper candidate qualification. Passing the producer's 19 release-cut gates and pytest does not establish that evidence. The generated handoff now says wrapper qualification is pending unless reviewed receipts bind the candidate pair, commit and bundle, and adds an unchecked requirement for every candidate gate, explicitly including the full reform contract, metrics review, reform responsiveness and scorecard replay. It waives no gate or acceptance decision.
+
+The optional --pr-base defaults to main and is used only if an operator separately enables --push and --open-pr. This lets a later authorized production run explicitly target budget-local-data-manifest when stacking on #704; --base-ref continues to select the checkout base independently. Neither argument enables an external action.
+
+Validation: 13 lightweight offline stdlib unittest regressions passed in 0.708 seconds. Ruff check and Ruff format --check passed. No qualification pipeline, replay or external action was started.
diff --git a/.job/tool-patches/0003-certifier-qualification-handoff.patch b/.job/tool-patches/0003-certifier-qualification-handoff.patch
new file mode 100644
index 00000000..af6aba29
--- /dev/null
+++ b/.job/tool-patches/0003-certifier-qualification-handoff.patch
@@ -0,0 +1,104 @@
+From 6cc73d5ac8ef350777609fad6f3e19e1e4255b77 Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 07:43:40 -0400
+Subject: [PATCH] Label certification handoff qualification pending and allow
+ stacked PR target
+
+---
+ .../test_certify_uk_cut_in_policyengine_py.py | 22 +++++++++++++++++++
+ tools/certify_uk_cut_in_policyengine_py.py    | 18 ++++++++++++++-
+ 2 files changed, 39 insertions(+), 1 deletion(-)
+
+diff --git a/tests/test_certify_uk_cut_in_policyengine_py.py b/tests/test_certify_uk_cut_in_policyengine_py.py
+index 358f88f..04738fd 100644
+--- a/tests/test_certify_uk_cut_in_policyengine_py.py
++++ b/tests/test_certify_uk_cut_in_policyengine_py.py
+@@ -165,6 +165,14 @@ class RunnerTests(unittest.TestCase):
+             4,
+         )
+ 
++    def test_pr_base_accepts_explicit_stack_target(self):
++        common = ["--national-tag", TAG, "--drop-regional-release"]
++        self.assertEqual(runner.parse(common).pr_base, "main")
++        args = runner.parse(common + ["--pr-base", "budget-local-data-manifest"])
++        self.assertEqual(args.pr_base, "budget-local-data-manifest")
++        self.assertFalse(args.push)
++        self.assertFalse(args.open_pr)
++
+     def test_local_base_is_resolved_once_and_used_for_old_bundle(self):
+         with tempfile.TemporaryDirectory() as tmp:
+             args = runner.parse(
+@@ -301,6 +309,20 @@ class RunnerTests(unittest.TestCase):
+             "UC floor acceptance", (root / "certification.pr-body.md").read_text()
+         )
+ 
++    def test_handoff_marks_wrapper_qualification_pending(self):
++        _, _, root = self.run_mock_pipeline()
++        body = (root / "certification.pr-body.md").read_text()
++        for expected in (
++            "Mechanical handoff; wrapper qualification pending",
++            "19 producer release-cut gates and passing pytest do not establish",
++            "- [ ] **Wrapper qualification.**",
++            "full reform contract",
++            "metrics review and reform responsiveness",
++            "scorecard replay evidence",
++            "this script waives no gate",
++        ):
++            self.assertIn(expected, body)
++
+ 
+ if __name__ == "__main__":
+     unittest.main()
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index 76e819a..5f32fc2 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -428,6 +428,11 @@ def pr_body(context: dict[str, Any]) -> str:
+         f"Certifies Microcosm UK national cut `{context['national_tag']}` "
+         f"(policyengine-uk {context['model']}, core {context['core']}).",
+         "",
++        "**Mechanical handoff; wrapper qualification pending** unless reviewed "
++        "receipts cover this exact candidate pair, commit and bundle. Microcosm's "
++        "19 producer release-cut gates and passing pytest do not establish "
++        "wrapper qualification.",
++        "",
+         f"- **Regional route:** {context['regional_summary']}",
+         f"- **Compatibility basis:** `{context.get('basis')}`",
+         f"- **Made by** `tools/certify_uk_cut_in_policyengine_py.py` in "
+@@ -448,6 +453,12 @@ def pr_body(context: dict[str, Any]) -> str:
+         "",
+         "## Needs a person before this leaves draft",
+         "",
++        "- [ ] **Wrapper qualification.** Attach exact-pair receipts for every "
++        "required candidate gate, including the full reform contract, metrics "
++        "review and reform responsiveness; attach and review scorecard replay "
++        "evidence. Keep qualification pending until the evidence is reviewed. "
++        "Record any explicit reviewer-approved exception; this script waives "
++        "no gate.",
+         "- [ ] **UC floor acceptance.** A later Microcosm cut with the same gap "
+         "needs the same explicit acceptance in its pull request "
+         "(data-certification.md, recorded exceptions). Accepted by: ___",
+@@ -523,6 +534,11 @@ def parse(argv: list[str] | None) -> argparse.Namespace:
+     parser.add_argument("--skip-tests", action="store_true")
+     parser.add_argument("--test-workers", type=int, default=4)
+     parser.add_argument("--push", action="store_true")
++    parser.add_argument(
++        "--pr-base",
++        default="main",
++        help="pull-request target branch (default: main), independent of --base-ref",
++    )
+     parser.add_argument("--open-pr", action="store_true", help="draft; needs --push")
+     parser.add_argument("--dry-run", action="store_true", help="print the plan only")
+     args = parser.parse_args(argv)
+@@ -814,7 +830,7 @@ def certify(args: argparse.Namespace) -> int:
+         run(["git", "push", "-u", "origin", branch])
+     if args.open_pr:
+         run(
+-            ["gh", "pr", "create", "--draft", "--repo", PEPY_REPO, "--base", "main",
++            ["gh", "pr", "create", "--draft", "--repo", PEPY_REPO, "--base", args.pr_base,
+              "--head", branch, "--title",
+              f"Certify Microcosm UK cut {args.national_tag}", "--body-file", str(body)]
+         )  # fmt: skip
+-- 
+2.55.0
+
diff --git a/.job/tool-patches/0004-certifier-exact-pair-surface-NOTE.md b/.job/tool-patches/0004-certifier-exact-pair-surface-NOTE.md
new file mode 100644
index 00000000..56640514
--- /dev/null
+++ b/.job/tool-patches/0004-certifier-exact-pair-surface-NOTE.md
@@ -0,0 +1,7 @@
+For microcosm #1348's owner: fourth local-only rehearsal patch; apply after 0001–0003. No heavy probe or external action was run for this patch.
+
+The model-surface subprocess imported policyengine_uk, which initializes its module-level country system, then constructed another CountryTaxBenefitSystem. The second construction duplicates the expensive parameter-tree work. The probe now reads the country package's public `variables` registry established during import. Import itself can remain expensive; prewarm the two exact model/core environments before the Budget cut rather than assuming this removes the cold-start cost.
+
+The original probe pinned only policyengine-uk, allowing uv to resolve a newer compatible core (the rehearsal observed 3.33.12 rather than the cut's 3.33.3). `model_surface` now accepts an optional core version, pins it through a second --with requirement when supplied, and records the intended model/core in its timing record. Certification passes the regional cut's own core and the target certification core for the two probes. Standalone callers that omit core retain the previous optional behavior.
+
+Validation: 16 lightweight offline stdlib unittest regressions passed in 0.338 seconds. New tests check argv/core identity, country-registry reuse without constructor calls, and both manifest-derived core versions reaching their respective probes. Ruff check and Ruff format --check passed. The current live process loaded earlier code; exact-core live verification is separate evidence and is not claimed by this patch.
diff --git a/.job/tool-patches/0004-certifier-exact-pair-surface.patch b/.job/tool-patches/0004-certifier-exact-pair-surface.patch
new file mode 100644
index 00000000..e5643ee0
--- /dev/null
+++ b/.job/tool-patches/0004-certifier-exact-pair-surface.patch
@@ -0,0 +1,182 @@
+From 4e9e0f18dcf7db0510b4a6e4c073be1eba71f6e9 Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 07:46:46 -0400
+Subject: [PATCH] Probe country input registry once at exact model and core
+ versions
+
+---
+ .../test_certify_uk_cut_in_policyengine_py.py | 89 +++++++++++++++++++
+ tools/certify_uk_cut_in_policyengine_py.py    | 27 ++++--
+ 2 files changed, 108 insertions(+), 8 deletions(-)
+
+diff --git a/tests/test_certify_uk_cut_in_policyengine_py.py b/tests/test_certify_uk_cut_in_policyengine_py.py
+index 04738fd..765be2a 100644
+--- a/tests/test_certify_uk_cut_in_policyengine_py.py
++++ b/tests/test_certify_uk_cut_in_policyengine_py.py
+@@ -9,6 +9,7 @@ import sys
+ import tempfile
+ import types
+ import unittest
++from contextlib import redirect_stdout
+ from pathlib import Path
+ from unittest.mock import Mock, patch
+ 
+@@ -140,6 +141,94 @@ class RunnerTests(unittest.TestCase):
+                 runner.model_surface("2.147.2")
+             spawn.assert_not_called()
+ 
++    def test_model_surface_pins_core_and_records_intended_pair(self):
++        surface = {"all": ["x"], "inputs": ["x"]}
++        with tempfile.TemporaryDirectory() as tmp:
++            timings = Path(tmp) / "timings.jsonl"
++            with (
++                patch.object(runner, "resource_guard", return_value={}),
++                patch.object(
++                    runner.subprocess,
++                    "run",
++                    return_value=subprocess.CompletedProcess(
++                        [], 0, json.dumps(surface), ""
++                    ),
++                ) as spawn,
++            ):
++                self.assertEqual(
++                    runner.model_surface("2.147.2", "3.33.3", timings=timings), surface
++                )
++            argv = spawn.call_args.args[0]
++            self.assertIn("policyengine-uk==2.147.2", argv)
++            self.assertIn("policyengine-core==3.33.3", argv)
++            record = json.loads(timings.read_text())
++            self.assertEqual(record["model_version"], "2.147.2")
++            self.assertEqual(record["core_version"], "3.33.3")
++
++    def test_surface_script_reuses_country_registry_without_construction(self):
++        constructor = Mock(side_effect=AssertionError("duplicate system construction"))
++        country = types.SimpleNamespace(
++            variables={
++                "input": types.SimpleNamespace(is_input_variable=lambda: True),
++                "formula": types.SimpleNamespace(is_input_variable=lambda: False),
++            },
++            CountryTaxBenefitSystem=constructor,
++        )
++        output = io.StringIO()
++        with (
++            patch.dict(sys.modules, {"policyengine_uk": country}),
++            redirect_stdout(output),
++        ):
++            exec(runner._SURFACE_SCRIPT, {})
++        constructor.assert_not_called()
++        self.assertEqual(
++            json.loads(output.getvalue()),
++            {"all": ["formula", "input"], "inputs": ["input"]},
++        )
++
++    def test_regional_surface_comparison_uses_both_cut_core_versions(self):
++        dense_tag = "microcosm-uk-2024-25-dense-20261019T000432Z-ee5c2dbe"
++        national = runner.CutVersions(TAG, "2.147.2", "3.33.3", ())
++        dense = runner.CutVersions(dense_tag, "2.132.6", "3.29.0", ())
++        with tempfile.TemporaryDirectory() as tmp:
++            args = runner.parse(
++                [
++                    "--national-tag",
++                    TAG,
++                    "--dense-tag",
++                    dense_tag,
++                    "--dry-run",
++                    "--base-ref",
++                    "local",
++                    "--pepy-repo",
++                    tmp,
++                    "--skip-writer-check",
++                    "offline regression",
++                    "--accept-regional-model-mismatch",
++                    "recorded rehearsal mismatch",
++                ]
++            )
++            with (
++                patch.object(
++                    runner.subprocess,
++                    "check_output",
++                    side_effect=[
++                        "microcosm",
++                        "immutable-sha",
++                        json.dumps({"data_releases": {"uk": {}}}),
++                    ],
++                ),
++                patch.object(runner, "read_cut", side_effect=[national, dense]),
++                patch.object(
++                    runner, "model_surface", return_value={"all": [], "inputs": []}
++                ) as probe,
++            ):
++                self.assertEqual(runner.certify(args), 0)
++            self.assertEqual(probe.call_args_list[0].args, (dense.model, dense.core))
++            self.assertEqual(
++                probe.call_args_list[1].args, (national.model, national.core)
++            )
++
+     def test_workers_are_limited(self):
+         for workers in (0, 5):
+             with self.assertRaises(SystemExit):
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index 5f32fc2..57c0dc1 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -350,20 +350,29 @@ def rewrite_regional_constants(
+ 
+ 
+ _SURFACE_SCRIPT = (
+-    "import json; from policyengine_uk import CountryTaxBenefitSystem as S; "
+-    "s = S(); print(json.dumps({'all': sorted(s.variables), 'inputs': sorted("
+-    "n for n, v in s.variables.items() if v.is_input_variable())}))"
++    "import json; from policyengine_uk import variables; "
++    "print(json.dumps({'all': sorted(variables), 'inputs': sorted("
++    "n for n, v in variables.items() if v.is_input_variable())}))"
+ )
+ 
+ 
+ def model_surface(
+-    version: str, *, timings: Path | None = None, resource_root: Path | None = None
++    version: str,
++    core_version: str | None = None,
++    *,
++    timings: Path | None = None,
++    resource_root: Path | None = None,
+ ) -> dict[str, list[str]]:
+-    """Every variable and every input variable of one policyengine-uk version."""
++    """Read the country's existing variable registry at the requested model/core."""
+ 
++    packages = ["--with", f"policyengine-uk=={version}"]
++    if core_version:
++        packages += ["--with", f"policyengine-core=={core_version}"]
+     started = time.monotonic()
+     timing = {
+         "step": f"model_surface:{version}",
++        "model_version": version,
++        "core_version": core_version,
+         "started_at": datetime.now(UTC).isoformat(),
+         "returncode": None,
+     }
+@@ -373,7 +382,7 @@ def model_surface(
+         result = subprocess.run(
+         [
+             "uv", "run", "--no-project", "--python", PEPY_PYTHON,
+-            "--with", f"policyengine-uk=={version}", "python", "-c", _SURFACE_SCRIPT,
++            *packages, "python", "-c", _SURFACE_SCRIPT,
+         ],
+         capture_output=True,
+         text=True,
+@@ -645,8 +654,10 @@ def certify(args: argparse.Namespace) -> int:
+                     "where policyengine.py supports one."
+                 )
+             surface = input_surface_change(
+-                model_surface(dense.model, timings=timings, resource_root=repo),
+-                model_surface(model, timings=timings, resource_root=repo),
++                model_surface(
++                    dense.model, dense.core, timings=timings, resource_root=repo
++                ),
++                model_surface(model, core, timings=timings, resource_root=repo),
+             )
+             if surface["dropped"] and not args.allow_dropped_columns:
+                 raise CertifyError(
+-- 
+2.55.0
+
diff --git a/.job/tool-patches/0005-certifier-scope-country-imports-NOTE.md b/.job/tool-patches/0005-certifier-scope-country-imports-NOTE.md
new file mode 100644
index 00000000..3f0e8ab3
--- /dev/null
+++ b/.job/tool-patches/0005-certifier-scope-country-imports-NOTE.md
@@ -0,0 +1,9 @@
+For microcosm #1348's owner: fifth local-only rehearsal patch; apply after 0001–0004. No heavy execution or external action was run for this patch.
+
+The live certified-constant rewrite spent minutes importing `policyengine.provenance.uk_qualification`: importing any policyengine submodule first runs policyengine/__init__.py, which eagerly initializes both installed country modules. The constant replacement/rewrite helpers themselves operate only on record JSON, string literals and files. The repository already exposes POLICYENGINE_SKIP_COUNTRY_IMPORTS=1 for provenance-only imports. This patch applies that environment variable to the constant-rewrite subprocess only.
+
+The UC-floor subprocess receives the same scoped variable to avoid the wrapper's eager country imports. Its actual UK calculation remains intact: uk_uc_floor.check_uc_floor directly imports policyengine_uk.Microsimulation and computes the weighted national UC totals. No floor criterion, receipt check or acceptance requirement changes. Snapshots and the full test suite receive no skip flag.
+
+Validation: 17 lightweight offline stdlib unittest regressions passed in 0.329 seconds. The new regression asserts that exactly constant-rewrite and UC-floor receive the flag, while snapshot update and full tests preserve their environments. Ruff check and Ruff format --check passed. The current live process loaded earlier code, so this patch does not claim to measure an optimized rewrite.
+
+The optional command-display shortening was left out to keep this patch focused on measured import overhead; full commands remain in the handoff receipts.
diff --git a/.job/tool-patches/0005-certifier-scope-country-imports.patch b/.job/tool-patches/0005-certifier-scope-country-imports.patch
new file mode 100644
index 00000000..3b013ad1
--- /dev/null
+++ b/.job/tool-patches/0005-certifier-scope-country-imports.patch
@@ -0,0 +1,78 @@
+From 6f707cf925d8f3ec1017e3b46eb89a5df78d75b4 Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 07:55:10 -0400
+Subject: [PATCH] Avoid unrelated country initialization in constant rewrite
+ and UC floor
+
+---
+ .../test_certify_uk_cut_in_policyengine_py.py | 19 ++++++++++++++++++-
+ tools/certify_uk_cut_in_policyengine_py.py    |  2 ++
+ 2 files changed, 20 insertions(+), 1 deletion(-)
+
+diff --git a/tests/test_certify_uk_cut_in_policyengine_py.py b/tests/test_certify_uk_cut_in_policyengine_py.py
+index 765be2a..66a6e4d 100644
+--- a/tests/test_certify_uk_cut_in_policyengine_py.py
++++ b/tests/test_certify_uk_cut_in_policyengine_py.py
+@@ -307,7 +307,9 @@ class RunnerTests(unittest.TestCase):
+                 ],
+             )
+ 
+-    def run_mock_pipeline(self, *, pytest_code=0, uc_code=0, receipt=None):
++    def run_mock_pipeline(
++        self, *, pytest_code=0, uc_code=0, receipt=None, call_details=None
++    ):
+         tmp = tempfile.TemporaryDirectory()
+         self.addCleanup(tmp.cleanup)
+         root = Path(tmp.name)
+@@ -340,6 +342,8 @@ class RunnerTests(unittest.TestCase):
+ 
+         def shell(argv, **kwargs):
+             calls.append(argv)
++            if call_details is not None:
++                call_details.append((argv, kwargs))
+             if "constant-rewrite" == kwargs.get("step"):
+                 return subprocess.CompletedProcess(argv, 0, "[]", "")
+             if "UC-floor" == kwargs.get("step"):
+@@ -412,6 +416,19 @@ class RunnerTests(unittest.TestCase):
+         ):
+             self.assertIn(expected, body)
+ 
++    def test_country_import_skip_is_scoped_to_constants_and_uc_floor(self):
++        details = []
++        self.run_mock_pipeline(call_details=details)
++        envs = {kwargs.get("step"): kwargs.get("env", {}) for _, kwargs in details}
++        skipped = {
++            step
++            for step, env in envs.items()
++            if env.get("POLICYENGINE_SKIP_COUNTRY_IMPORTS") == "1"
++        }
++        self.assertEqual(skipped, {"constant-rewrite", "UC-floor"})
++        self.assertEqual(envs["snapshots"], {"PE_UPDATE_SNAPSHOTS": "1"})
++        self.assertEqual(envs["tests"], {})
++
+ 
+ if __name__ == "__main__":
+     unittest.main()
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index 57c0dc1..c2bc0fd 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -746,6 +746,7 @@ def certify(args: argparse.Namespace) -> int:
+             "print(json.dumps(w(Path('.'), r(old, new))))",
+             json.dumps(record), json.dumps(new_record),
+         ], step="constant-rewrite",
++        env={"POLICYENGINE_SKIP_COUNTRY_IMPORTS": "1"},
+     )  # fmt: skip
+     constants = json.loads(rewrite.stdout.strip().splitlines()[-1] or "[]")
+     with timed_step("regional-constant-rewrite", timings):
+@@ -767,6 +768,7 @@ def certify(args: argparse.Namespace) -> int:
+          new_record["default_dataset"], "--year", "2025", "--output", str(uc_path)],
+         check=False,
+         step="UC-floor",
++        env={"POLICYENGINE_SKIP_COUNTRY_IMPORTS": "1"},
+     )  # fmt: skip
+     if uc.returncode and not uc_path.exists():
+         raise CertifyError(
+-- 
+2.55.0
+
diff --git a/.job/tool-patches/0006-certifier-measured-uc-summary-NOTE.md b/.job/tool-patches/0006-certifier-measured-uc-summary-NOTE.md
new file mode 100644
index 00000000..dab4b302
--- /dev/null
+++ b/.job/tool-patches/0006-certifier-measured-uc-summary-NOTE.md
@@ -0,0 +1,7 @@
+For microcosm #1348's owner: sixth local-only rehearsal patch; apply after 0001–0005. No heavy execution or external action was run for this patch.
+
+The live cut passed the UC floor, but the original generated handoff always requested acceptance of the previous cut's gap. This patch carries the full measured receipt in context, reports a concise passed/FAILED result with both measure-to-benchmark ratios, and adds the explicit UC acceptance checkbox only for an actual measured failure. It does not truncate JSON into a potentially incomplete receipt summary.
+
+Fresh receipts remain mandatory. Missing/incomplete/contradictory receipts and disagreement between process exit status and measured result fail operationally; an actual measured failure still requires explicit human acceptance. Wrapper qualification remains separately pending.
+
+Validation: 20 lightweight offline stdlib unittest regressions passed. New coverage checks passing and failing handoff text, exact rounded ratios, and rejection of incomplete or contradictory successful receipts. Ruff check and Ruff format checks passed. Live passing receipt remains .job/certification.uc_floor.json; this source-only patch does not rerun the national simulation.
diff --git a/.job/tool-patches/0006-certifier-measured-uc-summary.patch b/.job/tool-patches/0006-certifier-measured-uc-summary.patch
new file mode 100644
index 00000000..0f84ce89
--- /dev/null
+++ b/.job/tool-patches/0006-certifier-measured-uc-summary.patch
@@ -0,0 +1,195 @@
+From 4912f32c3a3b24a76ff98036bc3e26b48fca761b Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 08:16:44 -0400
+Subject: [PATCH] Report measured UC floor status and request acceptance only
+ for failures
+
+---
+ .../test_certify_uk_cut_in_policyengine_py.py | 45 ++++++++++++-
+ tools/certify_uk_cut_in_policyengine_py.py    | 67 +++++++++++++------
+ 2 files changed, 91 insertions(+), 21 deletions(-)
+
+diff --git a/tests/test_certify_uk_cut_in_policyengine_py.py b/tests/test_certify_uk_cut_in_policyengine_py.py
+index 66a6e4d..6a7acc9 100644
+--- a/tests/test_certify_uk_cut_in_policyengine_py.py
++++ b/tests/test_certify_uk_cut_in_policyengine_py.py
+@@ -310,6 +310,15 @@ class RunnerTests(unittest.TestCase):
+     def run_mock_pipeline(
+         self, *, pytest_code=0, uc_code=0, receipt=None, call_details=None
+     ):
++        if receipt is None and uc_code == 0:
++            receipt = {
++                "check": "uk_uc_floor",
++                "passed": True,
++                "measures": [
++                    {"name": "benefit_units", "ratio": 0.92524521, "passed": True},
++                    {"name": "spend", "ratio": 0.95781282, "passed": True},
++                ],
++            }
+         tmp = tempfile.TemporaryDirectory()
+         self.addCleanup(tmp.cleanup)
+         root = Path(tmp.name)
+@@ -394,13 +403,47 @@ class RunnerTests(unittest.TestCase):
+             receipt={
+                 "check": "uk_uc_floor",
+                 "passed": False,
+-                "measures": [{"name": "spend", "passed": False}],
++                "measures": [
++                    {"name": "benefit_units", "ratio": 0.8, "passed": False},
++                    {"name": "spend", "ratio": 0.95, "passed": True},
++                ],
+             },
+         )
+         self.assertEqual(result, 0)
+         self.assertIn(
+             "UC floor acceptance", (root / "certification.pr-body.md").read_text()
+         )
++        self.assertIn(
++            "FAILED; measured/benchmark ratios: benefit_units=0.800000, spend=0.950000",
++            (root / "certification.pr-body.md").read_text(),
++        )
++
++    def test_passed_uc_floor_reports_ratios_without_acceptance_request(self):
++        result, _, root = self.run_mock_pipeline()
++        self.assertEqual(result, 0)
++        body = (root / "certification.pr-body.md").read_text()
++        self.assertIn(
++            "passed; measured/benchmark ratios: benefit_units=0.925245, spend=0.957813",
++            body,
++        )
++        self.assertNotIn("UC floor acceptance", body)
++
++    def test_incomplete_uc_floor_receipt_refuses_even_after_zero_exit(self):
++        with self.assertRaisesRegex(runner.CertifyError, "failed operationally"):
++            self.run_mock_pipeline(receipt={})
++
++    def test_uc_floor_rejects_contradictory_measure_status(self):
++        with self.assertRaisesRegex(runner.CertifyError, "failed operationally"):
++            self.run_mock_pipeline(
++                receipt={
++                    "check": "uk_uc_floor",
++                    "passed": True,
++                    "measures": [
++                        {"name": "benefit_units", "ratio": 0.8, "passed": False},
++                        {"name": "spend", "ratio": 0.95, "passed": True},
++                    ],
++                }
++            )
+ 
+     def test_handoff_marks_wrapper_qualification_pending(self):
+         _, _, root = self.run_mock_pipeline()
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index c2bc0fd..8a93949 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -44,6 +44,7 @@ from __future__ import annotations
+ 
+ import argparse
+ import json
++import math
+ import os
+ import re
+ import shlex
+@@ -432,6 +433,33 @@ def overlay_diff(old_bundle: dict, new_bundle: dict) -> list[str]:
+     return sorted(old - new)
+ 
+ 
++def uc_floor_summary(receipt: dict[str, Any]) -> str:
++    """Summarize a complete measured floor without truncating its JSON receipt."""
++    measures = receipt.get("measures") or []
++    if (
++        receipt.get("check") != "uk_uc_floor"
++        or not isinstance(receipt.get("passed"), bool)
++        or not {"benefit_units", "spend"}.issubset(
++            {measure.get("name") for measure in measures}
++        )
++        or any(
++            not isinstance(measure.get("passed"), bool)
++            or not isinstance(measure.get("ratio"), (int, float))
++            or not math.isfinite(measure["ratio"])
++            for measure in measures
++        )
++        or receipt["passed"] != all(measure["passed"] for measure in measures)
++    ):
++        raise CertifyError(
++            "UC floor failed operationally: incomplete measured receipt."
++        )
++    status = "passed" if receipt["passed"] else "FAILED"
++    ratios = ", ".join(
++        f"{measure['name']}={measure['ratio']:.6f}" for measure in measures
++    )
++    return f"{status}; measured/benchmark ratios: {ratios}"
++
++
+ def pr_body(context: dict[str, Any]) -> str:
+     lines = [
+         f"Certifies Microcosm UK national cut `{context['national_tag']}` "
+@@ -468,10 +496,13 @@ def pr_body(context: dict[str, Any]) -> str:
+         "evidence. Keep qualification pending until the evidence is reviewed. "
+         "Record any explicit reviewer-approved exception; this script waives "
+         "no gate.",
+-        "- [ ] **UC floor acceptance.** A later Microcosm cut with the same gap "
+-        "needs the same explicit acceptance in its pull request "
+-        "(data-certification.md, recorded exceptions). Accepted by: ___",
+     ]
++    if (context.get("uc_floor_receipt") or {}).get("passed") is False:
++        lines.append(
++            "- [ ] **UC floor acceptance.** This cut has a measured floor failure "
++            "and needs explicit acceptance in its pull request "
++            "(data-certification.md, recorded exceptions). Accepted by: ___"
++        )
+     if context.get("dropped_overlays"):
+         lines.append(
+             "- [ ] **Dataset overlays dropped by certification:** "
+@@ -770,28 +801,23 @@ def certify(args: argparse.Namespace) -> int:
+         step="UC-floor",
+         env={"POLICYENGINE_SKIP_COUNTRY_IMPORTS": "1"},
+     )  # fmt: skip
+-    if uc.returncode and not uc_path.exists():
++    if not uc_path.exists():
+         raise CertifyError(
+             f"UC floor failed operationally (exit {uc.returncode}); no measured "
+             f"receipt was produced. See {log}."
+         )
+-    if uc_path.exists():
++    try:
+         receipt = json.loads(uc_path.read_text())
+-        if uc.returncode and not (
+-            uc.returncode == 1
+-            and receipt.get("check") == "uk_uc_floor"
+-            and receipt.get("passed") is False
+-            and receipt.get("measures")
+-        ):
+-            raise CertifyError(
+-                f"UC floor failed operationally (exit {uc.returncode}); "
+-                "the receipt is not a measured floor violation."
+-            )
+-    uc_summary = (
+-        uc_path.read_text().strip()[:600]
+-        if uc_path.exists()
+-        else f"exit {uc.returncode}"
+-    )
++        uc_summary = uc_floor_summary(receipt)
++    except (json.JSONDecodeError, AttributeError, TypeError) as error:
++        raise CertifyError(
++            "UC floor failed operationally: invalid measured receipt."
++        ) from error
++    if uc.returncode != (0 if receipt["passed"] else 1):
++        raise CertifyError(
++            f"UC floor failed operationally (exit {uc.returncode}); "
++            "the measured receipt disagrees with the process exit status."
++        )
+     tests_summary, tests_failed, tests_returncode = "skipped", [], 0
+     if not args.skip_tests:
+         xdist = run(
+@@ -822,6 +848,7 @@ def certify(args: argparse.Namespace) -> int:
+         "microcosm_commit": microcosm_commit,
+         "commands": run.commands,
+         "uc_floor": f"`{uc_summary}`",
++        "uc_floor_receipt": receipt,
+         "tests": tests_summary,
+         "tests_failed": tests_failed,
+         "constants": ", ".join(constants),
+-- 
+2.55.0
+
diff --git a/.job/tool-patches/0007-certifier-upstream-test-compatibility-NOTE.md b/.job/tool-patches/0007-certifier-upstream-test-compatibility-NOTE.md
new file mode 100644
index 00000000..3d7db1ce
--- /dev/null
+++ b/.job/tool-patches/0007-certifier-upstream-test-compatibility-NOTE.md
@@ -0,0 +1,9 @@
+For microcosm #1348's owner: apply this seventh patch file after 0001–0006. This mbox contains two coherent local commits: relocate the rehearsal tests (76cf090), then adapt imports and existing fixtures (19afd66). It excludes the local-only commit that imported your unchanged original test file for validation; that file already exists in #1348.
+
+The original #1348 tests were downloaded verbatim and exercised alongside the new regressions. Their behavioral assertions remain intact. The existing dry-run mock now accepts the explicit core/timing interface and asserts both the exact core and the immutable base used for reading the bundle. The existing handoff fixture supplies a measured failed UC receipt so its acceptance assertion continues to test a real required judgment. The file-I/O Hypothesis bijection property keeps its examples and assertions but disables its 200ms deadline: on the shared build host the same case took 283ms once and 1.75ms on retry. No functional assertion or gate was waived.
+
+The 20 new regressions move from a repository-root tests file into `packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py`, where Microcosm CI discovers them. They import the tool through the existing `tools` namespace. The unique filename also fixes pytest's baseline import-file mismatch between the two original basenames.
+
+Validation: the original 14 cases plus 20 new regressions passed serially: **34 passed in 26.19 seconds** using the rehearsal Python 3.13 environment, `--noconftest`, the Hypothesis plugin and workspace-only cache/temp paths. Full results: `.job/logs/tool-upstream-compatibility.log`. Baseline diagnostics: `.job/logs/tool-upstream-baseline.log` and `.job/logs/tool-upstream-baseline-behavior.log`. Ruff check and Ruff format --check passed for the tool and both test files.
+
+All seven exported patch files (eight local commits) applied cleanly with `git am` to a fresh local snapshot containing the original tool and the original existing test. All three final source files match the tested working tree byte-for-byte, and the validation worktree is clean. Receipts: `.job/patch-series-validation.json` and `.job/logs/tool-patch-series-validation.log`. No push, PR or external action occurred.
diff --git a/.job/tool-patches/0007-certifier-upstream-test-compatibility.patch b/.job/tool-patches/0007-certifier-upstream-test-compatibility.patch
new file mode 100644
index 00000000..1f3a6cd5
--- /dev/null
+++ b/.job/tool-patches/0007-certifier-upstream-test-compatibility.patch
@@ -0,0 +1,148 @@
+From 76cf090328b894792e26c4cfb6e28dd6cbab3143 Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 08:26:25 -0400
+Subject: [PATCH 1/2] Preserve upstream handoff coverage and discover rehearsal
+ regressions in microcosm CI
+
+---
+ .../engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py   | 0
+ 1 file changed, 0 insertions(+), 0 deletions(-)
+ rename tests/test_certify_uk_cut_in_policyengine_py.py => packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py (100%)
+
+diff --git a/tests/test_certify_uk_cut_in_policyengine_py.py b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
+similarity index 100%
+rename from tests/test_certify_uk_cut_in_policyengine_py.py
+rename to packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
+-- 
+2.55.0
+
+
+From 19afd668ee06a9b4c89b1755c826fa27f94a03ab Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 08:26:47 -0400
+Subject: [PATCH 2/2] Adapt upstream fixtures to measured UC and immutable
+ exact-pair interfaces
+
+---
+ .../test_certify_uk_cut_in_policyengine_py.py | 44 ++++++++++++++-----
+ ...st_certify_uk_cut_rehearsal_regressions.py | 10 +----
+ 2 files changed, 34 insertions(+), 20 deletions(-)
+
+diff --git a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_in_policyengine_py.py b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_in_policyengine_py.py
+index 2142351..53f8f82 100644
+--- a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_in_policyengine_py.py
++++ b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_in_policyengine_py.py
+@@ -5,9 +5,8 @@ from __future__ import annotations
+ import argparse
+ 
+ import pytest
+-from hypothesis import given
++from hypothesis import given, settings
+ from hypothesis import strategies as st
+-
+ from tools import certify_uk_cut_in_policyengine_py as handoff
+ 
+ NATIONAL = "microcosm-uk-2024-25-national-20261021T195620Z-a858c100"
+@@ -101,6 +100,7 @@ def test_the_regional_tag_is_rewritten_only_as_a_literal(tmp_path):
+     old=st.from_regex(r"[a-z0-9.\-]{3,20}", fullmatch=True),
+     new=st.from_regex(r"[a-z0-9.\-]{3,20}", fullmatch=True),
+ )
++@settings(deadline=None)  # This property performs file I/O on a shared build host.
+ def test_the_rewrite_is_a_bijection_on_literals(tmp_path_factory, old, new):
+     """Rewriting old->new then new->old restores a file of quoted literals."""
+ 
+@@ -138,6 +138,14 @@ def test_the_pr_body_leaves_every_judgment_to_a_person():
+             "dropped_overlays": ["enhanced_frs_2024_25"],
+             "warnings": ["regional release: certifying on a claim"],
+             "tests_failed": ["tests/test_models.py::test_x"],
++            "uc_floor_receipt": {
++                "check": "uk_uc_floor",
++                "passed": False,
++                "measures": [
++                    {"name": "benefit_units", "ratio": 0.8, "passed": False},
++                    {"name": "spend", "ratio": 0.95, "passed": True},
++                ],
++            },
+         }
+     )
+     for needed in (
+@@ -162,7 +170,14 @@ def test_a_dry_run_refuses_an_uncovered_dense_cut_before_writing(monkeypatch, tm
+         "2.132.6": {"all": ["a", "b", "f"], "inputs": ["a", "b"]},
+         "2.134.4": {"all": ["a", "b", "f", "n"], "inputs": ["a", "b", "f", "n"]},
+     }
+-    monkeypatch.setattr(handoff, "model_surface", lambda version: surfaces[version])
++
++    def model_surface(version, core_version=None, **kwargs):
++        assert core_version == "3.33.3"
++        assert kwargs["resource_root"] == tmp_path
++        assert kwargs["timings"].suffix == ".jsonl"
++        return surfaces[version]
++
++    monkeypatch.setattr(handoff, "model_surface", model_surface)
+     monkeypatch.setattr(
+         handoff.subprocess,
+         "run",
+@@ -172,14 +187,20 @@ def test_a_dry_run_refuses_an_uncovered_dense_cut_before_writing(monkeypatch, tm
+         '{"data_releases": {"uk": {"regional_source_manifest_uri": '
+         f'"{handoff.manifest_uri(DENSE, handoff.DENSE_RELEASE)}"}}}}}}'
+     )
+-    real_check_output = handoff.subprocess.check_output
+-    monkeypatch.setattr(
+-        handoff.subprocess,
+-        "check_output",
+-        lambda argv, **k: (
+-            bundle if argv[:2] == ["git", "show"] else real_check_output(argv, **k)
+-        ),
+-    )
++    base_commit = "b" * 40
++
++    def check_output(argv, **kwargs):
++        if argv[:2] == ["git", "show"]:
++            assert (
++                argv[2] == f"{base_commit}:src/policyengine/data/bundle/manifest.json"
++            )
++            return bundle
++        if argv == ["git", "rev-parse", "HEAD"]:
++            return "a" * 40
++        assert argv == ["git", "rev-parse", "--verify", "origin/main^{commit}"]
++        return base_commit
++
++    monkeypatch.setattr(handoff.subprocess, "check_output", check_output)
+     args = handoff.parse(
+         ["--national-tag", NATIONAL, "--keep-regional-release", "--dry-run",
+          "--pepy-repo", str(tmp_path)]
+@@ -260,4 +281,3 @@ def test_the_pr_body_lists_the_area_input_surface():
+     )
+     assert "built on 2.132.6" in body
+     assert "local_bus_uncapped_single_fare" in body
+-
+diff --git a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
+index 6a7acc9..c50999a 100644
+--- a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
++++ b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
+@@ -1,6 +1,5 @@
+ """Offline regression tests for the certification orchestration safeguards."""
+ 
+-import importlib.util
+ import io
+ import json
+ import os
+@@ -13,13 +12,8 @@ from contextlib import redirect_stdout
+ from pathlib import Path
+ from unittest.mock import Mock, patch
+ 
+-SPEC = importlib.util.spec_from_file_location(
+-    "certify_runner",
+-    Path(__file__).resolve().parents[1] / "tools/certify_uk_cut_in_policyengine_py.py",
+-)
+-runner = importlib.util.module_from_spec(SPEC)
+-sys.modules[SPEC.name] = runner
+-SPEC.loader.exec_module(runner)
++from tools import certify_uk_cut_in_policyengine_py as runner
++
+ TAG = "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a"
+ 
+ 
+-- 
+2.55.0
+
diff --git a/.job/tool-patches/0008-certifier-live-test-diagnostics-NOTE.md b/.job/tool-patches/0008-certifier-live-test-diagnostics-NOTE.md
new file mode 100644
index 00000000..eded2040
--- /dev/null
+++ b/.job/tool-patches/0008-certifier-live-test-diagnostics-NOTE.md
@@ -0,0 +1,7 @@
+For microcosm #1348's owner: eighth local-only patch; apply after 0001–0007. No heavy execution or external action was run for this patch.
+
+The live full-suite run printed only dots/F under -q, so failures seen at 16% and 33% could not be identified while the long remainder ran. Full-suite pytest now uses -vv and --durations=20: the streamed log identifies test nodes as they finish, and the final report names the slowest tests. Shell still streams child output into its log rather than flooding the tool console. Snapshots retain -q; worker limits, fixtures and test assertions are unchanged.
+
+Validation: the existing 20 offline orchestration regressions passed serially in 1.204 seconds using the rehearsal Python 3.13 environment, with full results in `.job/logs/tool-diagnostics-regressions.log`. Ruff check and Ruff format --check passed. No output-flag-only test was added.
+
+The patch applied cleanly on the already validated fresh snapshot. The complete eight-file series (nine commits) now matches the tested working tree byte-for-byte and leaves the validation worktree clean. Updated receipts: `.job/patch-series-validation.json` and `.job/logs/tool-patch-series-validation.log`. The earlier original-plus-regressions run remains 34 passed in 26.19 seconds; this diagnostic-only change was then covered by the 20-regression rerun.
diff --git a/.job/tool-patches/0008-certifier-live-test-diagnostics.patch b/.job/tool-patches/0008-certifier-live-test-diagnostics.patch
new file mode 100644
index 00000000..4699b4b3
--- /dev/null
+++ b/.job/tool-patches/0008-certifier-live-test-diagnostics.patch
@@ -0,0 +1,28 @@
+From 364f69090f851f97435c756271d980ee721ccdb6 Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 08:33:51 -0400
+Subject: [PATCH] Expose full-suite test identities and slowest durations in
+ streamed logs
+
+---
+ tools/certify_uk_cut_in_policyengine_py.py | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index 8a93949..72a5b85 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -829,8 +829,8 @@ def certify(args: argparse.Namespace) -> int:
+                 step="install-pytest-xdist",
+             )
+         tests = run(
+-            [python, "-m", "pytest", "-n", str(args.test_workers), "-q",
+-             "-p", "no:cacheprovider"],
++            [python, "-m", "pytest", "-n", str(args.test_workers), "-vv",
++             "--durations=20", "-p", "no:cacheprovider"],
+             check=False,
+             step="tests",
+         )  # fmt: skip
+-- 
+2.55.0
+
diff --git a/.job/tool-patches/0009-certifier-stall-tracebacks-NOTE.md b/.job/tool-patches/0009-certifier-stall-tracebacks-NOTE.md
new file mode 100644
index 00000000..28033c49
--- /dev/null
+++ b/.job/tool-patches/0009-certifier-stall-tracebacks-NOTE.md
@@ -0,0 +1,11 @@
+For microcosm #1348's owner: ninth local-only patch; apply after 0001–0008. This patch was tested with synthetic/offline cases only; no real country/data computation was started.
+
+The first full suite paused at 91% for long enough that the quiet log could not distinguish a slow test from a deadlock; it later advanced to 93%, so that observation does not prove a hang. Future full-suite invocations now set pytest's built-in `faulthandler_timeout=300`. A test taking more than five minutes emits all worker-thread stacks to stderr/the streamed log. The diagnostic is scoped to the full suite, leaves snapshots unchanged, and does not enable faulthandler's exit-on-timeout behavior or another kill policy.
+
+Source inspection explains plausible long-tail cost, without identifying the current test: `test_uk_year_file_data_year.py` runs six Hypothesis examples that each cut/project and compare wrapper/direct real-model runs; US integration tests exercise real engines; `test_us_year_file_stamp.py` contains 300-, 300- and 200-example HDF5 properties with deadlines disabled. PR workflow comments record 28–54-minute historical suite runs, some cancellations at the old 60-minute limit, and a current 90-minute job cap. Current open-file inspection found no active HDF5 files or TCP connections for the rehearsal workers. Exact stalled-node diagnosis was unavailable because the first process had loaded the old quiet command and process attachment was sandbox-blocked.
+
+Validation: 21 offline orchestration regressions passed in 0.866 seconds, including scope and absence of an exit policy. A real one-worker xdist smoke test set a 0.1-second threshold on a synthetic 0.3-second sleep: it emitted a traceback and completed successfully (`1 passed in 19.76s`). This verifies that the installed pytest/xdist path reports stalls without terminating a valid slow test. Logs: `.job/logs/tool-stall-regressions.log` and `.job/logs/tool-stall-xdist-smoke.log`. Ruff check and Ruff format --check passed.
+
+A final serial run combined the original 14 cases and all 21 added regressions on the latest source: **35 passed in 102.54 seconds**. The resource precheck passed at load1 71.95 and 105.54 decimal GB free. Full results: `.job/logs/tool-upstream-compatibility-final.log`.
+
+The patch applied cleanly to the existing original-snapshot validation tree. The complete nine-file series (ten commits) matches the working source files byte-for-byte and leaves that tree clean; `.job/patch-series-validation.json` and `.job/logs/tool-patch-series-validation.log` are updated. No active-tree edit, interruption, push or PR was performed.
diff --git a/.job/tool-patches/0009-certifier-stall-tracebacks.patch b/.job/tool-patches/0009-certifier-stall-tracebacks.patch
new file mode 100644
index 00000000..38d3f7d3
--- /dev/null
+++ b/.job/tool-patches/0009-certifier-stall-tracebacks.patch
@@ -0,0 +1,53 @@
+From 86428c97bbb438c0a6fc27fc9a9aad2ae5d9a5e0 Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 09:08:50 -0400
+Subject: [PATCH] Dump nonfatal worker tracebacks when full-suite tests exceed
+ five minutes
+
+---
+ .../test_certify_uk_cut_rehearsal_regressions.py   | 14 ++++++++++++++
+ tools/certify_uk_cut_in_policyengine_py.py         |  3 ++-
+ 2 files changed, 16 insertions(+), 1 deletion(-)
+
+diff --git a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
+index c50999a..5b4fef5 100644
+--- a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
++++ b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
+@@ -466,6 +466,20 @@ class RunnerTests(unittest.TestCase):
+         self.assertEqual(envs["snapshots"], {"PE_UPDATE_SNAPSHOTS": "1"})
+         self.assertEqual(envs["tests"], {})
+ 
++    def test_stall_diagnostic_is_fullsuite_only_and_does_not_request_exit(self):
++        details = []
++        self.run_mock_pipeline(call_details=details)
++        steps = {kwargs.get("step"): argv for argv, kwargs in details}
++        fullsuite = steps["tests"]
++        overrides = [
++            fullsuite[index + 1]
++            for index, option in enumerate(fullsuite)
++            if option == "-o"
++        ]
++        self.assertIn("faulthandler_timeout=300", overrides)
++        self.assertFalse(any("exit_on_timeout" in option for option in fullsuite))
++        self.assertFalse(any("faulthandler" in option for option in steps["snapshots"]))
++
+ 
+ if __name__ == "__main__":
+     unittest.main()
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index 72a5b85..346adba 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -830,7 +830,8 @@ def certify(args: argparse.Namespace) -> int:
+             )
+         tests = run(
+             [python, "-m", "pytest", "-n", str(args.test_workers), "-vv",
+-             "--durations=20", "-p", "no:cacheprovider"],
++             "--durations=20", "-o", "faulthandler_timeout=300",
++             "-p", "no:cacheprovider"],
+             check=False,
+             step="tests",
+         )  # fmt: skip
+-- 
+2.55.0
+
diff --git a/.job/tool-patches/0010-certifier-temp-boundary-preflight-NOTE.md b/.job/tool-patches/0010-certifier-temp-boundary-preflight-NOTE.md
new file mode 100644
index 00000000..2b143f42
--- /dev/null
+++ b/.job/tool-patches/0010-certifier-temp-boundary-preflight-NOTE.md
@@ -0,0 +1,11 @@
+For microcosm #1348's owner: tenth local-only patch; apply after 0001–0009. No heavy country/data calculation or outside-workspace write was performed for this patch.
+
+The first full suite completed with 4,304 passed, 14 skipped and three failures. All three share an environmental cause: TMPDIR was inside the assigned checkout. The version fixture inherited the ancestor repository's 6.10.1 tags instead of its synthetic 3.4.2 maximum. The two disclosure-audit tests correctly rejected their supposed outside-repository positive paths because those paths remained descendants of the assigned .git. Their in-repository rejection assertions had already passed. Tracebacks: `.job/certification.certify.log`, around lines 420–535.
+
+Before network/model work, a full-suite run now resolves its effective temporary directory and refuses any .git file or directory in its ancestry. The message explains both failure mechanisms and instructs a normal runner to set TMPDIR to a private writable path outside every Git repository. A timed preflight receipt records the check. Dry-run plans and explicit --skip-tests mechanical handoffs do not attempt full-suite validation, so that check does not block them.
+
+The workspace-only instruction makes a genuinely outside-Git positive audit fixture impossible in this rehearsal: every allowed descendant resolves under the assigned .git. Keep that constraint and the production disclosure guard. Preserve the completed 4,304/14/3 result as environmental failure evidence, avoid another unchanged hour-long suite, finish the mechanical handoff with tests explicitly skipped, and rerun the three positive paths/full suite on a normal isolated runner. A skipped suite supplies no passing certification or wrapper-qualification evidence. The real base also needs reviewed #704 plus the #699 prerequisite illustrated by policyengine.py patch 0000.
+
+Validation: 25 offline orchestration regressions passed in 3.926 seconds. New boundary coverage includes actual Git directories and worktree .git files, symlink resolution, a mocked outside-Git location with no write, and refusal before network/model work. Existing pipeline mocks isolate this environmental preflight from their separate behavior assertions. Ruff check and Ruff format --check passed. Full results: `.job/logs/tool-temp-boundary-regressions.log`.
+
+The patch applied cleanly to the original-snapshot validation tree. All ten patch files (eleven commits) leave that tree clean and produce sources matching the latest tested files byte-for-byte. `.job/patch-series-validation.json` distinguishes the latest 25 targeted cases from the earlier combined 35-case run, which predates this patch. No production guard, test assertion, push or PR was changed or bypassed.
diff --git a/.job/tool-patches/0010-certifier-temp-boundary-preflight.patch b/.job/tool-patches/0010-certifier-temp-boundary-preflight.patch
new file mode 100644
index 00000000..52724c27
--- /dev/null
+++ b/.job/tool-patches/0010-certifier-temp-boundary-preflight.patch
@@ -0,0 +1,152 @@
+From c9f4f460dd53e86d1cc3708255d0b33e864350a1 Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 09:21:32 -0400
+Subject: [PATCH] Refuse full-suite fixtures nested inside a Git repository
+ before heavy work
+
+---
+ ...st_certify_uk_cut_rehearsal_regressions.py | 74 +++++++++++++++++++
+ tools/certify_uk_cut_in_policyengine_py.py    | 21 ++++++
+ 2 files changed, 95 insertions(+)
+
+diff --git a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
+index 5b4fef5..15188f9 100644
+--- a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
++++ b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
+@@ -135,6 +135,75 @@ class RunnerTests(unittest.TestCase):
+                 runner.model_surface("2.147.2")
+             spawn.assert_not_called()
+ 
++    def test_temp_preflight_rejects_git_directory_or_worktree_file(self):
++        for marker_is_directory in (True, False):
++            with self.subTest(marker_is_directory=marker_is_directory):
++                with tempfile.TemporaryDirectory() as tmp:
++                    repository = Path(tmp) / "repository"
++                    repository.mkdir()
++                    marker = repository / ".git"
++                    if marker_is_directory:
++                        marker.mkdir()
++                    else:
++                        marker.write_text("gitdir: /unused/worktree-metadata\n")
++                    directory = repository / "private-temp"
++                    directory.mkdir()
++                    with self.assertRaisesRegex(
++                        runner.CertifyError, "Set TMPDIR.*outside every Git"
++                    ) as refusal:
++                        runner.check_test_temp_directory(directory)
++                    self.assertIn(str(repository), str(refusal.exception))
++
++    def test_temp_preflight_resolves_symlinks_before_checking_ancestors(self):
++        with tempfile.TemporaryDirectory() as tmp:
++            repository = Path(tmp) / "repository"
++            repository.mkdir()
++            (repository / ".git").mkdir()
++            directory = repository / "private-temp"
++            directory.mkdir()
++            alias = Path(tmp) / "alias"
++            alias.symlink_to(directory, target_is_directory=True)
++            with self.assertRaisesRegex(runner.CertifyError, "inside Git"):
++                runner.check_test_temp_directory(alias)
++
++    def test_temp_preflight_accepts_an_outside_git_location_without_writing(self):
++        external = Path("/external-private-temp")
++        with (
++            patch.object(runner.tempfile, "gettempdir", return_value=str(external)),
++            patch.object(Path, "exists", return_value=False),
++        ):
++            self.assertEqual(runner.check_test_temp_directory(), external)
++
++    def test_temp_preflight_refuses_before_network_or_model_work(self):
++        with tempfile.TemporaryDirectory() as tmp:
++            args = runner.parse(
++                [
++                    "--national-tag",
++                    TAG,
++                    "--drop-regional-release",
++                    "--pepy-repo",
++                    tmp,
++                ]
++            )
++            with (
++                patch.object(
++                    runner.subprocess, "check_output", return_value="microcosm"
++                ),
++                patch.object(
++                    runner,
++                    "check_test_temp_directory",
++                    side_effect=runner.CertifyError("temporary directory inside Git"),
++                ),
++                patch.object(runner, "read_cut") as manifest,
++                patch.object(runner, "model_surface") as surface,
++                patch.object(runner.subprocess, "run") as spawn,
++            ):
++                with self.assertRaisesRegex(runner.CertifyError, "inside Git"):
++                    runner.certify(args)
++            manifest.assert_not_called()
++            surface.assert_not_called()
++            spawn.assert_not_called()
++
+     def test_model_surface_pins_core_and_records_intended_pair(self):
+         surface = {"all": ["x"], "inputs": ["x"]}
+         with tempfile.TemporaryDirectory() as tmp:
+@@ -374,6 +443,11 @@ class RunnerTests(unittest.TestCase):
+                 return_value=runner.CutVersions(TAG, "2.147.2", "3.0", ()),
+             ),
+             patch.object(runner.Shell, "__call__", side_effect=shell),
++            patch.object(
++                runner,
++                "check_test_temp_directory",
++                return_value=root / "isolated-test-temp",
++            ),
+         ):
+             result = runner.certify(args)
+         return result, calls, root
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index 346adba..f169c37 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -51,6 +51,7 @@ import shlex
+ import shutil
+ import subprocess
+ import sys
++import tempfile
+ import time
+ from contextlib import contextmanager
+ from dataclasses import dataclass
+@@ -181,6 +182,23 @@ def resource_guard(root: Path) -> dict[str, float | int]:
+     return {"load1": load1, "free_bytes": free}
+ 
+ 
++def check_test_temp_directory(directory: Path | None = None) -> Path:
++    """Refuse a full suite whose temporary fixtures inherit an ancestor Git repo."""
++    directory = Path(directory or tempfile.gettempdir()).expanduser().resolve()
++    for ancestor in (directory, *directory.parents):
++        if (ancestor / ".git").exists():
++            raise CertifyError(
++                f"Full-suite temporary directory {directory} is inside Git "
++                f"repository {ancestor}. Version fixtures would inherit its tags "
++                "and private disclosure audit fixtures correctly refuse Git paths. "
++                "Set TMPDIR to a private writable directory outside every Git "
++                "repository before certification. If workspace-only execution "
++                "prevents that, report full-suite validation blocked; --skip-tests "
++                "records a mechanical handoff only and does not qualify the cut."
++            )
++    return directory
++
++
+ def record_timing(path: Path, record: dict[str, Any]) -> None:
+     path.parent.mkdir(parents=True, exist_ok=True)
+     with path.open("a") as handle:
+@@ -609,6 +627,9 @@ def certify(args: argparse.Namespace) -> int:
+     worktree = args.worktree or repo.parent / "_worktrees" / f"pepy-{branch}"
+     log = worktree.parent / f"{worktree.name}.certify.log"
+     timings = log.with_suffix(".timings.jsonl")
++    if not args.skip_tests and not args.dry_run:
++        with timed_step("test-temp-preflight", timings) as timing:
++            timing["temporary_directory"] = str(check_test_temp_directory())
+     base_ref = args.base_ref
+     if base_ref.startswith("origin/"):
+         remote_branch = base_ref.removeprefix("origin/")
+-- 
+2.55.0
+
diff --git a/.job/tool-patches/0011-certifier-resume-worktree-NOTE.md b/.job/tool-patches/0011-certifier-resume-worktree-NOTE.md
new file mode 100644
index 00000000..7ae3b192
--- /dev/null
+++ b/.job/tool-patches/0011-certifier-resume-worktree-NOTE.md
@@ -0,0 +1,11 @@
+For microcosm #1348's owner: eleventh local-only patch; apply after 0001–0010. Source commit: 7273b5f. No reset, deletion, history rewrite, push or PR was performed.
+
+The final mechanical attempt passed certify-data, then correctly stopped before lock when load1 reached 127.64 (>110). Its worktree and generated metadata were preserved, but the original runner refused any existing worktree. New explicit --resume-worktree replays the same stages after validating that the path is a registered worktree of --pepy-repo, its common Git directory and deterministic certification branch match, HEAD equals the resolved immutable base, there are no merge conflicts or unrelated tracked/index changes, and any modified UK manifest names this cut. Committed descendants are deliberately refused; their history stays intact. --dry-run can validate/describe reuse without mutating the worktree. Every existing load/disk guard remains in force.
+
+The handoff replays all stages; it does not infer a passing stage from a partial receipt. It preserves auxiliary untracked files. Final staging now names exact generated paths instead of git add -A: project/lock, the three known bundle manifest/TRO files, certified constant files, the ten existing household snapshot filenames, and the exact cut changelog. Only that exact changelog may be staged from untracked files. Untracked data links, operator notes and helper/private files—even under bundle or snapshot directories—remain unstaged. New generated paths need a reviewed allowlist update.
+
+Validation: 29 targeted offline regressions passed in 1.246 seconds. Coverage checks wrong registration/branch/repository, committed HEAD, merge conflicts, unrelated edits, wrong manifest cut, no reset/worktree recreation, and preservation of untracked helpers/data links. A read-only validation of the actual interrupted worktree passed and confirmed its HEAD and three generated-file hashes were unchanged: `.job/resume-preflight-validation.json`.
+
+The latest combined suite then passed **43 cases and 10 subtests in 101.13 seconds**: 14 original cases plus 29 added regressions. Only this offline tool-test child received POLICYENGINE_SKIP_COUNTRY_IMPORTS=1; it contains mocked model surfaces and file/Git helpers, with no real country-engine assertion disabled. Its resource precheck passed at load1 52.67 and 104.28 decimal GB free. Logs: `.job/logs/tool-resume-regressions.log` and `.job/logs/tool-resume-combined.log`. Ruff lint and formatting passed.
+
+All eleven patch files (twelve commits) applied cleanly to the original-snapshot validation tree; resulting source files match the tested files byte-for-byte and the tree is clean. Receipts: `.job/patch-series-validation.json` and `.job/logs/tool-patch-series-validation.log`. This patch enables recovery after capacity returns; it does not bypass overload, failed tests or pending exact-pair wrapper qualification.
diff --git a/.job/tool-patches/0011-certifier-resume-worktree.patch b/.job/tool-patches/0011-certifier-resume-worktree.patch
new file mode 100644
index 00000000..11fd99e0
--- /dev/null
+++ b/.job/tool-patches/0011-certifier-resume-worktree.patch
@@ -0,0 +1,412 @@
+From 7273b5f105275a1d383cb251aef88a5ed95053e3 Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 09:49:09 -0400
+Subject: [PATCH] Resume unfinished certification worktrees and stage only
+ generated artifacts
+
+---
+ ...st_certify_uk_cut_rehearsal_regressions.py | 129 +++++++++++-
+ tools/certify_uk_cut_in_policyengine_py.py    | 186 ++++++++++++++++--
+ 2 files changed, 296 insertions(+), 19 deletions(-)
+
+diff --git a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
+index 15188f9..2b0e01b 100644
+--- a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
++++ b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
+@@ -370,8 +370,114 @@ class RunnerTests(unittest.TestCase):
+                 ],
+             )
+ 
++    def test_certification_changes_preserve_auxiliary_untracked_files(self):
++        manifest = "src/policyengine/data/bundle/manifest.json"
++        changelog = f"changelog.d/certify-uk-{TAG}.changed.md"
++        with patch.object(
++            runner.subprocess,
++            "check_output",
++            side_effect=[
++                f"pyproject.toml\0{manifest}\0",
++                f"{changelog}\0data\0operator-notes.md\0"
++                "src/policyengine/data/bundle/operator-private.json\0"
++                "tests/fixtures/household_calculator_snapshots/operator-helper.json\0",
++            ],
++        ):
++            paths = runner.certification_changes(Path.cwd(), TAG)
++        self.assertEqual(paths, sorted(["pyproject.toml", manifest, changelog]))
++
++    def test_certification_changes_refuse_unrelated_tracked_edits(self):
++        with patch.object(
++            runner.subprocess,
++            "check_output",
++            return_value="src/policyengine/core/simulation.py\0",
++        ):
++            with self.assertRaisesRegex(runner.CertifyError, "unrelated tracked"):
++                runner.certification_changes(Path.cwd(), TAG)
++
++    def test_resume_validates_identity_and_preserves_unfinished_generated_files(self):
++        for problem in (
++            None,
++            "branch",
++            "registration",
++            "owner",
++            "head",
++            "merge",
++            "tracked",
++            "cut",
++        ):
++            with self.subTest(problem=problem), tempfile.TemporaryDirectory() as tmp:
++                root = Path(tmp)
++                repo, worktree = root / "repo", root / "worktree"
++                repo.mkdir()
++                worktree.mkdir()
++                branch, base = "certify-uk-a133f37a", "b" * 40
++                relative = "src/policyengine/data/bundle/manifest.json"
++                path = worktree / relative
++                path.parent.mkdir(parents=True)
++                path.write_text(
++                    json.dumps(
++                        {
++                            "data_releases": {
++                                "uk": {
++                                    "data_package": {
++                                        "release_manifest_revision": "another-cut"
++                                        if problem == "cut"
++                                        else TAG
++                                    }
++                                }
++                            }
++                        }
++                    )
++                )
++                original = path.read_bytes()
++
++                def output(argv, *, cwd, **kwargs):
++                    if argv[1:3] == ["worktree", "list"]:
++                        location = (
++                            root / "elsewhere"
++                            if problem == "registration"
++                            else worktree
++                        )
++                        registered_branch = "other" if problem == "branch" else branch
++                        return f"worktree {location}\0HEAD {base}\0branch refs/heads/{registered_branch}\0\0"
++                    if argv[1:] == ["rev-parse", "--git-common-dir"]:
++                        if problem == "owner" and cwd == worktree:
++                            return str(root / "other/.git")
++                        return str(repo / ".git")
++                    if argv[1:] == ["rev-parse", "HEAD"]:
++                        return "c" * 40 if problem == "head" else base
++                    if argv[1:3] == ["ls-files", "--unmerged"]:
++                        return "unmerged" if problem == "merge" else ""
++                    if argv[1:3] == ["diff", "--name-only"]:
++                        return (
++                            "README.md\0" if problem == "tracked" else relative + "\0"
++                        )
++                    if argv[1:3] == ["ls-files", "--others"]:
++                        return "data\0operator-notes.md\0"
++                    self.fail(f"unexpected validation command: {argv}")
++
++                with (
++                    patch.object(runner.subprocess, "check_output", side_effect=output),
++                    patch.object(runner.subprocess, "run") as mutation,
++                ):
++                    if problem:
++                        with self.assertRaises(runner.CertifyError):
++                            runner.validate_resume_worktree(
++                                repo, worktree, branch, base, TAG
++                            )
++                    else:
++                        self.assertEqual(
++                            runner.validate_resume_worktree(
++                                repo, worktree, branch, base, TAG
++                            ),
++                            [relative],
++                        )
++                    mutation.assert_not_called()
++                self.assertEqual(path.read_bytes(), original)
++
+     def run_mock_pipeline(
+-        self, *, pytest_code=0, uc_code=0, receipt=None, call_details=None
++        self, *, pytest_code=0, uc_code=0, receipt=None, call_details=None, resume=False
+     ):
+         if receipt is None and uc_code == 0:
+             receipt = {
+@@ -387,6 +493,10 @@ class RunnerTests(unittest.TestCase):
+         root = Path(tmp.name)
+         worktree = root / "certification"
+         bundle = {"data_releases": {"uk": {"default_dataset": "candidate"}}}
++        if resume:
++            path = worktree / "src/policyengine/data/bundle/manifest.json"
++            path.parent.mkdir(parents=True)
++            path.write_text(json.dumps(bundle))
+         args = runner.parse(
+             [
+                 "--national-tag",
+@@ -400,6 +510,7 @@ class RunnerTests(unittest.TestCase):
+                 str(worktree),
+                 "--skip-writer-check",
+                 "offline regression",
++                *(["--resume-worktree"] if resume else []),
+             ]
+         )
+         calls = []
+@@ -448,6 +559,12 @@ class RunnerTests(unittest.TestCase):
+                 "check_test_temp_directory",
+                 return_value=root / "isolated-test-temp",
+             ),
++            patch.object(runner, "validate_resume_worktree", return_value=[]),
++            patch.object(
++                runner,
++                "certification_changes",
++                return_value=["src/policyengine/data/bundle/manifest.json"],
++            ),
+         ):
+             result = runner.certify(args)
+         return result, calls, root
+@@ -461,6 +578,16 @@ class RunnerTests(unittest.TestCase):
+         worktree_call = next(c for c in calls if c[:3] == ["git", "worktree", "add"])
+         self.assertEqual(worktree_call[-1], "immutable-sha")
+ 
++    def test_resume_replays_without_creating_or_resetting_worktree(self):
++        result, calls, _ = self.run_mock_pipeline(resume=True)
++        self.assertEqual(result, 0)
++        self.assertFalse(any(call[:3] == ["git", "worktree", "add"] for call in calls))
++        self.assertFalse(any(call[:2] == ["git", "reset"] for call in calls))
++        self.assertIn(
++            ["git", "add", "--", "src/policyengine/data/bundle/manifest.json"], calls
++        )
++        self.assertNotIn(["git", "add", "-A"], calls)
++
+     def test_uc_operational_failure_refuses(self):
+         with self.assertRaisesRegex(runner.CertifyError, "failed operationally"):
+             self.run_mock_pipeline(uc_code=1)
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index f169c37..6270ab4 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -199,6 +199,135 @@ def check_test_temp_directory(directory: Path | None = None) -> Path:
+     return directory
+ 
+ 
++def certification_path(relative: str, national_tag: str) -> bool:
++    """Files generated by this handoff, excluding local data and operator files."""
++    snapshots = {
++        "uk_couple_two_kids",
++        "uk_model_surface",
++        "uk_single_adult_employment_income",
++        "uk_single_adult_no_income",
++        "uk_single_parent_one_child",
++        "us_married_two_kids_high_income",
++        "us_model_surface",
++        "us_single_adult_employment_income",
++        "us_single_adult_no_income",
++        "us_single_parent_one_child",
++    }
++    return relative in {
++        "pyproject.toml",
++        "uv.lock",
++        *CONSTANT_FILES,
++        f"changelog.d/certify-uk-{national_tag}.changed.md",
++        "src/policyengine/data/bundle/manifest.json",
++        "src/policyengine/data/bundle/uk.trace.tro.jsonld",
++        "src/policyengine/data/bundle/us.trace.tro.jsonld",
++        *(
++            f"tests/fixtures/household_calculator_snapshots/{name}.json"
++            for name in snapshots
++        ),
++    }
++
++
++def certification_changes(worktree: Path, national_tag: str) -> list[str]:
++    """Preserve operator files and refuse unrelated tracked/index changes."""
++    tracked = subprocess.check_output(
++        ["git", "diff", "--name-only", "-z", "HEAD"], cwd=worktree, text=True
++    ).split("\0")
++    unexpected = [
++        path for path in tracked if path and not certification_path(path, national_tag)
++    ]
++    if unexpected:
++        raise CertifyError(
++            "unrelated tracked changes are preserved; refusing to replay or commit: "
++            + ", ".join(unexpected)
++        )
++    untracked = subprocess.check_output(
++        ["git", "ls-files", "--others", "--exclude-standard", "-z"],
++        cwd=worktree,
++        text=True,
++    ).split("\0")
++    changelog = f"changelog.d/certify-uk-{national_tag}.changed.md"
++    return sorted(
++        {path for path in tracked if path}
++        | {path for path in untracked if path == changelog}
++    )
++
++
++def validate_resume_worktree(
++    repo: Path, worktree: Path, branch: str, base_commit: str, national_tag: str
++) -> list[str]:
++    """Adopt only an unfinished, registered handoff at the exact resolved base."""
++    if not worktree.is_dir():
++        raise CertifyError(f"resume worktree {worktree} does not exist.")
++    records, record = [], {}
++    output = subprocess.check_output(
++        ["git", "worktree", "list", "--porcelain", "-z"], cwd=repo, text=True
++    )
++    for field in output.split("\0"):
++        if not field:
++            if record:
++                records.append(record)
++                record = {}
++        else:
++            key, _, value = field.partition(" ")
++            record[key] = value
++    if record:
++        records.append(record)
++    entry = next(
++        (
++            item
++            for item in records
++            if "worktree" in item
++            and Path(item["worktree"]).resolve() == worktree.resolve()
++        ),
++        None,
++    )
++    if entry is None or entry.get("branch") != f"refs/heads/{branch}":
++        raise CertifyError(
++            "resume worktree is not registered on the expected certification branch."
++        )
++    common_dirs = []
++    for directory in (repo, worktree):
++        common = Path(
++            subprocess.check_output(
++                ["git", "rev-parse", "--git-common-dir"],
++                cwd=directory,
++                text=True,
++            ).strip()
++        )
++        common_dirs.append((directory / common).resolve())
++    if common_dirs[0] != common_dirs[1]:
++        raise CertifyError("resume worktree belongs to another Git repository.")
++    head = subprocess.check_output(
++        ["git", "rev-parse", "HEAD"],
++        cwd=worktree,
++        text=True,
++    ).strip()
++    if head != base_commit or entry.get("HEAD") != base_commit:
++        raise CertifyError(
++            "resume requires HEAD at the exact resolved base commit; committed "
++            "descendants/history are preserved. Use a fresh worktree for another run."
++        )
++    if subprocess.check_output(
++        ["git", "ls-files", "--unmerged", "-z"],
++        cwd=worktree,
++        text=True,
++    ):
++        raise CertifyError(
++            "resume worktree has unresolved merges; no files were changed."
++        )
++    paths = certification_changes(worktree, national_tag)
++    manifest_path = "src/policyengine/data/bundle/manifest.json"
++    if manifest_path in paths:
++        record = json.loads((worktree / manifest_path).read_text())["data_releases"][
++            "uk"
++        ]
++        revision = (record.get("data_package") or {}).get("release_manifest_revision")
++        if revision != national_tag:
++            raise CertifyError("resume worktree's modified manifest names another cut.")
++    return paths
++
++
+ def record_timing(path: Path, record: dict[str, Any]) -> None:
+     path.parent.mkdir(parents=True, exist_ok=True)
+     with path.open("a") as handle:
+@@ -583,6 +712,11 @@ def parse(argv: list[str] | None) -> argparse.Namespace:
+         default=Path("~/PolicyEngine/policyengine.py").expanduser(),
+     )
+     parser.add_argument("--worktree", type=Path, help="default: beside the repo")
++    parser.add_argument(
++        "--resume-worktree",
++        action="store_true",
++        help="replay stages in an unfinished registered worktree at the exact base",
++    )
+     parser.add_argument(
+         "--base-ref",
+         default="origin/main",
+@@ -653,6 +787,18 @@ def certify(args: argparse.Namespace) -> int:
+             text=True,
+         ).strip()
+ 
++    replay_paths = []
++    if args.resume_worktree:
++        with timed_step("resume-worktree-validation", timings):
++            replay_paths = validate_resume_worktree(
++                repo, worktree, branch, base_commit, args.national_tag
++            )
++    elif worktree.exists():
++        raise CertifyError(
++            f"{worktree} exists; use --resume-worktree for an unfinished handoff "
++            "or choose a fresh --worktree."
++        )
++
+     if args.skip_writer_check:
+         print(f"writer check skipped: {args.skip_writer_check}")
+     else:
+@@ -737,27 +883,28 @@ def certify(args: argparse.Namespace) -> int:
+         "regional": regional_summary,
+         "branch": branch,
+         "worktree": str(worktree),
++        "resume_worktree": args.resume_worktree,
++        "replayed_generated_paths": replay_paths,
+     }
+     print(json.dumps(plan, indent=2))
+     if args.dry_run:
+         return 0
+-    if worktree.exists():
+-        raise CertifyError(f"{worktree} exists; remove it or pass --worktree.")
+-    with timed_step("git-worktree", timings):
+-        subprocess.run(
+-            [
+-                "git",
+-                "worktree",
+-                "add",
+-                "--quiet",
+-                "-b",
+-                branch,
+-                str(worktree),
+-                base_commit,
+-            ],
+-            cwd=repo,
+-            check=True,
+-        )
++    if not args.resume_worktree:
++        with timed_step("git-worktree", timings):
++            subprocess.run(
++                [
++                    "git",
++                    "worktree",
++                    "add",
++                    "--quiet",
++                    "-b",
++                    branch,
++                    str(worktree),
++                    base_commit,
++                ],
++                cwd=repo,
++                check=True,
++            )
+     run = Shell(worktree, log, timings=timings)
+     python = str(worktree / ".venv/bin/python")
+     sync = ["uv", "sync", "--frozen", "--extra", "uk", "--extra", "dev",
+@@ -881,7 +1028,10 @@ def certify(args: argparse.Namespace) -> int:
+     }
+     body = worktree.parent / f"{worktree.name}.pr-body.md"
+     body.write_text(pr_body(context))
+-    run(["git", "add", "-A"])
++    with timed_step("generated-file-validation", timings):
++        staged_paths = certification_changes(worktree, args.national_tag)
++    if staged_paths:
++        run(["git", "add", "--", *staged_paths], step="stage-certification")
+     run(
+         ["git", "commit", "-q", "-m",
+          f"Certify Microcosm UK cut {args.national_tag}\n\n"
+-- 
+2.55.0
+
diff --git a/.job/tool-patches/0012-certifier-pytest-startup-diagnostics-NOTE.md b/.job/tool-patches/0012-certifier-pytest-startup-diagnostics-NOTE.md
new file mode 100644
index 00000000..dc17b213
--- /dev/null
+++ b/.job/tool-patches/0012-certifier-pytest-startup-diagnostics-NOTE.md
@@ -0,0 +1,13 @@
+For microcosm #1348's owner: twelfth local-only patch; apply after 0001–0011. Source commit: 0e2c1fd. The active rehearsal process loaded 7273b5f and was not restarted or changed by this patch.
+
+Snapshots can spend substantial wall time importing pytest/conftest and initializing country models before pytest's own session clock or per-test timeout starts. The first snapshot command took 432.518 seconds externally but reported 77.34 seconds internally; that gap includes unmeasured startup and shutdown costs. A later run was observed reading a US parameter YAML during a much longer delay. Static constructor/cache findings and attribution limits are in `.job/research-snapshot-startup.md`; this patch does not claim an exact cause for that entire delay.
+
+Snapshots and the full-suite controller now run through a small Python bootstrap. It duplicates original stderr before importing pytest, arms a nonfatal repeating five-minute faulthandler timer on that descriptor, forwards every pytest argument and its exit code, then cancels the timer and closes the duplicate in finally. Keeping the original pipe descriptor lets startup stacks reach the certification log even while pytest captures fd 2. Normal imports, snapshot update settings, test assertions, worker limits and the existing xdist worker per-test timeout remain unchanged.
+
+Scope limits: this covers the snapshot process and full-suite controller. Xdist starts workers through a separate entry point; their precollection imports are not covered by this bootstrap, while the existing per-test hook remains. Pytest may replace/cancel its process-global timer once per-test hooks run. The timer is diagnostic and does not exit the process; it ends when pytest.main returns, so later interpreter shutdown is not measured by it.
+
+Validation: 31 targeted offline regressions passed in 1.788 seconds, including argument/exit-status propagation and cleanup after pytest errors. A real synthetic conftest import slept for 0.5 seconds with a 0.1-second diagnostic threshold: its startup stack was observed live through the original stderr pipe before exit, and its test passed. Smoke wall time was 3.893 seconds. Receipts: `.job/startup-diagnostic-smoke.json` and `.job/logs/tool-startup-conftest-smoke.log`.
+
+The latest combined suite passed **45 cases and 12 subtests in 78.90 seconds**: 14 original cases and 31 added regressions. Its child-only skip-country environment applies to mocked/file/Git tool tests, never the real snapshots. The resource guard passed at load1 68.02 and 100.91 decimal GB free. Full result: `.job/logs/tool-startup-combined.log`. Ruff lint and formatting passed.
+
+All twelve patch files (thirteen commits) clean-applied to the original-snapshot validation tree; final files match the tested source bytes and that tree is clean. Receipts remain `.job/patch-series-validation.json` and `.job/logs/tool-patch-series-validation.log`. No active scratch source/data, production guard, assertion, push or PR was changed or bypassed.
diff --git a/.job/tool-patches/0012-certifier-pytest-startup-diagnostics.patch b/.job/tool-patches/0012-certifier-pytest-startup-diagnostics.patch
new file mode 100644
index 00000000..76ee631f
--- /dev/null
+++ b/.job/tool-patches/0012-certifier-pytest-startup-diagnostics.patch
@@ -0,0 +1,148 @@
+From 0e2c1fda4f5019165e41d25b25c355dc94a8e197 Mon Sep 17 00:00:00 2001
+From: Budget rehearsal 
+Date: Fri, 23 Oct 2026 10:41:15 -0400
+Subject: [PATCH] Report pytest startup stalls through original stderr without
+ terminating tests
+
+---
+ ...st_certify_uk_cut_rehearsal_regressions.py | 57 ++++++++++++++++++-
+ tools/certify_uk_cut_in_policyengine_py.py    | 27 ++++++++-
+ 2 files changed, 80 insertions(+), 4 deletions(-)
+
+diff --git a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
+index 2b0e01b..5c95dfc 100644
+--- a/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
++++ b/packages/microcosm-build/tests/engine_free/uk/test_certify_uk_cut_rehearsal_regressions.py
+@@ -1,5 +1,6 @@
+ """Offline regression tests for the certification orchestration safeguards."""
+ 
++import faulthandler
+ import io
+ import json
+ import os
+@@ -68,6 +69,52 @@ class RunnerTests(unittest.TestCase):
+             self.assertEqual(record["returncode"], 2)
+             self.assertGreaterEqual(record["elapsed_seconds"], 0)
+ 
++    def test_pytest_bootstrap_preserves_arguments_and_exit_status(self):
++        arguments = ["tests/snapshot.py", "-q"]
++        argv = runner.pytest_command("python", arguments)
++        self.assertEqual(argv[:2], ["python", "-c"])
++        self.assertEqual(argv[3:], arguments)
++        for exit_status in (0, 4):
++            with self.subTest(exit_status=exit_status):
++                stream = Mock()
++                pytest = types.SimpleNamespace(main=Mock(return_value=exit_status))
++                with (
++                    patch.dict(sys.modules, {"pytest": pytest}),
++                    patch.object(sys, "argv", ["-c", *arguments]),
++                    patch.object(os, "dup", return_value=123) as duplicate,
++                    patch.object(os, "fdopen", return_value=stream) as opened,
++                    patch.object(faulthandler, "dump_traceback_later") as timer,
++                    patch.object(faulthandler, "cancel_dump_traceback_later") as cancel,
++                ):
++                    with self.assertRaises(SystemExit) as result:
++                        exec(argv[2], {})
++                self.assertEqual(result.exception.code, exit_status)
++                pytest.main.assert_called_once_with(arguments)
++                duplicate.assert_called_once_with(2)
++                opened.assert_called_once_with(123, "w")
++                timer.assert_called_once_with(300.0, repeat=True, file=stream)
++                cancel.assert_called_once_with()
++                stream.close.assert_called_once_with()
++
++    def test_pytest_bootstrap_cleans_up_after_pytest_exception(self):
++        stream = Mock()
++        pytest = types.SimpleNamespace(
++            main=Mock(side_effect=RuntimeError("pytest failed"))
++        )
++        argv = runner.pytest_command("python", ["tests/test_x.py"])
++        with (
++            patch.dict(sys.modules, {"pytest": pytest}),
++            patch.object(sys, "argv", ["-c", "tests/test_x.py"]),
++            patch.object(os, "dup", return_value=123),
++            patch.object(os, "fdopen", return_value=stream),
++            patch.object(faulthandler, "dump_traceback_later"),
++            patch.object(faulthandler, "cancel_dump_traceback_later") as cancel,
++        ):
++            with self.assertRaisesRegex(RuntimeError, "pytest failed"):
++                exec(argv[2], {})
++        cancel.assert_called_once_with()
++        stream.close.assert_called_once_with()
++
+     def test_guard_prevents_process_and_records_refusal(self):
+         with tempfile.TemporaryDirectory() as tmp:
+             shell = runner.Shell(Path(tmp), Path(tmp) / "run.log")
+@@ -679,7 +726,15 @@ class RunnerTests(unittest.TestCase):
+         ]
+         self.assertIn("faulthandler_timeout=300", overrides)
+         self.assertFalse(any("exit_on_timeout" in option for option in fullsuite))
+-        self.assertFalse(any("faulthandler" in option for option in steps["snapshots"]))
++        snapshot = steps["snapshots"]
++        snapshot_overrides = [
++            snapshot[index + 1]
++            for index, option in enumerate(snapshot)
++            if option == "-o"
++        ]
++        self.assertFalse(
++            any("faulthandler_timeout" in option for option in snapshot_overrides)
++        )
+ 
+ 
+ if __name__ == "__main__":
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index 6270ab4..7dcb9e3 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -334,6 +334,27 @@ def record_timing(path: Path, record: dict[str, Any]) -> None:
+         handle.write(json.dumps(record) + "\n")
+ 
+ 
++def pytest_command(
++    python: str, arguments: list[str], *, startup_timeout: float = 300.0
++) -> list[str]:
++    """Diagnose imports before pytest's session/per-test clocks can start."""
++    if not math.isfinite(startup_timeout) or startup_timeout <= 0:
++        raise ValueError("pytest startup timeout must be positive and finite")
++    bootstrap = (
++        "import faulthandler, os, sys\n"
++        "diagnostic_stream = os.fdopen(os.dup(2), 'w')\n"
++        f"faulthandler.dump_traceback_later({startup_timeout!r}, "
++        "repeat=True, file=diagnostic_stream)\n"
++        "try:\n"
++        "    import pytest\n"
++        "    raise SystemExit(pytest.main(sys.argv[1:]))\n"
++        "finally:\n"
++        "    faulthandler.cancel_dump_traceback_later()\n"
++        "    diagnostic_stream.close()\n"
++    )
++    return [python, "-c", bootstrap, *arguments]
++
++
+ @contextmanager
+ def timed_step(name: str, path: Path):
+     """Make network preflight and local Python work visible, including failures."""
+@@ -951,7 +972,7 @@ def certify(args: argparse.Namespace) -> int:
+     with timed_step("regional-constant-rewrite", timings):
+         constants += rewrite_regional_constants(worktree, old_regional, new_regional)
+     run(
+-        [python, "-m", "pytest", "tests/test_household_calculator_snapshot.py", "-q"],
++        pytest_command(python, ["tests/test_household_calculator_snapshot.py", "-q"]),
+         env={"PE_UPDATE_SNAPSHOTS": "1"},
+         step="snapshots",
+     )
+@@ -997,9 +1018,9 @@ def certify(args: argparse.Namespace) -> int:
+                 step="install-pytest-xdist",
+             )
+         tests = run(
+-            [python, "-m", "pytest", "-n", str(args.test_workers), "-vv",
++            pytest_command(python, ["-n", str(args.test_workers), "-vv",
+              "--durations=20", "-o", "faulthandler_timeout=300",
+-             "-p", "no:cacheprovider"],
++             "-p", "no:cacheprovider"]),
+             check=False,
+             step="tests",
+         )  # fmt: skip
+-- 
+2.55.0
+
diff --git a/.job/tool-patches/0013-reservation-branch-provenance.patch b/.job/tool-patches/0013-reservation-branch-provenance.patch
new file mode 100644
index 00000000..c1fb32f3
--- /dev/null
+++ b/.job/tool-patches/0013-reservation-branch-provenance.patch
@@ -0,0 +1,108 @@
+diff --git a/tools/certify_uk_cut_in_policyengine_py.py b/tools/certify_uk_cut_in_policyengine_py.py
+index 7dcb9e36d..c6c51f34f 100644
+--- a/tools/certify_uk_cut_in_policyengine_py.py
++++ b/tools/certify_uk_cut_in_policyengine_py.py
+@@ -170,6 +170,20 @@ def regional_tag(flags: list[str]) -> str | None:
+ def resource_guard(root: Path) -> dict[str, float | int]:
+     """Refuse work before spawning a process on an overloaded or full host."""
+ 
++    reservation = os.environ.get("PE_CERTIFY_RESERVATION_SCRIPT")
++    if reservation:
++        result = subprocess.run(
++            [sys.executable, reservation, "allowed", "uk-hub"],
++            capture_output=True,
++            text=True,
++            timeout=30,
++            check=False,
++        )
++        if result.returncode:
++            raise CertifyError(
++                "UK hub reservation refused this step: "
++                + (result.stdout + result.stderr).strip()[-1000:]
++            )
+     load1 = os.getloadavg()[0]
+     while not root.exists():
+         root = root.parent
+@@ -362,6 +376,7 @@ def timed_step(name: str, path: Path):
+     record = {"step": name, "started_at": datetime.now(UTC).isoformat()}
+     print(f"[start] {name}", flush=True)
+     try:
++        record.update(resource_guard(path.parent))
+         yield record
+         record.setdefault("returncode", 0)
+     except Exception as error:
+@@ -464,7 +479,9 @@ class Shell:
+         return result
+ 
+ 
+-def writer_check(national_tag: str, pepy_repo: Path) -> list[str]:
++def writer_check(
++    national_tag: str, pepy_repo: Path, branch: str | None = None
++) -> list[str]:
+     """Open pull requests or branches already certifying this cut."""
+ 
+     found = []
+@@ -485,7 +502,7 @@ def writer_check(national_tag: str, pepy_repo: Path) -> list[str]:
+         )
+     for pr in json.loads(result.stdout or "[]"):
+         found.append(f"#{pr['number']} {pr['title']} ({pr['headRefName']})")
+-    branch = f"certify-uk-{national_tag.rsplit('-', 1)[-1]}"
++    branch = branch or f"certify-uk-{national_tag.rsplit('-', 1)[-1]}"
+     remote = subprocess.run(
+         ["git", "ls-remote", "--heads", "origin", branch],
+         cwd=pepy_repo,
+@@ -733,6 +750,7 @@ def parse(argv: list[str] | None) -> argparse.Namespace:
+         default=Path("~/PolicyEngine/policyengine.py").expanduser(),
+     )
+     parser.add_argument("--worktree", type=Path, help="default: beside the repo")
++    parser.add_argument("--branch", help="default: certify-uk-")
+     parser.add_argument(
+         "--resume-worktree",
+         action="store_true",
+@@ -773,15 +791,33 @@ def main(argv: list[str] | None = None) -> int:
+ 
+ 
+ def certify(args: argparse.Namespace) -> int:
+-    microcosm_commit = subprocess.check_output(
+-        ["git", "rev-parse", "HEAD"], cwd=Path(__file__).resolve().parents[1], text=True
+-    ).strip()
+     repo = args.pepy_repo
++    resource_guard(repo)
++    microcosm_commit = os.environ.get("PE_CERTIFY_TOOL_SOURCE_COMMIT")
++    if microcosm_commit:
++        if not re.fullmatch(r"[0-9a-f]{40}", microcosm_commit):
++            raise CertifyError("PE_CERTIFY_TOOL_SOURCE_COMMIT must be a full Git SHA.")
++    else:
++        microcosm_commit = subprocess.check_output(
++            ["git", "rev-parse", "HEAD"],
++            cwd=Path(__file__).resolve().parents[1],
++            text=True,
++        ).strip()
+     suffix = args.national_tag.rsplit("-", 1)[-1]
+-    branch = f"certify-uk-{suffix}"
++    branch = args.branch or f"certify-uk-{suffix}"
+     worktree = args.worktree or repo.parent / "_worktrees" / f"pepy-{branch}"
+     log = worktree.parent / f"{worktree.name}.certify.log"
+     timings = log.with_suffix(".timings.jsonl")
++    with timed_step("branch-preflight", timings):
++        result = subprocess.run(
++            ["git", "check-ref-format", "--branch", branch],
++            cwd=repo,
++            capture_output=True,
++            text=True,
++            check=False,
++        )
++        if result.returncode or branch.startswith("-"):
++            raise CertifyError(f"Invalid certification branch: {branch!r}.")
+     if not args.skip_tests and not args.dry_run:
+         with timed_step("test-temp-preflight", timings) as timing:
+             timing["temporary_directory"] = str(check_test_temp_directory())
+@@ -824,7 +860,7 @@ def certify(args: argparse.Namespace) -> int:
+         print(f"writer check skipped: {args.skip_writer_check}")
+     else:
+         with timed_step("writer-check", timings):
+-            others = writer_check(args.national_tag, repo)
++            others = writer_check(args.national_tag, repo, branch)
+         if others:
+             raise CertifyError(
+                 "this cut is already being certified: "
diff --git a/.job/tool-patches/README.md b/.job/tool-patches/README.md
new file mode 100644
index 00000000..dd5550c1
--- /dev/null
+++ b/.job/tool-patches/README.md
@@ -0,0 +1,39 @@
+# Portable certification tool
+
+Upstream is Microcosm #1348, branch `uk-cut-anyone`, commit
+`61e25de8a673f4187b2d882acab02d94d4a6da99`. The twelve original rehearsal
+patches are copied verbatim and their digests and clean application receipts
+are in `../tool-patches.json`. `0007` contains two commits.
+
+The original series did not apply directly to that exact Git commit: its
+tool and upstream test each lack one final blank line present in the
+rehearsal source snapshots. Apply `0000-tool-trailing-newline-compat.patch`
+first. It restores those two blank lines only; the tool's pre-patch blob
+then exactly matches `0001`'s expected blob. This file is unrelated to the
+rehearsal's `0000-prerequisite-699.patch`, which is deliberately absent.
+
+Apply original `0001` through `0012` in filename order using
+`git am --no-3way`. Then apply local
+`0013-reservation-branch-provenance.patch` with `git apply --check` and
+`git apply --index`, and commit. It adds per-step reservation checks,
+optional `--branch`, and portable source provenance. It changes no evidence
+requirements or national/regional certification semantics.
+
+Run `python3 .job/prepare-tool.py` to reconstruct these inputs inside the
+assigned workspace. It refuses to replace an existing checkout, verifies
+the full resulting Git tree and standalone tool SHA-256, and checks the
+old/new Microcosm build ancestry. Commit IDs may differ when reconstructing
+because the locally applied commits have new committer timestamps; trees
+and tool bytes must match. The standalone tool at
+`../certify_uk_cut_in_policyengine_py.py` is the exact resulting source.
+
+The heavy runner supplies `PE_CERTIFY_RESERVATION_SCRIPT` and
+`PE_CERTIFY_TOOL_SOURCE_COMMIT`. The former invokes
+`reservation.py allowed uk-hub` before every timed preflight stage,
+subprocess stage, and model-surface calculation. Those stages also enforce
+load1 at most 110 and free disk at least 55,000,000,000 bytes. The latter
+keeps the Microcosm source identity when invoking the portable snapshot.
+
+Preparation validation was limited to clean patch applications, Git
+ancestry, syntax parsing, `--help`, and `git diff --check`. No test suite,
+simulation, snapshot, UC-floor command, or national dataset load ran.

From 36d7969023125e11bc644886bc2b3b81d7d5e86a Mon Sep 17 00:00:00 2001
From: Max Ghenis 
Date: Fri, 23 Oct 2026 13:28:18 -0400
Subject: [PATCH 3/8] Add guarded same-line UK two-by-two attribution tooling

---
 .job/attribution-definitions.json  | 1379 ++++++++++++++++++++++++++++
 .job/attribution-fixture.json      |  291 ++++++
 .job/attribution-light-checks.json |  135 +++
 .job/attribution-mapping.json      |   15 +
 .job/attribution.schema.json       |   67 ++
 .job/run-attribution.py            |  965 +++++++++++++++++++
 6 files changed, 2852 insertions(+)
 create mode 100644 .job/attribution-definitions.json
 create mode 100644 .job/attribution-fixture.json
 create mode 100644 .job/attribution-light-checks.json
 create mode 100644 .job/attribution-mapping.json
 create mode 100644 .job/attribution.schema.json
 create mode 100644 .job/run-attribution.py

diff --git a/.job/attribution-definitions.json b/.job/attribution-definitions.json
new file mode 100644
index 00000000..f5917a02
--- /dev/null
+++ b/.job/attribution-definitions.json
@@ -0,0 +1,1379 @@
+{
+  "schema_version": 1,
+  "source_model_version": "2.147.2",
+  "source_model_commit": "fe0b99a36bd309a722b19937345e34290cb6dd73",
+  "source_files": [
+    {
+      "path": "policyengine_uk/utils/aggregate_impact/report_spec.yaml",
+      "sha256": "41562850bb70049e7b05f4608ff68d586d014f8c5a044edb0032e64ecff01c18",
+      "ref": "2.147.2"
+    },
+    {
+      "path": "policyengine_uk/data/fiscal_partition.yaml",
+      "sha256": "d4c1e9f0816033b9e330c2222b8d3596a8c03e016f0229b16114e3312cfab686",
+      "ref": "2.147.2"
+    },
+    {
+      "path": "policyengine_uk/variables/gov/gov_tax.py",
+      "sha256": "7b4b90ed5515315a94a1080c400ed5bf7fcff4e3395d963f8d8fd2ca78ce11b4",
+      "ref": "2.132.6"
+    },
+    {
+      "path": "policyengine_uk/variables/gov/gov_spending.py",
+      "sha256": "0150171077417b9d8ee94828584be48aa320b2c1acfa367d909ba33139727cb0",
+      "ref": "2.132.6"
+    },
+    {
+      "path": "policyengine_uk/variables/gov/hmrc/household_tax.py",
+      "sha256": "35319da2e59dfb58292535bb788ffe42957564855c5a699fde088a83cf1454d5",
+      "ref": "2.132.6"
+    },
+    {
+      "path": "policyengine_uk/variables/household/income/household_benefits.py",
+      "sha256": "e7161c0b985183e995dc1f08d2807333dfa9b9c1b355d2c043a5d8985a21489a",
+      "ref": "2.132.6"
+    }
+  ],
+  "flags": {
+    "gbp": {
+      "absolute": 100000000,
+      "relative": 0.001
+    },
+    "count": {
+      "absolute": 5000,
+      "relative": 0.005
+    },
+    "rate": {
+      "absolute": 0.001
+    },
+    "index": {
+      "absolute": 0.001
+    },
+    "decile_relative": {
+      "absolute": 0.001
+    },
+    "gbp_household": {
+      "absolute": 10,
+      "relative": 0.001
+    },
+    "reform_gbp": {
+      "absolute": 100000000,
+      "relative": 0.01
+    }
+  },
+  "poverty": {
+    "measures": [
+      {
+        "id": "absolute_bhc",
+        "label": "Absolute poverty, before housing costs",
+        "variable": "in_poverty_bhc"
+      },
+      {
+        "id": "absolute_ahc",
+        "label": "Absolute poverty, after housing costs",
+        "variable": "in_poverty_ahc"
+      },
+      {
+        "id": "relative_bhc",
+        "label": "Relative poverty, before housing costs",
+        "variable": "in_relative_poverty_bhc"
+      },
+      {
+        "id": "relative_ahc",
+        "label": "Relative poverty, after housing costs",
+        "variable": "in_relative_poverty_ahc"
+      }
+    ],
+    "groups": [
+      {
+        "id": "all",
+        "label": "Everyone"
+      },
+      {
+        "id": "children",
+        "label": "Children",
+        "variable": "is_hbai_dependent_child"
+      },
+      {
+        "id": "working_age_adults",
+        "label": "Working-age adults",
+        "variable": "is_hbai_working_age_adult"
+      },
+      {
+        "id": "pensioners",
+        "label": "Pensioners",
+        "variable": "is_hbai_pensioner"
+      }
+    ]
+  },
+  "heads": [
+    {
+      "id": "income_tax",
+      "label": "Income tax",
+      "variable": "income_tax",
+      "entity": "person",
+      "obr_line": "Income tax",
+      "obr_table_3_1": "Income tax",
+      "report_group": "income_tax",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "kind": "tax"
+    },
+    {
+      "id": "nics_individual",
+      "label": "National Insurance (employee and self-employed)",
+      "variable": "national_insurance",
+      "entity": "person",
+      "obr_line": "National insurance contributions",
+      "obr_table_3_1": "NICs",
+      "report_group": "national_insurance",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "kind": "tax"
+    },
+    {
+      "id": "nics_employer",
+      "label": "National Insurance (employer)",
+      "variable": "ni_employer",
+      "entity": "person",
+      "obr_line": "National insurance contributions",
+      "obr_table_3_1": "NICs",
+      "report_group": "national_insurance",
+      "payer": "employer",
+      "totals": [
+        "gov_tax"
+      ],
+      "sign": "nonnegative",
+      "kind": "tax"
+    },
+    {
+      "id": "employer_nics_response_capital",
+      "label": "Employer NI change borne by capital",
+      "variable": "employer_ni_response_capital_incidence",
+      "entity": "person",
+      "obr_line": "National insurance contributions",
+      "obr_table_3_1": "NICs",
+      "report_group": "national_insurance",
+      "payer": "corporate incidence",
+      "totals": [
+        "household_tax"
+      ],
+      "sign": "any",
+      "zero_at_baseline": "expected",
+      "kind": "tax"
+    },
+    {
+      "id": "employer_nics_response_consumer",
+      "label": "Employer NI change borne by consumers",
+      "variable": "employer_ni_response_consumer_incidence",
+      "entity": "person",
+      "obr_line": "National insurance contributions",
+      "obr_table_3_1": "NICs",
+      "report_group": "national_insurance",
+      "payer": "consumer incidence",
+      "totals": [
+        "household_tax"
+      ],
+      "sign": "any",
+      "zero_at_baseline": "expected",
+      "kind": "tax"
+    },
+    {
+      "id": "capital_gains_tax",
+      "label": "Capital gains tax",
+      "variable": "capital_gains_tax",
+      "entity": "person",
+      "obr_line": "Capital gains tax",
+      "obr_table_3_1": "Capital taxes",
+      "report_group": "capital_taxes",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "kind": "tax"
+    },
+    {
+      "id": "student_loan_repayments",
+      "label": "Student loan repayments",
+      "variable": "student_loan_repayments",
+      "entity": "person",
+      "obr_line": "not_an_obr_current_receipt",
+      "obr_table_3_1": null,
+      "obr_line_note": "A financial transaction: repayments reduce the student loan book, not borrowing, so they are outside current receipts. The model counts them in gov_tax and household_tax; reports should show them separately. The independent specification left them out of both tax totals.",
+      "report_group": "financial_transactions",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax"
+      ],
+      "sign": "nonnegative",
+      "kind": "tax"
+    },
+    {
+      "id": "vat",
+      "label": "VAT",
+      "variable": "vat",
+      "entity": "household",
+      "obr_line": "Value added tax",
+      "obr_table_3_1": "Value added tax",
+      "report_group": "vat",
+      "payer": "consumer incidence",
+      "totals": [
+        "gov_tax"
+      ],
+      "sign": "nonnegative",
+      "data_note": "Negative for 76 households on policyengine-uk-data 1.58.0 (enhanced_frs_2024_25) in every year from 2025 to 2030, -\u00a372m weighted in 2026: full_rate_vat_consumption is negative there because the dataset's consumption (44 households) or full_rate_vat_expenditure_rate (32 households) is negative. policyengine-uk-data issue 565. Measured 2026-10-11.",
+      "kind": "tax"
+    },
+    {
+      "id": "vat_change",
+      "label": "Change in VAT from its baseline",
+      "variable": "vat_change",
+      "entity": "household",
+      "obr_line": "Value added tax",
+      "obr_table_3_1": "Value added tax",
+      "report_group": "vat",
+      "payer": "consumer incidence",
+      "totals": [
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "any",
+      "zero_at_baseline": "expected",
+      "reads": [
+        {
+          "head": "vat",
+          "route": "vat_change adds vat and subtracts baseline_vat",
+          "why": "a change measure built from the level; the two are never in the same total"
+        }
+      ],
+      "kind": "tax"
+    },
+    {
+      "id": "private_school_vat",
+      "label": "VAT on private school fees",
+      "variable": "private_school_vat",
+      "entity": "household",
+      "obr_line": "Value added tax",
+      "obr_table_3_1": "Value added tax",
+      "report_group": "vat",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax"
+      ],
+      "sign": "nonnegative",
+      "zero_at_baseline": "expected",
+      "zero_at_baseline_note": "gov.contrib.labour.private_school_vat is 0 at current law in this model, so the head is reform-only here.",
+      "reads": [
+        {
+          "head": "income_tax",
+          "route": "private_school_vat -> attends_private_school -> income_tax",
+          "why": "attendance is imputed from income; it decides who attends, not how much tax is counted"
+        },
+        {
+          "head": "nics_individual",
+          "route": "private_school_vat -> attends_private_school -> (net income) -> national_insurance",
+          "why": "as above"
+        },
+        {
+          "head": "council_tax",
+          "route": "private_school_vat -> attends_private_school -> household_benefits -> council_tax_benefit -> council_tax",
+          "why": "as above"
+        }
+      ],
+      "kind": "tax"
+    },
+    {
+      "id": "fuel_duty",
+      "label": "Fuel duties",
+      "variable": "fuel_duty",
+      "entity": "household",
+      "obr_line": "Fuel duties",
+      "obr_table_3_1": "Fuel duties",
+      "report_group": "excise_duties",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "kind": "tax"
+    },
+    {
+      "id": "alcohol_duty",
+      "label": "Alcohol duties",
+      "variable": "alcohol_duty",
+      "entity": "household",
+      "obr_line": "Alcohol duties",
+      "obr_table_3_1": "Alcohol and tobacco duties",
+      "report_group": "excise_duties",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax"
+      ],
+      "sign": "nonnegative",
+      "data_note": "Zero for every household on policyengine-uk-data 1.58.0 (enhanced_frs_2024_25) in every year from 2025 to 2030, measured 2026-10-11. Issue 1872.",
+      "kind": "tax"
+    },
+    {
+      "id": "tobacco_duty",
+      "label": "Tobacco duties",
+      "variable": "tobacco_duty",
+      "entity": "household",
+      "obr_line": "Tobacco duties",
+      "obr_table_3_1": "Alcohol and tobacco duties",
+      "report_group": "excise_duties",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax"
+      ],
+      "sign": "nonnegative",
+      "data_note": "Zero for every household on policyengine-uk-data 1.58.0 (enhanced_frs_2024_25) in every year from 2025 to 2030, measured 2026-10-11. Issue 1873.",
+      "kind": "tax"
+    },
+    {
+      "id": "vehicle_excise_duty",
+      "label": "Vehicle excise duties",
+      "variable": "vehicle_excise_duty",
+      "entity": "household",
+      "obr_line": "Vehicle excise duties",
+      "obr_table_3_1": null,
+      "report_group": "excise_duties",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax"
+      ],
+      "sign": "nonnegative",
+      "data_note": "Zero for every household on policyengine-uk-data 1.58.0 (enhanced_frs_2024_25) in every year from 2025 to 2030, measured 2026-10-11. Issue 1874.",
+      "kind": "tax"
+    },
+    {
+      "id": "tv_licence",
+      "label": "TV licence fee",
+      "variable": "tv_licence",
+      "entity": "household",
+      "obr_line": "Licence fee receipts",
+      "obr_table_3_1": null,
+      "report_group": "other_receipts",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "reads": [
+        {
+          "head": "income_tax",
+          "route": "tv_licence -> tv_licence_discount -> pension_credit -> pension_credit_income -> income_tax",
+          "why": "the over-75 discount depends on Pension Credit receipt, a means test on net income"
+        },
+        {
+          "head": "nics_individual",
+          "route": "tv_licence -> tv_licence_discount -> pension_credit -> pension_credit_income -> national_insurance",
+          "why": "as above"
+        }
+      ],
+      "kind": "tax"
+    },
+    {
+      "id": "council_tax",
+      "label": "Council tax",
+      "variable": "council_tax",
+      "entity": "household",
+      "obr_line": "Council tax",
+      "obr_line_note": "Gross of Council Tax Reduction. The OBR's receipts are net of discounts and reduction schemes; the model counts the reduction as spending (council_tax_benefit) instead. Compare with the OBR row net of it. The independent specification chose the net variable, council_tax_less_benefit.",
+      "obr_table_3_1": "PSNB-neutral receipts",
+      "report_group": "local_taxes",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "kind": "tax"
+    },
+    {
+      "id": "high_value_council_tax_surcharge",
+      "label": "High value council tax surcharge",
+      "variable": "high_value_council_tax_surcharge",
+      "entity": "household",
+      "obr_line": "Council tax",
+      "obr_table_3_1": "PSNB-neutral receipts",
+      "report_group": "local_taxes",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax"
+      ],
+      "sign": "nonnegative",
+      "kind": "tax"
+    },
+    {
+      "id": "domestic_rates",
+      "label": "Northern Ireland domestic rates",
+      "variable": "domestic_rates",
+      "entity": "household",
+      "obr_line": "Council tax",
+      "obr_table_3_1": null,
+      "obr_line_note": "The OBR adds Northern Irish domestic rates to its council tax receipts (see sources).",
+      "report_group": "local_taxes",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "kind": "tax"
+    },
+    {
+      "id": "business_rates",
+      "label": "Business rates",
+      "variable": "business_rates",
+      "entity": "household",
+      "obr_line": "Business rates",
+      "obr_table_3_1": "Business rates",
+      "report_group": "business_rates",
+      "payer": "corporate incidence",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "kind": "tax"
+    },
+    {
+      "id": "sdlt_household",
+      "label": "Stamp duty land tax on the household's own purchases",
+      "variable": "expected_sdlt",
+      "entity": "household",
+      "obr_line": "Property transaction taxes",
+      "obr_table_3_1": "Capital taxes",
+      "report_group": "property_transaction_taxes",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "kind": "tax"
+    },
+    {
+      "id": "sdlt_corporate",
+      "label": "Stamp duty land tax paid by companies",
+      "variable": "corporate_sdlt",
+      "entity": "household",
+      "obr_line": "Property transaction taxes",
+      "obr_table_3_1": "Capital taxes",
+      "report_group": "property_transaction_taxes",
+      "payer": "corporate incidence",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "kind": "tax"
+    },
+    {
+      "id": "land_transaction_tax",
+      "label": "Land transaction tax (Wales)",
+      "variable": "expected_ltt",
+      "entity": "household",
+      "obr_line": "Property transaction taxes",
+      "obr_table_3_1": "Capital taxes",
+      "report_group": "property_transaction_taxes",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "data_note": "expected_ltt multiplies an already purchase-drawn liability by property_sale_rate, discounting it twice (issue 2246). The independent specification chose land_transaction_tax for that reason.",
+      "kind": "tax"
+    },
+    {
+      "id": "land_and_buildings_transaction_tax",
+      "label": "Land and buildings transaction tax (Scotland)",
+      "variable": "expected_lbtt",
+      "entity": "household",
+      "obr_line": "Property transaction taxes",
+      "obr_table_3_1": "Capital taxes",
+      "report_group": "property_transaction_taxes",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "data_note": "expected_lbtt multiplies an already purchase-drawn liability by property_sale_rate, discounting it twice (issue 2246). The independent specification chose land_and_buildings_transaction_tax for that reason.",
+      "kind": "tax"
+    },
+    {
+      "id": "corporate_incident_tax_change",
+      "label": "Unspecified tax change borne by capital",
+      "variable": "corporate_incident_tax_revenue_change",
+      "entity": "household",
+      "obr_line": "not_an_obr_current_receipt",
+      "obr_table_3_1": null,
+      "obr_line_note": "A lump-sum revenue change allocated by shareholding.",
+      "report_group": "incidence_changes",
+      "payer": "corporate incidence",
+      "totals": [
+        "gov_tax",
+        "household_tax"
+      ],
+      "sign": "any",
+      "zero_at_baseline": "expected",
+      "kind": "tax"
+    },
+    {
+      "id": "consumer_incident_tax_change",
+      "label": "Unspecified tax change borne by consumers",
+      "variable": "consumer_incident_tax_revenue_change",
+      "entity": "household",
+      "obr_line": "not_an_obr_current_receipt",
+      "obr_table_3_1": null,
+      "obr_line_note": "A lump-sum revenue change allocated by consumption.",
+      "report_group": "incidence_changes",
+      "payer": "consumer incidence",
+      "totals": [
+        "gov_tax",
+        "household_tax"
+      ],
+      "sign": "any",
+      "zero_at_baseline": "expected",
+      "kind": "tax"
+    },
+    {
+      "id": "wealth_tax",
+      "label": "Wealth tax",
+      "variable": "wealth_tax",
+      "entity": "household",
+      "obr_line": "not_an_obr_current_receipt",
+      "obr_table_3_1": null,
+      "obr_line_note": "A hypothetical tax.",
+      "report_group": "hypothetical_taxes",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "zero_at_baseline": "expected",
+      "kind": "tax"
+    },
+    {
+      "id": "non_primary_residence_wealth_tax",
+      "label": "Wealth tax excluding the main residence",
+      "variable": "non_primary_residence_wealth_tax",
+      "entity": "household",
+      "obr_line": "not_an_obr_current_receipt",
+      "obr_table_3_1": null,
+      "obr_line_note": "A hypothetical tax.",
+      "report_group": "hypothetical_taxes",
+      "payer": "household",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "zero_at_baseline": "expected",
+      "kind": "tax"
+    },
+    {
+      "id": "land_value_tax",
+      "label": "Land value tax",
+      "variable": "LVT",
+      "entity": "household",
+      "obr_line": "not_an_obr_current_receipt",
+      "obr_table_3_1": null,
+      "obr_line_note": "A hypothetical tax.",
+      "report_group": "hypothetical_taxes",
+      "payer": "mixed",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "zero_at_baseline": "expected",
+      "kind": "tax"
+    },
+    {
+      "id": "carbon_tax",
+      "label": "Carbon tax",
+      "variable": "carbon_tax",
+      "entity": "household",
+      "obr_line": "not_an_obr_current_receipt",
+      "obr_table_3_1": null,
+      "obr_line_note": "A hypothetical tax.",
+      "report_group": "hypothetical_taxes",
+      "payer": "mixed",
+      "totals": [
+        "gov_tax",
+        "household_tax",
+        "pre_budget_change_household_tax"
+      ],
+      "sign": "nonnegative",
+      "zero_at_baseline": "expected",
+      "kind": "tax"
+    },
+    {
+      "id": "state_pension",
+      "label": "State Pension",
+      "variable": "state_pension",
+      "entity": "person",
+      "report_group": "pensioner_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "pension_credit",
+      "label": "Pension Credit",
+      "variable": "pension_credit",
+      "entity": "benunit",
+      "report_group": "pensioner_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "winter_fuel_payment",
+      "label": "Winter Fuel Payment",
+      "variable": "winter_fuel_allowance",
+      "entity": "household",
+      "report_group": "pensioner_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "pawhp",
+      "label": "Pension Age Winter Heating Payment (Scotland)",
+      "variable": "pawhp",
+      "entity": "household",
+      "report_group": "pensioner_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "universal_credit",
+      "label": "Universal Credit",
+      "variable": "universal_credit",
+      "entity": "benunit",
+      "report_group": "working_age_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "housing_benefit",
+      "label": "Housing Benefit",
+      "variable": "housing_benefit",
+      "entity": "benunit",
+      "report_group": "working_age_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "council_tax_reduction",
+      "label": "Council Tax Reduction",
+      "variable": "council_tax_benefit",
+      "entity": "benunit",
+      "report_group": "working_age_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "esa_income",
+      "label": "Employment and Support Allowance (income-related)",
+      "variable": "esa_income",
+      "entity": "benunit",
+      "report_group": "working_age_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "esa_contrib",
+      "label": "Employment and Support Allowance (contribution-based)",
+      "variable": "esa_contrib",
+      "entity": "person",
+      "report_group": "working_age_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "jsa_income",
+      "label": "Jobseeker's Allowance (income-based)",
+      "variable": "jsa_income",
+      "entity": "benunit",
+      "report_group": "working_age_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "jsa_contrib",
+      "label": "Jobseeker's Allowance (contribution-based)",
+      "variable": "jsa_contrib",
+      "entity": "person",
+      "report_group": "working_age_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "income_support",
+      "label": "Income Support",
+      "variable": "income_support",
+      "entity": "benunit",
+      "report_group": "working_age_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "working_tax_credit",
+      "label": "Working Tax Credit",
+      "variable": "working_tax_credit",
+      "entity": "benunit",
+      "report_group": "working_age_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "child_tax_credit",
+      "label": "Child Tax Credit",
+      "variable": "child_tax_credit",
+      "entity": "benunit",
+      "report_group": "working_age_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "incapacity_benefit",
+      "label": "Incapacity Benefit",
+      "variable": "incapacity_benefit",
+      "entity": "person",
+      "report_group": "working_age_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "pip",
+      "label": "Personal Independence Payment",
+      "variable": "pip",
+      "entity": "person",
+      "report_group": "disability_and_carers_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "dla",
+      "label": "Disability Living Allowance",
+      "variable": "dla",
+      "entity": "person",
+      "report_group": "disability_and_carers_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "attendance_allowance",
+      "label": "Attendance Allowance",
+      "variable": "attendance_allowance",
+      "entity": "person",
+      "report_group": "disability_and_carers_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "carers_allowance",
+      "label": "Carer's Allowance",
+      "variable": "carers_allowance",
+      "entity": "person",
+      "report_group": "disability_and_carers_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "carer_support_payment",
+      "label": "Carer Support Payment (Scotland)",
+      "variable": "carer_support_payment",
+      "entity": "person",
+      "report_group": "disability_and_carers_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "scottish_carer_supplement",
+      "label": "Carer's Allowance Supplement (Scotland)",
+      "variable": "scottish_carer_supplement",
+      "entity": "person",
+      "report_group": "disability_and_carers_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "sda",
+      "label": "Severe Disablement Allowance",
+      "variable": "sda",
+      "entity": "person",
+      "report_group": "disability_and_carers_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "iidb",
+      "label": "Industrial Injuries Disablement Benefit",
+      "variable": "iidb",
+      "entity": "person",
+      "report_group": "disability_and_carers_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "afcs",
+      "label": "Armed Forces Compensation Scheme",
+      "variable": "afcs",
+      "entity": "person",
+      "report_group": "disability_and_carers_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "war_widows_pension",
+      "label": "War Widow's or Widower's Pension",
+      "variable": "war_widows_pension",
+      "entity": "person",
+      "report_group": "disability_and_carers_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "child_benefit",
+      "label": "Child Benefit",
+      "variable": "child_benefit",
+      "entity": "benunit",
+      "report_group": "child_and_family_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "scottish_child_payment",
+      "label": "Scottish Child Payment",
+      "variable": "scottish_child_payment",
+      "entity": "person",
+      "report_group": "child_and_family_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "two_child_limit_payment",
+      "label": "Two Child Limit Payment (Scotland, counterfactual)",
+      "variable": "two_child_limit_payment",
+      "entity": "benunit",
+      "report_group": "child_and_family_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "zero_at_baseline": "expected",
+      "kind": "spending"
+    },
+    {
+      "id": "healthy_start_vouchers",
+      "label": "Healthy Start vouchers",
+      "variable": "healthy_start_vouchers",
+      "entity": "person",
+      "report_group": "child_and_family_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "maternity_allowance",
+      "label": "Maternity Allowance",
+      "variable": "maternity_allowance",
+      "entity": "person",
+      "report_group": "child_and_family_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "statutory_maternity_pay",
+      "label": "Statutory Maternity Pay",
+      "variable": "statutory_maternity_pay",
+      "entity": "person",
+      "report_group": "child_and_family_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "statutory_paternity_pay",
+      "label": "Statutory Paternity Pay",
+      "variable": "statutory_paternity_pay",
+      "entity": "person",
+      "report_group": "child_and_family_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "statutory_sick_pay",
+      "label": "Statutory Sick Pay",
+      "variable": "statutory_sick_pay",
+      "entity": "person",
+      "report_group": "child_and_family_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "ssmg",
+      "label": "Sure Start Maternity Grant",
+      "variable": "ssmg",
+      "entity": "person",
+      "report_group": "child_and_family_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "bsp",
+      "label": "Bereavement Support Payment",
+      "variable": "bsp",
+      "entity": "person",
+      "report_group": "child_and_family_benefits",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "tax_free_childcare",
+      "label": "Tax-Free Childcare",
+      "variable": "tax_free_childcare",
+      "entity": "person",
+      "report_group": "childcare_and_education",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "extended_childcare_entitlement",
+      "label": "Extended childcare entitlement",
+      "variable": "extended_childcare_entitlement",
+      "entity": "benunit",
+      "report_group": "childcare_and_education",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "universal_childcare_entitlement",
+      "label": "Universal childcare entitlement",
+      "variable": "universal_childcare_entitlement",
+      "entity": "person",
+      "report_group": "childcare_and_education",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "targeted_childcare_entitlement",
+      "label": "Targeted childcare entitlement",
+      "variable": "targeted_childcare_entitlement",
+      "entity": "person",
+      "report_group": "childcare_and_education",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "care_to_learn",
+      "label": "Care to Learn",
+      "variable": "care_to_learn",
+      "entity": "person",
+      "report_group": "childcare_and_education",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "childcare_grant",
+      "label": "Childcare Grant",
+      "variable": "childcare_grant",
+      "entity": "person",
+      "report_group": "childcare_and_education",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "parents_learning_allowance",
+      "label": "Parents' Learning Allowance",
+      "variable": "parents_learning_allowance",
+      "entity": "person",
+      "report_group": "childcare_and_education",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "adult_dependants_grant",
+      "label": "Adult Dependants' Grant",
+      "variable": "adult_dependants_grant",
+      "entity": "person",
+      "report_group": "childcare_and_education",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "travel_grant",
+      "label": "Travel Grant",
+      "variable": "travel_grant",
+      "entity": "person",
+      "report_group": "childcare_and_education",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "disabled_students_allowance",
+      "label": "Disabled Students' Allowance",
+      "variable": "disabled_students_allowance",
+      "entity": "person",
+      "report_group": "childcare_and_education",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "bursary_fund_16_to_19",
+      "label": "16 to 19 Bursary Fund",
+      "variable": "bursary_fund_16_to_19",
+      "entity": "person",
+      "report_group": "childcare_and_education",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "kind": "spending"
+    },
+    {
+      "id": "nhs_spending",
+      "label": "NHS spending",
+      "variable": "nhs_spending",
+      "entity": "person",
+      "report_group": "public_services_in_kind",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "caseload": false,
+      "kind": "spending"
+    },
+    {
+      "id": "dfe_education_spending",
+      "label": "State education spending",
+      "variable": "dfe_education_spending",
+      "entity": "household",
+      "report_group": "public_services_in_kind",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "caseload": false,
+      "kind": "spending"
+    },
+    {
+      "id": "dft_subsidy_spending",
+      "label": "Transport subsidy spending",
+      "variable": "dft_subsidy_spending",
+      "entity": "household",
+      "report_group": "public_services_in_kind",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "caseload": false,
+      "kind": "spending"
+    },
+    {
+      "id": "cost_of_living_support_payment",
+      "label": "Cost-of-living support payment",
+      "variable": "cost_of_living_support_payment",
+      "entity": "household",
+      "report_group": "cost_of_living_support",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "zero_at_baseline": "expected",
+      "kind": "spending"
+    },
+    {
+      "id": "energy_bills_rebate",
+      "label": "Energy Bills Rebate",
+      "variable": "energy_bills_rebate",
+      "entity": "household",
+      "report_group": "cost_of_living_support",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "zero_at_baseline": "expected",
+      "kind": "spending"
+    },
+    {
+      "id": "epg_subsidy",
+      "label": "Energy Price Guarantee subsidy",
+      "variable": "epg_subsidy",
+      "entity": "household",
+      "report_group": "cost_of_living_support",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "nonnegative",
+      "zero_at_baseline": "expected",
+      "kind": "spending"
+    },
+    {
+      "id": "basic_income",
+      "label": "Basic income",
+      "variable": "basic_income",
+      "entity": "person",
+      "report_group": "hypothetical_spending",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "any",
+      "zero_at_baseline": "expected",
+      "kind": "spending"
+    },
+    {
+      "id": "disability_basic_income",
+      "label": "Disability basic income",
+      "variable": "disability_basic_income",
+      "entity": "person",
+      "report_group": "hypothetical_spending",
+      "totals": [
+        "gov_spending",
+        "household_benefits"
+      ],
+      "sign": "any",
+      "zero_at_baseline": "expected",
+      "kind": "spending"
+    },
+    {
+      "id": "other_public_spending_budget_change",
+      "label": "Other public spending budget change",
+      "variable": "other_public_spending_budget_change",
+      "entity": "household",
+      "report_group": "hypothetical_spending",
+      "totals": [
+        "gov_spending"
+      ],
+      "sign": "any",
+      "zero_at_baseline": "expected",
+      "caseload": false,
+      "kind": "spending"
+    }
+  ],
+  "model_double_counts": [],
+  "definition_notes": [
+    "Union of 2.147.2 fiscal heads and 2.132.6 government and household tax/benefit components. Fiscal partition unavailable in 2.132.6.",
+    "Heads are reported individually; do not add overlapping legacy/composite heads. gov_balance is the exact model variable; this script does not silently replace it with a corrected accounting total."
+  ]
+}
diff --git a/.job/attribution-fixture.json b/.job/attribution-fixture.json
new file mode 100644
index 00000000..07357b8c
--- /dev/null
+++ b/.job/attribution-fixture.json
@@ -0,0 +1,291 @@
+{
+  "description": "Ten wholly invented observations per cell; no country package imported and no real household values. Weights 1..10 make manual aggregate expectations explicit.",
+  "weights": [
+    1,
+    2,
+    3,
+    4,
+    5,
+    6,
+    7,
+    8,
+    9,
+    10
+  ],
+  "definitions": {
+    "flags": {
+      "gbp": {
+        "absolute": 100000000,
+        "relative": 0.001
+      },
+      "count": {
+        "absolute": 5000,
+        "relative": 0.005
+      },
+      "rate": {
+        "absolute": 0.001
+      },
+      "index": {
+        "absolute": 0.001
+      },
+      "decile_relative": {
+        "absolute": 0.001
+      },
+      "gbp_household": {
+        "absolute": 10,
+        "relative": 0.001
+      },
+      "reform_gbp": {
+        "absolute": 100000000,
+        "relative": 0.01
+      }
+    },
+    "heads": [
+      {
+        "id": "synthetic_tax",
+        "variable": "synthetic_tax",
+        "kind": "tax",
+        "label": "Synthetic tax"
+      },
+      {
+        "id": "synthetic_benefit",
+        "variable": "synthetic_benefit",
+        "kind": "spending",
+        "label": "Synthetic benefit"
+      }
+    ],
+    "poverty": {
+      "measures": [
+        {
+          "id": "synthetic",
+          "variable": "synthetic_poverty",
+          "label": "Synthetic poverty"
+        }
+      ],
+      "groups": [
+        {
+          "id": "all",
+          "label": "Everyone"
+        }
+      ]
+    }
+  },
+  "cells": {
+    "old_data__old_model": {
+      "values": {
+        "gov_balance": [
+          100000000,
+          100000000,
+          100000000,
+          100000000,
+          100000000,
+          100000000,
+          100000000,
+          100000000,
+          100000000,
+          100000000
+        ],
+        "synthetic_tax": [
+          130000000,
+          130000000,
+          130000000,
+          130000000,
+          130000000,
+          130000000,
+          130000000,
+          130000000,
+          130000000,
+          130000000
+        ],
+        "synthetic_benefit": [
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000
+        ],
+        "synthetic_poverty": [
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0
+        ]
+      }
+    },
+    "old_data__new_model": {
+      "values": {
+        "gov_balance": [
+          110000000,
+          110000000,
+          110000000,
+          110000000,
+          110000000,
+          110000000,
+          110000000,
+          110000000,
+          110000000,
+          110000000
+        ],
+        "synthetic_tax": [
+          140000000,
+          140000000,
+          140000000,
+          140000000,
+          140000000,
+          140000000,
+          140000000,
+          140000000,
+          140000000,
+          140000000
+        ],
+        "synthetic_benefit": [
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000
+        ],
+        "synthetic_poverty": [
+          1,
+          1,
+          1,
+          1,
+          1,
+          1,
+          1,
+          1,
+          1,
+          1
+        ]
+      }
+    },
+    "new_data__old_model": {
+      "values": {
+        "gov_balance": [
+          105000000,
+          105000000,
+          105000000,
+          105000000,
+          105000000,
+          105000000,
+          105000000,
+          105000000,
+          105000000,
+          105000000
+        ],
+        "synthetic_tax": [
+          135000000,
+          135000000,
+          135000000,
+          135000000,
+          135000000,
+          135000000,
+          135000000,
+          135000000,
+          135000000,
+          135000000
+        ],
+        "synthetic_benefit": [
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000
+        ],
+        "synthetic_poverty": [
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0,
+          0
+        ]
+      }
+    },
+    "new_data__new_model": {
+      "values": {
+        "gov_balance": [
+          118000000,
+          118000000,
+          118000000,
+          118000000,
+          118000000,
+          118000000,
+          118000000,
+          118000000,
+          118000000,
+          118000000
+        ],
+        "synthetic_tax": [
+          148000000,
+          148000000,
+          148000000,
+          148000000,
+          148000000,
+          148000000,
+          148000000,
+          148000000,
+          148000000,
+          148000000
+        ],
+        "synthetic_benefit": [
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000,
+          30000000
+        ],
+        "synthetic_poverty": [
+          1,
+          1,
+          1,
+          1,
+          1,
+          1,
+          1,
+          1,
+          1,
+          1
+        ]
+      }
+    }
+  },
+  "expected_gov_balance_deltas": {
+    "model_on_old_data": 550000000,
+    "model_on_new_data": 715000000,
+    "data_on_old_model": 275000000,
+    "data_on_new_model": 440000000,
+    "total_change": 990000000
+  },
+  "expected_interaction": 165000000
+}
diff --git a/.job/attribution-light-checks.json b/.job/attribution-light-checks.json
new file mode 100644
index 00000000..7a8dfa63
--- /dev/null
+++ b/.job/attribution-light-checks.json
@@ -0,0 +1,135 @@
+{
+  "schema_version": 1,
+  "scope": "Argument handling and output schema on ten invented records per cell only; formatter/linter checks on the attribution script.",
+  "script_sha256": "249f33898a6b07a869b269d55cca75438ccd0a5f1df7589e0e75142195b35eba",
+  "tests_run": [
+    {
+      "command": [
+        "/opt/homebrew/opt/python@3.14/bin/python3.14",
+        ".job/run-attribution.py",
+        "--help"
+      ],
+      "expected_exit": 0,
+      "exit": 0,
+      "passed": true,
+      "stdout": "usage: run-attribution.py [-h] {run,cell,fixture-check,validate} ...\n\nSerial, isolated 2x2 attribution for the Saturday UK national recut. Only\n`fixture-check`, `validate`, and --help are permitted during preparation.\n`run` and its internal `cell` command load national data and belong to the\nreserved heavy phase. This is private certification tooling, not public API.\n\npositional arguments:\n  {run,cell,fixture-check,validate}\n    run                 HEAVY: serial four-cell national attribution\n    cell                ==SUPPRESS==\n    fixture-check       LIGHT: validate tiny synthetic fixture only\n    validate            LIGHT: validate an existing JSON report\n\noptions:\n  -h, --help            show this help message and exit"
+    },
+    {
+      "command": [
+        "/opt/homebrew/opt/python@3.14/bin/python3.14",
+        ".job/run-attribution.py",
+        "run"
+      ],
+      "expected_exit": 2,
+      "exit": 2,
+      "passed": true,
+      "stdout": ""
+    },
+    {
+      "command": [
+        "/opt/homebrew/opt/python@3.14/bin/python3.14",
+        ".job/run-attribution.py",
+        "run",
+        "--old-sha256",
+        "wrong"
+      ],
+      "expected_exit": 2,
+      "exit": 2,
+      "passed": true,
+      "stdout": ""
+    },
+    {
+      "command": [
+        "/opt/homebrew/opt/python@3.14/bin/python3.14",
+        ".job/run-attribution.py",
+        "cell",
+        "--cell-id",
+        "other"
+      ],
+      "expected_exit": 2,
+      "exit": 2,
+      "passed": true,
+      "stdout": ""
+    },
+    {
+      "command": [
+        "/opt/homebrew/opt/python@3.14/bin/python3.14",
+        ".job/run-attribution.py",
+        "fixture-check",
+        "--output",
+        "/private/tmp/budget-a133-attribution-synthetic.json"
+      ],
+      "expected_exit": 0,
+      "exit": 0,
+      "passed": true,
+      "stdout": "Synthetic weighted aggregates, 2x2 edges/interaction, flags, error-cell retention and output schema: passed; no model import or data load."
+    },
+    {
+      "command": [
+        "/opt/homebrew/opt/python@3.14/bin/python3.14",
+        ".job/run-attribution.py",
+        "validate",
+        "/private/tmp/budget-a133-attribution-synthetic.json"
+      ],
+      "expected_exit": 0,
+      "exit": 0,
+      "passed": true,
+      "stdout": ""
+    },
+    {
+      "command": [
+        "/opt/homebrew/opt/python@3.14/bin/python3.14",
+        ".job/run-attribution.py",
+        "validate",
+        "/private/tmp/budget-a133-attribution-synthetic-missing-cell.json"
+      ],
+      "expected_exit": 0,
+      "exit": 0,
+      "passed": true,
+      "stdout": ""
+    },
+    {
+      "command": [
+        "/opt/homebrew/opt/python@3.14/bin/python3.14",
+        ".job/run-attribution.py",
+        "validate",
+        "/private/tmp/budget-a133-attribution-synthetic-invalid.json"
+      ],
+      "expected_exit": "nonzero",
+      "exit": 1,
+      "passed": true,
+      "finding": "Malformed synthetic fiscal-year type rejected by output schema"
+    },
+    {
+      "command": [
+        "ruff",
+        "check",
+        ".job/run-attribution.py"
+      ],
+      "expected_exit": 0,
+      "exit": 0,
+      "passed": true,
+      "stdout": "All checks passed!"
+    },
+    {
+      "command": [
+        "ruff",
+        "format",
+        "--check",
+        ".job/run-attribution.py"
+      ],
+      "expected_exit": 0,
+      "exit": 0,
+      "passed": true,
+      "stdout": "1 file already formatted"
+    }
+  ],
+  "national_data_loads": 0,
+  "simulations_run": 0,
+  "test_suites_run": 0,
+  "snapshot_steps_run": 0,
+  "uc_floor_steps_run": 0,
+  "country_model_imported": false,
+  "all_passed": true,
+  "outputs": "/private/tmp/budget-a133-attribution-synthetic*.json"
+}
diff --git a/.job/attribution-mapping.json b/.job/attribution-mapping.json
new file mode 100644
index 00000000..cba36b0e
--- /dev/null
+++ b/.job/attribution-mapping.json
@@ -0,0 +1,15 @@
+{
+  "schema_version": 1,
+  "instructions": "After reading flagged movements and pinned git histories, add reviewed mappings. Never infer causation from a PR title or changed path alone. Unmapped moves remain explicitly unattributed. A mapping needs metric (fnmatch pattern), component (model or data), prs and/or commits, reason, reviewer; optionally effect (fnmatch pattern) and years (fiscal-year start integers). Both components must be covered to attribute a total_change edge.",
+  "example_only_do_not_copy_as_evidence": {
+    "metric": "tax.some_variable",
+    "component": "model",
+    "prs": [1234],
+    "commits": [],
+    "reason": "Explain the changed formula/input and direction, with reviewed evidence.",
+    "reviewer": "Named reviewer",
+    "effect": "model_*",
+    "years": [2026, 2027, 2028, 2029, 2030]
+  },
+  "mappings": []
+}
diff --git a/.job/attribution.schema.json b/.job/attribution.schema.json
new file mode 100644
index 00000000..e7763633
--- /dev/null
+++ b/.job/attribution.schema.json
@@ -0,0 +1,67 @@
+{
+  "$schema": "https://json-schema.org/draft/2020-12/schema",
+  "$id": "urn:policyengine:uk-a133f37a-attribution:1",
+  "title": "Same-line national UK 2x2 attribution receipt",
+  "type": "object",
+  "required": ["schema_version", "purpose", "synthetic_fixture", "years", "models", "cuts", "definitions", "history", "cells", "effects", "interactions", "unavailable_effects", "unattributed", "limitations", "scorecard_replay", "summary"],
+  "properties": {
+    "schema_version": {"const": 1},
+    "purpose": {"type": "string"},
+    "synthetic_fixture": {"type": "boolean"},
+    "years": {"const": [2026, 2027, 2028, 2029, 2030]},
+    "models": {"const": {"old": "2.132.6", "new": "2.147.2"}},
+    "cuts": {"type": "object", "required": ["old", "new"]},
+    "definitions": {"type": "object", "required": ["flags", "heads", "poverty"]},
+    "history": {"type": "object", "required": ["model", "data"]},
+    "cells": {
+      "type": "object",
+      "required": ["old_data__old_model", "old_data__new_model", "new_data__old_model", "new_data__new_model"],
+      "additionalProperties": {"$ref": "#/$defs/cell"},
+      "minProperties": 4,
+      "maxProperties": 4
+    },
+    "effects": {"type": "array", "items": {"$ref": "#/$defs/effect"}},
+    "unattributed": {"type": "array", "items": {"$ref": "#/$defs/effect"}},
+    "unavailable_effects": {"type": "array"},
+    "interactions": {"type": "array", "items": {"type": "object", "required": ["metric_id", "year", "value", "definition"], "properties": {"value": {"type": ["number", "null"]}}}},
+    "limitations": {"type": "array", "items": {"type": "string"}, "minItems": 1},
+    "scorecard_replay": {"type": "object", "required": ["status", "reason"], "properties": {"status": {"const": "unavailable"}}},
+    "summary": {"type": "object", "required": ["cells_computable", "flagged_moves", "unattributed_moves"]}
+  },
+  "$defs": {
+    "cell": {
+      "type": "object",
+      "required": ["id", "status", "metrics", "error"],
+      "properties": {
+        "id": {"type": "string"},
+        "status": {"enum": ["computable", "not_computable"]},
+        "metrics": {"type": "array", "items": {"$ref": "#/$defs/metric"}},
+        "error": {"type": ["object", "null"]}
+      }
+    },
+    "metric": {
+      "type": "object", "required": ["id", "variable", "kind", "year", "value", "status", "error"],
+      "properties": {
+        "id": {"type": "string"}, "variable": {"type": "string"},
+        "kind": {"enum": ["gbp", "count", "rate"]},
+        "year": {"enum": [2026, 2027, 2028, 2029, 2030]},
+        "value": {"type": ["number", "null"]},
+        "status": {"enum": ["ok", "suppressed", "not_computable"]},
+        "error": {"type": ["object", "null"]}
+      }
+    },
+    "effect": {
+      "type": "object", "required": ["metric_id", "year", "effect", "status", "base_cell", "head_cell", "base", "head", "delta", "threshold", "flagged", "reasons"],
+      "properties": {
+        "metric_id": {"type": "string"},
+        "year": {"enum": [2026, 2027, 2028, 2029, 2030]},
+        "effect": {"enum": ["model_on_old_data", "model_on_new_data", "data_on_old_model", "data_on_new_model", "total_change"]},
+        "status": {"enum": ["computable", "not_computable"]},
+        "base": {"type": ["number", "null"]}, "head": {"type": ["number", "null"]},
+        "delta": {"type": ["number", "null"]}, "threshold": {"type": ["number", "null"]},
+        "flagged": {"type": "boolean"}, "reasons": {"type": "array", "items": {"type": "string"}},
+        "attribution": {"type": "object", "required": ["status", "reviewed_mappings", "candidate_causes", "unmapped_components"], "properties": {"status": {"enum": ["attributed", "unattributed"]}}}
+      }
+    }
+  }
+}
diff --git a/.job/run-attribution.py b/.job/run-attribution.py
new file mode 100644
index 00000000..2cbc3ceb
--- /dev/null
+++ b/.job/run-attribution.py
@@ -0,0 +1,965 @@
+#!/usr/bin/env python3
+"""Serial, isolated 2x2 attribution for the Saturday UK national recut.
+
+Only `fixture-check`, `validate`, and --help are permitted during preparation.
+`run` and its internal `cell` command load national data and belong to the
+reserved heavy phase. This is private certification tooling, not public API.
+"""
+
+from __future__ import annotations
+
+import argparse
+import fnmatch
+import gc
+import hashlib
+import importlib.metadata
+import json
+import math
+import os
+import re
+import shutil
+import subprocess
+import sys
+import time
+import traceback
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parent
+VERSIONS = {"old": "2.132.6", "new": "2.147.2"}
+CUTS = {
+    "old": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71",
+    "new": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a",
+}
+YEARS = tuple(range(2026, 2031))
+CELL_IDS = tuple(
+    f"{data}_data__{model}_model" for data in VERSIONS for model in VERSIONS
+)
+EDGES = {
+    "model_on_old_data": ("old_data__old_model", "old_data__new_model"),
+    "model_on_new_data": ("new_data__old_model", "new_data__new_model"),
+    "data_on_old_model": ("old_data__old_model", "new_data__old_model"),
+    "data_on_new_model": ("old_data__new_model", "new_data__new_model"),
+    "total_change": ("old_data__old_model", "new_data__new_model"),
+}
+LIMITATIONS = [
+    "Same producer line, Budget week only; pinned model versions at each cut's build version.",
+    "Fiscal-year integer 2026 means FY2026-27; each model applies its own projection rules to fixed input bytes.",
+    "A model edge includes the pinned model's required dependency environment and projection changes; it is not a formula-only experiment.",
+    "The two conditional model effects and two conditional data effects can differ. The interaction is reported, not silently assigned to either cause.",
+    "Missing or uncomputable cross cells do not invalidate computable edges; they prevent the affected decomposition and interaction from being identified.",
+    "A missing variable is not treated as zero. Errors, missing variables and privacy suppressions have null values and no computed delta.",
+    "Heads are a union across model versions and may overlap. Government balance is measured directly; summing all reported heads is invalid.",
+    "Money totals are conservatively suppressed together when any nonzero head has fewer than ten positive-weight cases, preventing complementary disclosure through balance or other heads.",
+    "Poverty counts and rates use person weights and HBAI groups; cells below ten positive-weight observations or ten poor/nonpoor cases are suppressed together.",
+    "Source-path overlap generates review candidates, not proven causal attribution. Only reviewed mappings with an explanation count as attributed; every other flagged move is explicitly unattributed.",
+    "A reviewed mapping associates a move with changes; no PR-specific counterfactual is run and no numerical share is assigned to an individual PR.",
+    "Scorecard replay is unavailable until the Microcosm replay fix lands; this attribution is not a scorecard replay.",
+]
+
+
+def read_json(path):
+    return json.loads(Path(path).read_text())
+
+
+def write_json(path, value):
+    target = Path(path)
+    target.parent.mkdir(parents=True, exist_ok=True)
+    target.write_text(
+        json.dumps(value, indent=2, sort_keys=True, allow_nan=False) + "\n"
+    )
+
+
+def digest(path):
+    sha = hashlib.sha256()
+    with Path(path).open("rb") as handle:
+        for chunk in iter(lambda: handle.read(8 * 1024 * 1024), b""):
+            sha.update(chunk)
+    return sha.hexdigest()
+
+
+def git(repo, *arguments, strip=True):
+    # Caller can have an isolated policyengine.py GIT_DIR; never let it route
+    # reads intended for these source repositories into that Git directory.
+    env = {k: v for k, v in os.environ.items() if not k.startswith("GIT_")}
+    value = subprocess.check_output(
+        ["git", "-C", str(repo), *arguments], text=True, env=env
+    )
+    return value.strip() if strip else value
+
+
+def histories(model_repo, microcosm_repo, old_build, new_build):
+    out = {}
+    for name, repo, start, end in (
+        ("model", model_repo, VERSIONS["old"], VERSIONS["new"]),
+        ("data", microcosm_repo, old_build, new_build),
+    ):
+        start = git(repo, "rev-parse", f"{start}^{{commit}}")
+        end = git(repo, "rev-parse", f"{end}^{{commit}}")
+        # An inverted/nonancestor range would silently omit changes.
+        git(repo, "merge-base", "--is-ancestor", start, end)
+        lines = git(
+            repo,
+            "log",
+            "--first-parent",
+            "--reverse",
+            "--format=%H%x09%s",
+            f"{start}..{end}",
+        )
+        changes = []
+        for line in lines.splitlines():
+            commit, subject = line.split("\t", 1)
+            prs = sorted(set(int(n) for n in re.findall(r"#(\d+)", subject)))
+            paths = git(
+                repo,
+                "diff-tree",
+                "--no-commit-id",
+                "--name-only",
+                "-r",
+                f"{commit}^",
+                commit,
+            ).splitlines()
+            changes.append(
+                {"commit": commit, "subject": subject, "prs": prs, "paths": paths}
+            )
+        out[name] = {
+            "from": start,
+            "to": end,
+            "range": f"{start}..{end}",
+            "changes": changes,
+        }
+    return out
+
+
+def check_definitions(definitions, model_repo):
+    for item in definitions["source_files"]:
+        raw = git(model_repo, "show", f"{item['ref']}:{item['path']}", strip=False)
+        if hashlib.sha256(raw.encode()).hexdigest() != item["sha256"]:
+            raise ValueError(
+                f"Pinned attribution definition differs: {item['ref']}:{item['path']}"
+            )
+
+
+def candidate_binding(candidate, expected_sha):
+    candidate = candidate.expanduser().resolve()
+    command = ["git", "-C", str(candidate)]
+    if candidate == ROOT.parent and (ROOT / "git").is_dir():
+        command = ["git", "--git-dir", str(ROOT / "git"), "--work-tree", str(candidate)]
+    env = {k: v for k, v in os.environ.items() if not k.startswith("GIT_")}
+    commit = subprocess.check_output(
+        [*command, "rev-parse", "HEAD"], text=True, env=env
+    ).strip()
+    path = candidate / "src/policyengine/data/bundle/manifest.json"
+    manifest = read_json(path)
+    release = manifest["data_releases"]["uk"]
+    if manifest["packages"]["policyengine-uk"]["version"] != VERSIONS["new"]:
+        raise ValueError("Candidate does not pin policyengine-uk 2.147.2")
+    if manifest["packages"]["policyengine-core"]["version"] != "3.33.3":
+        raise ValueError("Candidate does not pin policyengine-core 3.33.3")
+    if release["build_id"] != CUTS["new"] or release["version"] != CUTS["new"]:
+        raise ValueError("Candidate does not bind the Saturday national cut")
+    if release["certified_data_artifact"]["sha256"] != expected_sha:
+        raise ValueError("Candidate does not bind the verified new national file")
+    if release.get("regional_source_manifest_uri"):
+        raise ValueError("Candidate must be national-only")
+    return {
+        "candidate_commit": commit,
+        "bundle_manifest_sha256": digest(path),
+        "bundle_uk_build_id": release["build_id"],
+    }
+
+
+def metrics(definitions):
+    result = [
+        {
+            "id": "gov_balance",
+            "variable": "gov_balance",
+            "kind": "gbp",
+            "label": "Government balance",
+        }
+    ]
+    result += [
+        {
+            "id": f"{h['kind']}.{h['variable']}",
+            "variable": h["variable"],
+            "kind": "gbp",
+            "label": h["label"],
+        }
+        for h in definitions["heads"]
+    ]
+    for measure in definitions["poverty"]["measures"]:
+        for group in definitions["poverty"]["groups"]:
+            for kind in ("count", "rate"):
+                result.append(
+                    {
+                        "id": f"poverty.{measure['id']}.{group['id']}.{kind}",
+                        "variable": measure["variable"],
+                        "group_variable": group.get("variable"),
+                        "kind": kind,
+                        "label": f"{measure['label']}, {group['label']}, {kind}",
+                    }
+                )
+    return result
+
+
+def safe_error(error):
+    # Country errors can include values of invalid input arrays. Preserve the
+    # error type and traceback location, but never publish unit-record values.
+    locations = [
+        f"{Path(frame.filename).name}:{frame.lineno}:{frame.name}"
+        for frame in traceback.extract_tb(error.__traceback__)
+    ]
+    return {
+        "type": type(error).__name__,
+        "message": "Country API raised an error; see restricted worker stderr for the original message.",
+        "locations": locations,
+    }
+
+
+def worker(args):
+    started = time.monotonic()
+    output = {
+        "id": args.cell_id,
+        "model_version": args.expected_version,
+        "data_cut": args.cut,
+        "data_sha256": args.sha256,
+        "status": "not_computable",
+        "error": None,
+        "metrics": [],
+    }
+    try:
+        installed = importlib.metadata.version("policyengine-uk")
+        if installed != args.expected_version:
+            raise RuntimeError(
+                f"Expected policyengine-uk {args.expected_version}, found {installed}"
+            )
+        if importlib.metadata.version("policyengine-core") != "3.33.3":
+            raise RuntimeError(
+                "The attribution requires policyengine-core 3.33.3 in both environments"
+            )
+        definitions = read_json(args.definitions)
+        output["runtime"] = {
+            "python": sys.version,
+            "executable": sys.executable,
+            "prefix": sys.prefix,
+            "policyengine_uk": installed,
+            "policyengine_core": importlib.metadata.version("policyengine-core"),
+            "numpy": importlib.metadata.version("numpy"),
+            "distributions": sorted(
+                f"{d.metadata['Name']}=={d.version}"
+                for d in importlib.metadata.distributions()
+                if d.metadata["Name"]
+            ),
+        }
+        import numpy as np
+        from policyengine_uk import Microsimulation
+
+        # Country-model API performs schema interpretation and uprating; no
+        # direct policyengine-core builders or ad hoc column dropping.
+        simulation = Microsimulation(dataset=str(args.data.resolve()))
+        output["status"] = "computable"
+        for year in YEARS:
+            weights = {}
+            monetary = []
+            for metric in metrics(definitions):
+                record = dict(
+                    metric,
+                    year=year,
+                    fiscal_year=f"{year}-{str(year + 1)[2:]}",
+                    value=None,
+                    status="not_computable",
+                    error=None,
+                )
+                try:
+                    variables = simulation.tax_benefit_system.variables
+                    record["required_for_diagonal"] = (
+                        metric["kind"] != "gbp"
+                        or metric["id"] == "gov_balance"
+                        or metric["variable"] in variables
+                    )
+                    if metric["variable"] not in variables:
+                        record["error"] = {
+                            "type": "MissingVariable",
+                            "message": f"{metric['variable']} is absent from policyengine-uk {installed}; it is not imputed as zero.",
+                        }
+                        output["metrics"].append(record)
+                        continue
+                    entity = variables[metric["variable"]].entity.key
+                    if entity not in weights:
+                        weights[entity] = np.asarray(
+                            simulation.calculate(
+                                f"{entity}_weight", year, unweighted=True
+                            ),
+                            dtype=np.float64,
+                        )
+                    weight = weights[entity]
+                    values = np.asarray(
+                        simulation.calculate(metric["variable"], year, unweighted=True),
+                        dtype=np.float64,
+                    )
+                    if (
+                        not np.all(np.isfinite(values))
+                        or not np.all(np.isfinite(weight))
+                        or np.any(weight < 0)
+                    ):
+                        raise ValueError("Nonfinite values or invalid weights")
+                    if metric["kind"] == "gbp":
+                        cases = int(np.count_nonzero((values != 0) & (weight > 0)))
+                        record.update(
+                            value=float(np.sum(values * weight, dtype=np.float64)),
+                            status="ok",
+                        )
+                        if 0 < cases < 10:
+                            record.update(value=None, status="suppressed")
+                        monetary.append(record)
+                    else:
+                        # Poverty variables are household booleans; mapping
+                        # directly to people repeats each person's status.
+                        values = np.asarray(
+                            simulation.calculate(
+                                metric["variable"],
+                                year,
+                                map_to="person",
+                                unweighted=True,
+                            ),
+                            dtype=np.float64,
+                        )
+                        if not np.all(np.isfinite(values)):
+                            raise ValueError("Nonfinite person poverty values")
+                        values = values.astype(bool)
+                        if "person" not in weights:
+                            weights["person"] = np.asarray(
+                                simulation.calculate(
+                                    "person_weight", year, unweighted=True
+                                ),
+                                dtype=np.float64,
+                            )
+                        weight = weights["person"]
+                        if not np.all(np.isfinite(weight)) or np.any(weight < 0):
+                            raise ValueError("Invalid person weights")
+                        group = np.ones_like(values, dtype=bool)
+                        if metric.get("group_variable"):
+                            group = np.asarray(
+                                simulation.calculate(
+                                    metric["group_variable"],
+                                    year,
+                                    map_to="person",
+                                    unweighted=True,
+                                ),
+                                dtype=bool,
+                            )
+                        observed = group & (weight > 0)
+                        cases, poor_cases, nonpoor_cases = (
+                            int(np.count_nonzero(mask))
+                            for mask in (
+                                observed,
+                                observed & values,
+                                observed & ~values,
+                            )
+                        )
+                        denominator = float(np.sum(weight[group], dtype=np.float64))
+                        headcount = float(
+                            np.sum(weight[group & values], dtype=np.float64)
+                        )
+                        if cases < 10 or 0 < poor_cases < 10 or 0 < nonpoor_cases < 10:
+                            record["status"] = "suppressed"
+                        elif denominator == 0:
+                            record["error"] = {
+                                "type": "NoPopulation",
+                                "message": "No positive weighted population in this group.",
+                            }
+                        else:
+                            record.update(
+                                value=headcount
+                                if metric["kind"] == "count"
+                                else headcount / denominator,
+                                status="ok",
+                            )
+                except Exception as error:
+                    record["error"] = safe_error(error)
+                    traceback.print_exc(file=sys.stderr)
+                output["metrics"].append(record)
+            # A whole-year monetary envelope is deliberately conservative:
+            # no residual of published heads can reveal a hidden head.
+            if any(record["status"] == "suppressed" for record in monetary):
+                for record in monetary:
+                    if record["status"] == "ok":
+                        record.update(
+                            value=None,
+                            status="suppressed",
+                            suppression_reason="complementary protection: a monetary head has fewer than ten cases",
+                        )
+            reset = getattr(simulation, "_invalidate_all_caches", None)
+            if reset is not None:
+                # The aggregate-impact tool uses this country API hook to
+                # discard calculated arrays while preserving supplied data.
+                reset()
+                gc.collect()
+    except Exception as error:
+        output["status"] = "not_computable"
+        output["error"] = safe_error(error)
+        traceback.print_exc(file=sys.stderr)
+    output["seconds"] = round(time.monotonic() - started, 3)
+    write_json(args.output, output)
+    return 0
+
+
+def edge_value(cells, cell_id, metric, year):
+    cell = cells[cell_id]
+    if cell["status"] != "computable":
+        return None, f"{cell_id}: not computable"
+    for record in cell["metrics"]:
+        if record["id"] == metric["id"] and record["year"] == year:
+            if record["status"] == "ok":
+                return record["value"], None
+            return None, f"{cell_id}: {record['status']}"
+    return None, f"{cell_id}: metric not available"
+
+
+def attribution(move, history, mappings):
+    component = (
+        "model"
+        if move["effect"].startswith("model_")
+        else "data"
+        if move["effect"].startswith("data_")
+        else None
+    )
+    allowed = [component] if component else ["model", "data"]
+    reviewed = []
+    candidates = []
+    for kind in allowed:
+        changes = history[kind]["changes"]
+        for change in changes:
+            variable = move["variable"].lower()
+            matching_paths = [
+                path
+                for path in change["paths"]
+                if Path(path).stem.lower() == variable
+                or f"/{variable}/" in path.lower()
+            ]
+            if matching_paths:
+                candidates.append(
+                    {
+                        "component": kind,
+                        "commit": change["commit"],
+                        "prs": change["prs"],
+                        "paths": matching_paths,
+                        "basis": "source-path overlap; requires human causal review",
+                    }
+                )
+        for mapping in mappings:
+            if mapping["component"] != kind or not fnmatch.fnmatchcase(
+                move["metric_id"], mapping["metric"]
+            ):
+                continue
+            if not fnmatch.fnmatchcase(move["effect"], mapping.get("effect", "*")):
+                continue
+            if mapping.get("years") and move["year"] not in mapping["years"]:
+                continue
+            selected = [
+                c
+                for c in changes
+                if any(c["commit"].startswith(s) for s in mapping.get("commits", []))
+                or set(c["prs"]) & set(mapping.get("prs", []))
+            ]
+            if not selected:
+                raise ValueError(
+                    f"Reviewed mapping references no change in the pinned {kind} range: {mapping}"
+                )
+            if (
+                not mapping.get("reason", "").strip()
+                or not mapping.get("reviewer", "").strip()
+            ):
+                raise ValueError("A reviewed mapping needs reason and reviewer")
+            reviewed.append(
+                {
+                    "component": kind,
+                    "reason": mapping["reason"],
+                    "reviewer": mapping["reviewer"],
+                    "changes": selected,
+                }
+            )
+    uncovered = sorted(set(allowed) - {item["component"] for item in reviewed})
+    return {
+        "status": "unattributed" if uncovered else "attributed",
+        "unmapped_components": uncovered,
+        "reviewed_mappings": reviewed,
+        "candidate_causes": candidates,
+    }
+
+
+def build_report(cells, definitions, history, mappings, fixture=False):
+    moves, missing = [], []
+    for metric in metrics(definitions):
+        for year in YEARS:
+            for effect, (start, end) in EDGES.items():
+                before, error_before = edge_value(cells, start, metric, year)
+                after, error_after = edge_value(cells, end, metric, year)
+                move = {
+                    "metric_id": metric["id"],
+                    "variable": metric["variable"],
+                    "kind": metric["kind"],
+                    "year": year,
+                    "effect": effect,
+                    "base_cell": start,
+                    "head_cell": end,
+                    "base": before,
+                    "head": after,
+                    "delta": None,
+                    "threshold": None,
+                    "flagged": False,
+                    "status": "not_computable",
+                    "reasons": [e for e in (error_before, error_after) if e],
+                }
+                if not move["reasons"]:
+                    threshold = definitions["flags"][metric["kind"]]
+                    bound = max(
+                        threshold["absolute"],
+                        threshold.get("relative", 0) * abs(before),
+                    )
+                    move.update(
+                        delta=after - before,
+                        threshold=bound,
+                        flagged=abs(after - before) > bound,
+                        status="computable",
+                    )
+                    if move["flagged"]:
+                        move["attribution"] = attribution(move, history, mappings)
+                else:
+                    missing.append(
+                        {
+                            "metric_id": metric["id"],
+                            "year": year,
+                            "effect": effect,
+                            "reasons": move["reasons"],
+                        }
+                    )
+                moves.append(move)
+    interactions = []
+    for metric in metrics(definitions):
+        for year in YEARS:
+            relevant = {
+                m["effect"]: m
+                for m in moves
+                if m["metric_id"] == metric["id"] and m["year"] == year
+            }
+            before, after = (
+                relevant["model_on_old_data"]["delta"],
+                relevant["model_on_new_data"]["delta"],
+            )
+            interactions.append(
+                {
+                    "metric_id": metric["id"],
+                    "year": year,
+                    "value": after - before
+                    if before is not None and after is not None
+                    else None,
+                    "definition": "model_on_new_data - model_on_old_data = data_on_new_model - data_on_old_model",
+                }
+            )
+    flags = [move for move in moves if move["flagged"]]
+    return {
+        "schema_version": 1,
+        "purpose": "same-line UK national 2x2 attribution",
+        "synthetic_fixture": fixture,
+        "years": list(YEARS),
+        "models": VERSIONS,
+        "cuts": CUTS,
+        "definitions": definitions,
+        "history": history,
+        "cells": cells,
+        "effects": moves,
+        "interactions": interactions,
+        "unavailable_effects": missing,
+        "unattributed": [
+            m for m in flags if m["attribution"]["status"] == "unattributed"
+        ],
+        "limitations": LIMITATIONS,
+        "scorecard_replay": {
+            "status": "unavailable",
+            "reason": "Awaiting Microcosm replay fix; rerun separately when it lands.",
+        },
+        "summary": {
+            "cells_computable": sum(
+                c["status"] == "computable" for c in cells.values()
+            ),
+            "flagged_moves": len(flags),
+            "unattributed_moves": sum(
+                m["attribution"]["status"] == "unattributed" for m in flags
+            ),
+        },
+    }
+
+
+def validate(report):
+    schema = read_json(ROOT / "attribution.schema.json")
+    validate_schema(report, schema, schema)
+    if report["schema_version"] != 1 or report["years"] != list(YEARS):
+        raise ValueError("Unexpected report schema or fiscal-year horizon")
+    if set(report["cells"]) != set(CELL_IDS):
+        raise ValueError("Report must retain all four cells, including errors")
+    for cell_id, cell in report["cells"].items():
+        if cell["id"] != cell_id or cell["status"] not in (
+            "computable",
+            "not_computable",
+        ):
+            raise ValueError(f"Malformed cell: {cell_id}")
+        if cell["status"] == "not_computable" and not cell.get("error"):
+            raise ValueError(f"Uncomputable cell must preserve its error: {cell_id}")
+        expected_metrics = {
+            (metric["id"], year)
+            for metric in metrics(report["definitions"])
+            for year in YEARS
+        }
+        actual_metrics = [(item["id"], item["year"]) for item in cell["metrics"]]
+        if cell["status"] == "computable" and (
+            len(actual_metrics) != len(set(actual_metrics))
+            or set(actual_metrics) != expected_metrics
+        ):
+            raise ValueError(
+                f"Computable cell must retain every metric/year exactly once: {cell_id}"
+            )
+        for item in cell["metrics"]:
+            if item["status"] == "ok" and (
+                not isinstance(item["value"], (int, float))
+                or not math.isfinite(item["value"])
+            ):
+                raise ValueError("OK metric must have a finite value")
+            if item["status"] != "ok" and item["value"] is not None:
+                raise ValueError("Unavailable metric must have a null value")
+    for move in report["effects"]:
+        if move["effect"] not in EDGES or move["year"] not in YEARS:
+            raise ValueError("Unknown effect or fiscal year")
+        if move["status"] == "computable" and not math.isfinite(move["delta"]):
+            raise ValueError("Computable edge must have a finite delta")
+        if move["status"] != "computable" and move["delta"] is not None:
+            raise ValueError("Unavailable edge must have a null delta")
+        if move["flagged"] and move.get("attribution", {}).get("status") not in (
+            "attributed",
+            "unattributed",
+        ):
+            raise ValueError("Every flagged edge needs an attribution verdict")
+    expected = [
+        m
+        for m in report["effects"]
+        if m["flagged"] and m["attribution"]["status"] == "unattributed"
+    ]
+    if report["unattributed"] != expected:
+        raise ValueError("Unattributed index does not cover every unmapped flag")
+    if report["scorecard_replay"]["status"] != "unavailable":
+        raise ValueError("Replay remains unavailable under this exception")
+    # JSON serialization rejects NaN/Infinity anywhere, including raw cells.
+    json.dumps(report, allow_nan=False)
+
+
+def validate_schema(value, schema, root, path="$"):
+    """Validate the schema features used by this receipt without dependencies.
+
+    The accompanying schema is also consumable by a full Draft 2020-12
+    validator. This intentionally small validator is for this fixed schema.
+    """
+    if "$ref" in schema:
+        target = root
+        for part in schema["$ref"].removeprefix("#/").split("/"):
+            target = target[part]
+        return validate_schema(value, target, root, path)
+    if "const" in schema and value != schema["const"]:
+        raise ValueError(f"{path}: value differs from schema constant")
+    if "enum" in schema and value not in schema["enum"]:
+        raise ValueError(f"{path}: value outside schema enumeration")
+    kinds = schema.get("type", [])
+    kinds = [kinds] if isinstance(kinds, str) else kinds
+    matches = {
+        "object": isinstance(value, dict),
+        "array": isinstance(value, list),
+        "string": isinstance(value, str),
+        "boolean": isinstance(value, bool),
+        "number": isinstance(value, (int, float)) and not isinstance(value, bool),
+        "null": value is None,
+    }
+    if kinds and not any(matches[kind] for kind in kinds):
+        raise ValueError(f"{path}: incorrect schema type")
+    if isinstance(value, dict):
+        if any(key not in value for key in schema.get("required", [])):
+            raise ValueError(f"{path}: missing required schema property")
+        if len(value) < schema.get("minProperties", 0) or len(value) > schema.get(
+            "maxProperties", math.inf
+        ):
+            raise ValueError(f"{path}: wrong number of schema properties")
+        properties = schema.get("properties", {})
+        for key, child in value.items():
+            selected = properties.get(key, schema.get("additionalProperties", True))
+            if selected is False:
+                raise ValueError(f"{path}.{key}: unexpected property")
+            if isinstance(selected, dict):
+                validate_schema(child, selected, root, f"{path}.{key}")
+    if isinstance(value, list):
+        if len(value) < schema.get("minItems", 0):
+            raise ValueError(f"{path}: too few schema items")
+        for index, child in enumerate(value):
+            validate_schema(child, schema.get("items", {}), root, f"{path}[{index}]")
+
+
+def run(args):
+    definitions = read_json(args.definitions)
+    check_definitions(definitions, args.model_repo)
+    history = histories(
+        args.model_repo, args.microcosm_repo, args.old_build, args.new_build
+    )
+    mappings = read_json(args.mapping)["mappings"] if args.mapping else []
+    binding = (
+        candidate_binding(args.candidate, args.new_sha256) if args.candidate else None
+    )
+    output = args.output.resolve()
+    output.parent.mkdir(parents=True, exist_ok=True)
+    if any(
+        (parent / ".git").exists() for parent in (output.parent, *output.parent.parents)
+    ):
+        raise ValueError("Attribution evidence and restricted logs must be outside Git")
+    cells = {}
+    for data in VERSIONS:
+        data_path = getattr(args, f"{data}_data").resolve()
+        expected = getattr(args, f"{data}_sha256")
+        if digest(data_path) != expected:
+            raise ValueError(
+                f"{data} national file SHA-256 differs from the pinned manifest"
+            )
+        for model in VERSIONS:
+            subprocess.run(
+                [
+                    sys.executable,
+                    str(args.reservation_script.resolve()),
+                    "allowed",
+                    "uk-hub",
+                ],
+                check=True,
+            )
+            load = os.getloadavg()[0]
+            free = shutil.disk_usage(output.parent).free
+            if load > 110 or free < 55_000_000_000:
+                raise RuntimeError(
+                    f"Cell resource guard refused: load1={load:.2f}, free_bytes={free}"
+                )
+            cell_id = f"{data}_data__{model}_model"
+            cell_output = output.parent / f"attribution-{cell_id}.json"
+            log_path = output.parent / f"attribution-{cell_id}.stderr.log"
+            # Preserve the virtualenv interpreter symlink: resolving it to
+            # the base Python would discard that environment's model pin.
+            interpreter = getattr(args, f"{model}_python").expanduser().absolute()
+            command = [
+                str(interpreter),
+                str(Path(__file__).resolve()),
+                "cell",
+                "--cell-id",
+                cell_id,
+                "--data",
+                str(data_path),
+                "--sha256",
+                expected,
+                "--cut",
+                CUTS[data],
+                "--expected-version",
+                VERSIONS[model],
+                "--definitions",
+                str(args.definitions.resolve()),
+                "--output",
+                str(cell_output),
+            ]
+            env = os.environ.copy()
+            env.pop("PYTHONPATH", None)
+            env["PYTHONNOUSERSITE"] = "1"
+            # These logs can contain model error input values: evidence stays
+            # outside Git, mode 0600, and only redacted errors enter report.
+            fd = os.open(log_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
+            with os.fdopen(fd, "w") as log:
+                process = subprocess.run(
+                    command, stdout=log, stderr=log, env=env, cwd=output.parent
+                )
+            if process.returncode or not cell_output.exists():
+                cells[cell_id] = {
+                    "id": cell_id,
+                    "status": "not_computable",
+                    "model_version": VERSIONS[model],
+                    "data_cut": CUTS[data],
+                    "data_sha256": expected,
+                    "metrics": [],
+                    "error": {
+                        "type": "WorkerExit",
+                        "message": f"Worker exited {process.returncode}; restricted log: {log_path.name}",
+                    },
+                }
+            else:
+                cells[cell_id] = read_json(cell_output)
+            cells[cell_id]["restricted_error_log"] = log_path.name
+            print(f"{cell_id}: {cells[cell_id]['status']}", flush=True)
+    report = build_report(cells, definitions, history, mappings)
+    if args.candidate:
+        if candidate_binding(args.candidate, args.new_sha256) != binding:
+            raise ValueError(
+                "Candidate commit or bundled manifest changed during attribution"
+            )
+        report["binding"] = binding
+    validate(report)
+    write_json(output, report)
+    print(json.dumps(report["summary"], sort_keys=True))
+    # Missing cross cells and unattributed moves are expected review states,
+    # not invented passes. Failed diagonal cells block the evidence package.
+    diagonals = [cells[c] for c in ("old_data__old_model", "new_data__new_model")]
+    return (
+        0
+        if all(
+            cell["status"] == "computable"
+            and not any(
+                item["status"] == "not_computable"
+                and item.get("required_for_diagonal", True)
+                for item in cell["metrics"]
+            )
+            for cell in diagonals
+        )
+        else 1
+    )
+
+
+def fixture_check(args):
+    fixture = read_json(ROOT / "attribution-fixture.json")
+    definitions = fixture["definitions"]
+    cells = {}
+    for cell_id, item in fixture["cells"].items():
+        records = []
+        for year in YEARS:
+            for metric in metrics(definitions):
+                amount = sum(
+                    v * w
+                    for v, w in zip(
+                        item["values"][metric["variable"]],
+                        fixture["weights"],
+                        strict=True,
+                    )
+                )
+                if metric["kind"] == "rate":
+                    amount /= sum(fixture["weights"])
+                records.append(
+                    dict(metric, year=year, value=amount, status="ok", error=None)
+                )
+        cells[cell_id] = {
+            "id": cell_id,
+            "status": "computable",
+            "model_version": VERSIONS[cell_id.split("__")[1].split("_")[0]],
+            "metrics": records,
+            "error": None,
+        }
+    history = {"model": {"changes": []}, "data": {"changes": []}}
+    report = build_report(cells, definitions, history, [], fixture=True)
+    validate(report)
+
+    def find(effect):
+        return next(
+            m
+            for m in report["effects"]
+            if m["metric_id"] == "gov_balance"
+            and m["year"] == 2026
+            and m["effect"] == effect
+        )
+
+    for effect, expected in fixture["expected_gov_balance_deltas"].items():
+        if find(effect)["delta"] != expected:
+            raise ValueError(f"Synthetic {effect} has wrong weighted delta")
+    if not report["unattributed"]:
+        raise ValueError("Synthetic flags must remain visibly unattributed")
+    if (
+        next(
+            i["value"]
+            for i in report["interactions"]
+            if i["metric_id"] == "gov_balance"
+        )
+        != fixture["expected_interaction"]
+    ):
+        raise ValueError("Synthetic 2x2 interaction is wrong")
+    write_json(args.output, report)
+    cells["new_data__old_model"] = {
+        "id": "new_data__old_model",
+        "status": "not_computable",
+        "metrics": [],
+        "error": {
+            "type": "SyntheticSchemaError",
+            "message": "Synthetic incompatible cut/model cell",
+        },
+    }
+    partial = build_report(cells, definitions, history, [], fixture=True)
+    validate(partial)
+    if (
+        next(
+            m
+            for m in partial["effects"]
+            if m["metric_id"] == "gov_balance"
+            and m["year"] == 2026
+            and m["effect"] == "model_on_old_data"
+        )["delta"]
+        != fixture["expected_gov_balance_deltas"]["model_on_old_data"]
+    ):
+        raise ValueError("Failed cross cell must preserve independent edges")
+    if any(i["value"] is not None for i in partial["interactions"]):
+        raise ValueError("Missing cross cell must leave interaction unidentified")
+    write_json(args.output.with_name(args.output.stem + "-missing-cell.json"), partial)
+    print(
+        "Synthetic weighted aggregates, 2x2 edges/interaction, flags, error-cell retention and output schema: passed; no model import or data load."
+    )
+    return 0
+
+
+def parser():
+    result = argparse.ArgumentParser(description=__doc__)
+    sub = result.add_subparsers(dest="command", required=True)
+    heavy = sub.add_parser("run", help="HEAVY: serial four-cell national attribution")
+    for side in VERSIONS:
+        heavy.add_argument(f"--{side}-python", type=Path, required=True)
+        heavy.add_argument(f"--{side}-data", type=Path, required=True)
+        heavy.add_argument(f"--{side}-sha256", required=True, type=sha256_argument)
+    heavy.add_argument("--model-repo", type=Path, required=True)
+    heavy.add_argument("--microcosm-repo", type=Path, required=True)
+    heavy.add_argument("--reservation-script", type=Path, required=True)
+    heavy.add_argument(
+        "--candidate",
+        type=Path,
+        help="Bind evidence to this final certified candidate commit and bundle manifest",
+    )
+    heavy.add_argument("--old-build", required=True)
+    heavy.add_argument("--new-build", default="829c1bc26")
+    heavy.add_argument(
+        "--definitions", type=Path, default=ROOT / "attribution-definitions.json"
+    )
+    heavy.add_argument(
+        "--mapping", type=Path, default=ROOT / "attribution-mapping.json"
+    )
+    heavy.add_argument("--output", type=Path, required=True)
+    heavy.set_defaults(action=run)
+    cell = sub.add_parser("cell", help=argparse.SUPPRESS)
+    cell.add_argument("--cell-id", choices=CELL_IDS, required=True)
+    cell.add_argument("--data", type=Path, required=True)
+    cell.add_argument("--sha256", type=sha256_argument, required=True)
+    cell.add_argument("--cut", choices=list(CUTS.values()), required=True)
+    cell.add_argument(
+        "--expected-version", choices=list(VERSIONS.values()), required=True
+    )
+    cell.add_argument("--definitions", type=Path, required=True)
+    cell.add_argument("--output", type=Path, required=True)
+    cell.set_defaults(action=worker)
+    check = sub.add_parser(
+        "fixture-check", help="LIGHT: validate tiny synthetic fixture only"
+    )
+    check.add_argument("--output", type=Path, required=True)
+    check.set_defaults(action=fixture_check)
+    verify = sub.add_parser("validate", help="LIGHT: validate an existing JSON report")
+    verify.add_argument("report", type=Path)
+    verify.set_defaults(action=lambda args: validate(read_json(args.report)) or 0)
+    return result
+
+
+def sha256_argument(text):
+    if not re.fullmatch(r"[0-9a-f]{64}", text):
+        raise argparse.ArgumentTypeError("Expected lowercase 64-character SHA-256")
+    return text
+
+
+if __name__ == "__main__":
+    arguments = parser().parse_args()
+    sys.exit(arguments.action(arguments))

From bd701bddb095cb3e9615edf6f688b8ca07eab082 Mon Sep 17 00:00:00 2001
From: Max Ghenis 
Date: Fri, 23 Oct 2026 13:28:18 -0400
Subject: [PATCH 4/8] Stage sequential guarded national certification evidence
 runner

---
 .job/certification-evidence.py          | 544 +++++++++++++
 .job/dense-schema.json                  | 986 ++++++++++++++++++++++++
 .job/index-evidence.py                  | 261 +++++++
 .job/metadata/new-release_manifest.json | 115 +++
 .job/metadata/old-release_manifest.json | 115 +++
 .job/pr704-HANDOFF.md                   |  44 ++
 .job/rehearsal-timings.csv              |  73 ++
 run-heavy.sh                            | 203 +++++
 8 files changed, 2341 insertions(+)
 create mode 100644 .job/certification-evidence.py
 create mode 100644 .job/dense-schema.json
 create mode 100755 .job/index-evidence.py
 create mode 100644 .job/metadata/new-release_manifest.json
 create mode 100644 .job/metadata/old-release_manifest.json
 create mode 100644 .job/pr704-HANDOFF.md
 create mode 100644 .job/rehearsal-timings.csv
 create mode 100755 run-heavy.sh

diff --git a/.job/certification-evidence.py b/.job/certification-evidence.py
new file mode 100644
index 00000000..d1b08487
--- /dev/null
+++ b/.job/certification-evidence.py
@@ -0,0 +1,544 @@
+#!/usr/bin/env python3
+"""Guarded, exact-cut evidence steps for the separate heavy-phase job.
+
+Argument parsing and --help use only the standard library. Private artifacts
+stay in an outside-Git Hugging Face cache; producer receipts contain only gate
+identities, statuses and hashes. This script does not grant release acceptance.
+"""
+
+from __future__ import annotations
+
+import argparse
+import hashlib
+import importlib
+import importlib.metadata
+import json
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+from pathlib import Path
+from types import SimpleNamespace
+from typing import Any
+
+ROOT = Path(__file__).resolve().parents[1]
+REPO = "policyengine/populace-uk-private"
+LINE = "microcosm-uk-2024-25-national"
+MODELS = {"old": "2.132.6", "new": "2.147.2"}
+CUTS = {
+    "old": f"{LINE}-20261019T083323Z-6cba5d71",
+    "new": f"{LINE}-20261023T075039Z-a133f37a",
+}
+DATA_SHA = {
+    "old": "810ba980005a8b907cb51464279fa5e0125464b7af9a4871b7f6744c30951ece",
+    "new": "64f83900ca3a1242fb017585234844ce89f743d32e41cd2b6558057777f914aa",
+}
+CORE = "3.33.3"
+
+
+def sha256(path: Path) -> str:
+    digest = hashlib.sha256()
+    with path.open("rb") as stream:
+        for block in iter(lambda: stream.read(1024 * 1024), b""):
+            digest.update(block)
+    return digest.hexdigest()
+
+
+def read_json(path: Path) -> dict[str, Any]:
+    value = json.loads(path.read_text())
+    if not isinstance(value, dict):
+        raise ValueError(f"Expected a JSON object: {path.name}")
+    return value
+
+
+def outside_git(path: Path) -> Path:
+    path = path.expanduser().resolve()
+    if path == ROOT or ROOT in path.parents:
+        raise ValueError("Storage, caches and evidence outputs must be outside Git")
+    if any((parent / ".git").exists() for parent in (path, *path.parents)):
+        raise ValueError(
+            "Storage, caches and evidence outputs must be outside every Git checkout"
+        )
+    return path
+
+
+def write_json(path: Path, value: dict[str, Any]) -> None:
+    path = outside_git(path)
+    path.parent.mkdir(parents=True, exist_ok=True)
+    with tempfile.NamedTemporaryFile("w", dir=path.parent, delete=False) as stream:
+        json.dump(value, stream, indent=2, sort_keys=True, allow_nan=False)
+        stream.write("\n")
+        temporary = Path(stream.name)
+    temporary.replace(path)
+
+
+def guard(storage: Path) -> None:
+    """Refuse each expensive action unless both reservation and resources allow it."""
+    storage = outside_git(storage)
+    storage.mkdir(parents=True, exist_ok=True)
+    script = Path(
+        os.environ.get(
+            "PE_CERTIFY_RESERVATION_SCRIPT",
+            "~/reviews/fleet-ops/tools/reservation.py",
+        )
+    ).expanduser()
+    subprocess.run([sys.executable, str(script), "allowed", "uk-hub"], check=True)
+    load1 = os.getloadavg()[0]
+    free = shutil.disk_usage(storage).free
+    if load1 > 110 or free < 55_000_000_000:
+        raise RuntimeError(
+            f"Resource guard refused: load1={load1:.2f}; free_bytes={free} "
+            "(requires load1 <=110 and free_bytes >=55000000000)"
+        )
+
+
+def clean_git_environment() -> dict[str, str]:
+    return {
+        key: value
+        for key, value in os.environ.items()
+        if key not in {"GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE"}
+    }
+
+
+def candidate_binding(candidate: Path, *, certified: bool) -> dict[str, Any]:
+    candidate = candidate.resolve()
+    command = ["git", "-C", str(candidate)]
+    if candidate == ROOT and (ROOT / ".job/git").is_dir():
+        command = [
+            "git",
+            "--git-dir",
+            str(ROOT / ".job/git"),
+            "--work-tree",
+            str(ROOT),
+        ]
+    head = subprocess.check_output(
+        [*command, "rev-parse", "HEAD"], text=True, env=clean_git_environment()
+    ).strip()
+    manifest_path = candidate / "src/policyengine/data/bundle/manifest.json"
+    manifest = read_json(manifest_path)
+    release = manifest["data_releases"]["uk"]
+    if certified:
+        if manifest["packages"]["policyengine-uk"]["version"] != MODELS["new"]:
+            raise ValueError("Candidate bundle does not pin policyengine-uk 2.147.2")
+        if manifest["packages"]["policyengine-core"]["version"] != CORE:
+            raise ValueError("Candidate bundle does not pin policyengine-core 3.33.3")
+        if release["build_id"] != CUTS["new"]:
+            raise ValueError("Candidate bundle identifies a different UK data cut")
+        if release["certified_data_artifact"]["sha256"] != DATA_SHA["new"]:
+            raise ValueError(
+                "Candidate bundle identifies different national data bytes"
+            )
+        if release.get("regional_source_manifest_uri"):
+            raise ValueError("Candidate still routes a regional release")
+    return {
+        "candidate_commit": head,
+        "bundle_manifest_sha256": sha256(manifest_path),
+        "bundle_uk_build_id": release["build_id"],
+        "evaluated_before_certification": not certified,
+    }
+
+
+def manifest(metadata: Path, label: str) -> tuple[dict[str, Any], str]:
+    path = metadata / f"{label}-release_manifest.json"
+    value = read_json(path)
+    build = value["build"]
+    if build["cut_tag"] != CUTS[label]:
+        raise ValueError(f"Unexpected {label} release cut")
+    if build["build_id"] != LINE:
+        raise ValueError("The producer line changed")
+    if build["built_with_model_package"]["version"] != MODELS[label]:
+        raise ValueError(f"Unexpected build-time model for {label} cut")
+    if build["built_with_core_package"]["version"] != CORE:
+        raise ValueError(f"Unexpected build-time core for {label} cut")
+    for artifact in value["artifacts"].values():
+        if artifact.get("repo_id") != REPO or artifact.get("revision") != CUTS[label]:
+            raise ValueError("Artifact is not pinned to the intended repository/cut")
+        expected = artifact.get("sha256", "")
+        if len(expected) != 64 or any(c not in "0123456789abcdef" for c in expected):
+            raise ValueError("Every artifact must have a SHA-256")
+    name = value["default_datasets"]["national"]
+    if value["artifacts"][name]["sha256"] != DATA_SHA[label]:
+        raise ValueError(f"Unexpected {label} national data SHA-256")
+    return value, sha256(path)
+
+
+def cache_directory(fallback: Path) -> Path:
+    setting = os.environ.get("HF_HUB_CACHE")
+    if setting:
+        return outside_git(Path(setting))
+    home = os.environ.get("HF_HOME")
+    return outside_git(Path(home) / "hub" if home else fallback / "hf-cache")
+
+
+def fetch(artifact: dict[str, Any], cache: Path) -> Path:
+    """Download/hash bytes without loading H5 or echoing private producer detail."""
+    guard(cache)
+    from huggingface_hub import hf_hub_download
+
+    filename = artifact["path"]
+    if artifact.get("kind") == "diagnostics" and not filename.startswith("releases/"):
+        filename = f"releases/{LINE}/{filename}"
+    downloaded = Path(
+        hf_hub_download(
+            repo_id=artifact["repo_id"],
+            repo_type="dataset",
+            revision=artifact["revision"],
+            filename=filename,
+            token=os.environ.get("HUGGING_FACE_TOKEN") or os.environ.get("HF_TOKEN"),
+            cache_dir=str(cache),
+        )
+    )
+    if sha256(downloaded) != artifact["sha256"]:
+        raise ValueError(f"Downloaded artifact SHA-256 mismatch: {filename}")
+    return downloaded
+
+
+def producer_gates(args: argparse.Namespace) -> int:
+    value, release_digest = manifest(args.metadata, "new")
+    cache = cache_directory(args.output.parent)
+    paths = {
+        name: fetch(artifact, cache) for name, artifact in value["artifacts"].items()
+    }
+    certification = read_json(paths["release_certification"])
+    if certification.get("shippable") is not True:
+        raise ValueError("Producer certification is not shippable")
+    if certification["candidate"]["sha256"] != DATA_SHA["new"]:
+        raise ValueError("Producer certification identifies other candidate bytes")
+    part_artifacts = {
+        "calibration_seam": "terminal_gates",
+        "release_cut": "release_cut_gates",
+        "spine": "spine_gates",
+    }
+    for part_name, artifact_name in part_artifacts.items():
+        part = certification["parts"][part_name]
+        artifact = value["artifacts"][artifact_name]
+        if part["sha256"] != artifact["sha256"]:
+            raise ValueError(f"Producer certification part SHA mismatch: {part_name}")
+        report = read_json(paths[artifact_name])
+        for key in ("gates_manifest_sha256", "policy_sha256"):
+            if part.get(key) != report.get(key) or not part.get(key):
+                raise ValueError(
+                    f"Producer certification part {key} mismatch: {part_name}"
+                )
+    gates_report = read_json(paths["release_cut_gates"])
+    raw_gates = gates_report.get("gates")
+    if not isinstance(raw_gates, dict):
+        raise ValueError("Expected release-cut gates keyed by gate ID")
+    ids = certification["parts"]["release_cut"]["entry_ids"]
+    if len(ids) != 19 or len(set(ids)) != 19 or set(raw_gates) != set(ids):
+        raise ValueError(
+            "Producer certification and report must identify the same 19 gates"
+        )
+    gates = [
+        {"gate_id": name, "status": raw_gates[name].get("status")}
+        for name in sorted(ids)
+    ]
+    if any(gate["status"] != "passed" for gate in gates):
+        raise ValueError("At least one release-cut gate did not pass")
+    if certification["parts"]["release_cut"].get("statuses") != {"passed": 19}:
+        raise ValueError(
+            "Producer certification does not attest 19 passed release-cut gates"
+        )
+    if (
+        gates_report.get("shippable") is not True
+        or gates_report.get("blocked_at_phase") is not None
+    ):
+        raise ValueError("Release-cut gate report is blocked or not shippable")
+    receipt = {
+        "schema_version": 1,
+        "step": "producer-gates",
+        "status": "pass",
+        **candidate_binding(args.candidate, certified=False),
+        "release_manifest_sha256": release_digest,
+        "cut_tag": CUTS["new"],
+        "dataset_sha256": DATA_SHA["new"],
+        "gates": gates,
+        "artifact_sha256": {
+            name: item["sha256"] for name, item in value["artifacts"].items()
+        },
+        "limitations": [
+            "Producer evidence only; wrapper/model/data gates are separate.",
+            "Raw producer detail remains private in the outside-Git cache.",
+        ],
+    }
+    write_json(args.output, receipt)
+    return 0
+
+
+def materialize(args: argparse.Namespace) -> int:
+    storage = outside_git(args.storage)
+    value, release_digest = manifest(args.metadata, args.label)
+    name = value["default_datasets"]["national"]
+    source = fetch(value["artifacts"][name], cache_directory(storage))
+    target = storage / "data" / f"{args.label}.h5"
+    target.parent.mkdir(parents=True, exist_ok=True)
+    if target.exists() or target.is_symlink():
+        if sha256(target) != DATA_SHA[args.label]:
+            raise ValueError(f"Existing {args.label} data file has different bytes")
+    else:
+        target.symlink_to(source.resolve())
+    receipt = {
+        "schema_version": 1,
+        "step": "materialize",
+        "status": "pass",
+        **candidate_binding(args.candidate, certified=False),
+        "label": args.label,
+        "cut_tag": CUTS[args.label],
+        "dataset_sha256": DATA_SHA[args.label],
+        "release_manifest_sha256": release_digest,
+        "data_path": str(target),
+        "data_loaded": False,
+    }
+    write_json(
+        args.output or storage / "evidence" / f"materialize-{args.label}.json", receipt
+    )
+    return 0
+
+
+def wrapper(candidate: Path) -> Any:
+    source = candidate.resolve() / "src"
+    sys.path.insert(0, str(source))
+    os.environ["POLICYENGINE_SKIP_COUNTRY_IMPORTS"] = "1"
+    gate_module = importlib.import_module("policyengine.provenance.uk_candidate")
+    if source not in Path(gate_module.__file__).resolve().parents:
+        raise ValueError(
+            "Imported qualification gates do not belong to the candidate source"
+        )
+    for package, expected in (
+        ("policyengine-uk", MODELS["new"]),
+        ("policyengine-core", CORE),
+    ):
+        if importlib.metadata.version(package) != expected:
+            raise ValueError(
+                f"The evidence environment must install {package}=={expected}"
+            )
+    return gate_module
+
+
+def checked_data(path: Path, label: str) -> Path:
+    path = outside_git(path)
+    if sha256(path) != DATA_SHA[label]:
+        raise ValueError(f"{label} data file SHA-256 does not match the pinned cut")
+    return path
+
+
+def qualified_metadata(args: argparse.Namespace) -> dict[str, Any]:
+    _, digest = manifest(args.metadata, "new")
+    return {
+        **candidate_binding(args.candidate, certified=True),
+        "release_manifest_sha256": digest,
+        "dataset_sha256": DATA_SHA["new"],
+        "model_version": MODELS["new"],
+        "core_version": CORE,
+    }
+
+
+def gate_record(outcome: Any) -> dict[str, Any]:
+    result, evidence = outcome
+    return {"result": result.model_dump(mode="json"), "evidence": evidence}
+
+
+def budget_inputs(args: argparse.Namespace) -> int:
+    binding = qualified_metadata(args)
+    module = wrapper(args.candidate)
+    old_data, new_data = (
+        checked_data(args.old_data, "old"),
+        checked_data(args.new_data, "new"),
+    )
+    qualification = args.candidate / "src/policyengine/data/qualification"
+    config = read_json(qualification / "uk.json")
+    critical_path = qualification / config["budget_inputs"]["critical"]
+    critical = read_json(critical_path)
+    storage = outside_git(args.output.parent)
+    storage.mkdir(parents=True, exist_ok=True)
+    # Resolving a venv's python symlink would select the base interpreter and
+    # silently lose the old model environment.
+    old_python = str(args.old_python.expanduser().absolute())
+    versions_code = (
+        "import importlib.metadata as m,json;print(json.dumps({n:m.version(n) "
+        "for n in ('policyengine-uk','policyengine-core')}))"
+    )
+    old_versions = json.loads(
+        subprocess.check_output([old_python, "-c", versions_code], text=True)
+    )
+    if old_versions != {"policyengine-uk": MODELS["old"], "policyengine-core": CORE}:
+        raise ValueError(
+            "Old input snapshot environment has different model/core versions"
+        )
+    with tempfile.TemporaryDirectory(prefix="inputs-", dir=storage) as directory:
+        work = Path(directory)
+        guard(storage)
+        previous = module.run_inputs_snapshot(
+            old_python,
+            critical=critical["critical"],
+            out=work / "previous.json",
+            dataset_path=old_data,
+        )
+        guard(storage)
+        current = module.run_inputs_snapshot(
+            sys.executable,
+            critical=critical["critical"],
+            out=work / "candidate.json",
+            dataset_path=new_data,
+        )
+    from policyengine_uk.system import system
+
+    previous_status = dict(previous["input_status"])
+    for name in system.variables:
+        previous_status.setdefault(
+            name,
+            {"in_model": False, "is_input": False, "in_data": False, "usable": False},
+        )
+    pair = SimpleNamespace(pair_id=binding["bundle_manifest_sha256"])
+    guard(storage)
+    inputs = module.budget_inputs(
+        pair,
+        new_data,
+        model_input_variables=[
+            name
+            for name, variable in system.variables.items()
+            if variable.is_input_variable()
+        ],
+        model_variables=system.variables,
+        critical_inputs=critical["critical"],
+        accepted_gaps=critical.get("accepted_gaps", {}),
+        previous_missing_inputs=previous["missing_inputs"],
+        previous_unusable_critical=[
+            item[0] for item in previous.get("unusable_critical", [])
+        ],
+        previous_input_status=previous_status,
+        previous_ever_usable_inputs=previous.get("ever_usable_inputs"),
+    )
+    guard(storage)
+    years = module.supported_years(
+        pair,
+        new_data,
+        required_years=config["required_years"],
+        budget_horizon_years=config["budget_horizon_years"],
+        previous_supported_years=previous["supported_years"],
+        projected_years=lambda _: current["supported_years"],
+    )
+    _, old_manifest_digest = manifest(args.metadata, "old")
+    receipt = {
+        "schema_version": 1,
+        "step": "budget-inputs",
+        **binding,
+        "previous_release_manifest_sha256": old_manifest_digest,
+        "previous_dataset_sha256": DATA_SHA["old"],
+        "previous_model_version": MODELS["old"],
+        "qualification_config_sha256": sha256(qualification / "uk.json"),
+        "critical_inputs_spec_sha256": sha256(critical_path),
+        "budget_inputs": gate_record(inputs),
+        "supported_years": gate_record(years),
+        "limitations": [
+            "Previous input coverage is recomputed on the direct previously certified pair.",
+            "Earlier ever-usable input lineage beyond that predecessor is not reconstructed.",
+        ],
+    }
+    write_json(args.output, receipt)
+    return (
+        2
+        if any(
+            outcome[0].status in {"fail", "error", "unavailable"}
+            for outcome in (inputs, years)
+        )
+        else 0
+    )
+
+
+def reform_contract(args: argparse.Namespace) -> int:
+    binding = qualified_metadata(args)
+    module = wrapper(args.candidate)
+    data = checked_data(args.new_data, "new")
+    config_path = args.candidate / "src/policyengine/data/qualification/uk.json"
+    settings = read_json(config_path)["reform_contract"]
+    if (
+        int(settings.get("seed", -1)) != 0
+        or int(settings.get("max_examples", -1)) != 20
+    ):
+        raise ValueError(
+            "Contract configuration must retain seed=0 and max_examples=20"
+        )
+    guard(args.output.parent)
+    outcome = module.reform_contract(
+        entrypoint=settings["entrypoint"],
+        dataset_path=data,
+        seed=0,
+        max_examples=20,
+        expected_versions={"policyengine-uk": MODELS["new"], "policyengine-core": CORE},
+        expected_dataset_sha256=DATA_SHA["new"],
+        pending_decisions=settings.get("pending_decisions", ()),
+    )
+    write_json(
+        args.output,
+        {
+            "schema_version": 1,
+            "step": "reform-contract",
+            **binding,
+            "qualification_config_sha256": sha256(config_path),
+            **gate_record(outcome),
+            "disclosure": "Uses the qualification gate's publishable_contract_report sanitization.",
+        },
+    )
+    return 2 if outcome[0].status in {"fail", "error", "unavailable"} else 0
+
+
+def replay_unavailable(args: argparse.Namespace) -> int:
+    write_json(
+        args.output,
+        {
+            "schema_version": 1,
+            "step": "scorecard-replay",
+            "status": "unavailable",
+            **qualified_metadata(args),
+            "reason": "Microcosm scorecard replay fix has not landed; exact-pair replay is unavailable.",
+            "acceptance_basis": "Coordinator 23 October decision under d1682 and d1344; Budget-week same-line re-cut exception.",
+            "limitations": [
+                "Unavailable is not a replay pass; replace this receipt after the replay fix lands."
+            ],
+        },
+    )
+    return 0
+
+
+def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
+    parser = argparse.ArgumentParser(description=__doc__)
+    commands = parser.add_subparsers(dest="command", required=True)
+    for name, function in (
+        ("producer-gates", producer_gates),
+        ("materialize", materialize),
+        ("budget-inputs", budget_inputs),
+        ("reform-contract", reform_contract),
+        ("replay-unavailable", replay_unavailable),
+    ):
+        command = commands.add_parser(name, help=function.__doc__)
+        command.add_argument("--candidate", "--wrapper", type=Path, default=ROOT)
+        command.add_argument("--metadata", type=Path, default=ROOT / ".job/metadata")
+        command.add_argument("--output", type=Path, required=name != "materialize")
+        command.set_defaults(function=function)
+        if name == "materialize":
+            command.add_argument("--label", choices=("old", "new"), required=True)
+            command.add_argument("--storage", type=Path, required=True)
+        if name == "budget-inputs":
+            command.add_argument("--old-python", type=Path, required=True)
+            command.add_argument("--old-data", type=Path, required=True)
+        if name in {"budget-inputs", "reform-contract"}:
+            command.add_argument("--new-data", type=Path, required=True)
+    return parser.parse_args(argv)
+
+
+def main(argv: list[str] | None = None) -> int:
+    args = parse_args(argv)
+    try:
+        return args.function(args)
+    except Exception as error:
+        # Never dump raw private producer documents or survey arrays.
+        print(
+            f"{args.command} failed ({type(error).__name__}): {error}", file=sys.stderr
+        )
+        return 2
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())
diff --git a/.job/dense-schema.json b/.job/dense-schema.json
new file mode 100644
index 00000000..2165ed8e
--- /dev/null
+++ b/.job/dense-schema.json
@@ -0,0 +1,986 @@
+{
+  "step": "dense-schema-inspection",
+  "bytes": 719952075,
+  "sha256": "8e10e130d0260e0b52e2e29902f0502ebdff76a14a3e57d2c876bf4cd8db5872",
+  "source_uri": "hf://policyengine/populace-uk-private/microcosm_uk_2024_25_dense.h5@microcosm-uk-2024-25-dense-20261019T000432Z-ee5c2dbe",
+  "download_guard": {
+    "load1": 27.71728515625,
+    "free_bytes": 113860476928
+  },
+  "inspection_guard": {
+    "load1": 28.62060546875,
+    "free_bytes": 113861021696
+  },
+  "wrapper_materialization_seconds": 2.116,
+  "H5_schema_inspection_seconds": 1.584,
+  "schema_rule": "Entity table-format HDF5: use compound dtype field names from person/table, benunit/table and household/table; exclude pandas index fields and time_period metadata; cross-check columns against each entity's _i_table index names. No arrays or attributes read.",
+  "root_count": 4,
+  "root_dataset_count": 0,
+  "root_group_count": 4,
+  "roots_with_dataset_descendants_count": 4,
+  "dataset_depth_counts": {
+    "4": 2990,
+    "2": 4
+  },
+  "entity_column_counts": {
+    "person": 165,
+    "benunit": 27,
+    "household": 102
+  },
+  "entity_column_names": {
+    "person": [
+      "a_and_e_visits",
+      "aa_category",
+      "access_fund",
+      "admitted_patient_visits",
+      "adult_ema",
+      "afcs_reported",
+      "age",
+      "age_left_full_time_education",
+      "age_started_or_accepted_current_education_or_training",
+      "attends_private_school",
+      "attends_private_school_random_draw",
+      "bsp_reported",
+      "bus_in_london_trips",
+      "bus_pass_eligible",
+      "capital_gains",
+      "capital_gains_asset_type",
+      "capital_gains_badr",
+      "capital_gains_residential_property",
+      "care_hours",
+      "carers_allowance_reported",
+      "cgt_residential_probability",
+      "charitable_investment_gifts",
+      "child_benefit_reported",
+      "child_ema",
+      "child_tax_credit_reported",
+      "childcare_expenses",
+      "clone_index",
+      "council_tax_benefit_reported",
+      "current_education",
+      "disabled_students_allowance_eligible_expenses",
+      "dividend_income",
+      "dla_m_category",
+      "dla_sc_category",
+      "education_grants",
+      "employee_pension_contributions",
+      "employer_pension_contributions",
+      "employment_income",
+      "employment_sector",
+      "employment_status",
+      "esa_contrib_reported",
+      "esa_health_condition_proxy",
+      "esa_includes_support_component",
+      "esa_income_reported",
+      "esa_support_group_proxy",
+      "free_school_breakfasts",
+      "free_school_fruit_veg",
+      "free_school_meals",
+      "gender",
+      "gift_aid",
+      "has_lifetime_isa",
+      "healthy_start_vouchers",
+      "highest_education",
+      "hmrc_spi_assessable_income",
+      "hmrc_spi_employed_income",
+      "hmrc_spi_employment_benefits",
+      "hmrc_spi_employment_expenses",
+      "hmrc_spi_incapacity_benefit_income",
+      "hmrc_spi_miscellaneous_employment_income",
+      "hmrc_spi_other_income",
+      "hmrc_spi_other_social_security_income",
+      "hmrc_spi_pay",
+      "hmrc_spi_state_pension_income",
+      "hmrc_spi_taxable_termination_pay",
+      "hmrc_spi_total_earned_income",
+      "hmrc_spi_total_investment_income",
+      "hmrc_spi_unemployment_benefit_income",
+      "hours_worked",
+      "housing_benefit_reported",
+      "iidb_reported",
+      "income_support_reported",
+      "is_before_universal_credit_qualifying_young_person_terminal_date",
+      "is_benunit_head",
+      "is_blind",
+      "is_claimant_or_partner",
+      "is_disabled_for_benefits",
+      "is_engaged_in_owned_company_trade",
+      "is_enhanced_disabled_for_benefits",
+      "is_hbai_dependent_child",
+      "is_household_head",
+      "is_in_approved_training",
+      "is_in_non_advanced_education",
+      "is_looked_after_by_local_authority",
+      "is_parent",
+      "is_severely_disabled_for_benefits",
+      "is_uc_claimant",
+      "jsa_contrib_reported",
+      "jsa_income_reported",
+      "legacy_jobseeker_proxy",
+      "lifetime_isa_balance",
+      "local_bus_single_fare_share",
+      "local_bus_trips",
+      "local_bus_use_band",
+      "loss_relief",
+      "lump_sum_income",
+      "maintenance_expenses",
+      "maintenance_income",
+      "marital_status",
+      "miscellaneous_income",
+      "months_since_last_birthday",
+      "nhs_a_and_e_spending",
+      "nhs_admitted_patient_spending",
+      "nhs_outpatient_spending",
+      "ni_class_4_losses_brought_forward",
+      "ons_family_index",
+      "ons_family_role",
+      "ossben_identifiable_subset",
+      "other_investment_income",
+      "other_local_bus_trips",
+      "outpatient_visits",
+      "owned_company_carries_on_property_business",
+      "owned_company_carries_on_trade",
+      "owned_company_income_share",
+      "owned_company_is_main_employment",
+      "pension_contributions_via_salary_sacrifice",
+      "pension_credit_reported",
+      "person_benunit_id",
+      "person_build_id",
+      "person_household_id",
+      "person_id",
+      "person_is_spi_income_band_carrier",
+      "person_is_spi_income_recipient",
+      "person_source_id",
+      "person_support_channel",
+      "person_support_clone_index",
+      "personal_pension_contributions",
+      "pip_dl_category",
+      "pip_m_category",
+      "private_pension_income",
+      "private_transfer_income",
+      "property_finance_costs",
+      "property_income",
+      "property_rental_income",
+      "receives_benefits_in_own_right",
+      "relationship_to_head",
+      "rent_paid_as_boarder",
+      "rent_paid_as_lodger",
+      "reports_outstanding_student_loan",
+      "salary_sacrifice_asked",
+      "salary_sacrifice_reported",
+      "savings_interest_income",
+      "sda_reported",
+      "self_employment_income",
+      "sic_industry_division",
+      "srp_regular_code5",
+      "ssmg_reported",
+      "stands_as_sole_owner_or_partner_of_company",
+      "state_pension_reported",
+      "statutory_maternity_pay",
+      "statutory_paternity_pay",
+      "statutory_sick_pay",
+      "student_loan_balance",
+      "student_loan_plan",
+      "student_loan_repayments",
+      "student_loans",
+      "tax_free_childcare_spend_routed_share",
+      "tax_free_savings_income",
+      "trading_loss",
+      "uc_is_in_gainful_self_employment",
+      "uc_is_in_startup_period",
+      "universal_credit_reported",
+      "winter_fuel_allowance_reported",
+      "working_tax_credit_reported",
+      "would_claim_carers_allowance",
+      "would_claim_marriage_allowance",
+      "would_claim_scp"
+    ],
+    "benunit": [
+      "benunit_build_id",
+      "benunit_id",
+      "benunit_source_id",
+      "benunit_support_channel",
+      "benunit_support_clone_index",
+      "child_benefit_opts_out",
+      "clone_index",
+      "dependent_children",
+      "frs_benunit_capital",
+      "has_mixed_age_couple_pension_credit_saving",
+      "is_married",
+      "liable_for_share_of_household_rent",
+      "maximum_extended_childcare_hours_usage",
+      "pension_credit_reported_capital",
+      "uc_deduction_combination",
+      "uc_deduction_random_draw",
+      "uc_deduction_type_random_draw",
+      "uc_latent_deduction_rate",
+      "uc_reported_capital",
+      "would_claim_child_benefit",
+      "would_claim_extended_childcare",
+      "would_claim_pc",
+      "would_claim_targeted_childcare",
+      "would_claim_tfc",
+      "would_claim_uc",
+      "would_claim_uc_childcare",
+      "would_claim_universal_childcare"
+    ],
+    "household": [
+      "accommodation_type",
+      "alcohol_and_tobacco_consumption",
+      "atomic_area_basis",
+      "atomic_area_code",
+      "atomic_area_system",
+      "brma",
+      "bus_fare_spending",
+      "bus_subsidy_spending",
+      "cash_isa",
+      "cgt_residential_clone_index",
+      "cgt_support_copies",
+      "cgt_support_copy_index",
+      "clone_index",
+      "clothing_and_footwear_consumption",
+      "communication_consumption",
+      "constituency_code_oa",
+      "consumer_debt",
+      "corporate_wealth",
+      "council_tax",
+      "council_tax_band",
+      "council_tax_rebate",
+      "council_tax_reported",
+      "council_tax_single_adult_raw",
+      "dfe_education_spending",
+      "diesel_spending",
+      "domestic_energy_consumption",
+      "domestic_rates",
+      "education_consumption",
+      "electricity_consumption",
+      "external_child_payments",
+      "food_and_non_alcoholic_beverages_consumption",
+      "full_rate_vat_expenditure_rate",
+      "gas_consumption",
+      "geography_household_key",
+      "gross_financial_wealth",
+      "has_fuel_consumption",
+      "health_consumption",
+      "high_value_support_copies",
+      "high_value_support_copy_index",
+      "household_build_id",
+      "household_furnishings_consumption",
+      "household_id",
+      "household_is_capital_gains_clone",
+      "household_is_cgt_residential_clone",
+      "household_is_cgt_support_copy",
+      "household_is_high_value_support_copy",
+      "household_is_spi_income_band_donor",
+      "household_is_spi_synthetic",
+      "household_lifetime_isa_balance",
+      "household_local_bus_trips",
+      "household_owns_tv",
+      "household_source_id",
+      "household_support_channel",
+      "household_support_clone_index",
+      "household_weight",
+      "housing_service_charges",
+      "housing_water_and_electricity_consumption",
+      "itl1_code",
+      "itl2_code",
+      "itl3_code",
+      "la_code_oa",
+      "local_authority",
+      "lsoa_code",
+      "main_residence_value",
+      "main_residential_property_purchased_is_first_home",
+      "miscellaneous_consumption",
+      "mortgage_capital_repayment",
+      "mortgage_debt",
+      "mortgage_interest_repayment",
+      "msoa_code",
+      "net_financial_wealth",
+      "non_residential_property_value",
+      "num_bedrooms",
+      "num_vehicles",
+      "oa_code",
+      "ons_household_type",
+      "other_residential_property_value",
+      "owned_land",
+      "petrol_spending",
+      "private_pension_wealth",
+      "property_purchased",
+      "rail_subsidy_spending",
+      "rail_usage",
+      "recreation_consumption",
+      "region",
+      "region_code_oa",
+      "rent",
+      "restaurants_and_hotels_consumption",
+      "savings",
+      "source_household_id",
+      "source_household_key",
+      "source_year",
+      "spi_income_band_donor_lower_bound",
+      "stocks_and_shares_isa",
+      "structural_insurance_payments",
+      "subrent",
+      "tenure_type",
+      "transport_consumption",
+      "uses_energy_prepayment_meter",
+      "ward_code",
+      "water_and_sewerage_charges",
+      "would_evade_tv_licence_fee"
+    ]
+  },
+  "stored_variable_names": [
+    "a_and_e_visits",
+    "aa_category",
+    "access_fund",
+    "accommodation_type",
+    "admitted_patient_visits",
+    "adult_ema",
+    "afcs_reported",
+    "age",
+    "age_left_full_time_education",
+    "age_started_or_accepted_current_education_or_training",
+    "alcohol_and_tobacco_consumption",
+    "atomic_area_basis",
+    "atomic_area_code",
+    "atomic_area_system",
+    "attends_private_school",
+    "attends_private_school_random_draw",
+    "benunit_build_id",
+    "benunit_id",
+    "benunit_source_id",
+    "benunit_support_channel",
+    "benunit_support_clone_index",
+    "brma",
+    "bsp_reported",
+    "bus_fare_spending",
+    "bus_in_london_trips",
+    "bus_pass_eligible",
+    "bus_subsidy_spending",
+    "capital_gains",
+    "capital_gains_asset_type",
+    "capital_gains_badr",
+    "capital_gains_residential_property",
+    "care_hours",
+    "carers_allowance_reported",
+    "cash_isa",
+    "cgt_residential_clone_index",
+    "cgt_residential_probability",
+    "cgt_support_copies",
+    "cgt_support_copy_index",
+    "charitable_investment_gifts",
+    "child_benefit_opts_out",
+    "child_benefit_reported",
+    "child_ema",
+    "child_tax_credit_reported",
+    "childcare_expenses",
+    "clone_index",
+    "clothing_and_footwear_consumption",
+    "communication_consumption",
+    "constituency_code_oa",
+    "consumer_debt",
+    "corporate_wealth",
+    "council_tax",
+    "council_tax_band",
+    "council_tax_benefit_reported",
+    "council_tax_rebate",
+    "council_tax_reported",
+    "council_tax_single_adult_raw",
+    "current_education",
+    "dependent_children",
+    "dfe_education_spending",
+    "diesel_spending",
+    "disabled_students_allowance_eligible_expenses",
+    "dividend_income",
+    "dla_m_category",
+    "dla_sc_category",
+    "domestic_energy_consumption",
+    "domestic_rates",
+    "education_consumption",
+    "education_grants",
+    "electricity_consumption",
+    "employee_pension_contributions",
+    "employer_pension_contributions",
+    "employment_income",
+    "employment_sector",
+    "employment_status",
+    "esa_contrib_reported",
+    "esa_health_condition_proxy",
+    "esa_includes_support_component",
+    "esa_income_reported",
+    "esa_support_group_proxy",
+    "external_child_payments",
+    "food_and_non_alcoholic_beverages_consumption",
+    "free_school_breakfasts",
+    "free_school_fruit_veg",
+    "free_school_meals",
+    "frs_benunit_capital",
+    "full_rate_vat_expenditure_rate",
+    "gas_consumption",
+    "gender",
+    "geography_household_key",
+    "gift_aid",
+    "gross_financial_wealth",
+    "has_fuel_consumption",
+    "has_lifetime_isa",
+    "has_mixed_age_couple_pension_credit_saving",
+    "health_consumption",
+    "healthy_start_vouchers",
+    "high_value_support_copies",
+    "high_value_support_copy_index",
+    "highest_education",
+    "hmrc_spi_assessable_income",
+    "hmrc_spi_employed_income",
+    "hmrc_spi_employment_benefits",
+    "hmrc_spi_employment_expenses",
+    "hmrc_spi_incapacity_benefit_income",
+    "hmrc_spi_miscellaneous_employment_income",
+    "hmrc_spi_other_income",
+    "hmrc_spi_other_social_security_income",
+    "hmrc_spi_pay",
+    "hmrc_spi_state_pension_income",
+    "hmrc_spi_taxable_termination_pay",
+    "hmrc_spi_total_earned_income",
+    "hmrc_spi_total_investment_income",
+    "hmrc_spi_unemployment_benefit_income",
+    "hours_worked",
+    "household_build_id",
+    "household_furnishings_consumption",
+    "household_id",
+    "household_is_capital_gains_clone",
+    "household_is_cgt_residential_clone",
+    "household_is_cgt_support_copy",
+    "household_is_high_value_support_copy",
+    "household_is_spi_income_band_donor",
+    "household_is_spi_synthetic",
+    "household_lifetime_isa_balance",
+    "household_local_bus_trips",
+    "household_owns_tv",
+    "household_source_id",
+    "household_support_channel",
+    "household_support_clone_index",
+    "household_weight",
+    "housing_benefit_reported",
+    "housing_service_charges",
+    "housing_water_and_electricity_consumption",
+    "iidb_reported",
+    "income_support_reported",
+    "is_before_universal_credit_qualifying_young_person_terminal_date",
+    "is_benunit_head",
+    "is_blind",
+    "is_claimant_or_partner",
+    "is_disabled_for_benefits",
+    "is_engaged_in_owned_company_trade",
+    "is_enhanced_disabled_for_benefits",
+    "is_hbai_dependent_child",
+    "is_household_head",
+    "is_in_approved_training",
+    "is_in_non_advanced_education",
+    "is_looked_after_by_local_authority",
+    "is_married",
+    "is_parent",
+    "is_severely_disabled_for_benefits",
+    "is_uc_claimant",
+    "itl1_code",
+    "itl2_code",
+    "itl3_code",
+    "jsa_contrib_reported",
+    "jsa_income_reported",
+    "la_code_oa",
+    "legacy_jobseeker_proxy",
+    "liable_for_share_of_household_rent",
+    "lifetime_isa_balance",
+    "local_authority",
+    "local_bus_single_fare_share",
+    "local_bus_trips",
+    "local_bus_use_band",
+    "loss_relief",
+    "lsoa_code",
+    "lump_sum_income",
+    "main_residence_value",
+    "main_residential_property_purchased_is_first_home",
+    "maintenance_expenses",
+    "maintenance_income",
+    "marital_status",
+    "maximum_extended_childcare_hours_usage",
+    "miscellaneous_consumption",
+    "miscellaneous_income",
+    "months_since_last_birthday",
+    "mortgage_capital_repayment",
+    "mortgage_debt",
+    "mortgage_interest_repayment",
+    "msoa_code",
+    "net_financial_wealth",
+    "nhs_a_and_e_spending",
+    "nhs_admitted_patient_spending",
+    "nhs_outpatient_spending",
+    "ni_class_4_losses_brought_forward",
+    "non_residential_property_value",
+    "num_bedrooms",
+    "num_vehicles",
+    "oa_code",
+    "ons_family_index",
+    "ons_family_role",
+    "ons_household_type",
+    "ossben_identifiable_subset",
+    "other_investment_income",
+    "other_local_bus_trips",
+    "other_residential_property_value",
+    "outpatient_visits",
+    "owned_company_carries_on_property_business",
+    "owned_company_carries_on_trade",
+    "owned_company_income_share",
+    "owned_company_is_main_employment",
+    "owned_land",
+    "pension_contributions_via_salary_sacrifice",
+    "pension_credit_reported",
+    "pension_credit_reported_capital",
+    "person_benunit_id",
+    "person_build_id",
+    "person_household_id",
+    "person_id",
+    "person_is_spi_income_band_carrier",
+    "person_is_spi_income_recipient",
+    "person_source_id",
+    "person_support_channel",
+    "person_support_clone_index",
+    "personal_pension_contributions",
+    "petrol_spending",
+    "pip_dl_category",
+    "pip_m_category",
+    "private_pension_income",
+    "private_pension_wealth",
+    "private_transfer_income",
+    "property_finance_costs",
+    "property_income",
+    "property_purchased",
+    "property_rental_income",
+    "rail_subsidy_spending",
+    "rail_usage",
+    "receives_benefits_in_own_right",
+    "recreation_consumption",
+    "region",
+    "region_code_oa",
+    "relationship_to_head",
+    "rent",
+    "rent_paid_as_boarder",
+    "rent_paid_as_lodger",
+    "reports_outstanding_student_loan",
+    "restaurants_and_hotels_consumption",
+    "salary_sacrifice_asked",
+    "salary_sacrifice_reported",
+    "savings",
+    "savings_interest_income",
+    "sda_reported",
+    "self_employment_income",
+    "sic_industry_division",
+    "source_household_id",
+    "source_household_key",
+    "source_year",
+    "spi_income_band_donor_lower_bound",
+    "srp_regular_code5",
+    "ssmg_reported",
+    "stands_as_sole_owner_or_partner_of_company",
+    "state_pension_reported",
+    "statutory_maternity_pay",
+    "statutory_paternity_pay",
+    "statutory_sick_pay",
+    "stocks_and_shares_isa",
+    "structural_insurance_payments",
+    "student_loan_balance",
+    "student_loan_plan",
+    "student_loan_repayments",
+    "student_loans",
+    "subrent",
+    "tax_free_childcare_spend_routed_share",
+    "tax_free_savings_income",
+    "tenure_type",
+    "trading_loss",
+    "transport_consumption",
+    "uc_deduction_combination",
+    "uc_deduction_random_draw",
+    "uc_deduction_type_random_draw",
+    "uc_is_in_gainful_self_employment",
+    "uc_is_in_startup_period",
+    "uc_latent_deduction_rate",
+    "uc_reported_capital",
+    "universal_credit_reported",
+    "uses_energy_prepayment_meter",
+    "ward_code",
+    "water_and_sewerage_charges",
+    "winter_fuel_allowance_reported",
+    "working_tax_credit_reported",
+    "would_claim_carers_allowance",
+    "would_claim_child_benefit",
+    "would_claim_extended_childcare",
+    "would_claim_marriage_allowance",
+    "would_claim_pc",
+    "would_claim_scp",
+    "would_claim_targeted_childcare",
+    "would_claim_tfc",
+    "would_claim_uc",
+    "would_claim_uc_childcare",
+    "would_claim_universal_childcare",
+    "would_evade_tv_licence_fee"
+  ],
+  "stored_unique_column_count": 292,
+  "ignored_by_target_count": 83,
+  "ignored_by_target": [
+    "age_left_full_time_education",
+    "atomic_area_basis",
+    "atomic_area_code",
+    "atomic_area_system",
+    "benunit_build_id",
+    "benunit_source_id",
+    "benunit_support_channel",
+    "benunit_support_clone_index",
+    "capital_gains_asset_type",
+    "cgt_residential_clone_index",
+    "cgt_residential_probability",
+    "cgt_support_copies",
+    "cgt_support_copy_index",
+    "clone_index",
+    "constituency_code_oa",
+    "council_tax_rebate",
+    "council_tax_reported",
+    "council_tax_single_adult_raw",
+    "dependent_children",
+    "esa_health_condition_proxy",
+    "esa_support_group_proxy",
+    "free_school_breakfasts",
+    "frs_benunit_capital",
+    "geography_household_key",
+    "has_fuel_consumption",
+    "high_value_support_copies",
+    "high_value_support_copy_index",
+    "hmrc_spi_assessable_income",
+    "hmrc_spi_employed_income",
+    "hmrc_spi_employment_benefits",
+    "hmrc_spi_employment_expenses",
+    "hmrc_spi_incapacity_benefit_income",
+    "hmrc_spi_miscellaneous_employment_income",
+    "hmrc_spi_other_income",
+    "hmrc_spi_other_social_security_income",
+    "hmrc_spi_pay",
+    "hmrc_spi_state_pension_income",
+    "hmrc_spi_taxable_termination_pay",
+    "hmrc_spi_total_earned_income",
+    "hmrc_spi_total_investment_income",
+    "hmrc_spi_unemployment_benefit_income",
+    "household_build_id",
+    "household_is_capital_gains_clone",
+    "household_is_cgt_residential_clone",
+    "household_is_cgt_support_copy",
+    "household_is_high_value_support_copy",
+    "household_is_spi_income_band_donor",
+    "household_is_spi_synthetic",
+    "household_local_bus_trips",
+    "household_source_id",
+    "household_support_channel",
+    "household_support_clone_index",
+    "itl1_code",
+    "itl2_code",
+    "itl3_code",
+    "la_code_oa",
+    "legacy_jobseeker_proxy",
+    "local_bus_use_band",
+    "lsoa_code",
+    "msoa_code",
+    "oa_code",
+    "ons_family_index",
+    "ons_family_role",
+    "ons_household_type",
+    "ossben_identifiable_subset",
+    "person_build_id",
+    "person_is_spi_income_band_carrier",
+    "person_is_spi_income_recipient",
+    "person_source_id",
+    "person_support_channel",
+    "person_support_clone_index",
+    "region_code_oa",
+    "relationship_to_head",
+    "reports_outstanding_student_loan",
+    "salary_sacrifice_asked",
+    "salary_sacrifice_reported",
+    "source_household_id",
+    "source_household_key",
+    "source_year",
+    "spi_income_band_donor_lower_bound",
+    "srp_regular_code5",
+    "subrent",
+    "ward_code"
+  ],
+  "already_unknown_to_build_model": [
+    "age_left_full_time_education",
+    "atomic_area_basis",
+    "atomic_area_code",
+    "atomic_area_system",
+    "benunit_build_id",
+    "benunit_source_id",
+    "benunit_support_channel",
+    "benunit_support_clone_index",
+    "capital_gains_asset_type",
+    "cgt_residential_clone_index",
+    "cgt_residential_probability",
+    "cgt_support_copies",
+    "cgt_support_copy_index",
+    "clone_index",
+    "constituency_code_oa",
+    "council_tax_rebate",
+    "council_tax_reported",
+    "council_tax_single_adult_raw",
+    "dependent_children",
+    "esa_health_condition_proxy",
+    "esa_support_group_proxy",
+    "free_school_breakfasts",
+    "frs_benunit_capital",
+    "geography_household_key",
+    "has_fuel_consumption",
+    "high_value_support_copies",
+    "high_value_support_copy_index",
+    "hmrc_spi_assessable_income",
+    "hmrc_spi_employed_income",
+    "hmrc_spi_employment_benefits",
+    "hmrc_spi_employment_expenses",
+    "hmrc_spi_incapacity_benefit_income",
+    "hmrc_spi_miscellaneous_employment_income",
+    "hmrc_spi_other_income",
+    "hmrc_spi_other_social_security_income",
+    "hmrc_spi_pay",
+    "hmrc_spi_state_pension_income",
+    "hmrc_spi_taxable_termination_pay",
+    "hmrc_spi_total_earned_income",
+    "hmrc_spi_total_investment_income",
+    "hmrc_spi_unemployment_benefit_income",
+    "household_build_id",
+    "household_is_capital_gains_clone",
+    "household_is_cgt_residential_clone",
+    "household_is_cgt_support_copy",
+    "household_is_high_value_support_copy",
+    "household_is_spi_income_band_donor",
+    "household_is_spi_synthetic",
+    "household_local_bus_trips",
+    "household_source_id",
+    "household_support_channel",
+    "household_support_clone_index",
+    "itl1_code",
+    "itl2_code",
+    "itl3_code",
+    "la_code_oa",
+    "legacy_jobseeker_proxy",
+    "local_bus_use_band",
+    "lsoa_code",
+    "msoa_code",
+    "oa_code",
+    "ons_family_index",
+    "ons_family_role",
+    "ons_household_type",
+    "ossben_identifiable_subset",
+    "person_build_id",
+    "person_is_spi_income_band_carrier",
+    "person_is_spi_income_recipient",
+    "person_source_id",
+    "person_support_channel",
+    "person_support_clone_index",
+    "region_code_oa",
+    "relationship_to_head",
+    "reports_outstanding_student_loan",
+    "salary_sacrifice_asked",
+    "salary_sacrifice_reported",
+    "source_household_id",
+    "source_household_key",
+    "source_year",
+    "spi_income_band_donor_lower_bound",
+    "srp_regular_code5",
+    "subrent",
+    "ward_code"
+  ],
+  "newly_lost_build_model_variables_count": 0,
+  "newly_lost_build_model_variables": [],
+  "target_inputs_absent_count": 143,
+  "target_inputs_absent": [
+    "additional_residential_property_purchased",
+    "adult_dependants_grant_has_other_adult_dependant",
+    "adult_dependants_grant_other_adult_income",
+    "adult_dependants_grant_receives_postgraduate_loan",
+    "armed_forces_independence_payment",
+    "base_net_income",
+    "baseline_child_benefit_entitlement",
+    "baseline_income_support_entitlement",
+    "baseline_pension_credit_entitlement",
+    "beer_draught_share",
+    "beer_litres",
+    "benunit_reported_capital",
+    "blind_persons_allowance",
+    "bursary_fund_16_to_19_in_care_or_care_leaver",
+    "bursary_fund_16_to_19_participation_costs",
+    "bursary_fund_16_to_19_self_supporting",
+    "bus_fare_spending_reported",
+    "capital_allowances",
+    "capital_gains_before_response",
+    "capital_gains_carried_interest",
+    "car_co2_emissions",
+    "car_engine_size",
+    "car_first_registration_date",
+    "car_fuel_type",
+    "car_is_ved_exempt",
+    "car_list_price",
+    "childcare_grant_child_has_special_educational_needs",
+    "childcare_grant_full_time_student",
+    "childcare_grant_provider_is_nanny",
+    "childcare_grant_receives_nhs_childcare_support",
+    "childcare_grant_receives_postgraduate_loan",
+    "cider_draught_share",
+    "cider_litres",
+    "cigarettes_per_week",
+    "cigars_grams_per_week",
+    "covenanted_payments",
+    "cumulative_non_residential_rent",
+    "cumulative_residential_rent",
+    "deficiency_relief",
+    "defined_benefit_pension_share",
+    "directly_held_shares",
+    "disabled_students_allowance_receives_equivalent_support",
+    "early_years_school_start_deferred",
+    "ebr_energy_bills_credit_eligible",
+    "employee_pension_contributions_reported",
+    "employment_expenses",
+    "employment_income_before_lsr",
+    "families",
+    "free_school_milk",
+    "hand_rolling_tobacco_grams_per_week",
+    "heated_tobacco_grams_per_week",
+    "households",
+    "housing_benefit_board_and_attendance_determination",
+    "in_FE",
+    "in_HE",
+    "in_original_frs",
+    "in_rural_fuel_duty_relief_area",
+    "in_specified_or_temporary_accommodation",
+    "incapacity_benefit_reported",
+    "individual_savings_account_interest_income",
+    "is_about_to_start_paid_work",
+    "is_apprentice",
+    "is_full_year_category_a_director",
+    "is_newly_self_employed",
+    "is_shared_accommodation",
+    "local_bus_uncapped_single_fare",
+    "lpg_kg",
+    "married_couples_allowance",
+    "maternity_allowance_reported",
+    "max_free_entitlement_hours_used",
+    "meals_included_in_rent",
+    "natural_gas_kg",
+    "ni_class_3",
+    "non_residential_property_purchased",
+    "non_residential_property_secured_debt",
+    "non_residential_rent",
+    "original_weight",
+    "other_deductions",
+    "other_fermented_draught_share",
+    "other_fermented_litres",
+    "other_residential_property_secured_debt",
+    "other_tax_credits",
+    "other_tobacco_grams_per_week",
+    "owned_company_capital",
+    "owned_company_holding_value",
+    "owned_company_intermediary_earnings_chapter",
+    "owned_company_intermediary_earnings_from_main_employment",
+    "owned_company_trade_assets",
+    "pension_annual_allowance_taxable_lump_sum_death_benefits",
+    "pension_contributions_via_salary_sacrifice_pre_2015",
+    "pension_income",
+    "people",
+    "person_benunit_role",
+    "person_household_role",
+    "pip_dl_points",
+    "pip_m_points",
+    "property_finance_costs_brought_forward",
+    "raw_person_weight",
+    "receives_limited_capability_for_work_credits",
+    "role",
+    "salary_sacrifice_returned_to_income",
+    "scottish_funded_early_learning_childcare_start_date_reached",
+    "self_employment_gross_receipts",
+    "sparkling_cider_draught_share",
+    "sparkling_cider_litres",
+    "spi_imputed",
+    "spirits_draught_share",
+    "spirits_litres",
+    "sublet_income",
+    "tax_free_childcare_meets_uk_connection",
+    "tax_free_childcare_on_adoption_leave",
+    "tax_free_childcare_on_carers_leave",
+    "tax_free_childcare_on_qualifying_leave",
+    "tax_free_childcare_on_shared_parental_leave",
+    "tax_free_childcare_self_employment_start_up_period",
+    "tax_free_childcare_uses_qualifying_provider",
+    "tax_reported",
+    "travel_grant_abroad_placement",
+    "travel_grant_clinical_placement",
+    "travel_grant_eligible_expenses",
+    "travel_grant_receives_means_tested_nhs_bursary",
+    "uc_has_offer_of_paid_work_starting_by_end_of_next_assessment_period",
+    "uc_is_adopter_in_first_year",
+    "uc_is_foster_parent_or_new_friend_or_family_carer",
+    "uc_is_in_pregnancy_or_post_confinement_period",
+    "uc_is_responsible_foster_parent_of_child_under_one",
+    "uc_is_student_with_no_work_related_requirements",
+    "uc_is_temporarily_absent_from_claimant_household",
+    "uc_migrated",
+    "uc_receives_new_claimant_health_element",
+    "unit_and_investment_trusts",
+    "war_widows_pension_reported",
+    "wine_draught_share",
+    "wine_litres",
+    "working_tax_credit_run_on",
+    "would_claim_adult_dependants_grant",
+    "would_claim_bursary_fund_16_to_19",
+    "would_claim_care_to_learn",
+    "would_claim_childcare_grant",
+    "would_claim_disabled_students_allowance",
+    "would_claim_parents_learning_allowance",
+    "would_claim_travel_grant",
+    "would_claim_uc_at_legacy_closure"
+  ],
+  "registry_diff_count": 16,
+  "registry_diff_actually_absent": [
+    "additional_residential_property_purchased",
+    "defined_benefit_pension_share",
+    "directly_held_shares",
+    "early_years_school_start_deferred",
+    "is_full_year_category_a_director",
+    "local_bus_uncapped_single_fare",
+    "non_residential_property_purchased",
+    "non_residential_property_secured_debt",
+    "other_residential_property_secured_debt",
+    "pension_annual_allowance_taxable_lump_sum_death_benefits",
+    "pension_contributions_via_salary_sacrifice_pre_2015",
+    "pip_dl_points",
+    "pip_m_points",
+    "salary_sacrifice_returned_to_income",
+    "unit_and_investment_trusts",
+    "war_widows_pension_reported"
+  ],
+  "registry_diff_already_stored": [],
+  "targeted_presence": {
+    "additional_residential_property_purchased": false,
+    "defined_benefit_pension_share": false,
+    "directly_held_shares": false,
+    "early_years_school_start_deferred": false,
+    "is_full_year_category_a_director": false,
+    "local_bus_uncapped_single_fare": false,
+    "non_residential_property_purchased": false,
+    "non_residential_property_secured_debt": false,
+    "other_residential_property_secured_debt": false,
+    "pension_annual_allowance_taxable_lump_sum_death_benefits": false,
+    "pension_contributions_via_salary_sacrifice_pre_2015": false,
+    "pip_dl_points": false,
+    "pip_m_points": false,
+    "salary_sacrifice_returned_to_income": false,
+    "unit_and_investment_trusts": false,
+    "war_widows_pension_reported": false
+  },
+  "microdata_values_read": false,
+  "process_seconds_including_secret": 15.949
+}
diff --git a/.job/index-evidence.py b/.job/index-evidence.py
new file mode 100755
index 00000000..a7844211
--- /dev/null
+++ b/.job/index-evidence.py
@@ -0,0 +1,261 @@
+#!/usr/bin/env python3
+"""Index completed local evidence without granting an acceptance or publishing it."""
+
+from __future__ import annotations
+
+import argparse
+import hashlib
+import json
+import shutil
+import subprocess
+from pathlib import Path
+
+TAG = "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a"
+DATA_SHA = "64f83900ca3a1242fb017585234844ce89f743d32e41cd2b6558057777f914aa"
+
+
+def require(condition: bool, message: str) -> None:
+    if not condition:
+        raise ValueError(message)
+
+
+def digest(path: Path) -> str:
+    result = hashlib.sha256()
+    with path.open("rb") as stream:
+        for block in iter(lambda: stream.read(1024 * 1024), b""):
+            result.update(block)
+    return result.hexdigest()
+
+
+def main() -> None:
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument("--candidate", type=Path, required=True)
+    parser.add_argument("--evidence", type=Path, required=True)
+    parser.add_argument("--preparation", required=True)
+    args = parser.parse_args()
+    candidate = args.candidate.resolve()
+    evidence = args.evidence.resolve()
+    bundle_path = candidate / "src/policyengine/data/bundle/manifest.json"
+    bundle = json.loads(bundle_path.read_text())
+    uk = bundle["data_releases"]["uk"]
+    require(uk["build_id"] == TAG, "Unexpected UK cut")
+    require(
+        bundle["packages"]["policyengine-uk"]["version"] == "2.147.2",
+        "Unexpected model",
+    )
+    require(
+        bundle["packages"]["policyengine-core"]["version"] == "3.33.3",
+        "Unexpected core",
+    )
+    require(
+        not uk.get("regional_source_manifest_uri"), "National-only release required"
+    )
+    require(
+        set(uk.get("region_datasets", {})) <= {"national"},
+        "Regional output routes remain",
+    )
+    require(
+        uk["datasets"][uk["default_dataset"]]["sha256"] == DATA_SHA,
+        "Unexpected national data bytes",
+    )
+    head = subprocess.check_output(
+        ["git", "-C", str(candidate), "rev-parse", "HEAD"], text=True
+    ).strip()
+    subprocess.run(
+        [
+            "git",
+            "-C",
+            str(candidate),
+            "merge-base",
+            "--is-ancestor",
+            args.preparation,
+            head,
+        ],
+        check=True,
+    )
+    require(
+        head != args.preparation,
+        "Mechanical certification has not committed a candidate",
+    )
+    require(
+        not subprocess.check_output(
+            [
+                "git",
+                "-C",
+                str(candidate),
+                "status",
+                "--porcelain",
+                "--untracked-files=no",
+            ],
+            text=True,
+        ).strip(),
+        "Evidence must bind an unchanged committed candidate",
+    )
+    bundle_digest = digest(bundle_path)
+    required = [
+        "producer-gates.json",
+        "replay.json",
+        "budget-inputs.json",
+        "reform-contract.json",
+        "attribution.json",
+        "old-environment.txt",
+        "materialize-old.json",
+        "materialize-new.json",
+    ]
+    for name in required:
+        require((evidence / name).is_file(), f"Missing evidence: {name}")
+    records = {
+        name: json.loads((evidence / name).read_text())
+        for name in required
+        if name.endswith(".json")
+    }
+    for name in (
+        "replay.json",
+        "budget-inputs.json",
+        "reform-contract.json",
+        "attribution.json",
+    ):
+        binding = (
+            records[name]["binding"] if name == "attribution.json" else records[name]
+        )
+        require(
+            binding.get("candidate_commit") == head, f"Stale candidate binding: {name}"
+        )
+        require(
+            binding.get("bundle_manifest_sha256") == bundle_digest,
+            f"Stale bundle binding: {name}",
+        )
+        require(
+            binding.get("bundle_uk_build_id") == TAG, f"Different cut binding: {name}"
+        )
+    producer = records["producer-gates.json"]
+    require(
+        producer.get("candidate_commit") == args.preparation,
+        "Stale producer preparation binding",
+    )
+    require(
+        producer.get("status") == "pass" and producer.get("cut_tag") == TAG,
+        "Producer gates did not pass on this cut",
+    )
+    require(
+        producer.get("dataset_sha256") == DATA_SHA,
+        "Producer gates identify other data bytes",
+    )
+    for label in ("old", "new"):
+        receipt = records[f"materialize-{label}.json"]
+        require(
+            receipt.get("candidate_commit") == args.preparation,
+            f"Stale {label} materialization binding",
+        )
+        require(receipt.get("status") == "pass", f"Failed {label} materialization")
+    for suffix in (
+        "uc_floor.json",
+        "certify.log",
+        "certify.timings.jsonl",
+        "pr-body.md",
+    ):
+        source = candidate.parent / f"{candidate.name}.{suffix}"
+        require(source.is_file(), f"Missing tool artifact: {source}")
+        shutil.copyfile(source, evidence / f"mechanical-{suffix}")
+    timings = {}
+    for line in (
+        (evidence / "mechanical-certify.timings.jsonl").read_text().splitlines()
+    ):
+        record = json.loads(line)
+        timings[record["step"]] = record
+    for name in ("certify-data", "TROs", "snapshots", "strict-check", "tests"):
+        require(
+            timings.get(name, {}).get("returncode") == 0,
+            f"No successful final mechanical stage: {name}",
+        )
+    uc = json.loads((evidence / "mechanical-uc_floor.json").read_text())
+    require(
+        uc.get("check") == "uk_uc_floor" and isinstance(uc.get("passed"), bool),
+        "No measured UC-floor receipt",
+    )
+    require(
+        uc.get("model_version") == "2.147.2" and TAG in uc.get("dataset", ""),
+        "Stale UC-floor receipt",
+    )
+    require(
+        timings.get("UC-floor", {}).get("returncode") == (0 if uc["passed"] else 1),
+        "UC-floor timing disagrees with measured receipt",
+    )
+    replay = records["replay.json"]
+    require(
+        replay.get("status") == "unavailable",
+        "Update the reviewed exception before changing replay status",
+    )
+    require(
+        not records["attribution.json"].get("synthetic_fixture"),
+        "Synthetic attribution is not release evidence",
+    )
+    gate_statuses = {
+        "budget_inputs": records["budget-inputs.json"]["budget_inputs"]["result"][
+            "status"
+        ],
+        "supported_years": records["budget-inputs.json"]["supported_years"]["result"][
+            "status"
+        ],
+        "reform_contract": records["reform-contract.json"]["result"]["status"],
+    }
+    for name, status in gate_statuses.items():
+        require(
+            status in {"pass", "needs_review"},
+            f"Gate {name} is {status}; resolve before indexing completed evidence",
+        )
+    # The runner appends the index step after this command exits, and tee is
+    # still writing this command's own log. Index only immutable snapshots.
+    outer_timings = evidence / "timings.jsonl"
+    require(outer_timings.is_file(), "Missing sequential runner timings")
+    shutil.copyfile(outer_timings, evidence / "timings-before-index.jsonl")
+    live_files = {"evidence-index.json", "evidence-index.log", "timings.jsonl"}
+    index = {
+        "schema_version": 1,
+        "status": "evidence collected; review required; replay unavailable",
+        "preparation_commit": args.preparation,
+        "candidate_commit": head,
+        "bundle_manifest_sha256": bundle_digest,
+        "cut_tag": uk["build_id"],
+        "model": "2.147.2",
+        "core": "3.33.3",
+        "regional_outputs": "absent",
+        "replay": replay,
+        "qualification_gates": gate_statuses,
+        "mechanical_stages": {
+            name: timings[name]["returncode"]
+            for name in (
+                "certify-data",
+                "TROs",
+                "snapshots",
+                "strict-check",
+                "UC-floor",
+                "tests",
+            )
+        },
+        "excluded_live_files": sorted(live_files),
+        "files": [
+            {
+                "path": file.name,
+                "sha256": digest(file),
+                "size_bytes": file.stat().st_size,
+            }
+            for file in sorted(evidence.iterdir())
+            if file.is_file() and file.name not in live_files
+        ],
+        "human_actions": [
+            "Review UC-floor and gate results; record any explicit fresh acceptance.",
+            "Map every flagged attribution move or retain its unattributed label and limitation.",
+            "Confirm old build 810315ac2, inferred from pinned producer certification path.",
+            "Replay remains unavailable until the Microcosm replay fix lands.",
+            "Verify/create certification issue, replace PR-body placeholder, add issue changelog.",
+            "Run normal format/lint/PR checks; evidence must bind final candidate if it changes.",
+            "Push only the reviewed certification branch and open a canonical draft stacked on #704.",
+        ],
+    }
+    (evidence / "evidence-index.json").write_text(json.dumps(index, indent=2) + "\n")
+    print(json.dumps({"candidate_commit": head, "status": index["status"]}))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/.job/metadata/new-release_manifest.json b/.job/metadata/new-release_manifest.json
new file mode 100644
index 00000000..32f1ab80
--- /dev/null
+++ b/.job/metadata/new-release_manifest.json
@@ -0,0 +1,115 @@
+{
+ "schema_version": 1,
+ "data_package": {
+  "name": "microcosm-data",
+  "version": "0.1.0"
+ },
+ "default_datasets": {
+  "national": "microcosm_uk_2024_25"
+ },
+ "build": {
+  "build_id": "microcosm-uk-2024-25-national",
+  "built_at": "2026-10-23T09:57:14.344348+00:00",
+  "built_with_core_package": {
+   "name": "policyengine-core",
+   "version": "3.33.3"
+  },
+  "built_with_model_package": {
+   "name": "policyengine-uk",
+   "version": "2.147.2"
+  },
+  "attempt_id": "uk-frs-calibration-attempt-20261023T075039Z-a133f37a",
+  "cut_tag": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a"
+ },
+ "compatible_core_packages": [
+  {
+   "name": "policyengine-core",
+   "specifier": "==3.33.3"
+  }
+ ],
+ "compatible_model_packages": [
+  {
+   "name": "policyengine-uk",
+   "specifier": "==2.147.2"
+  }
+ ],
+ "artifacts": {
+  "microcosm_uk_2024_25": {
+   "kind": "microdata",
+   "path": "microcosm_uk_2024_25.h5",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a",
+   "sha256": "64f83900ca3a1242fb017585234844ce89f743d32e41cd2b6558057777f914aa"
+  },
+  "microcosm_uk_2024_25_calibration": {
+   "kind": "calibration",
+   "path": "microcosm_uk_2024_25_calibration.npz",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a",
+   "sha256": "074872730ad5678cdb4e483d0141ece912be65d6bc032d01bf756f2c3868f2a7"
+  },
+  "calibration_diagnostics": {
+   "kind": "diagnostics",
+   "path": "calibration_diagnostics.json",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a",
+   "sha256": "0c975ad04a2d1c0207c3be480d68f645c3a1b24f786c3eef76d6e984dfea0637"
+  },
+  "release_certification": {
+   "kind": "diagnostics",
+   "path": "release_certification.json",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a",
+   "sha256": "633661c40492645236c0d286576c6206d1fe87609ac7ce90234f625397b9260c"
+  },
+  "terminal_gates": {
+   "kind": "diagnostics",
+   "path": "terminal_gates.json",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a",
+   "sha256": "dac50ba94464ca4e2b494fd8ffcd04e47da6dd6e49e9f53bdb7384de7983097f"
+  },
+  "release_cut_gates": {
+   "kind": "diagnostics",
+   "path": "release_cut_gates.json",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a",
+   "sha256": "19f9b8bde4cafcfd179607541c5c8fa4247c97858a4ace7a1838bbacd223bc17"
+  },
+  "spine_gates": {
+   "kind": "diagnostics",
+   "path": "spine_gates.json",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a",
+   "sha256": "7959f2a19b6535e67f8d9cb2c37ee68f027892ca10d9d8422be18a8261798f34"
+  },
+  "score_vs_enhanced_frs": {
+   "kind": "diagnostics",
+   "path": "score_vs_enhanced_frs.json",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a",
+   "sha256": "cb4dcde88bf016e0646cdc46a9ab9adf9973c77b16a0cb9e1a25987d57b212ab"
+  }
+ },
+ "description": "UK national calibration pipeline release assembled from attempt uk-frs-calibration-attempt-20261023T075039Z-a133f37a at immutable cut microcosm-uk-2024-25-national-20261023T075039Z-a133f37a.",
+ "publisher_labels": {
+  "dwp": "Department for Work and Pensions",
+  "hmrc": "HM Revenue and Customs",
+  "isc": "Independent Schools Council",
+  "obr": "Office for Budget Responsibility",
+  "ons": "Office for National Statistics",
+  "scotgov": "Scottish Government",
+  "slc": "Student Loans Company",
+  "voa": "Valuation Office Agency",
+  "dfe": "Department for Education",
+  "dft": "Department for Transport",
+  "orr": "Office of Rail and Road",
+  "welshgov": "Welsh Government",
+  "dfi_ni": "Department for Infrastructure (Northern Ireland)",
+  "nithc": "Northern Ireland Transport Holding Company",
+  "mhclg": "Ministry of Housing, Communities and Local Government",
+  "dfc_ni": "Department for Communities (Northern Ireland)",
+  "microcosm": "Microcosm derived statistics",
+  "desnz": "Department for Energy Security and Net Zero"
+ }
+}
\ No newline at end of file
diff --git a/.job/metadata/old-release_manifest.json b/.job/metadata/old-release_manifest.json
new file mode 100644
index 00000000..c4b8e658
--- /dev/null
+++ b/.job/metadata/old-release_manifest.json
@@ -0,0 +1,115 @@
+{
+ "schema_version": 1,
+ "data_package": {
+  "name": "microcosm-data",
+  "version": "0.1.0"
+ },
+ "default_datasets": {
+  "national": "microcosm_uk_2024_25"
+ },
+ "build": {
+  "build_id": "microcosm-uk-2024-25-national",
+  "built_at": "2026-10-19T10:17:15.545356+00:00",
+  "built_with_core_package": {
+   "name": "policyengine-core",
+   "version": "3.33.3"
+  },
+  "built_with_model_package": {
+   "name": "policyengine-uk",
+   "version": "2.132.6"
+  },
+  "attempt_id": "uk-frs-calibration-attempt-20261019T083323Z-6cba5d71",
+  "cut_tag": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71"
+ },
+ "compatible_core_packages": [
+  {
+   "name": "policyengine-core",
+   "specifier": "==3.33.3"
+  }
+ ],
+ "compatible_model_packages": [
+  {
+   "name": "policyengine-uk",
+   "specifier": "==2.132.6"
+  }
+ ],
+ "artifacts": {
+  "microcosm_uk_2024_25": {
+   "kind": "microdata",
+   "path": "microcosm_uk_2024_25.h5",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71",
+   "sha256": "810ba980005a8b907cb51464279fa5e0125464b7af9a4871b7f6744c30951ece"
+  },
+  "microcosm_uk_2024_25_calibration": {
+   "kind": "calibration",
+   "path": "microcosm_uk_2024_25_calibration.npz",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71",
+   "sha256": "104a8253bf1aebc91147c9bbb40e2603d1025317ba8c6b991383d5e4d91852c2"
+  },
+  "calibration_diagnostics": {
+   "kind": "diagnostics",
+   "path": "calibration_diagnostics.json",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71",
+   "sha256": "34978e2bfde3f2641769c9c3a8f60455c59ecae5a50524b359d74388d7edc888"
+  },
+  "release_certification": {
+   "kind": "diagnostics",
+   "path": "release_certification.json",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71",
+   "sha256": "6a68cc93595fc5a7ad451d75a902523c25659de27014f50431dd5e19af5bd959"
+  },
+  "terminal_gates": {
+   "kind": "diagnostics",
+   "path": "terminal_gates.json",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71",
+   "sha256": "e29439d5ce0ea098d32579284ac06f26f086feba2fb8d12d1e88ac9a54840808"
+  },
+  "release_cut_gates": {
+   "kind": "diagnostics",
+   "path": "release_cut_gates.json",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71",
+   "sha256": "c91c1364d9c3be98493039d4e1dd8c2256881a434c1f124dd2ed0701bfaf7751"
+  },
+  "spine_gates": {
+   "kind": "diagnostics",
+   "path": "spine_gates.json",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71",
+   "sha256": "6dbba976dd1304c07da531382cecb11c9cb69512de0ee65126b71f032a0f37d6"
+  },
+  "score_vs_enhanced_frs": {
+   "kind": "diagnostics",
+   "path": "score_vs_enhanced_frs.json",
+   "repo_id": "policyengine/populace-uk-private",
+   "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71",
+   "sha256": "ba8a675f36e7c836bf5521e5933e21bb689442734b7340e05cabd3e0cc55323d"
+  }
+ },
+ "description": "UK national calibration pipeline release assembled from attempt uk-frs-calibration-attempt-20261019T083323Z-6cba5d71 at immutable cut microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71.",
+ "publisher_labels": {
+  "dwp": "Department for Work and Pensions",
+  "hmrc": "HM Revenue and Customs",
+  "isc": "Independent Schools Council",
+  "obr": "Office for Budget Responsibility",
+  "ons": "Office for National Statistics",
+  "scotgov": "Scottish Government",
+  "slc": "Student Loans Company",
+  "voa": "Valuation Office Agency",
+  "dfe": "Department for Education",
+  "dft": "Department for Transport",
+  "orr": "Office of Rail and Road",
+  "welshgov": "Welsh Government",
+  "dfi_ni": "Department for Infrastructure (Northern Ireland)",
+  "nithc": "Northern Ireland Transport Holding Company",
+  "mhclg": "Ministry of Housing, Communities and Local Government",
+  "dfc_ni": "Department for Communities (Northern Ireland)",
+  "microcosm": "Microcosm derived statistics",
+  "desnz": "Department for Energy Security and Net Zero"
+ }
+}
\ No newline at end of file
diff --git a/.job/pr704-HANDOFF.md b/.job/pr704-HANDOFF.md
new file mode 100644
index 00000000..d0029679
--- /dev/null
+++ b/.job/pr704-HANDOFF.md
@@ -0,0 +1,44 @@
+# PR #704 review-fix handoff
+
+- Draft PR: https://github.com/PolicyEngine/policyengine.py/pull/704
+- Branch: `budget-local-data-manifest`; confirmed reviewed remote head `fd4e099ed3ff768041f3a537e6e38527c07b0bbf`.
+- Validated implementation head SHA: `0d7bf1a641d61b8951f47ab12d8d4867f57df140`.
+- The following commit updates only this handoff. Its exact final head is recorded in `.job/HEAD` and `.job/local-data-manifest.bundle`.
+
+## Fixes
+
+- Scan every TRO definition of the manifest artifact ID, including direct graph nodes, nested definitions and artifact references; require one distinct valid SHA-256 assertion. Preserve canonical inline/location checks.
+- Validate both local and fetched manifest artifacts after revision rewriting. The certified/default artifact must be present and anchored; every overlapping managed artifact must agree with bundled repo/path/revision, repository type and pinned SHA-256. Compare identity fields separately to prevent ambiguous URI concatenation. Primary manifests may omit separately pinned regional artifacts and overlays.
+- Share the existing release-relative diagnostics/evidence normalization with certification. Exact-pair model/core/build and fingerprint acceptance logic remains unchanged; unset fetch/cache/offline behavior is preserved for consistent bundles.
+- Replace function attributes with a typed callable cache facade.
+- Make the documented export conditional on successful download/verification/atomic replacement; remove release coordination from permanent public docs.
+- PR body records the invariants and both downstream actions. No other repository was edited.
+
+## Validation
+
+**3,322 unique cases passed; five skipped.**
+
+- Initial local/TRO-pin run: 88 passed, including two 80-example Hypothesis properties (byte mutation and duplicate placement).
+- Broad 56-module provenance/certification/disclosure run: 3,256 passed, five skipped, one failure from the old missing-default error-message assertion. It now refuses earlier in the shared validator; the corrected complete TRO regeneration module passed all 40 cases on rerun.
+- Release/bootstrap-focused run: 189 passed, including 59 cases outside the broad selection and two new default-anchor cases.
+- Final affected manifest/certification run: 280 passed, including all 82 local-manifest cases, all 12 TRO-pin cases and four new repo/path-boundary cases. No failures remain in pytest's failure cache.
+- `make format`, `make lint`, focused `mypy --follow-imports=silent src/policyengine/provenance/manifest.py`, `git diff --check`, and generated receipt schema checks passed. Documentation shell block passes `bash -n`.
+- Every pytest invocation checked load1 <=110 and available disk >=55,000,000,000 bytes; no pytest workers. Observed load1 9.04–19.02; available disk 144.3–146.9 GB decimal. Tests used Python 3.13.9.
+- Detailed logs and selected modules remain under `.job/`. No private manifests or microdata were committed.
+
+## Downstream actions
+
+- Dashboard: refresh vendored `tests/fixtures/w4/execution_receipt.v1.schema.json` when upgrading.
+- Scorecard: `pipeline/uk_replay_variants.py:730` retains the setting in offline isolation, while `:750` requires exact runtime bundle equality. Clear `POLICYENGINE_*_DATA_MANIFEST_FILE` during isolation.
+- Leave the PR draft; do not merge. No versions, country pins, data or certified releases changed.
+
+## Workspace Git metadata and delivery
+
+The protected workspace `.git` points into the caller's repository. All mutable Git metadata is instead inside this workspace:
+
+```bash
+GIT_DIR="$PWD/.job/git-copy/.git" GIT_WORK_TREE="$PWD" git status
+GIT_DIR="$PWD/.job/git-copy/.git" GIT_WORK_TREE="$PWD" git log -4 --oneline
+```
+
+The original pointer and caller checkout were not changed. `.job/local-data-manifest.bundle` is portable relative to main `485dc23302ec4abc6a5f7df5c6c03b8525f64c4d`. The single final push is attempted after committing this handoff; its outcome is recorded in `.job/PUSH_RESULT.txt`. `.job/HEAD` contains the final branch SHA. No history was rewritten and nothing was pushed to main or master.
diff --git a/.job/rehearsal-timings.csv b/.job/rehearsal-timings.csv
new file mode 100644
index 00000000..3d405c89
--- /dev/null
+++ b/.job/rehearsal-timings.csv
@@ -0,0 +1,73 @@
+attempt,step,started_at,elapsed_seconds,returncode,load1,free_bytes,error
+initial-diagnostic,model_surface:2.132.6,2026-10-23T11:32:12.427921+00:00,381.8603484169944,0,38.52587890625,120696770560,
+initial-diagnostic,model_surface:2.147.2,2026-10-23T11:38:34.308371+00:00,15.074302291002823,,18.54248046875,118710542336,
+initial,resolve-base,2026-10-23T11:39:17.488467+00:00,2.14441879199876,0,,,
+initial,writer-check,2026-10-23T11:39:19.694901+00:00,3.019312666998303,0,,,
+initial,national-manifest,2026-10-23T11:39:22.715322+00:00,12.15035275000264,0,,,
+initial,old-bundle,2026-10-23T11:39:34.873822+00:00,0.9380026660001022,0,,,
+initial,regional-manifest,2026-10-23T11:39:35.812923+00:00,0.12825395799882244,0,,,
+initial,model_surface:2.132.6,2026-10-23T11:39:35.941365+00:00,148.88913045799563,0,39.75244140625,118139797504,
+initial,model_surface:2.147.2,2026-10-23T11:42:04.836620+00:00,164.33662383299816,0,41.64404296875,117805809664,
+initial,git-worktree,2026-10-23T11:44:49.176423+00:00,1.0937937920025433,0,,,
+initial,initial-sync,2026-10-23T11:44:50.270698+00:00,419.76851829199586,0,25.1884765625,117976055808,
+initial,certify-data,2026-10-23T11:51:50.056992+00:00,21.854724040997098,0,64.32568359375,116202569728,
+initial,lock,2026-10-23T11:52:11.912712+00:00,4.146781625000585,0,65.6708984375,116156506112,
+initial,certified-sync,2026-10-23T11:52:16.059873+00:00,24.32709679099935,0,65.6708984375,116114145280,
+initial,release-lock,2026-10-23T11:52:40.389076+00:00,0.8149001670026337,0,57.1171875,115941769216,
+initial,TROs,2026-10-23T11:52:41.204556+00:00,6.342896082998777,0,57.1171875,115943071744,
+initial,constant-rewrite,2026-10-23T11:52:47.554261+00:00,257.85679700000037,0,54.41455078125,115937558528,
+initial,regional-constant-rewrite,2026-10-23T11:57:05.440358+00:00,3.7874997360631824e-05,0,,,
+initial,snapshots,2026-10-23T11:57:05.441019+00:00,432.5181601660006,0,39.1220703125,115074195456,
+initial,strict-check,2026-10-23T12:04:18.007370+00:00,25.098915749993466,0,37.77294921875,114763296768,
+initial,UC-floor,2026-10-23T12:04:43.151241+00:00,462.22260950000054,0,36.3095703125,114737799168,
+initial,check-pytest-xdist,2026-10-23T12:12:25.431624+00:00,1.4604139580042101,1,29.1318359375,114805809152,
+initial,install-pytest-xdist,2026-10-23T12:12:26.896729+00:00,3.489559957997699,0,29.1318359375,114805747712,
+initial,tests,2026-10-23T12:12:30.390716+00:00,3822.5370580839954,1,28.560546875,114822316032,
+initial,git add -A,2026-10-23T13:16:13.093784+00:00,4.137483207996411,0,70.18212890625,105114398720,
+initial,"git commit -q -m 'Certify Microcosm UK cut microcosm-uk-2024-25-national-20261023T075039Z-a133f37a
+
+Co-Authored-By: Claude Opus 5.5 '",2026-10-23T13:16:17.318467+00:00,1.6272991670048214,0,70.18212890625,105110925312,
+fixed,resolve-base,2026-10-23T13:08:31.736757+00:00,0.07518733399774646,0,,,
+fixed,writer-check,2026-10-23T13:08:32.210038+00:00,2.5503954579980928,0,,,
+fixed,national-manifest,2026-10-23T13:08:34.766453+00:00,9.839460499999404,0,,,
+fixed,old-bundle,2026-10-23T13:08:44.645972+00:00,0.5265442090021679,0,,,
+fixed,regional-manifest,2026-10-23T13:08:45.179401+00:00,0.12422808300470933,0,,,
+fixed,model_surface:2.132.6,2026-10-23T13:08:45.306960+00:00,202.086186833003,0,74.24169921875,106981933056,
+fixed,model_surface:2.147.2,2026-10-23T13:12:07.401037+00:00,238.94449395799893,0,51.7138671875,105636188160,
+fixed,git-worktree,2026-10-23T13:16:06.564867+00:00,6.426752667000983,0,,,
+fixed,initial-sync,2026-10-23T13:16:13.014860+00:00,91.17135974999837,,70.18212890625,105114841088,
+final-mechanical,resolve-base,2026-10-23T13:22:48.705892+00:00,3.65955025000585,0,,,
+final-mechanical,writer-check,2026-10-23T13:22:52.373108+00:00,2.902177292002307,0,,,
+final-mechanical,national-manifest,2026-10-23T13:22:55.281919+00:00,11.46702849999565,0,,,
+final-mechanical,old-bundle,2026-10-23T13:23:06.780095+00:00,0.5221937499954947,0,,,
+final-mechanical,regional-manifest,2026-10-23T13:23:07.332526+00:00,0.24198158299986972,0,,,
+final-mechanical,model_surface:2.132.6,2026-10-23T13:23:07.574735+00:00,233.14734829100053,0,75.80859375,103674413056,
+final-mechanical,model_surface:2.147.2,2026-10-23T13:27:00.732630+00:00,186.68840604200523,0,70.81884765625,101664190464,
+final-mechanical,git-worktree,2026-10-23T13:30:07.484402+00:00,4.439784458001668,0,,,
+final-mechanical,initial-sync,2026-10-23T13:30:11.930255+00:00,204.9921843330012,0,79.6884765625,101989212160,
+final-mechanical,certify-data,2026-10-23T13:33:37.021089+00:00,199.30662320800184,0,91.18896484375,104086351872,
+final-mechanical,lock,2026-10-23T13:36:56.496798+00:00,0.0371767499964335,,,,"resource guard: load1=127.64 (maximum 110), free=103.93 GB (minimum 55 decimal GB)."
+final-mechanical,resolve-base,2026-10-23T13:49:40.629599+00:00,0.3631557080007042,0,,,
+final-mechanical,resume-worktree-validation,2026-10-23T13:49:41.097314+00:00,4.394521542002622,0,,,
+final-mechanical,writer-check,2026-10-23T13:49:45.507687+00:00,4.9102465830001165,0,,,
+final-mechanical,national-manifest,2026-10-23T13:49:50.445108+00:00,14.786858125000435,0,,,
+final-mechanical,old-bundle,2026-10-23T13:50:05.309038+00:00,0.7071054159969208,0,,,
+final-mechanical,regional-manifest,2026-10-23T13:50:06.016660+00:00,0.2176802920002956,0,,,
+final-mechanical,model_surface:2.132.6,2026-10-23T13:50:06.234568+00:00,191.6306953750027,0,50.857421875,104290840576,
+final-mechanical,model_surface:2.147.2,2026-10-23T13:53:17.877585+00:00,160.8386956250033,0,44.1171875,104107941888,
+final-mechanical,initial-sync,2026-10-23T13:55:58.727413+00:00,164.3704907079955,0,60.30615234375,103843016704,
+final-mechanical,certify-data,2026-10-23T13:58:43.188578+00:00,124.50857891699707,0,70.70068359375,102866767872,
+final-mechanical,lock,2026-10-23T14:00:47.698415+00:00,33.57901754199702,0,54.76806640625,103367610368,
+final-mechanical,certified-sync,2026-10-23T14:01:21.385063+00:00,138.304973833001,0,37.154296875,103260262400,
+final-mechanical,release-lock,2026-10-23T14:03:39.752243+00:00,4.718372834002366,0,22.58447265625,102819999744,
+final-mechanical,TROs,2026-10-23T14:03:44.511029+00:00,84.54258745900006,0,21.89697265625,102812872704,
+final-mechanical,constant-rewrite,2026-10-23T14:05:09.064180+00:00,67.11932424999395,0,21.1513671875,101623799808,
+final-mechanical,regional-constant-rewrite,2026-10-23T14:06:16.232244+00:00,1.9874998542945832e-05,0,,,
+final-mechanical,snapshots,2026-10-23T14:06:16.232648+00:00,2333.7267790420083,0,61.04248046875,101463547904,
+final-mechanical,strict-check,2026-10-23T14:45:10.093512+00:00,49.002103374994476,0,101.62158203125,102513467392,
+final-mechanical,UC-floor,2026-10-23T14:45:59.143668+00:00,301.7782627080014,0,76.78271484375,100024487936,
+final-mechanical,generated-file-validation,2026-10-23T14:51:00.946424+00:00,3.1073072500003036,0,,,
+final-mechanical,stage-certification,2026-10-23T14:51:04.054267+00:00,0.6547722909890581,0,75.8173828125,100722311168,
+final-mechanical,"git commit -q -m 'Certify Microcosm UK cut microcosm-uk-2024-25-national-20261023T075039Z-a133f37a
+
+Co-Authored-By: Claude Opus 5.5 '",2026-10-23T14:51:04.709529+00:00,0.5087777079897933,0,75.8173828125,100722466816,
diff --git a/run-heavy.sh b/run-heavy.sh
new file mode 100755
index 00000000..760faf2f
--- /dev/null
+++ b/run-heavy.sh
@@ -0,0 +1,203 @@
+#!/usr/bin/env bash
+# Heavy phase only. Preparation must never invoke this script without --help.
+set -euo pipefail
+set +x
+
+ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
+BRANCH=certify-uk-a133f37a-national
+BASE_704=da2ae8fb8e97145093e35366686df99c1e38449e
+TAG=microcosm-uk-2024-25-national-20261023T075039Z-a133f37a
+STEPS='prepare-tool producer-gates materialize-old materialize-new old-environment prepare-repository certify replay-status budget-inputs reform-contract attribution evidence-index'
+START=prepare-tool
+RESUME=0
+while (($#)); do
+    case "$1" in
+        --help|-h)
+            cat <<'EOF'
+Usage: ./run-heavy.sh [--from STEP] [--resume-certification]
+
+Heavy phase only, after 2026-10-24 00:30Z and reservation clearance.
+Runs sequentially; each step checks reservation.py allowed uk-hub,
+load1 <=110 and free disk >=55,000,000,000 bytes. Stops on any failure.
+No push or PR is performed. Supply HF_TOKEN/HUGGING_FACE_TOKEN or agent-secret.
+
+PE_CERTIFY_STORAGE: private scratch outside Git (default /private/tmp/uk-a133f37a-national-UID).
+PE_CERTIFY_MODEL_REPO: read-only policyengine-uk repository with both model tags.
+PE_CERTIFY_RESERVATION_SCRIPT: fleet reservation.py (default ~/reviews/fleet-ops/tools/reservation.py).
+Steps: prepare-tool producer-gates materialize-old materialize-new old-environment
+       prepare-repository certify replay-status budget-inputs reform-contract attribution evidence-index
+--from reuses earlier artifacts in the same storage/workspace; it never marks them passing.
+--resume-certification is only for an interrupted, uncommitted tool worktree.
+EOF
+            exit 0 ;;
+        --from) [[ $# -ge 2 ]] || { echo '--from requires STEP' >&2; exit 64; }; START=$2; shift 2 ;;
+        --resume-certification) RESUME=1; shift ;;
+        *) echo "Unknown argument: $1" >&2; exit 64 ;;
+    esac
+done
+case " $STEPS " in *" $START "*) ;; *) echo "Unknown step: $START" >&2; exit 64 ;; esac
+
+STORAGE="${PE_CERTIFY_STORAGE:-/private/tmp/uk-a133f37a-national-$UID}"
+[[ "$STORAGE" == /* ]] || { echo 'PE_CERTIFY_STORAGE must be an absolute path' >&2; exit 64; }
+export PE_CERTIFY_RESERVATION_SCRIPT="${PE_CERTIFY_RESERVATION_SCRIPT:-$HOME/reviews/fleet-ops/tools/reservation.py}"
+export PE_CERTIFY_STORAGE="$STORAGE"
+MODEL_REPO="${PE_CERTIFY_MODEL_REPO:-$HOME/PolicyEngine/policyengine-uk}"
+HEAVY_REPO="$ROOT/.job/heavy-repo"
+CANDIDATE="$ROOT/.job/certification"
+EVIDENCE="$STORAGE/evidence"
+METADATA="$ROOT/.job/metadata"
+OLD_PYTHON="$STORAGE/venv-old/bin/python"
+NEW_PYTHON="$CANDIDATE/.venv/bin/python"
+export OMP_NUM_THREADS=4 OPENBLAS_NUM_THREADS=4 MKL_NUM_THREADS=4 NUMEXPR_NUM_THREADS=4
+export PYTHONUNBUFFERED=1
+unset POLICYENGINE_UK_DATA_MANIFEST_FILE POLICYENGINE_US_DATA_MANIFEST_FILE
+unset VIRTUAL_ENV GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
+
+source_git() {
+    if [[ -d "$ROOT/.job/git" ]]; then
+        git --git-dir="$ROOT/.job/git" --work-tree="$ROOT" "$@"
+    else
+        git -C "$ROOT" "$@"
+    fi
+}
+guard() {
+    python3 "$PE_CERTIFY_RESERVATION_SCRIPT" allowed uk-hub || return $?
+    python3 - "$ROOT" "$STORAGE" <<'PY'
+import datetime, os, pathlib, shutil, sys
+assert datetime.datetime.now(datetime.timezone.utc) >= datetime.datetime(2026, 10, 24, 0, 30, tzinfo=datetime.timezone.utc), 'Heavy phase may not start before 2026-10-24 00:30Z'
+load = os.getloadavg()[0]
+for name in sys.argv[1:]:
+    path = pathlib.Path(name).resolve()
+    while not path.exists():
+        path = path.parent
+    free = shutil.disk_usage(path).free
+    if load > 110 or free < 55_000_000_000:
+        raise SystemExit(f'HOLD: load1={load:.2f}, free={free} bytes at {path}')
+print(f'Guard passed: load1={load:.2f}; disk >=55 decimal GB')
+PY
+}
+
+# This guard precedes scratch directories, environment installation and downloads.
+guard
+mkdir -p "$STORAGE"
+chmod 700 "$STORAGE"
+STORAGE="$(cd -- "$STORAGE" && pwd -P)"
+export PE_CERTIFY_STORAGE="$STORAGE"
+python3 - "$STORAGE" <<'PY'
+from pathlib import Path
+import sys
+path = Path(sys.argv[1])
+if any((p / '.git').exists() for p in (path, *path.parents)):
+    raise SystemExit('PE_CERTIFY_STORAGE must be outside every Git repository')
+PY
+mkdir -p "$EVIDENCE" "$STORAGE/tmp" "$STORAGE/cache"
+export TMPDIR="$STORAGE/tmp" UV_CACHE_DIR="$STORAGE/cache/uv"
+export UV_PYTHON_INSTALL_DIR="$STORAGE/cache/python" HF_HOME="$STORAGE/cache/hf"
+export XDG_CACHE_HOME="$STORAGE/cache/xdg" MPLCONFIGDIR="$STORAGE/cache/matplotlib"
+export NUMBA_CACHE_DIR="$STORAGE/cache/numba"
+if [[ -z "${HUGGING_FACE_TOKEN:-${HF_TOKEN:-}}" ]]; then
+    IFS= read -r HUGGING_FACE_TOKEN < <(agent-secret get HUGGING_FACE_TOKEN maxghenis)
+fi
+export HUGGING_FACE_TOKEN="${HUGGING_FACE_TOKEN:-${HF_TOKEN:-}}"
+[[ -n "$HUGGING_FACE_TOKEN" ]] || { echo 'No Hugging Face token available' >&2; exit 1; }
+export HF_TOKEN="$HUGGING_FACE_TOKEN"
+source_git merge-base --is-ancestor "$BASE_704" HEAD
+[[ "$(source_git branch --show-current)" == "$BRANCH" ]] || { echo "Expected preparation branch $BRANCH" >&2; exit 1; }
+[[ -z "$(source_git status --porcelain --untracked-files=no)" ]] || { echo 'Commit tracked preparation changes before running' >&2; exit 1; }
+PREP_SHA="$(source_git rev-parse HEAD)"
+
+REACHED=0
+step() {
+    local name=$1
+    shift
+    if [[ "$name" == "$START" ]]; then REACHED=1; fi
+    if [[ "$REACHED" == 0 ]]; then return; fi
+    guard
+    printf '\nStarting %s\n' "$name"
+    local started=$SECONDS
+    local rc=0
+    # Wait for tee so logs are complete before another step indexes them.
+    # pipefail also propagates a log write failure.
+    "$@" 2>&1 | tee "$EVIDENCE/$name.log" || rc=$?
+    python3 - "$EVIDENCE/timings.jsonl" "$name" "$((SECONDS-started))" "$rc" <<'PY'
+import datetime, json, sys
+with open(sys.argv[1], 'a') as stream:
+    stream.write(json.dumps({'step': sys.argv[2], 'elapsed_seconds': int(sys.argv[3]), 'returncode': int(sys.argv[4]), 'finished_at': datetime.datetime.now(datetime.timezone.utc).isoformat()})+'\n')
+PY
+    [[ "$rc" == 0 ]] || { echo "Stopped at $name (exit $rc); retain receipts and use --from after resolving it" >&2; exit "$rc"; }
+}
+prepare_repository() {
+    [[ ! -e "$CANDIDATE" || "$RESUME" == 1 ]] || { echo 'Candidate exists; see handoff resume guidance' >&2; return 1; }
+    if [[ ! -d "$HEAVY_REPO/.git" ]]; then
+        local source
+        if [[ -d "$ROOT/.job/git" ]]; then source="$ROOT/.job/git"; else source="$(source_git rev-parse --absolute-git-dir)"; fi
+        git clone --quiet --no-hardlinks --single-branch --branch "$BRANCH" "$source" "$HEAVY_REPO" || return $?
+        git -C "$HEAVY_REPO" branch -m preparation-source || return $?
+        git -C "$HEAVY_REPO" remote set-url origin https://github.com/PolicyEngine/policyengine.py.git || return $?
+        # A literal file symlink is not matched by the repository's data/ pattern.
+        printf '/data\n/.venv/\n' >> "$HEAVY_REPO/.git/info/exclude" || return $?
+    fi
+    git -C "$HEAVY_REPO" cat-file -e "$PREP_SHA^{commit}" || return $?
+    [[ "$(git -C "$HEAVY_REPO" rev-parse preparation-source)" == "$PREP_SHA" ]] || {
+        echo 'Scratch repository belongs to another preparation commit; choose a fresh workspace' >&2; return 1;
+    }
+}
+old_environment() {
+    [[ -x "$OLD_PYTHON" ]] || uv venv --python 3.13 "$STORAGE/venv-old" || return $?
+    guard || return $?
+    uv pip install --python "$OLD_PYTHON" 'policyengine-uk==2.132.6' 'policyengine-core==3.33.3' tables || return $?
+    uv pip freeze --python "$OLD_PYTHON" > "$EVIDENCE/old-environment.txt"
+}
+certify() {
+    local resume_args=()
+    [[ "$RESUME" == 0 ]] || resume_args+=(--resume-worktree)
+    export PE_CERTIFY_TOOL_SOURCE_COMMIT
+    PE_CERTIFY_TOOL_SOURCE_COMMIT="$(git -C "$ROOT/.job/microcosm" rev-parse HEAD)" || return $?
+    # The tool runs snapshots, strict check, UC floor and the four-worker full
+    # suite sequentially. Its patched internal guard also checks reservations.
+    uv run --no-project --python 3.13 --with huggingface_hub \
+        python "$ROOT/.job/certify_uk_cut_in_policyengine_py.py" \
+        --pepy-repo "$HEAVY_REPO" --base-ref "$PREP_SHA" \
+        --branch "$BRANCH" --worktree "$CANDIDATE" \
+        --national-tag "$TAG" --drop-regional-release \
+        --model-version 2.147.2 --core-version 3.33.3 \
+        --test-workers 4 --pr-base budget-local-data-manifest "${resume_args[@]}"
+}
+evidence_index() {
+    python3 "$ROOT/.job/index-evidence.py" --candidate "$CANDIDATE" \
+        --evidence "$EVIDENCE" --preparation "$PREP_SHA"
+}
+
+step prepare-tool python3 "$ROOT/.job/prepare-tool.py"
+step producer-gates uv run --no-project --python 3.13 --with huggingface_hub \
+    python "$ROOT/.job/certification-evidence.py" producer-gates \
+    --metadata "$METADATA" --output "$EVIDENCE/producer-gates.json"
+for label in old new; do
+    step "materialize-$label" uv run --no-project --python 3.13 --with huggingface_hub \
+        python "$ROOT/.job/certification-evidence.py" materialize \
+        --label "$label" --metadata "$METADATA" --storage "$STORAGE"
+done
+step old-environment old_environment
+step prepare-repository prepare_repository
+step certify certify
+step replay-status "$NEW_PYTHON" "$ROOT/.job/certification-evidence.py" replay-unavailable \
+    --candidate "$CANDIDATE" --output "$EVIDENCE/replay.json"
+step budget-inputs "$NEW_PYTHON" "$ROOT/.job/certification-evidence.py" budget-inputs \
+    --candidate "$CANDIDATE" --old-python "$OLD_PYTHON" \
+    --old-data "$STORAGE/data/old.h5" --new-data "$STORAGE/data/new.h5" \
+    --output "$EVIDENCE/budget-inputs.json"
+step reform-contract "$NEW_PYTHON" "$ROOT/.job/certification-evidence.py" reform-contract \
+    --candidate "$CANDIDATE" --new-data "$STORAGE/data/new.h5" \
+    --output "$EVIDENCE/reform-contract.json"
+step attribution python3 "$ROOT/.job/run-attribution.py" run \
+    --candidate "$CANDIDATE" \
+    --old-python "$OLD_PYTHON" --new-python "$NEW_PYTHON" \
+    --old-data "$STORAGE/data/old.h5" --new-data "$STORAGE/data/new.h5" \
+    --old-sha256 810ba980005a8b907cb51464279fa5e0125464b7af9a4871b7f6744c30951ece \
+    --new-sha256 64f83900ca3a1242fb017585234844ce89f743d32e41cd2b6558057777f914aa \
+    --model-repo "$MODEL_REPO" --microcosm-repo "$ROOT/.job/microcosm" \
+    --old-build 810315ac2 --new-build 829c1bc26 \
+    --reservation-script "$PE_CERTIFY_RESERVATION_SCRIPT" \
+    --mapping "$ROOT/.job/attribution-mapping.json" --output "$EVIDENCE/attribution.json"
+step evidence-index evidence_index
+printf '\nEvidence retained in %s; human review is required before push/PR.\n' "$EVIDENCE"

From c1dfd0d92127af7bd676fdbd890667e4de7e67cb Mon Sep 17 00:00:00 2001
From: Max Ghenis 
Date: Fri, 23 Oct 2026 13:31:43 -0400
Subject: [PATCH 5/8] Record guarded heavy-phase handoff and portable branch
 bundle

---
 .job/HANDOFF.md              | 171 +++++++++++++++++++++++++++++------
 .job/preparation-checks.json |  66 ++++++++++++++
 .job/preparation.gitbundle   | Bin 0 -> 118960 bytes
 3 files changed, 207 insertions(+), 30 deletions(-)
 create mode 100644 .job/preparation-checks.json
 create mode 100644 .job/preparation.gitbundle

diff --git a/.job/HANDOFF.md b/.job/HANDOFF.md
index d0029679..80afb6e1 100644
--- a/.job/HANDOFF.md
+++ b/.job/HANDOFF.md
@@ -1,44 +1,155 @@
-# PR #704 review-fix handoff
+# Saturday a133f37a national certification preparation
 
-- Draft PR: https://github.com/PolicyEngine/policyengine.py/pull/704
-- Branch: `budget-local-data-manifest`; confirmed reviewed remote head `fd4e099ed3ff768041f3a537e6e38527c07b0bbf`.
-- Validated implementation head SHA: `0d7bf1a641d61b8951f47ab12d8d4867f57df140`.
-- The following commit updates only this handoff. Its exact final head is recorded in `.job/HEAD` and `.job/local-data-manifest.bundle`.
+Branch: `certify-uk-a133f37a-national`, based directly on reviewed #704 head
+`da2ae8fb8e97145093e35366686df99c1e38449e`. No #699 prerequisite was applied.
+No simulations, national H5 downloads/loads, suites, snapshots or UC-floor
+steps ran. Nothing was pushed; no issue or PR was opened.
 
-## Fixes
+## Commits and delivery
 
-- Scan every TRO definition of the manifest artifact ID, including direct graph nodes, nested definitions and artifact references; require one distinct valid SHA-256 assertion. Preserve canonical inline/location checks.
-- Validate both local and fetched manifest artifacts after revision rewriting. The certified/default artifact must be present and anchored; every overlapping managed artifact must agree with bundled repo/path/revision, repository type and pinned SHA-256. Compare identity fields separately to prevent ambiguous URI concatenation. Primary manifests may omit separately pinned regional artifacts and overlays.
-- Share the existing release-relative diagnostics/evidence normalization with certification. Exact-pair model/core/build and fingerprint acceptance logic remains unchanged; unset fetch/cache/offline behavior is preserved for consistent bundles.
-- Replace function attributes with a typed callable cache facade.
-- Make the documented export conditional on successful download/verification/atomic replacement; remove release coordination from permanent public docs.
-- PR body records the invariants and both downstream actions. No other repository was edited.
+| Commit | Step |
+| --- | --- |
+| `d730443d` | PR-body draft and scoped same-line re-cut exception |
+| `58e0536c` | Portable #1348 tool, twelve rehearsal patches and application receipts |
+| `36d79690` | Guarded 2×2 attribution, source definitions, schema and tiny fixture |
+| `bd701bdd` | Sequential runner, exact-cut evidence adapters and pinned metadata |
+| Final handoff commit | This handoff, light-check receipt and portable implementation bundle |
 
-## Validation
+The assigned `.git` pointer leads to sandbox-protected caller metadata and was
+preserved. Mutable Git metadata and the branch live in **`.job/git`** here:
 
-**3,322 unique cases passed; five skipped.**
+```bash
+GIT_DIR="$PWD/.job/git" GIT_WORK_TREE="$PWD" git log -5 --oneline
+GIT_DIR="$PWD/.job/git" GIT_WORK_TREE="$PWD" git status --short
+```
+
+Committed `.job/preparation.gitbundle` exports the four implementation commits
+through `bd701bdd`, requiring `da2ae8fb`; `git bundle verify` passed. It excludes
+its containing handoff commit. This preserves actual #704 branch ancestry even
+if Subfleet's salvage ref starts from the original detached checkout. In a new
+writable scratch checkout, fetch #704, import the bundle's
+`refs/heads/certify-uk-a133f37a-national`, and check out that branch. Do not use a
+salvage snapshot without #704 ancestry as the certification base. The previous
+committed #704 handoff is preserved in `.job/pr704-HANDOFF.md`.
+
+## Running the separate heavy phase
 
-- Initial local/TRO-pin run: 88 passed, including two 80-example Hypothesis properties (byte mutation and duplicate placement).
-- Broad 56-module provenance/certification/disclosure run: 3,256 passed, five skipped, one failure from the old missing-default error-message assertion. It now refuses earlier in the shared validator; the corrected complete TRO regeneration module passed all 40 cases on rerun.
-- Release/bootstrap-focused run: 189 passed, including 59 cases outside the broad selection and two new default-anchor cases.
-- Final affected manifest/certification run: 280 passed, including all 82 local-manifest cases, all 12 TRO-pin cases and four new repo/path-boundary cases. No failures remain in pytest's failure cache.
-- `make format`, `make lint`, focused `mypy --follow-imports=silent src/policyengine/provenance/manifest.py`, `git diff --check`, and generated receipt schema checks passed. Documentation shell block passes `bash -n`.
-- Every pytest invocation checked load1 <=110 and available disk >=55,000,000,000 bytes; no pytest workers. Observed load1 9.04–19.02; available disk 144.3–146.9 GB decimal. Tests used Python 3.13.9.
-- Detailed logs and selected modules remain under `.job/`. No private manifests or microdata were committed.
+From this workspace or the imported branch, **after 2026-10-24 00:30Z**:
 
-## Downstream actions
+```bash
+export PE_CERTIFY_STORAGE=/private/tmp/uk-a133f37a-national-"$UID"
+./run-heavy.sh
+```
 
-- Dashboard: refresh vendored `tests/fixtures/w4/execution_receipt.v1.schema.json` when upgrading.
-- Scorecard: `pipeline/uk_replay_variants.py:730` retains the setting in offline isolation, while `:750` requires exact runtime bundle equality. Clear `POLICYENGINE_*_DATA_MANIFEST_FILE` during isolation.
-- Leave the PR draft; do not merge. No versions, country pins, data or certified releases changed.
+Storage must be absolute, private, writable and outside every Git repository.
+All temporary/cache paths and evidence outputs go there. Supply HF_TOKEN or
+HUGGING_FACE_TOKEN; otherwise the runner reads agent-secret in memory. Needs
+uv, Python 3.13 access, gh authentication, private HF read access and network.
 
-## Workspace Git metadata and delivery
+Before every step: `~/reviews/fleet-ops/tools/reservation.py allowed uk-hub`,
+load1 ≤110 and free disk ≥55,000,000,000 bytes (decimal). Tool internal stages
+and individual attribution workers also recheck the reservation/resources.
+Thread caps and `--test-workers 4` are explicit; evidence steps run serially.
 
-The protected workspace `.git` points into the caller's repository. All mutable Git metadata is instead inside this workspace:
+The runner verifies/reconstructs `.job/microcosm`, creates `.job/heavy-repo` on
+`preparation-source`, then creates the requested certification branch in
+`.job/certification`, descending from the clean preparation commit. The exact
+command is in `run-heavy.sh`:
 
 ```bash
-GIT_DIR="$PWD/.job/git-copy/.git" GIT_WORK_TREE="$PWD" git status
-GIT_DIR="$PWD/.job/git-copy/.git" GIT_WORK_TREE="$PWD" git log -4 --oneline
+uv run --no-project --python 3.13 --with huggingface_hub \
+  python .job/certify_uk_cut_in_policyengine_py.py \
+  --pepy-repo "$PWD/.job/heavy-repo" --base-ref "$PREP_SHA" \
+  --branch certify-uk-a133f37a-national --worktree "$PWD/.job/certification" \
+  --national-tag microcosm-uk-2024-25-national-20261023T075039Z-a133f37a \
+  --drop-regional-release --model-version 2.147.2 --core-version 3.33.3 \
+  --test-workers 4 --pr-base budget-local-data-manifest
 ```
 
-The original pointer and caller checkout were not changed. `.job/local-data-manifest.bundle` is portable relative to main `485dc23302ec4abc6a5f7df5c6c03b8525f64c4d`. The single final push is attempted after committing this handoff; its outcome is recorded in `.job/PUSH_RESULT.txt`. `.job/HEAD` contains the final branch SHA. No history was rewritten and nothing was pushed to main or master.
+PREP_SHA and tool provenance are resolved by the runner. The tool performs
+lock/TROs, snapshots, strict check, UC floor, full suite and generated-file
+commit. Other steps verify producer gates, materialize both hash-pinned cuts,
+install old model/core, record replay unavailable, run the original #574
+contract and Budget-inputs gates with predecessor comparison, run attribution,
+and index evidence against the candidate commit/bundle. The uk-data resolver
+is unused because it resolves a different producer line. No push/PR flags.
+
+On failure, receipts remain under `$PE_CERTIFY_STORAGE/evidence/`.
+`--from STEP` resumes with earlier artifacts and the same preparation/storage.
+For an interrupted **uncommitted** tool run: `--from certify --resume-certification`.
+For an already committed candidate, resume at replay-status, budget-inputs,
+reform-contract, attribution or evidence-index; do not rerun certify over it.
+The final index refuses stale receipts and incomplete mechanical stages.
+
+## Runtime planning from the rehearsal
+
+Source `~/reviews/uk-hub/jobs/budget-certify-rehearsal/evidence/timings.csv` is
+copied to `.job/rehearsal-timings.csv`. Observations, not guarantees:
+
+| Step | Observed elapsed / planning basis |
+| --- | --- |
+| Tool/repository preparation | Worktree creation 1–6 s; fresh clone/patching unmeasured |
+| Producer gates / old+new materialization | Manifest read 10–15 s; complete artifact download/hash unmeasured, cache dependent |
+| Old environment | Installation unmeasured; old-model surface probes 149–233 s were a different operation |
+| Initial sync | 2.7–7.0 min |
+| certify-data | 22–199 s |
+| Lock / certified sync | 4–34 s / 24–138 s |
+| Release lock / TROs | 1–5 s / 6–85 s |
+| Constants | 1.1–4.3 min |
+| Snapshots | 7.2–38.9 min |
+| Strict check | 25–49 s |
+| UC floor | 5.0–7.7 min |
+| Full suite, four workers | 63.7 min: 4,304 passed, 14 skipped, **3 environmental failures**; no passing-duration measurement |
+| Replay-unavailable receipt | Unmeasured, metadata only; no replay dispatch |
+| Budget-inputs / supported years | No standalone measurement in CSV |
+| Full reform contract, seed 0 / max_examples 20 | No standalone measurement; research's 135–145 min estimate covers the older **whole qualification battery**, not this step |
+| Four-cell attribution | Unmeasured; budget all four serial national runs |
+| Evidence index | Unmeasured, local hashes/metadata |
+
+This national-only route avoids the rehearsal's regional model-surface probes.
+Outside-Git TMPDIR addresses the old suite's temporary-directory failures;
+that anticipated fix has not been tested here and is not passing evidence.
+
+## Patch provenance and light verification
+
+#1348 source: uk-cut-anyone at `61e25de8a673f4187b2d882acab02d94d4a6da99`.
+Direct application initially failed: upstream tool and one test each lacked a
+trailing blank line present in the rehearsal source. Separate compatibility
+patch restores those two lines. All twelve original files then clean-applied
+with git am --no-3way (0007 contains two commits). Separate local adaptation
+adds reservation checks, optional branch name and portable provenance. See
+`.job/tool-patches/README.md` and `.job/tool-patches.json`. No #699 patch included.
+
+Passed: shell syntax/help, Python AST/help, Ruff on new custom scripts,
+source/tree/patch digest verification, and ten attribution light checks for
+CLI handling, tiny weighted synthetic schema, missing cross cells and malformed
+schema rejection. Receipts: `.job/preparation-checks.json` and
+`.job/attribution-light-checks.json`. No repository tests were run.
+
+## Human work still required
+
+- Review real gates/snapshots; obtain fresh explicit acceptance for any UC-floor
+  or input gap. #685 does not supply new-cut acceptance. Replay stays
+  **unavailable** until the Microcosm replay fix lands, never a pass.
+- Review flagged moves against attached model PR/Microcosm histories. The
+  mapping file starts empty: path overlap is only a candidate, unmapped moves
+  stay **unattributed**. Add named reviewed mappings, then rerun attribution/index.
+  Coverage: 84 fiscal/legacy heads, all poverty definitions/HBAI groups,
+  FY2026-27–FY2030-31; failed cross cells retain errors and independent effects.
+- Confirm old build `810315ac22b81c15c008cfb2723ee99e18725fe4`, inferred from
+  the pinned producer certification path. Git verifies it precedes new build
+  `829c1bc26c0457c668fe1dfc1e866cb4a752654a`. Reports list missing-variable,
+  dependency and disclosure limits. Budget-inputs recomputes the direct
+  predecessor; older ever-usable lineage is explicitly not reconstructed.
+- Verify/create a certification **issue**, replace first-line
+  `Fixes #` in pr-body.md and add its changelog fragment.
+  #574 is a PR, not a suitable issue. Run normal format/lint/PR checks; bind
+  evidence again if the candidate changes.
+- Heavy-phase job owns pushing/opening the canonical **draft**, stacked on #704.
+  Use reviewed pr-body.md plus measured receipts; keep the tool's mechanical
+  draft as evidence. This preparation and runner do not push or open PRs.
+- Constituency/local-authority outputs remain absent until Monday 26 October,
+  unless María (juaristi22) overrides. Dense context (16 absent new inputs,
+  0 newly lost variables) is preserved in `.job/dense-schema.json`. Release and
+  adoption timing/owners are in pr-body.md; allow for queues, regeneration,
+  API deployment and rollback preparation before the Budget go/no-go.
diff --git a/.job/preparation-checks.json b/.job/preparation-checks.json
new file mode 100644
index 00000000..7a13e1f4
--- /dev/null
+++ b/.job/preparation-checks.json
@@ -0,0 +1,66 @@
+{
+  "scope": "Light preparation only; no certification/evidence heavy command executed",
+  "branch": "certify-uk-a133f37a-national",
+  "base_704": "da2ae8fb8e97145093e35366686df99c1e38449e",
+  "syntax_parsed": [
+    ".job/run-attribution.py",
+    ".job/certification-evidence.py",
+    ".job/index-evidence.py",
+    ".job/prepare-tool.py",
+    ".job/certify_uk_cut_in_policyengine_py.py"
+  ],
+  "checks": [
+    {
+      "command": [
+        "bash",
+        "-n",
+        "run-heavy.sh"
+      ],
+      "returncode": 0,
+      "output": ""
+    },
+    {
+      "command": [
+        "ruff",
+        "check",
+        ".job/run-attribution.py",
+        ".job/certification-evidence.py",
+        ".job/index-evidence.py",
+        ".job/prepare-tool.py"
+      ],
+      "returncode": 0,
+      "output": "All checks passed!"
+    },
+    {
+      "command": [
+        "ruff",
+        "format",
+        "--check",
+        ".job/run-attribution.py",
+        ".job/certification-evidence.py",
+        ".job/index-evidence.py",
+        ".job/prepare-tool.py"
+      ],
+      "returncode": 0,
+      "output": "4 files already formatted"
+    }
+  ],
+  "attribution_fixture_receipt": ".job/attribution-light-checks.json",
+  "patch_application_receipt": ".job/tool-patches.json",
+  "metadata_reads": "Two pinned release manifests and producer receipt schemas only; no H5 download or load",
+  "source_sha256": {
+    ".job/run-attribution.py": "249f33898a6b07a869b269d55cca75438ccd0a5f1df7589e0e75142195b35eba",
+    ".job/certification-evidence.py": "c12754ad420014f9b7aa00041997fe616303264fc05a022e10188e3c0349c9ea",
+    ".job/index-evidence.py": "11ff31fb0a13cd2c4470992f4711138d727992fe06530b69e27f7078e09ad0cf",
+    ".job/prepare-tool.py": "9db9eabe5502194d2623f342bb4c05653c7cfb5ecbe9f564b3b55ee7370a0808",
+    ".job/certify_uk_cut_in_policyengine_py.py": "941df515c0609cfcb27af3143ccb3e09fb089975843ea510b4e2022f7733d5ae"
+  },
+  "simulations": 0,
+  "national_data_loads": 0,
+  "test_suites": 0,
+  "snapshot_steps": 0,
+  "uc_floor_steps": 0,
+  "heavy_runner_executed": false,
+  "pushed": false,
+  "pr_opened": false
+}
diff --git a/.job/preparation.gitbundle b/.job/preparation.gitbundle
new file mode 100644
index 0000000000000000000000000000000000000000..394110fd0ddca1939040120fbec0fe5fb8b48839
GIT binary patch
literal 118960
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	K^|Mq!yD
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ⅈ;WYer%I3NGyOzX;t(<*cF
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

fdO8(xspMtRQ7Ib9jiWjk{pbU1PA| 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 literal 0 HcmV?d00001 From 2175a6f99003bfbba3b7f5efea1159ddbcb80af0 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 23 Oct 2026 13:36:16 -0400 Subject: [PATCH 6/8] Start the heavy phase when Fleet ops releases the host Fleet ops lets budget-certify start once Route A's reservation releases (expected 19:30-20:30Z on 23 Oct), not at a fixed 00:30Z. The reservation check in guard() is now the only start gate; load and disk checks are unchanged. Co-Authored-By: Claude Opus 5.5 --- run-heavy.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/run-heavy.sh b/run-heavy.sh index 760faf2f..7678eeff 100755 --- a/run-heavy.sh +++ b/run-heavy.sh @@ -16,7 +16,7 @@ while (($#)); do cat <<'EOF' Usage: ./run-heavy.sh [--from STEP] [--resume-certification] -Heavy phase only, after 2026-10-24 00:30Z and reservation clearance. +Heavy phase only, after Fleet ops releases the host (reservation.py allowed uk-hub says yes). Runs sequentially; each step checks reservation.py allowed uk-hub, load1 <=110 and free disk >=55,000,000,000 bytes. Stops on any failure. No push or PR is performed. Supply HF_TOKEN/HUGGING_FACE_TOKEN or agent-secret. @@ -64,7 +64,6 @@ guard() { python3 "$PE_CERTIFY_RESERVATION_SCRIPT" allowed uk-hub || return $? python3 - "$ROOT" "$STORAGE" <<'PY' import datetime, os, pathlib, shutil, sys -assert datetime.datetime.now(datetime.timezone.utc) >= datetime.datetime(2026, 10, 24, 0, 30, tzinfo=datetime.timezone.utc), 'Heavy phase may not start before 2026-10-24 00:30Z' load = os.getloadavg()[0] for name in sys.argv[1:]: path = pathlib.Path(name).resolve() From 7bade50d0282c6a01ff3dd38c114b36bd73f8d7b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 24 Oct 2026 01:32:57 -0400 Subject: [PATCH 7/8] Certify Microcosm UK cut microcosm-uk-2024-25-national-20261023T075039Z-a133f37a Co-Authored-By: Claude Opus 5.5 --- ...ional-20261023T075039Z-a133f37a.changed.md | 1 + pyproject.toml | 6 +- src/policyengine/data/bundle/manifest.json | 85 ++++++++----------- .../data/bundle/uk.trace.tro.jsonld | 33 +++---- .../data/bundle/us.trace.tro.jsonld | 4 +- .../uk_couple_two_kids.json | 8 +- .../uk_model_surface.json | 4 +- .../uk_single_adult_employment_income.json | 4 +- .../uk_single_adult_no_income.json | 4 +- .../uk_single_parent_one_child.json | 4 +- tests/test_models.py | 4 +- tests/test_release_manifests.py | 8 +- tests/test_uk_regions.py | 2 +- uv.lock | 18 ++-- 14 files changed, 86 insertions(+), 99 deletions(-) create mode 100644 changelog.d/certify-uk-microcosm-uk-2024-25-national-20261023T075039Z-a133f37a.changed.md diff --git a/changelog.d/certify-uk-microcosm-uk-2024-25-national-20261023T075039Z-a133f37a.changed.md b/changelog.d/certify-uk-microcosm-uk-2024-25-national-20261023T075039Z-a133f37a.changed.md new file mode 100644 index 00000000..6d22aa77 --- /dev/null +++ b/changelog.d/certify-uk-microcosm-uk-2024-25-national-20261023T075039Z-a133f37a.changed.md @@ -0,0 +1 @@ +Certify the UK populace data release `microcosm-uk-2024-25-national-20261023T075039Z-a133f37a` (microcosm_uk_2024_25, policyengine-uk 2.147.2) into the PolicyEngine bundle manifest. diff --git a/pyproject.toml b/pyproject.toml index 41a98159..e3770f0e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,12 +42,12 @@ graph = [ models = [ "policyengine-core==3.33.3", "policyengine-us==2.2.1", - "policyengine-uk==2.132.6", + "policyengine-uk==2.147.2", "spm-calculator==1.0.0", ] uk = [ "policyengine-core==3.33.3", - "policyengine-uk==2.132.6", + "policyengine-uk==2.147.2", ] us = [ "policyengine-core==3.33.3", @@ -72,7 +72,7 @@ dev = [ "rfc8785>=0.1.4", "policyengine-core==3.33.3", "policyengine-us==2.2.1", - "policyengine-uk==2.132.6", + "policyengine-uk==2.147.2", "spm-calculator==1.0.0", ] diff --git a/src/policyengine/data/bundle/manifest.json b/src/policyengine/data/bundle/manifest.json index 0c21eb0d..e4e720d6 100644 --- a/src/policyengine/data/bundle/manifest.json +++ b/src/policyengine/data/bundle/manifest.json @@ -16,31 +16,32 @@ }, "data_releases": { "uk": { - "build_id": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", + "build_id": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", "bundle_id": "uk-6.10.1", "certification": { - "built_with_model_version": "2.132.6", + "built_with_model_version": "2.147.2", "certified_by": "policyengine.py bundle certification", - "certified_for_model_version": "2.132.6", + "certified_for_core_version": "3.33.3", + "certified_for_model_version": "2.147.2", "compatibility_basis": "built_with_model_package", - "core_binding": "bundle_pin", - "data_build_id": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71" + "core_binding": "certified", + "data_build_id": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a" }, "certified_data_artifact": { - "build_id": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", + "build_id": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", "data_package": { "name": "microcosm-data", "version": "0.1.0" }, "dataset": "microcosm_uk_2024_25", - "sha256": "810ba980005a8b907cb51464279fa5e0125464b7af9a4871b7f6744c30951ece", - "uri": "hf://policyengine/populace-uk-private/microcosm_uk_2024_25.h5@microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71" + "sha256": "64f83900ca3a1242fb017585234844ce89f743d32e41cd2b6558057777f914aa", + "uri": "hf://policyengine/populace-uk-private/microcosm_uk_2024_25.h5@microcosm-uk-2024-25-national-20261023T075039Z-a133f37a" }, "country_id": "uk", "data_package": { "name": "microcosm-data", "release_manifest_path": "releases/microcosm-uk-2024-25-national/release_manifest.json", - "release_manifest_revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", + "release_manifest_revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", "repo_id": "policyengine/populace-uk-private", "repo_type": "dataset", "version": "0.1.0" @@ -50,86 +51,70 @@ "calibration_diagnostics": { "path": "releases/microcosm-uk-2024-25-national/calibration_diagnostics.json", "repo_id": "policyengine/populace-uk-private", - "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", - "sha256": "34978e2bfde3f2641769c9c3a8f60455c59ecae5a50524b359d74388d7edc888" + "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", + "sha256": "0c975ad04a2d1c0207c3be480d68f645c3a1b24f786c3eef76d6e984dfea0637" }, "microcosm_uk_2024_25": { "path": "microcosm_uk_2024_25.h5", "repo_id": "policyengine/populace-uk-private", - "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", - "sha256": "810ba980005a8b907cb51464279fa5e0125464b7af9a4871b7f6744c30951ece" + "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", + "sha256": "64f83900ca3a1242fb017585234844ce89f743d32e41cd2b6558057777f914aa" }, "microcosm_uk_2024_25_calibration": { "path": "microcosm_uk_2024_25_calibration.npz", "repo_id": "policyengine/populace-uk-private", - "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", - "sha256": "104a8253bf1aebc91147c9bbb40e2603d1025317ba8c6b991383d5e4d91852c2" - }, - "microcosm_uk_2024_25_dense": { - "path": "microcosm_uk_2024_25_dense.h5", - "repo_id": "policyengine/populace-uk-private", - "repo_type": "dataset", - "revision": "microcosm-uk-2024-25-dense-20261019T000432Z-ee5c2dbe", - "sha256": "8e10e130d0260e0b52e2e29902f0502ebdff76a14a3e57d2c876bf4cd8db5872" + "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", + "sha256": "074872730ad5678cdb4e483d0141ece912be65d6bc032d01bf756f2c3868f2a7" }, "release_certification": { "path": "releases/microcosm-uk-2024-25-national/release_certification.json", "repo_id": "policyengine/populace-uk-private", - "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", - "sha256": "6a68cc93595fc5a7ad451d75a902523c25659de27014f50431dd5e19af5bd959" + "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", + "sha256": "633661c40492645236c0d286576c6206d1fe87609ac7ce90234f625397b9260c" }, "release_cut_gates": { "path": "releases/microcosm-uk-2024-25-national/release_cut_gates.json", "repo_id": "policyengine/populace-uk-private", - "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", - "sha256": "c91c1364d9c3be98493039d4e1dd8c2256881a434c1f124dd2ed0701bfaf7751" + "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", + "sha256": "19f9b8bde4cafcfd179607541c5c8fa4247c97858a4ace7a1838bbacd223bc17" }, "score_vs_enhanced_frs": { "path": "releases/microcosm-uk-2024-25-national/score_vs_enhanced_frs.json", "repo_id": "policyengine/populace-uk-private", - "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", - "sha256": "ba8a675f36e7c836bf5521e5933e21bb689442734b7340e05cabd3e0cc55323d" + "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", + "sha256": "cb4dcde88bf016e0646cdc46a9ab9adf9973c77b16a0cb9e1a25987d57b212ab" }, "spine_gates": { "path": "releases/microcosm-uk-2024-25-national/spine_gates.json", "repo_id": "policyengine/populace-uk-private", - "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", - "sha256": "6dbba976dd1304c07da531382cecb11c9cb69512de0ee65126b71f032a0f37d6" + "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", + "sha256": "7959f2a19b6535e67f8d9cb2c37ee68f027892ca10d9d8422be18a8261798f34" }, "terminal_gates": { "path": "releases/microcosm-uk-2024-25-national/terminal_gates.json", "repo_id": "policyengine/populace-uk-private", - "revision": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", - "sha256": "e29439d5ce0ea098d32579284ac06f26f086feba2fb8d12d1e88ac9a54840808" + "revision": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", + "sha256": "dac50ba94464ca4e2b494fd8ffcd04e47da6dd6e49e9f53bdb7384de7983097f" } }, "default_dataset": "microcosm_uk_2024_25", - "default_dataset_uri": "hf://policyengine/populace-uk-private/microcosm_uk_2024_25.h5@microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", + "default_dataset_uri": "hf://policyengine/populace-uk-private/microcosm_uk_2024_25.h5@microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", "model_package": { "name": "policyengine-uk", - "sha256": "cc54025f2a3bc54be4f8d024d1e6f11ed29fed75ca513b214f2d2963c5bf4354", - "version": "2.132.6", - "wheel_url": "https://files.pythonhosted.org/packages/11/f2/c003cd7a95cb499737a5b8af0d619ba4e759c8bc7e55a2bb5c7efa46abd1/policyengine_uk-2.132.6-py3-none-any.whl" + "sha256": "4b48ab1008b00610abc38192927ac9d4bb22037b6bbd9578926c2899e639f972", + "version": "2.147.2", + "wheel_url": "https://files.pythonhosted.org/packages/96/dd/54ddc9226756877e630a3f9fcda4dbf2ae142635e96a564cab3a062ced62/policyengine_uk-2.147.2-py3-none-any.whl" }, "policyengine_version": "6.10.1", "region_datasets": { - "constituency": { - "path_template": "microcosm_uk_2024_25_dense.h5" - }, - "local_authority": { - "path_template": "microcosm_uk_2024_25_dense.h5" - }, "national": { "path_template": "microcosm_uk_2024_25.h5" } }, - "regional_release_kind": "local_area", - "regional_release_manifest_uri": "https://huggingface.co/datasets/policyengine/populace-uk-private/resolve/microcosm-uk-2024-25-dense-20261019T000432Z-ee5c2dbe/releases/microcosm-uk-2024-25-dense/release_manifest.json", - "regional_source_manifest_uri": "hf://dataset/policyengine/populace-uk-private@microcosm-uk-2024-25-dense-20261019T000432Z-ee5c2dbe/releases/microcosm-uk-2024-25-dense/release_manifest.json", - "release_manifest_uri": "https://huggingface.co/datasets/policyengine/populace-uk-private/resolve/microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71/releases/microcosm-uk-2024-25-national/release_manifest.json", + "release_manifest_uri": "https://huggingface.co/datasets/policyengine/populace-uk-private/resolve/microcosm-uk-2024-25-national-20261023T075039Z-a133f37a/releases/microcosm-uk-2024-25-national/release_manifest.json", "schema_version": 1, - "source_manifest_uri": "hf://dataset/policyengine/populace-uk-private@microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71/releases/microcosm-uk-2024-25-national/release_manifest.json", - "version": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71" + "source_manifest_uri": "hf://dataset/policyengine/populace-uk-private@microcosm-uk-2024-25-national-20261023T075039Z-a133f37a/releases/microcosm-uk-2024-25-national/release_manifest.json", + "version": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a" }, "us": { "build_id": "populace-us-2024-spm-20260915", @@ -363,10 +348,10 @@ "policyengine-uk": { "country": "uk", "import_name": "policyengine_uk", - "install_requirement": "policyengine-uk==2.132.6", + "install_requirement": "policyengine-uk==2.147.2", "name": "policyengine-uk", "role": "country_model", - "version": "2.132.6" + "version": "2.147.2" }, "policyengine-us": { "country": "us", diff --git a/src/policyengine/data/bundle/uk.trace.tro.jsonld b/src/policyengine/data/bundle/uk.trace.tro.jsonld index 5030ad7a..2322b406 100644 --- a/src/policyengine/data/bundle/uk.trace.tro.jsonld +++ b/src/policyengine/data/bundle/uk.trace.tro.jsonld @@ -17,7 +17,7 @@ "schema:name": "PolicyEngine", "schema:url": "https://policyengine.org" }, - "schema:dateCreated": "2026-10-19T10:17:15.545356+00:00", + "schema:dateCreated": "2026-10-23T09:57:14.344348+00:00", "schema:description": "TRACE TRO for certified runtime bundle uk-6.10.1 covering the bundle manifest, the certified dataset artifact, the country model wheel, and the country data release manifest when it is available.", "schema:name": "policyengine uk certified bundle TRO", "trov:createdWith": { @@ -45,7 +45,7 @@ "trov:hasArtifact": { "@id": "composition/1/artifact/data_release_manifest" }, - "trov:hasLocation": "https://huggingface.co/datasets/policyengine/populace-uk-private/resolve/microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71/releases/microcosm-uk-2024-25-national/release_manifest.json" + "trov:hasLocation": "https://huggingface.co/datasets/policyengine/populace-uk-private/resolve/microcosm-uk-2024-25-national-20261023T075039Z-a133f37a/releases/microcosm-uk-2024-25-national/release_manifest.json" }, { "@id": "arrangement/1/location/dataset", @@ -53,7 +53,7 @@ "trov:hasArtifact": { "@id": "composition/1/artifact/dataset" }, - "trov:hasLocation": "https://huggingface.co/datasets/policyengine/populace-uk-private/resolve/microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71/microcosm_uk_2024_25.h5" + "trov:hasLocation": "https://huggingface.co/datasets/policyengine/populace-uk-private/resolve/microcosm-uk-2024-25-national-20261023T075039Z-a133f37a/microcosm_uk_2024_25.h5" }, { "@id": "arrangement/1/location/model_wheel", @@ -61,7 +61,7 @@ "trov:hasArtifact": { "@id": "composition/1/artifact/model_wheel" }, - "trov:hasLocation": "https://files.pythonhosted.org/packages/11/f2/c003cd7a95cb499737a5b8af0d619ba4e759c8bc7e55a2bb5c7efa46abd1/policyengine_uk-2.132.6-py3-none-any.whl" + "trov:hasLocation": "https://files.pythonhosted.org/packages/96/dd/54ddc9226756877e630a3f9fcda4dbf2ae142635e96a564cab3a062ced62/policyengine_uk-2.147.2-py3-none-any.whl" } ] } @@ -75,51 +75,52 @@ "@type": "trov:ResearchArtifact", "schema:name": "policyengine.py bundle manifest for uk", "trov:mimeType": "application/json", - "trov:sha256": "095203eb2b45580c7afae0a3c89201f98a4426e4a1e76719cfdc28615ed9e5ca" + "trov:sha256": "d28213451af6d538a7c742b8ad5182e00b25e784ab68638e8428adc400012f01" }, { "@id": "composition/1/artifact/data_release_manifest", "@type": "trov:ResearchArtifact", "schema:name": "microcosm-data release manifest 0.1.0", "trov:mimeType": "application/json", - "trov:sha256": "49f9fa820db2e6544ff5cdceeb2d868a504843a6a1c1c06b37188621e700066f" + "trov:sha256": "63089003fa140b729e9daf24fb430e926f7cb5bd142e3cf6bfa106115aae6aa2" }, { "@id": "composition/1/artifact/dataset", "@type": "trov:ResearchArtifact", "schema:name": "microcosm_uk_2024_25", "trov:mimeType": "application/x-hdf5", - "trov:sha256": "810ba980005a8b907cb51464279fa5e0125464b7af9a4871b7f6744c30951ece" + "trov:sha256": "64f83900ca3a1242fb017585234844ce89f743d32e41cd2b6558057777f914aa" }, { "@id": "composition/1/artifact/model_wheel", "@type": "trov:ResearchArtifact", - "schema:name": "policyengine-uk==2.132.6 wheel", + "schema:name": "policyengine-uk==2.147.2 wheel", "trov:mimeType": "application/zip", - "trov:sha256": "cc54025f2a3bc54be4f8d024d1e6f11ed29fed75ca513b214f2d2963c5bf4354" + "trov:sha256": "4b48ab1008b00610abc38192927ac9d4bb22037b6bbd9578926c2899e639f972" } ], "trov:hasFingerprint": { "@id": "composition/1/fingerprint", "@type": "trov:CompositionFingerprint", - "trov:sha256": "0ade76b548f319f98c7ce9426be439bd7c9897fa78d18fe574aab607528282cb" + "trov:sha256": "f164ec51bf8a7a347b437ecf9c461591b6f35a4c5050135352cfc96d2c2f51b8" } }, "trov:hasPerformance": { "@id": "trp/1", "@type": "trov:TransparentResearchPerformance", - "pe:builtWithModelVersion": "2.132.6", + "pe:builtWithModelVersion": "2.147.2", "pe:certifiedBy": "policyengine.py bundle certification", - "pe:certifiedForModelVersion": "2.132.6", + "pe:certifiedForCoreVersion": "3.33.3", + "pe:certifiedForModelVersion": "2.147.2", "pe:compatibilityBasis": "built_with_model_package", - "pe:coreBinding": "bundle_pin", - "pe:dataBuildId": "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71", + "pe:coreBinding": "certified", + "pe:dataBuildId": "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a", "pe:emittedIn": "repository-bundle", - "rdfs:comment": "Certification of build microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71 for policyengine-uk 2.132.6.", + "rdfs:comment": "Certification of build microcosm-uk-2024-25-national-20261023T075039Z-a133f37a for policyengine-uk 2.147.2.", "trov:accessedArrangement": { "@id": "arrangement/1" }, - "trov:startedAtTime": "2026-10-19T10:17:15.545356+00:00", + "trov:startedAtTime": "2026-10-23T09:57:14.344348+00:00", "trov:wasConductedBy": { "@id": "trs" } diff --git a/src/policyengine/data/bundle/us.trace.tro.jsonld b/src/policyengine/data/bundle/us.trace.tro.jsonld index 59b518ca..1820877b 100644 --- a/src/policyengine/data/bundle/us.trace.tro.jsonld +++ b/src/policyengine/data/bundle/us.trace.tro.jsonld @@ -74,7 +74,7 @@ "@type": "trov:ResearchArtifact", "schema:name": "policyengine.py bundle manifest for us", "trov:mimeType": "application/json", - "trov:sha256": "095203eb2b45580c7afae0a3c89201f98a4426e4a1e76719cfdc28615ed9e5ca" + "trov:sha256": "d28213451af6d538a7c742b8ad5182e00b25e784ab68638e8428adc400012f01" }, { "@id": "composition/1/artifact/data_release_manifest", @@ -101,7 +101,7 @@ "trov:hasFingerprint": { "@id": "composition/1/fingerprint", "@type": "trov:CompositionFingerprint", - "trov:sha256": "85adc682b3d9684102287c08b7f06bd101b0e00fb5dd57692bb79956669371b4" + "trov:sha256": "37c146a215f35786a31a7f88d09817941507baf867c92b33fc2ff99b2b14c685" } }, "trov:hasPerformance": { diff --git a/tests/fixtures/household_calculator_snapshots/uk_couple_two_kids.json b/tests/fixtures/household_calculator_snapshots/uk_couple_two_kids.json index 28fa19e2..2d539470 100644 --- a/tests/fixtures/household_calculator_snapshots/uk_couple_two_kids.json +++ b/tests/fixtures/household_calculator_snapshots/uk_couple_two_kids.json @@ -13,14 +13,14 @@ "household.equiv_hbai_household_net_income": 52510.29, "household.fuel_duty": 0.0, "household.hbai_household_net_income": 73514.4, - "household.household_benefits": 9441.78, + "household.household_benefits": 9872.8, "household.household_count_people": 4.0, - "household.household_gross_income": 99441.78, + "household.household_gross_income": 99872.8, "household.household_id": 0.0, "household.household_income_decile": 10.0, "household.household_market_income": 90000.0, - "household.household_net_income": 80438.73, - "household.household_tax": 19003.05, + "household.household_net_income": 80869.8, + "household.household_tax": 19003.0, "household.household_wealth_decile": 10.0, "household.household_weight": 1.0, "household.in_poverty_ahc": 0.0, diff --git a/tests/fixtures/household_calculator_snapshots/uk_model_surface.json b/tests/fixtures/household_calculator_snapshots/uk_model_surface.json index 25270d57..c926e1f2 100644 --- a/tests/fixtures/household_calculator_snapshots/uk_model_surface.json +++ b/tests/fixtures/household_calculator_snapshots/uk_model_surface.json @@ -5,7 +5,7 @@ "has_income_tax": true, "has_region_registry": true, "model_package_name": "policyengine-uk", - "num_parameters_bucketed_100s": 32, - "num_variables_bucketed_100s": 11, + "num_parameters_bucketed_100s": 42, + "num_variables_bucketed_100s": 12, "region_registry_country": "uk" } diff --git a/tests/fixtures/household_calculator_snapshots/uk_single_adult_employment_income.json b/tests/fixtures/household_calculator_snapshots/uk_single_adult_employment_income.json index ab0db499..a96a4e09 100644 --- a/tests/fixtures/household_calculator_snapshots/uk_single_adult_employment_income.json +++ b/tests/fixtures/household_calculator_snapshots/uk_single_adult_employment_income.json @@ -19,8 +19,8 @@ "household.household_id": 0.0, "household.household_income_decile": 10.0, "household.household_market_income": 30000.0, - "household.household_net_income": 24939.55, - "household.household_tax": 5060.45, + "household.household_net_income": 24939.6, + "household.household_tax": 5060.4, "household.household_wealth_decile": 10.0, "household.household_weight": 1.0, "household.in_poverty_ahc": 0.0, diff --git a/tests/fixtures/household_calculator_snapshots/uk_single_adult_no_income.json b/tests/fixtures/household_calculator_snapshots/uk_single_adult_no_income.json index 391af449..2dba151c 100644 --- a/tests/fixtures/household_calculator_snapshots/uk_single_adult_no_income.json +++ b/tests/fixtures/household_calculator_snapshots/uk_single_adult_no_income.json @@ -19,8 +19,8 @@ "household.household_id": 0.0, "household.household_income_decile": 10.0, "household.household_market_income": 0.0, - "household.household_net_income": 4918.75, - "household.household_tax": 180.04, + "household.household_net_income": 4918.8, + "household.household_tax": 180.0, "household.household_wealth_decile": 10.0, "household.household_weight": 1.0, "household.in_poverty_ahc": 1.0, diff --git a/tests/fixtures/household_calculator_snapshots/uk_single_parent_one_child.json b/tests/fixtures/household_calculator_snapshots/uk_single_parent_one_child.json index c0d339da..3c4e5025 100644 --- a/tests/fixtures/household_calculator_snapshots/uk_single_parent_one_child.json +++ b/tests/fixtures/household_calculator_snapshots/uk_single_parent_one_child.json @@ -19,8 +19,8 @@ "household.household_id": 0.0, "household.household_income_decile": 10.0, "household.household_market_income": 25000.0, - "household.household_net_income": 24342.45, - "household.household_tax": 3660.45, + "household.household_net_income": 24342.5, + "household.household_tax": 3660.4, "household.household_wealth_decile": 10.0, "household.household_weight": 1.0, "household.in_poverty_ahc": 0.0, diff --git a/tests/test_models.py b/tests/test_models.py index 682925a0..4c38e3a2 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -43,13 +43,13 @@ def test_has_release_manifest_metadata(self): assert uk_latest.release_manifest is not None assert uk_latest.release_manifest.country_id == "uk" assert uk_latest.model_package.name == "policyengine-uk" - assert uk_latest.model_package.version == "2.132.6" + assert uk_latest.model_package.version == "2.147.2" assert uk_latest.data_package.name == "microcosm-data" assert uk_latest.data_package.version == "0.1.0" assert ( uk_latest.default_dataset_uri == "hf://policyengine/populace-uk-private/microcosm_uk_2024_25.h5" - "@microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71" + "@microcosm-uk-2024-25-national-20261023T075039Z-a133f37a" ) def test_has_hundreds_of_parameters(self): diff --git a/tests/test_release_manifests.py b/tests/test_release_manifests.py index 006070e2..a638b91e 100644 --- a/tests/test_release_manifests.py +++ b/tests/test_release_manifests.py @@ -73,11 +73,11 @@ "hf://policyengine/populace-us/populace_us_2024_acs_local.h5" f"@{US_LOCAL_AREA_RELEASE_ID}" ) -UK_MODEL_VERSION = "2.132.6" -UK_BUILT_WITH_MODEL_VERSION = "2.132.6" +UK_MODEL_VERSION = "2.147.2" +UK_BUILT_WITH_MODEL_VERSION = "2.147.2" UK_DATA_RELEASE_VERSION = "0.1.0" -UK_DATA_RELEASE_ID = "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71" -UK_DATA_RELEASE_REVISION = "microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71" +UK_DATA_RELEASE_ID = "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a" +UK_DATA_RELEASE_REVISION = "microcosm-uk-2024-25-national-20261023T075039Z-a133f37a" UK_DATA_RELEASE_PATH = "releases/microcosm-uk-2024-25-national/release_manifest.json" UK_LOCAL_AREA_RELEASE_ID = "microcosm-uk-2024-25-dense-20261019T000432Z-ee5c2dbe" # The prior certified default (policyengine-uk-data 1.58.1), kept resolvable diff --git a/tests/test_uk_regions.py b/tests/test_uk_regions.py index 29c6b27e..63a1f019 100644 --- a/tests/test_uk_regions.py +++ b/tests/test_uk_regions.py @@ -24,7 +24,7 @@ # release its constituency and local-authority regions are routed to. UK_CERTIFIED_NATIONAL_URI = ( "hf://policyengine/populace-uk-private/microcosm_uk_2024_25.h5" - "@microcosm-uk-2024-25-national-20261019T083323Z-6cba5d71" + "@microcosm-uk-2024-25-national-20261023T075039Z-a133f37a" ) UK_CERTIFIED_LOCAL_AREA_URI = ( "hf://policyengine/populace-uk-private/microcosm_uk_2024_25_dense.h5" diff --git a/uv.lock b/uv.lock index 7009bd4c..8f3e6ed9 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 5 +revision = 3 requires-python = ">=3.11, <3.15" resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -197,7 +197,7 @@ name = "cffi" version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser" }, + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ @@ -1776,7 +1776,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess" }, + { name = "ptyprocess", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ @@ -1894,9 +1894,9 @@ requires-dist = [ { name = "policyengine-core", marker = "extra == 'models'", specifier = "==3.33.3" }, { name = "policyengine-core", marker = "extra == 'uk'", specifier = "==3.33.3" }, { name = "policyengine-core", marker = "extra == 'us'", specifier = "==3.33.3" }, - { name = "policyengine-uk", marker = "extra == 'dev'", specifier = "==2.132.6" }, - { name = "policyengine-uk", marker = "extra == 'models'", specifier = "==2.132.6" }, - { name = "policyengine-uk", marker = "extra == 'uk'", specifier = "==2.132.6" }, + { name = "policyengine-uk", marker = "extra == 'dev'", specifier = "==2.147.2" }, + { name = "policyengine-uk", marker = "extra == 'models'", specifier = "==2.147.2" }, + { name = "policyengine-uk", marker = "extra == 'uk'", specifier = "==2.147.2" }, { name = "policyengine-us", marker = "extra == 'dev'", specifier = "==2.2.1" }, { name = "policyengine-us", marker = "extra == 'models'", specifier = "==2.2.1" }, { name = "policyengine-us", marker = "extra == 'us'", specifier = "==2.2.1" }, @@ -1946,7 +1946,7 @@ wheels = [ [[package]] name = "policyengine-uk" -version = "2.132.6" +version = "2.147.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "microdf-python" }, @@ -1954,9 +1954,9 @@ dependencies = [ { name = "pydantic" }, { name = "tables" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/88/bc06328a739b335d4ef5f2d07fd36cc6a3a5e0f6ec1b2c774a14e77e43a3/policyengine_uk-2.132.6.tar.gz", hash = "sha256:f9ce261d9881eee32fab96341c7080cd0c86717b40785b5cdaff44b07262a771", size = 2560464, upload-time = "2026-10-15T10:08:14.263Z" } +sdist = { url = "https://files.pythonhosted.org/packages/89/16/14fe43a9e0323eb29e2877e7860356d7ae682dd89575973adaa4f25a3e33/policyengine_uk-2.147.2.tar.gz", hash = "sha256:7a718b03cb09e21d7e00cfe655b9a2082af5edba747471b5aec20e651e1cc180", size = 3191436, upload-time = "2026-10-22T18:40:49.878Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/f2/c003cd7a95cb499737a5b8af0d619ba4e759c8bc7e55a2bb5c7efa46abd1/policyengine_uk-2.132.6-py3-none-any.whl", hash = "sha256:cc54025f2a3bc54be4f8d024d1e6f11ed29fed75ca513b214f2d2963c5bf4354", size = 3882707, upload-time = "2026-10-15T10:08:11.572Z" }, + { url = "https://files.pythonhosted.org/packages/96/dd/54ddc9226756877e630a3f9fcda4dbf2ae142635e96a564cab3a062ced62/policyengine_uk-2.147.2-py3-none-any.whl", hash = "sha256:4b48ab1008b00610abc38192927ac9d4bb22037b6bbd9578926c2899e639f972", size = 4705225, upload-time = "2026-10-22T18:40:47.418Z" }, ] [[package]] From e02d41bceab57756373b2dc3bcf78635b856ec81 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 24 Oct 2026 01:34:49 -0400 Subject: [PATCH 8/8] Make the UK region tests follow the bundle's area routing This cut is national-only (--drop-regional-release), so the manifest routes no constituency or local-authority regions. The region, manifest and geography-disclosure tests now assert that routing: with no local_area release the area regions are absent and their lookups return nothing; with one, the earlier assertions stand. A new test checks that a routed area's error still names its dataset, whatever the packaged routing. Co-Authored-By: Claude Opus 5.5 --- tests/test_release_manifests.py | 33 +++++++---- tests/test_uk_geography_disclosure.py | 41 ++++++++++++- tests/test_uk_regions.py | 84 ++++++++++++++++++--------- 3 files changed, 117 insertions(+), 41 deletions(-) diff --git a/tests/test_release_manifests.py b/tests/test_release_manifests.py index a638b91e..1ffe0dfd 100644 --- a/tests/test_release_manifests.py +++ b/tests/test_release_manifests.py @@ -336,22 +336,31 @@ def test__given_uk_dataset_name__then_resolves_to_versioned_hf_url(self): assert resolved == UK_CERTIFIED_DATASET_URI - def test__given_uk_manifest__then_area_regions_route_to_local_release(self): + def test__given_uk_manifest__then_area_regions_follow_the_regional_release( + self, + ): manifest = get_release_manifest("uk") - - assert manifest.regional_release_kind == "local_area" - assert { + region_paths = { region_type: template.path_template for region_type, template in manifest.region_datasets.items() - } == { - "national": "microcosm_uk_2024_25.h5", - "constituency": "microcosm_uk_2024_25_dense.h5", - "local_authority": "microcosm_uk_2024_25_dense.h5", } - assert ( - resolve_dataset_reference("uk", "microcosm_uk_2024_25_dense") - == UK_LOCAL_AREA_DATASET_URI - ) + + if manifest.regional_release_kind == "local_area": + assert region_paths == { + "national": "microcosm_uk_2024_25.h5", + "constituency": "microcosm_uk_2024_25_dense.h5", + "local_authority": "microcosm_uk_2024_25_dense.h5", + } + assert ( + resolve_dataset_reference("uk", "microcosm_uk_2024_25_dense") + == UK_LOCAL_AREA_DATASET_URI + ) + else: + # A national-only cut (--drop-regional-release) routes no area + # regions and bundles no local-area dataset. + assert manifest.regional_release_kind is None + assert region_paths == {"national": "microcosm_uk_2024_25.h5"} + assert "microcosm_uk_2024_25_dense" not in manifest.datasets def test__given_uk_legacy_dataset_names__then_resolves_bundled_aliases(self): assert ( diff --git a/tests/test_uk_geography_disclosure.py b/tests/test_uk_geography_disclosure.py index 07334692..e3e4cb6e 100644 --- a/tests/test_uk_geography_disclosure.py +++ b/tests/test_uk_geography_disclosure.py @@ -467,7 +467,10 @@ def test_the_adapter_is_exported_from_outputs(): def test_a_missing_area_column_names_the_dataset_that_carries_it(): - from policyengine.provenance.manifest import uk_area_dataset_hint + from policyengine.provenance.manifest import ( + get_release_manifest, + uk_area_dataset_hint, + ) baseline, reform = _households() for frame in (baseline, reform): @@ -475,7 +478,41 @@ def test_a_missing_area_column_names_the_dataset_that_carries_it(): with pytest.raises(ValueError, match="no household an area") as error: _compute(baseline, reform, levels=("constituency",)) hint = uk_area_dataset_hint("constituency") - assert "microcosm_uk_2024_25_dense" in hint + if "constituency" in get_release_manifest("uk").region_datasets: + assert "microcosm_uk_2024_25_dense" in hint + assert hint in str(error.value) + else: + # A national-only cut routes no area regions, so there is no + # dataset to name. + assert hint == "" + assert "certified bundle routes" not in str(error.value) + + +def test_a_routed_area_names_its_dataset_in_the_error(monkeypatch): + """Whatever the packaged routing, a routed area's error names its dataset.""" + from policyengine.provenance import manifest + + record = manifest.get_release_manifest("uk") + data = record.model_dump() + data["region_datasets"]["constituency"] = { + "path_template": "microcosm_uk_2024_25_dense.h5" + } + data["datasets"]["microcosm_uk_2024_25_dense"] = { + "path": "microcosm_uk_2024_25_dense.h5", + "repo_id": "policyengine/populace-uk-private", + "repo_type": "dataset", + "revision": "microcosm-uk-2024-25-dense-20261019T000432Z-ee5c2dbe", + } + routed = type(record).model_validate(data) + monkeypatch.setattr(manifest, "get_release_manifest", lambda country: routed) + + baseline, reform = _households() + for frame in (baseline, reform): + del frame["constituency_code_oa"] + with pytest.raises(ValueError, match="no household an area") as error: + _compute(baseline, reform, levels=("constituency",)) + hint = manifest.uk_area_dataset_hint("constituency") + assert "'microcosm_uk_2024_25_dense'" in hint assert hint in str(error.value) diff --git a/tests/test_uk_regions.py b/tests/test_uk_regions.py index 63a1f019..82ede9df 100644 --- a/tests/test_uk_regions.py +++ b/tests/test_uk_regions.py @@ -15,6 +15,7 @@ ) from policyengine.countries.uk.regions import ( UK_COUNTRIES, + area_regions_routed_to_local_release, build_uk_region_registry, uk_region_registry, ) @@ -31,6 +32,12 @@ "@microcosm-uk-2024-25-dense-20261019T000432Z-ee5c2dbe" ) UK_AREA_REGION_COUNT = 650 + 361 +# Whether the packaged bundle routes constituency and local-authority regions +# to a local-area release. A national-only cut (--drop-regional-release) +# routes none, so the default registry holds only the national region and the +# four countries, and area lookups find nothing. +ROUTED_TO_LOCAL_RELEASE = area_regions_routed_to_local_release() +ROUTED_AREA_REGION_COUNT = UK_AREA_REGION_COUNT if ROUTED_TO_LOCAL_RELEASE else 0 class TestUKCountries: @@ -180,65 +187,88 @@ def test__given_northern_ireland_region__then_filters_from_national(self): assert ni.requires_filter assert ni.scoping_strategy.variable_value == "NORTHERN_IRELAND" - def test__given_uk_national__then_children_are_countries_and_areas(self): + def test__given_uk_national__then_children_are_countries_and_routed_areas( + self, + ): """Given: UK national region When: Getting its children - Then: The 4 countries, 650 constituencies and 361 local authorities + Then: The 4 countries, and the 650 constituencies and 361 local + authorities only when the bundle routes them to a local-area release """ # When uk_children = uk_region_registry.get_children("uk") # Then - assert Counter(c.region_type for c in uk_children) == { - "country": 4, - "constituency": 650, - "local_authority": 361, - } + expected = {"country": 4} + if ROUTED_TO_LOCAL_RELEASE: + expected |= {"constituency": 650, "local_authority": 361} + assert Counter(c.region_type for c in uk_children) == expected def test__given_uk_registry__then_national_and_area_regions_have_datasets( self, ): - """Given: UK region registry routed to a Microcosm local-area release + """Given: UK region registry When: Getting dataset regions - Then: National loads the national dataset, area regions the local one + Then: National loads the national dataset; area regions load the + local-area release only when the bundle routes them to one """ # When dataset_regions = uk_region_registry.get_dataset_regions() # Then - assert len(dataset_regions) == 1 + UK_AREA_REGION_COUNT + assert len(dataset_regions) == 1 + ROUTED_AREA_REGION_COUNT paths = {r.region_type: set() for r in dataset_regions} for region in dataset_regions: paths[region.region_type].add(region.dataset_path) - assert paths == { - "national": {UK_CERTIFIED_NATIONAL_URI}, - "constituency": {UK_CERTIFIED_LOCAL_AREA_URI}, - "local_authority": {UK_CERTIFIED_LOCAL_AREA_URI}, - } + expected = {"national": {UK_CERTIFIED_NATIONAL_URI}} + if ROUTED_TO_LOCAL_RELEASE: + expected |= { + "constituency": {UK_CERTIFIED_LOCAL_AREA_URI}, + "local_authority": {UK_CERTIFIED_LOCAL_AREA_URI}, + } + assert paths == expected def test__given_uk_registry__then_countries_and_areas_filter(self): """Given: UK region registry When: Getting filter regions - Then: The 4 countries and every area region require a filter + Then: The 4 countries, and every routed area region, require a filter """ # When filter_regions = uk_region_registry.get_filter_regions() # Then - assert len(filter_regions) == 4 + UK_AREA_REGION_COUNT - assert {r.region_type for r in filter_regions} == { - "country", - "constituency", - "local_authority", - } - - def test__given_default_registry__then_has_1016_regions(self): - """Given: Default UK registry routed to a Microcosm local-area release + assert len(filter_regions) == 4 + ROUTED_AREA_REGION_COUNT + expected = {"country"} + if ROUTED_TO_LOCAL_RELEASE: + expected |= {"constituency", "local_authority"} + assert {r.region_type for r in filter_regions} == expected + + def test__given_default_registry__then_counts_the_routed_regions(self): + """Given: Default UK registry When: Counting regions - Then: 1 national + 4 countries + 650 constituencies + 361 LAs = 1,016 + Then: 1 national + 4 countries, plus 650 constituencies + 361 LAs + (1,016 in all) when the bundle routes them to a local-area release + """ + # Then + assert len(uk_region_registry) == 5 + ROUTED_AREA_REGION_COUNT + + def test__given_default_registry__then_area_lookups_follow_the_routing(self): + """Given: Default UK registry + When: Looking up a constituency and a local authority + Then: Both load the local-area release when the bundle routes them to + one; a national-only bundle has neither """ + # When + constituency = uk_region_registry.get("constituency/E14001063") + local_authority = uk_region_registry.get("local_authority/N09000011") + # Then - assert len(uk_region_registry) == 5 + UK_AREA_REGION_COUNT + if ROUTED_TO_LOCAL_RELEASE: + assert constituency.dataset_path == UK_CERTIFIED_LOCAL_AREA_URI + assert local_authority.dataset_path == UK_CERTIFIED_LOCAL_AREA_URI + else: + assert constituency is None + assert local_authority is None class TestUKRegionRegistryBuilder: