You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Max greenlit scoping the "bigger UK dataset" arm 2026-07-22. This epic is the UK analog of the US dense/local arm: one national rowwise artifact whose rows carry finest-available geography and whose weights are calibrated against a constituency/local-authority target surface — built by cloning the compact FRS-based pool, because the UK has no ACS-scale microdata to substitute for density. It subsumes the publication goal of #146 (rowwise H5 + long weights), sits under the #144 migration umbrella, and is the sibling of #355 (the pool should get a smaller national default at the same time as this arm gets bigger).
Everything below is designed against the machinery that already exists (uk_runtime/rowwise_geography.py, rowwise_dataset.py, geography_ladder.py, local_targets.py, local_solver.py, local_runner.py, tools/build_uk_rowwise_dataset.py); the epic is mostly wiring, fences, and targets, not new solvers.
Base artifact and pipeline order (fence-determined, not a preference)
The clone input is the national staging H5 — the output of the national orchestration seam after the 145/0 input-coverage gate and the adjudicated HMRC/SPI replay — not the raw certified compact:
certified compact (f17306cc…)
national staging (829e843f…)
households
535,080 (200,000 zero-weight dead SPI)
535,080 (rebuilt SPI channel, positive mass)
persons
1,157,100
1,157,739 (724,960 FRS + 432,779 SPI)
benunits
618,980
618,884
household mass
28,840,551.182
28,840,551.182 (conserved; 1 mass-change record)
weight kind
(unlabeled)
importance + mass log in H5 attrs
bytes
1,315,880,118
1,532,379,785 (local_untracked)
(Compact figures verified directly against the cached certified H5; staging figures from the committed national_staging_build_record.json.)
The ordering is forced by the fences in hmrc_restoration.py: the HMRC replay byte-binds its input to the certified candidate (_assert_dataset_matches_certified_candidate checks the source-file fingerprint captured at SHA-256 verification), so cloning can never run upstream of the seam, and cloning the certified compact directly would clone 200k dead SPI households and skip the 145/0 restoration. Pipeline:
certified compact (byte-verified)
→ national seam (retained FRS leaves → HMRC/SPI replay, IMPORTANCE weights, mass log)
→ staging H5 ← the 145/0 gate passes here
→ rowwise clone + geography ladder assignment (this epic)
→ one uniform local+national calibration solve (this epic)
→ rowwise release, private HF only (this epic)
Clone count K: rows, memory, support
The pool is already a 10× clone: household_id = tier·10⁸ + base with exactly 53,508 unique base ids × 10 tiers (verified on the certified H5; #355 documents the construction). eFRS-grain source lineage is therefore recoverable as household_id mod 10⁸, and the rowwise build must carry it — per-area "distinct source households" diagnostics computed at pool grain would overstate real support 10×.
K below means the additional rowwise multiplier on top of that 10×. Clones add geographic resolution and solver degrees of freedom only — zero new information — so K is sized by per-area support, not ambition:
K
households
persons
H5 (≈, linear)
mean rows/constituency (650)
smallest constituency (≈)
1
535,080
1,157,739
~1.5 GB
823
~240
2
1,070,160
2,315,478
~3.1 GB
1,646
~480
4
2,140,320
4,630,956
~6.1 GB
3,293
~960
10
5,350,800
11,577,390
~15 GB
8,232
~2,400
The smallest-constituency column is the population-proportional expectation for a Na h-Eileanan an Iar-scale area and is approximate; the dry-run tool (increment 2) computes the exact expected support per constituency from the ladder/crosswalk populations and the pool's region mix. For calibration comparison: the US dense arm supports 436 CDs on a 337,704-record pool ≈ 775 records/CD (#355, #449) — K=1 is already at US-dense per-area scale, because the 10× pool cloning did the density work up front.
Proposal: pilot at K=1 (geography assignment only, no row growth), first published candidate at K=2–4, chosen from the dry-run support/ESS diagnostics. K>4 requires a measured justification. avoid_constituency_collisions stays on (no source household repeated in the same constituency across rowwise clones).
Geography assignment: the OA ladder is the operator
Two mechanisms exist. The crosswalk path (#142/#143) samples an OA within (country, region) ∝ population and covers all four countries. The OA ladder (#349 ratified, #354 merged) samples a 2024 Westminster constituency within the region ∝ census household counts, then an OA within the constituency ∝ population, carries sha-pinned best-fit lookups and per-layer vintages (vintage_policy: error), and ships a release-blocking gate — but is England & Wales only today (575 of 650 constituencies).
The epic declares the ladder as the assignment operator (it is the ratified US-block-ladder parallel with vintage discipline). Work items:
extend the ladder artifact to Scotland (2022 census OAs, NRS sources) and Northern Ireland (Data Zones 2021, NISRA) — geography_sources.py already pins those source URLs for the crosswalk path; they need per-layer vintage pinning in oa_ladder_sources.py;
until S/NI land, a full-UK rowwise build through the ladder fails loudly on the missing regions (already the ladder's contract — no silent partial coverage);
rewire tools/build_uk_rowwise_dataset.py from the crosswalk sampler to assign_uk_geography_ladder + uk_geography_ladder_gate, keeping the crosswalk as the coverage-check path.
Local target surface: census first, then bind
local_targets.py computes today, at household grain:
constituency: HMRC employment/self-employment amount+count (SPI frame via income_tax > 0), age bands (8 × 10y), UC households, UC households by child count (0/1/2/3+);
local authority: the HMRC/age/UC families plus ONS equivalised net income BHC/AHC, housing costs, tenure (4 classes), private rent.
That mirrors the deprecated uk-data constituency surface. What official UK local data offers beyond it (ONS mid-year age structure by PCON24/LAD; HMRC Personal Incomes by constituency — taxpayer counts and employment/self-employment/pension totals; DWP Stat-Xplore UC/Pension Credit/Housing Benefit by constituency and LA; Census 2021/2022 tenure and composition; ONS PIPR rents by LA; ONS small-area income at MSOA) needs a census artifact, not prose: increment 1 ships a tool that enumerates every in-code metric family and every documented official family with area type, source, vintage, and status (bound_in_code / documented_unpinned / needs_adjudication), checked in and tested, so binding work is enumerable and claimable per family.
Two standing fences on targets:
the 208 banded HMRC facts stay fenced (see below) — the constituency HMRC family is a different, unbanded surface and gets its own admissibility adjudication before it binds;
the income_tax > 0 SPI-frame membership in the HMRC metrics is a modeled proxy frame; the census must record that honestly and the binding adjudication must accept or replace it explicitly.
The local solve: one uniform operator, declared bounds (doctrine)
Per the #492 lane and #493: no per-target calibration knobs. The solve is one operator over the whole surface — national families (149 targets in the published release, plus the natively declared CGT facts) and all area×metric rows together — with globally declared loss shape, cap, and weight-ratio bounds. A miss is local-support work (rows/clones/ladder) or target work (fix or fence the target), never a knob.
Binding: the rowwise artifact solves one weight vector of length 535,080·K where each row supports only its assigned constituency's rows (the US Build-N CD shape). The stacked all-areas×all-households machinery (build_stacked_local_matrix, Publish a Populace UK local variant with rowwise H5 and long weights #146 long weights) remains the research harness and can express the rowwise solve as its block-diagonal special case; the released artifact is the rowwise H5 with its solved household_weight column.
Read against hmrc_restoration.py, cloning survives the fences honestly if and only if the metadata rides along:
Replay-before-clone is structural — the replay's byte-binding to the certified candidate means the clone consumes the seam's output; the fence cannot be reordered away.
Mass conservation is exact under cloning: weights divide by K, every row duplicates, so total mass and every column's effective-mass share are invariant by construction. The 145/0 gate still re-runs on the rowwise output (fail-closed, never assumed), and the SPI channel's reviewed 50% mass share is re-checked post-solve as a declared bound.
Live gap (increment 2 fixes): rowwise_dataset.py reads and writes only the four entity tables — it drops the staging H5's populace_household_weight_kind (importance) and populace_mass_log_json attrs. Cloning the staging H5 through today's tool silently launders IMPORTANCE weights into unlabeled weights. The clone step must carry both attrs, refuse an input whose weight kind it does not recognize, and assert exact mass conservation against the input.
No calibration against the 208 banded facts, before or after cloning: complete FRS Total Income band assignment remains unavailable, so those facts remain reviewed exclusions with canonical fences in every rowwise release exactly as in the national seam. Nothing in this epic touches IMPORTANCE-weight replay internals, the reviewed release parameters, or the source contracts.
The local solve transitions weight kind explicitly (importance → locally calibrated) as a recorded step in the mass log, never implicitly.
Release contract for the rowwise artifact
The 145/0 contract covers input columns and carries over unchanged (the gate re-runs on the rowwise output). The rowwise artifact additionally promises, in its manifest:
identity chain: staging H5 sha → certified candidate sha → source-stage shas (donor/ODS identities by reference to the committed replay artifacts, never row-level data);
FRS-derived microdata must never reach a public repo. The rowwise H5 and every sidecar that carries household-grain rows publish exclusively to the private HF dataset repo policyengine/populace-uk-private (the certified compact's home), via populace-publish-release --repo-id policyengine/populace-uk-private --no-latest, tagged populace-uk-2023-rowwise-<git>-<ts>. Aggregate diagnostics (manifest, fit tables, support summaries, past-cap census) are committable to the repo exactly like the HMRC replay artifacts — no row-level donor or FRS data, no local paths.
Increment ladder
Local-target census tool — tools/census_uk_local_targets.py + checked-in census JSON + tests. Pure observability. (claimed, this lane)
Rowwise fence carriage + dry-run — carry weight-kind/mass-log attrs through clone read/write, refuse unrecognized weight kinds, assert exact mass conservation in the driver manifest, and add --dry-run: the full clone plan (rows/bytes at K, exact expected per-constituency support from crosswalk populations × the pool's region mix, id-multiplier and lineage checks) with no H5 written. (claimed, this lane)
Max greenlit scoping the "bigger UK dataset" arm 2026-07-22. This epic is the UK analog of the US dense/local arm: one national rowwise artifact whose rows carry finest-available geography and whose weights are calibrated against a constituency/local-authority target surface — built by cloning the compact FRS-based pool, because the UK has no ACS-scale microdata to substitute for density. It subsumes the publication goal of #146 (rowwise H5 + long weights), sits under the #144 migration umbrella, and is the sibling of #355 (the pool should get a smaller national default at the same time as this arm gets bigger).
Everything below is designed against the machinery that already exists (
uk_runtime/rowwise_geography.py,rowwise_dataset.py,geography_ladder.py,local_targets.py,local_solver.py,local_runner.py,tools/build_uk_rowwise_dataset.py); the epic is mostly wiring, fences, and targets, not new solvers.Base artifact and pipeline order (fence-determined, not a preference)
The clone input is the national staging H5 — the output of the national orchestration seam after the 145/0 input-coverage gate and the adjudicated HMRC/SPI replay — not the raw certified compact:
f17306cc…)829e843f…)importance+ mass log in H5 attrslocal_untracked)(Compact figures verified directly against the cached certified H5; staging figures from the committed
national_staging_build_record.json.)The ordering is forced by the fences in
hmrc_restoration.py: the HMRC replay byte-binds its input to the certified candidate (_assert_dataset_matches_certified_candidatechecks the source-file fingerprint captured at SHA-256 verification), so cloning can never run upstream of the seam, and cloning the certified compact directly would clone 200k dead SPI households and skip the 145/0 restoration. Pipeline:Clone count K: rows, memory, support
The pool is already a 10× clone:
household_id = tier·10⁸ + basewith exactly 53,508 unique base ids × 10 tiers (verified on the certified H5; #355 documents the construction). eFRS-grain source lineage is therefore recoverable ashousehold_id mod 10⁸, and the rowwise build must carry it — per-area "distinct source households" diagnostics computed at pool grain would overstate real support 10×.K below means the additional rowwise multiplier on top of that 10×. Clones add geographic resolution and solver degrees of freedom only — zero new information — so K is sized by per-area support, not ambition:
The smallest-constituency column is the population-proportional expectation for a Na h-Eileanan an Iar-scale area and is approximate; the dry-run tool (increment 2) computes the exact expected support per constituency from the ladder/crosswalk populations and the pool's region mix. For calibration comparison: the US dense arm supports 436 CDs on a 337,704-record pool ≈ 775 records/CD (#355, #449) — K=1 is already at US-dense per-area scale, because the 10× pool cloning did the density work up front.
Proposal: pilot at K=1 (geography assignment only, no row growth), first published candidate at K=2–4, chosen from the dry-run support/ESS diagnostics. K>4 requires a measured justification.
avoid_constituency_collisionsstays on (no source household repeated in the same constituency across rowwise clones).Geography assignment: the OA ladder is the operator
Two mechanisms exist. The crosswalk path (#142/#143) samples an OA within (country, region) ∝ population and covers all four countries. The OA ladder (#349 ratified, #354 merged) samples a 2024 Westminster constituency within the region ∝ census household counts, then an OA within the constituency ∝ population, carries sha-pinned best-fit lookups and per-layer vintages (
vintage_policy: error), and ships a release-blocking gate — but is England & Wales only today (575 of 650 constituencies).The epic declares the ladder as the assignment operator (it is the ratified US-block-ladder parallel with vintage discipline). Work items:
geography_sources.pyalready pins those source URLs for the crosswalk path; they need per-layer vintage pinning inoa_ladder_sources.py;tools/build_uk_rowwise_dataset.pyfrom the crosswalk sampler toassign_uk_geography_ladder+uk_geography_ladder_gate, keeping the crosswalk as the coverage-check path.Local target surface: census first, then bind
local_targets.pycomputes today, at household grain:income_tax > 0), age bands (8 × 10y), UC households, UC households by child count (0/1/2/3+);That mirrors the deprecated uk-data constituency surface. What official UK local data offers beyond it (ONS mid-year age structure by PCON24/LAD; HMRC Personal Incomes by constituency — taxpayer counts and employment/self-employment/pension totals; DWP Stat-Xplore UC/Pension Credit/Housing Benefit by constituency and LA; Census 2021/2022 tenure and composition; ONS PIPR rents by LA; ONS small-area income at MSOA) needs a census artifact, not prose: increment 1 ships a tool that enumerates every in-code metric family and every documented official family with area type, source, vintage, and status (
bound_in_code/documented_unpinned/needs_adjudication), checked in and tested, so binding work is enumerable and claimable per family.Two standing fences on targets:
income_tax > 0SPI-frame membership in the HMRC metrics is a modeled proxy frame; the census must record that honestly and the binding adjudication must accept or replace it explicitly.The local solve: one uniform operator, declared bounds (doctrine)
Per the #492 lane and #493: no per-target calibration knobs. The solve is one operator over the whole surface — national families (149 targets in the published release, plus the natively declared CGT facts) and all area×metric rows together — with globally declared loss shape, cap, and weight-ratio bounds. A miss is local-support work (rows/clones/ladder) or target work (fix or fence the target), never a knob.
build_stacked_local_matrix, Publish a Populace UK local variant with rowwise H5 and long weights #146 long weights) remains the research harness and can express the rowwise solve as its block-diagonal special case; the released artifact is the rowwise H5 with its solvedhousehold_weightcolumn.solve_stacked_local_weightscurrently defaultsmax_weight_ratio=100.0while the US dense design bound is 5× (max_weight_ratio anchors differently per arm: 5x vs design (dense) but ~25x effective (sparse refit re-anchors) — declare one stretch contract #493 wants one declared stretch contract). The epic picks the UK local bound explicitly and records it in the release manifest.HMRC IMPORTANCE-weight fences under cloning
Read against
hmrc_restoration.py, cloning survives the fences honestly if and only if the metadata rides along:rowwise_dataset.pyreads and writes only the four entity tables — it drops the staging H5'spopulace_household_weight_kind(importance) andpopulace_mass_log_jsonattrs. Cloning the staging H5 through today's tool silently launders IMPORTANCE weights into unlabeled weights. The clone step must carry both attrs, refuse an input whose weight kind it does not recognize, and assert exact mass conservation against the input.IMPORTANCE-weight replay internals, the reviewed release parameters, or the source contracts.Release contract for the rowwise artifact
The 145/0 contract covers input columns and carries over unchanged (the gate re-runs on the rowwise output). The rowwise artifact additionally promises, in its manifest:
dataset_role: non_default_local_areaper Release contract needs dataset-role classes: national-default checks reject valid non-default local-area artifacts #398 — never thelatest.jsonpointer (--no-latest), immutable tag, the compact (and eventually the UK small national default: L0-selected ~50k-household file from the 535k pool #355 small default) stays the national default. Until Release contract needs dataset-role classes: national-default checks reject valid non-default local-area artifacts #398's role-class validator lands, publication uses the Build-L routed path (all applicable gates recorded).Publish mechanics (licensing)
FRS-derived microdata must never reach a public repo. The rowwise H5 and every sidecar that carries household-grain rows publish exclusively to the private HF dataset repo
policyengine/populace-uk-private(the certified compact's home), viapopulace-publish-release --repo-id policyengine/populace-uk-private --no-latest, taggedpopulace-uk-2023-rowwise-<git>-<ts>. Aggregate diagnostics (manifest, fit tables, support summaries, past-cap census) are committable to the repo exactly like the HMRC replay artifacts — no row-level donor or FRS data, no local paths.Increment ladder
tools/census_uk_local_targets.py+ checked-in census JSON + tests. Pure observability. (claimed, this lane)--dry-run: the full clone plan (rows/bytes at K, exact expected per-constituency support from crosswalk populations × the pool's region mix, id-multiplier and lineage checks) with no H5 written. (claimed, this lane)Refs: #144, #146, #355, #349, #354 (ladder), #420 (145/0 contract), #398 (dataset roles), #492/#493 (solve doctrine), #275 (one national dataset, filter by geography), #142/#143 (crosswalk + driver), UK_COVERAGE_PROGRESS.md (fence history).