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
Parent: #495 (workstream G of the UK-migration epic #665). Depends on #708 and the machinery merged in #735.
Why
#622 gave the national surface one doctrine-clean path from Chronicle facts to compiled targets: contract + pinned feed + period → typed references and a membership classification, behind two release-blocking compile-parity gates. The local surface has no equivalent, and it is the only missing link in the local lane.
Both ends already exist. Chronicle wave 3 (PolicyEngine/chronicle#134) minted the local facts — 24 packages, ~95.6k facts at constituency and local-authority grain — and local_targets.compute_household_metrics already computes the household side of every metric in build/uk/uk_local_target_census.json (26 metrics across 7 families; 18 at constituency grain, 22 at LA). What is missing is the middle. Nothing compiles fact values into area-grain references, so of the census's ten sources only three are pinned (the OA-ladder household counts) and seven remain documented_unpinned: the local solve can compute its metric columns but cannot bind their targets.
Depends on
Migrate the UK local-geography and firms contracts out of Chronicle (chronicle#166 ruling) #708 — the uk_local_geography contract (25 targets: 4 HMRC income, 8 ONS age bands, 5 UC, 3 equivalised income, 4 tenure, 1 private rent) must land in build/uk/ as a UK resource first. This issue consumes the resource, not the duck-typed profile object metric_names_from_target_profile reads today.
Complete #622: compile the UK target surface from Ledger facts at the 2024-25 line #735 machinery — target_reference_authoring.py, target_materialization.py, ledger_targets.py (period-token collapse, assertion-policy enforcement, the declared multi-fact operations), the parameterized compile-parity gate in gates.py, and calibrate/matrix.py's entity collapse.
Scope
Area-grain authoring. Fan each contract target out over its declared geography_levels × the ladder's 650 constituencies / 361 local authorities, emitting one reference per (target, area) with ledger_selector.geography_level and geography_id pinned exactly as the national surface pins country / K02000001. Ship build/uk/local_target_references.json and local_target_reference_membership.json in the same shapes as the national pair (candidate/active counts, status counts, geography pins, uprating holds), with zero unexplained deferrals.
Geography identity, declared once. One crosswalk between the published area codes carried by the facts and the ladder's area ids, with vintages pinned per layer (PCON24 vs earlier constituency editions, LAD editions, the Scotland and Northern Ireland equivalents). A reference whose area code does not resolve at the declared vintage fails the compile — never a silent drop, never a best-effort match.
Per-family resolution, signed. Resolve or defer each family with a reason: HMRC income by area, ONS age structure, UC households (plus the constituency-only child-count splits), equivalised income (LA only), tenure (LA only), private rent (LA only). census_households stays ladder-derived — the compile records the artifact sha as its provenance rather than re-minting the values as facts.
Support oracle before binding. Per epic doctrine, run the oracle for each family before it binds. A family whose household-side support cannot carry its target is deferred with the measurement attached, not bound and left for the solver to absorb.
Compile-parity gate and frozen fixtures. Parameterize the existing parity gate over the local registry against two fixtures: the in-code default metric surface (metric_names(area_type) with no profile) and the incumbent uk-data local target surface. Every difference enumerated and signed in a committed resource, mirroring ledger_compile_parity_*_signed_differences.json.
Fact gaps go upstream. Any family the feed cannot answer is recorded here and requested in Chronicle — never worked around by minting a value microcosm-side.
local_target_references.json compiles from a sha-pinned consumer artifact at the declared period, and the membership file accounts for all 25 contract targets × their declared area levels, every deferral signed.
The compile-parity gate is release-blocking, passes on both committed fixtures, and refuses a reference whose geography id or vintage does not resolve.
The census artifact regenerates with the previously documented_unpinned sources either pinned or carrying an explicit signed reason they are not.
No target value enters from any path other than the pinned artifact; ladder-derived values carry the ladder artifact sha.
Parent: #495 (workstream G of the UK-migration epic #665). Depends on #708 and the machinery merged in #735.
Why
#622 gave the national surface one doctrine-clean path from Chronicle facts to compiled targets: contract + pinned feed + period → typed references and a membership classification, behind two release-blocking compile-parity gates. The local surface has no equivalent, and it is the only missing link in the local lane.
Both ends already exist. Chronicle wave 3 (PolicyEngine/chronicle#134) minted the local facts — 24 packages, ~95.6k facts at constituency and local-authority grain — and
local_targets.compute_household_metricsalready computes the household side of every metric inbuild/uk/uk_local_target_census.json(26 metrics across 7 families; 18 at constituency grain, 22 at LA). What is missing is the middle. Nothing compiles fact values into area-grain references, so of the census's ten sources only three are pinned (the OA-ladder household counts) and seven remaindocumented_unpinned: the local solve can compute its metric columns but cannot bind their targets.Depends on
uk_local_geographycontract (25 targets: 4 HMRC income, 8 ONS age bands, 5 UC, 3 equivalised income, 4 tenure, 1 private rent) must land inbuild/uk/as a UK resource first. This issue consumes the resource, not the duck-typed profile objectmetric_names_from_target_profilereads today.target_reference_authoring.py,target_materialization.py,ledger_targets.py(period-token collapse, assertion-policy enforcement, the declared multi-fact operations), the parameterized compile-parity gate ingates.py, andcalibrate/matrix.py's entity collapse.Scope
geography_levels× the ladder's 650 constituencies / 361 local authorities, emitting one reference per (target, area) withledger_selector.geography_levelandgeography_idpinned exactly as the national surface pinscountry/K02000001. Shipbuild/uk/local_target_references.jsonandlocal_target_reference_membership.jsonin the same shapes as the national pair (candidate/active counts, status counts, geography pins, uprating holds), with zero unexplained deferrals.census_householdsstays ladder-derived — the compile records the artifact sha as its provenance rather than re-minting the values as facts.metric_names(area_type)with no profile) and the incumbent uk-data local target surface. Every difference enumerated and signed in a committed resource, mirroringledger_compile_parity_*_signed_differences.json.Out of scope
review_required_before_bindingfences (the UK dense/local epic: rowwise constituency-grain dataset by cloning the national staging pool (US dense-arm analog) #495 increment-4 remainder issue).Acceptance
local_target_references.jsoncompiles from a sha-pinned consumer artifact at the declared period, and the membership file accounts for all 25 contract targets × their declared area levels, every deferral signed.documented_unpinnedsources either pinned or carrying an explicit signed reason they are not.🤖 Generated with Claude Code