#757 (#787, #793) completed the UK national line's release evidence: the seam writes calibration_diagnostics.json (shared schema 6 + uk_diagnostics) and its signed battery, the spine build runs its own battery, and tools/certify_uk_release_cut.py composes the three scoped reports into release_certification.json — the only artifact that may carry a UK shippability verdict. The dashboard already reads the UK: the calibration-diagnostics COUNTRY_REGISTRY entry resolves policyengine/populace-uk-private through latest.json.
Two gaps remain between a certified candidate and the dashboard: nothing assembles a publishable release directory, and the national release id escapes the contract's UK-keyed checks. This issue covers that plumbing, so that when a cut clears uk_target_fit (the 13-cell adjudication in #750 / #792), publishing is mechanical.
A. Release-directory assembler
required_release_files("microcosm-uk-2024-25-national") demands:
build_manifest.json, release_manifest.json, calibration_diagnostics.json, release_certification.json
The UK chain produces the last two. No UK tool writes the two manifests — every release_manifest.json producer on main is US, and #787 retired the June driver that owned UK packaging.
Needed: an assembler (tools/assemble_uk_release_dir.py, or a final mode on certify_uk_release_cut.py) that marshals existing evidence into releases/microcosm-uk-2024-25-national/:
- Identity:
UKReleaseIdentity.as_release_manifest_fields() (uk_runtime/release_identity.py) already emits country, tier, n_records, build_id — the manifest assembler was its declared consumer.
- Gates / provenance: the certification carries the gate census and the closed identity join (spine report → sidecar → build record → diagnostics → candidate bytes); the seam's build record and the spine sidecar carry the pins, seeds, vintages, and rules-engine version for
build_manifest.runtime / dataset.
- Self-check: the assembler calls
validate_release_dir on its own output (_check_build_manifest contract.py:799, _check_release_manifest :987, _check_cross_manifest_consistency :4871), so a bad directory refuses at assembly, not at publish.
B. Id-keyed contract coverage for the national id
microcosm-uk-2024-25-national (ruling 2026-08-27) drops the populace-uk- prefix, so it matches neither _is_uk_exact_k_release_id nor startswith("populace-uk-"). Two checks silently no-op for it — the green-by-absence class the #793 review round closed one layer down:
- The contract skips
_check_uk_calibration_diagnostics (contract.py:3903, :4320 gate on the exact-k predicate) — the entire uk_diagnostics validation: ESS fraction, weight concentration, zero-weight strata, geography pass rates. Exactly what the dashboard renders; the certification's field set does not cover it.
- The contract skips the
policyengine-uk model pin: _expected_model_package (:1416) returns None for the id, so neither build_manifest.runtime (:846) nor release_manifest.build.built_with_model_package (:1028) gets checked.
Fix: a _is_uk_release_id() helper covering all three shapes (exact-k, June legacy, national) for the diagnostics and model-package branches. Keep the terminal-gate-report branches exact-k-scoped — the certification already pins the part reports by digest.
C. Two-step publish: inspect in the dashboard, then promote
The publish layer already supports a staged decision; document it as the operator flow and close the tag gap below.
- Inspect —
python -m microcosm.data.publish_cli releases/microcosm-uk-2024-25-national --repo-id policyengine/populace-uk-private --artifact-root <dir> --no-latest --tag-name <per-cut>. Validates the full contract, commits the files under releases/<id>/, creates the immutable tag — moves no pointer, fires no Slack alert. The dashboard lists releases from the HF tree (releases?recursive=true), not the pointer, so the candidate renders via ?country=uk&release=microcosm-uk-2024-25-national while the current release stays put. (--tag-only commits nothing to main, so the dashboard cannot see it — not this flow.)
- Adjudicate in the release summary, target browser, and compare views.
- Promote or walk away — re-run publish without
--no-latest to move latest.json and fire SLACK_WEBHOOK_POPULACE_UK. Declining a cut = never promoting.
Two constraints:
- Per-cut tag names.
create_tag defaults the tag to the release id, and the national id is constant across cuts — the second cut's publish collides with the first cut's immutable tag. Adopt a convention (e.g. microcosm-uk-2024-25-national-<build_id>) and record the tag in the build record so Hub history stays joinable to the Logbook chain. Successive cuts overwrite releases/<id>/ on main — intended; history lives in tags and the Logbook.
- Only certified candidates are inspectable.
publish_release validates before any upload and recomputes shippability from the certification parts — a blocked candidate uploads nothing. The EVIDENCE tier is US-scoped; for blocked UK cuts the review surface stays local. Extending it is out of scope here.
D. Deployment checklist
Acceptance
- Assembling a certified candidate yields a directory that passes
validate_release_dir under microcosm-uk-2024-25-national, including the re-armed checks from B.
- A
--no-latest publish appears in the dashboard release list and renders in the summary, target, and compare views under ?country=uk, with latest.json untouched and no alert.
- Promotion moves the pointer, fires the alert, and the default UK view resolves the release — no dashboard-side changes anywhere in the flow.
- A second cut publishes under the same release id with a distinct tag.
Out of scope
#757 (#787, #793) completed the UK national line's release evidence: the seam writes
calibration_diagnostics.json(shared schema 6 +uk_diagnostics) and its signed battery, the spine build runs its own battery, andtools/certify_uk_release_cut.pycomposes the three scoped reports intorelease_certification.json— the only artifact that may carry a UK shippability verdict. The dashboard already reads the UK: the calibration-diagnosticsCOUNTRY_REGISTRYentry resolvespolicyengine/populace-uk-privatethroughlatest.json.Two gaps remain between a certified candidate and the dashboard: nothing assembles a publishable release directory, and the national release id escapes the contract's UK-keyed checks. This issue covers that plumbing, so that when a cut clears
uk_target_fit(the 13-cell adjudication in #750 / #792), publishing is mechanical.A. Release-directory assembler
required_release_files("microcosm-uk-2024-25-national")demands:The UK chain produces the last two. No UK tool writes the two manifests — every
release_manifest.jsonproducer on main is US, and #787 retired the June driver that owned UK packaging.Needed: an assembler (
tools/assemble_uk_release_dir.py, or a final mode oncertify_uk_release_cut.py) that marshals existing evidence intoreleases/microcosm-uk-2024-25-national/:UKReleaseIdentity.as_release_manifest_fields()(uk_runtime/release_identity.py) already emitscountry, tier,n_records,build_id— the manifest assembler was its declared consumer.build_manifest.runtime/dataset.validate_release_diron its own output (_check_build_manifestcontract.py:799,_check_release_manifest:987,_check_cross_manifest_consistency:4871), so a bad directory refuses at assembly, not at publish.B. Id-keyed contract coverage for the national id
microcosm-uk-2024-25-national(ruling 2026-08-27) drops thepopulace-uk-prefix, so it matches neither_is_uk_exact_k_release_idnorstartswith("populace-uk-"). Two checks silently no-op for it — the green-by-absence class the #793 review round closed one layer down:_check_uk_calibration_diagnostics(contract.py:3903, :4320 gate on the exact-k predicate) — the entireuk_diagnosticsvalidation: ESS fraction, weight concentration, zero-weight strata, geography pass rates. Exactly what the dashboard renders; the certification's field set does not cover it.policyengine-ukmodel pin:_expected_model_package(:1416) returnsNonefor the id, so neitherbuild_manifest.runtime(:846) norrelease_manifest.build.built_with_model_package(:1028) gets checked.Fix: a
_is_uk_release_id()helper covering all three shapes (exact-k, June legacy, national) for the diagnostics and model-package branches. Keep the terminal-gate-report branches exact-k-scoped — the certification already pins the part reports by digest.C. Two-step publish: inspect in the dashboard, then promote
The publish layer already supports a staged decision; document it as the operator flow and close the tag gap below.
python -m microcosm.data.publish_cli releases/microcosm-uk-2024-25-national --repo-id policyengine/populace-uk-private --artifact-root <dir> --no-latest --tag-name <per-cut>. Validates the full contract, commits the files underreleases/<id>/, creates the immutable tag — moves no pointer, fires no Slack alert. The dashboard lists releases from the HF tree (releases?recursive=true), not the pointer, so the candidate renders via?country=uk&release=microcosm-uk-2024-25-nationalwhile the current release stays put. (--tag-onlycommits nothing to main, so the dashboard cannot see it — not this flow.)--no-latestto movelatest.jsonand fireSLACK_WEBHOOK_POPULACE_UK. Declining a cut = never promoting.Two constraints:
create_tagdefaults the tag to the release id, and the national id is constant across cuts — the second cut's publish collides with the first cut's immutable tag. Adopt a convention (e.g.microcosm-uk-2024-25-national-<build_id>) and record the tag in the build record so Hub history stays joinable to the Logbook chain. Successive cuts overwritereleases/<id>/on main — intended; history lives in tags and the Logbook.publish_releasevalidates before any upload and recomputes shippability from the certification parts — a blocked candidate uploads nothing. The EVIDENCE tier is US-scoped; for blocked UK cuts the review surface stays local. Extending it is out of scope here.D. Deployment checklist
policyengine/populace-uk-privatewhere publish runs;MICROCOSM_UK_TERMINAL_GATE_SIGNING_KEYfor the cut.SLACK_WEBHOOK_POPULACE_UKset (missing webhook warns, never fails; fires only on promotion).HF_TOKENon the dashboard deployment (private repo; reads go through the server).latest.json— nothing to pre-seed. Until then the UK default view has no current release; inspect publishes render through the release list /?release=.Acceptance
validate_release_dirundermicrocosm-uk-2024-25-national, including the re-armed checks from B.--no-latestpublish appears in the dashboard release list and renders in the summary, target, and compare views under?country=uk, withlatest.jsonuntouched and no alert.Out of scope
uk_target_fitadjudication (UK WAS stage: split private pension wealth out of corporate_wealth (uk-data#452 M2) #750, Band edges derive from the exclusion-pruned register, silently widening surviving banded targets #792) — this issue is the plumbing that waits on it.StagingTelemetry, solverprogress_callback, a UK staging repo): not granted to the UK in the dashboard registry; a separate lane if wanted.ledger_source_record_id; the dashboard preferschronicle_record_idsand falls back to a humanizer. Cosmetic follow-up.