Repository navigation
One consistent UK target surface: enforced cross-grain reconciliation, declared uprating holds, nation household controls and the 942a1fa re-pin (#1123 PR A) - #1198
Conversation
…rget-surface receipt tool (#1123) The full graph's target kernels and the national kernel each listed their own modules, and neither listed the shared resolver (build/ledger_targets.py, where linear_combination and the period rules live) nor the HMRC uprating appliers, so an edit there could reuse a cached surface computed by the old code. uk_runtime/target_compile_modules.py now names the modules that decide a target value once; both kernels hash that tuple, and a test pins that every direct microcosm.build import of a listed module is listed or excluded with its reason (the country spec, covered by its fingerprint; spine modules imported for a constant or a type), so the coverage holds transitively. The compilation node's body moves into compile_uk_full_target_surface(), which tools/uk_target_surface_receipt.py shares: the tool writes every national row and every reconciled local cell with the reconciliation factors, holds and coverage receipts, and diffs two receipts row by row, so each target-surface change in #1123 states exactly which values it moves. load_uk_full_target_inputs(include_validation_periods=False) compiles the calibration year alone for the tool; the node is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d references do (#1123) private-pension amount references to hmrc.itl_2026.total_income_growth_by_ total_income_band but left the contract (the regeneration source) and the membership report on the flat OBR indices: a regeneration from the contract would have silently put the flat indices back. The three contract targets now declare the ITL band index with a selector note saying why, and the regenerated membership report records the band factors the references already apply. The national references regenerate with identical content; the committed file is now the generator's exact output again (ASCII escapes, as the regeneration test compares bytes), so test_committed_surfaces_regenerate_ from_pinned_feed[national] passes with the pinned feed. A new test refuses any reference or hold whose uprating index differs from its contract target's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ions, declared overlaps (#1123) The UK cross-grain operator now has a nation grain between country and region, lower rows that span several legs (England over its nine regions, England and Wales over regions plus Wales), same-geography partitions, and a refusal of a partial middle tier that would take a whole control onto the legs it covers. Control agreement is checked to 1e-12 rather than bitwise. The UK bridges, local routes, overlap relations and signature-incomplete targets move from code to uk/cross_grain_declarations.json, registered in the country package so the spec fingerprint covers them. A coverage check refuses any local target without a route to a control, any undeclared overlap across grains, and any stale declaration; uk/target_doctrine_exceptions.json tolerates the 22 gaps that remain, each naming the #1123 change that closes it. No target value moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… reconciled values (#1123) Before this, only the joint local surface was reconciled and only its local cells were written back: a region row rescaled to its country control fed the constituencies below it while the national register the solve binds kept the raw value. reconcile_uk_national_registry now reconciles the national register in both target loaders, before the measure exclusions and before the frozen scoring register is compared, so the full and the national-only graphs bind one value per quantity. Two relations reconcile there: exact measurement signatures across the country, nation and region grains (the CGT regional cells under the UK total), and the new declared band bridges, where a projected UK ITL Table 2.5 band, or a run of them, controls the SPI Table 3.11 regional cells nesting in it. The ten ITL/SPI-region overlap exceptions close. Validation-period registers stay as compiled. The joint local pass then refuses any factor away from one on a group whose lower grain is a control grain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…al at compile (#1123) A compiled UK target whose Chronicle fact is older than the calibration period must now either be uprated by a declared index or match a declared hold in uk/uprating_holds.json (matched_period, in_year_snapshot, control_rescaled, reviewed_no_index; each with reason, tracking and expiry, approved_by left null until signed). Both target loaders check the full compiled national register and the local register for the calibration year and refuse an undeclared, expired or stale hold; windows of the year's own subperiods (calendar-year means, monthly Stat-Xplore windows) are not holds. The holds a later #1123 change closes sit in target_doctrine_exceptions.json as undeclared_hold, each naming that change. A local area target that declares an uprating_index is now uprated at authoring and at compile exactly as a national target is (no local target declares one yet). The receipt tool records the holds and the national reconciliation. experiments/1123-target-vintage-audit.md records the C0 vintage audit behind the register. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…employer row's 2024-25 anchor (#1123 C6b) HMRC Table 6.2's 2024-25 reliefs are the latest edition. The employer NICs relief already anchors the sacrificed amount at 2024-25 and restates only the rate (#1069 c11); the income tax rates and the employee Class 1 main rate are unchanged from 2024-25 to 2025-26, so the other five relief targets follow it as a declared reviewed_no_index hold rather than an undeclared one. Growing every relief with earnings instead stays an open ruling. No value moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#1123 C11) A zero target is a real fact (no recipients in an area, no dwellings in a band): under the default scale max(abs(target), 1) it is measured in one unit of its basis, so a positive estimate is charged its full miss. That was true but incidental. The solve's and the scorer's target_loss_scales options now carry a zero_targets census (count, scale range, loss-weight range and zero-weighted rows), and tests pin a zero target's positive weight and loss contribution under an amount/count half-and-half weighting. The weighting rule itself does not change (microcosm#1124 owns that). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
) ONS's June 2026 public sector employment edition keys its quarter rows 03_2025, 06_2025, 09_2025, 12_2025, where the March 2026 edition wrote dec2024. The series key stripped the year but kept the month, so the four quarters read as four series and no monthly window could average them. A numeric month beside its year (MM_YYYY or YYYY-MM) is now a period fragment of a layout value, like a month name; a table number such as table_1_2 or a thirteenth "month" stays identity. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1123 C3) The artifact is chronicle main at the #314 merge (PolicyEngine/chronicle#313): 927,239 rows, consumer_fact.v4 with an unchanged schema, 2.7 times the 825406f feed. It brings household estimates by nation and region, mid-2025 population for the UK, regions and every local authority with ONS's revision of mid-2024, monthly UC by area, census residence and tenure tables, rent inputs, and newer SLC, ISC, public sector employment, land and DfI editions. Values that move, all to newer official editions: - the 149 national population rows to ONS mid-2025 (UK 69,483,939); - local-authority ages to mid-2025 (2,872 cells, Northern Ireland included); Scottish constituency ages to NRS's revised mid-2024 edition (452 cells); the UC May 2025 area cells to DWP's revised figures; - SLC repayments (FY2025-26), ISC (January 2025), DfI receipts (FY2025-26), land (preliminary end-2025, bound explicitly: total GBP 6.87tn against 7.12tn at end-2024) and public sector employment (the mean of its four 2025 quarters, 6,176,000, instead of a December snapshot). Three selectors move with the data: the ONS mid-2025 workbook lays its rows out by geography, so the population selectors pin ages through dimension_values and no longer require the age layout (without this the UK total fell back to mid-2023); land binds the preliminary rows; public sector employment declares its quarterly window. Three vendor selections pin the periods their stages were built on, so spine inputs move only by publisher revisions: mid-2024 population (UK and twelve regional totals) and the DfI edition's rounding of eleven earlier values. The hold register follows: 76 re-pin exceptions close; ISC is an in-year snapshot; SLC student support (its provisional 2025/26 rows are summary totals, not the products bound) and cars fuel duty (no fiscal-to-calendar roll-forward yet) are reviewed holds; constituency ages are control_rescaled under the mid-2025 regional controls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ands (#1123 C7) scaled_by_ratio restates a cell to a narrower universe, so its ratio is a share in (0, 1]. Carrying an official estimate for an earlier period to the target period needs growth: Wales's mid-2024 household estimate by its mid-2025/mid-2024 population, Northern Ireland's census-2021 households by LPS dwelling stock 2025/2021. rolled_forward_by_ratio computes base * numerator / denominator, where the base is the reference's own selector (one cell, or the sum of the cells it resolves at its latest period), the numerator is pinned to the target period and the denominator to the base's own period, each resolving exactly once; the growth is bounded to (0.5, 2] so a mis-pinned operand cannot pass for growth. Every input, the ratio and both periods are recorded on the spec. This is the only change to the shared build/ledger_targets.py in #1123. The base operand may move the base below the reference's geography (a nation's estimate built from its districts' census cells); the cell keeps the reference's own geography. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… area cells to the SPI bands (#1123 C8) SPI Table 3.6 and the area Tables 3.14 and 3.15 cover UK income tax payers only, and they agree within 0.5% at source. The national SPI band rows now carry income_tax > 0 like the area rows; the employment and self-employment counts count income != 0 at both grains, as HMRC counts a loss-making source with nil income (Table 3.10 notes); the area filter concept is unified. The area rows, held at 2023-24 until now, take HMRC's own all-ranges growth from ITL Table 2.5 (taxpayer counts x1.0763, total income x1.1340, calendar- 2025 window over 2023-24). A new fan-out sum bridge then reconciles them per grain to the national SPI band partition summed over every band of the full compiled register (the measure-excluded GBP 1m-and-over band included), refusing a factor more than 2% from one. Fan-outs are never exact-signature controls: the coverage check no longer counts a grouped national target as covering a local one, and the ten fan-out overlaps that exposed (UC payment distributions, CGT banded tables) are declared. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on tenure under them (#1123 C9) Nations win (decision of 2026-10-07): four new national targets carry each nation's official household estimate for 2025 and control its census household cells through a per-geography bridge: - England's nine regions, ONS LFS April to June 2025 (summing to 24.329m); - Scotland, NRS June 2025 (2,570,799); - Wales, Welsh Government mid-2024 rolled to 2025 by Wales's mid-2025 over revised mid-2024 population; - Northern Ireland, the eleven districts' Census 2021 households (768,810) rolled by LPS dwelling stock April 2025 over April 2021. The UK household-composition partition no longer parents the census cells; the national reconciliation scales it onto the nations' sum through a declared sum bridge, and A15 uprates the census cells to that sum, with the LFS UK total kept as a diagnostic and its gap recorded. The census tenure cells partition their authority's households (share_of_parent), so they follow the authority's reconciliation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1123 C9b) A census tenure cell's share is drifted, for English authorities, by its category's share of the authority's dwellings in 2024 over 2022 (ONS SPREE, vendored as ons_spree_tenure_dwellings.json). Dwellings move shares, never levels: the level comes from the tenure partition. Authorities in the other nations hold their census shares, said so on each spec. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… receipt communal residents (#1123 C10) The area UC caseload and its child bands move from the May 2025 snapshot to the mean of the twelve calendar-2025 months of DWP's monthly Stat-Xplore series (the three-plus band sums its three published counts), the window the national UC rows use, so the bridge to the GB caseload reconciles like with like. The child bands partition each constituency's caseload (share_of_parent: the unknown-children category is not bound). The receipt tool gains a census communal-establishment validation (--communal, RM120 and UV101a): validation only, as decided; 114 constituencies and 64 authorities have at least 10% of their 18-24 residents in communal establishments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…C11) uk/local_small_cell_rule.json declares an absolute household floor (50, proposed, unsigned) for council-tax band cells. The local generator's second pass defers, by rule rather than by area name, every active cell below it (16 today, including Blaenau Gwent band H and Barking and Dagenham band G), records each deferred value, and carries the rule's expiry, which the loader enforces at its review date. The joint surface still counts a deferred cell's value in its leg's control, so deferring it does not inflate its neighbours. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#1123) DWP's May 2025 UC area cells keep their established record-set spec id inside the January 2025 to May 2026 monthly series (PolicyEngine/chronicle#314), so consumers that select the May cells alone keep working. The window guard compared spec ids and refused the calendar-2025 window, although the publication, release key, raw file and parsed table are one. It now compares the parsed record set; a spec id is a selector's identity, not a property of the publication. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
National authoring compiled each reference against its own source's facts and the operand facts found there. A roll-forward's growth can come from another publisher than its base: Wales's household estimate (Welsh Government) by ONS population, Northern Ireland's census households (NISRA) by LPS dwelling stock. Operand selectors that name another source now read that source's facts too, appended after the unchanged same-source pool, as the runtime compile already does through reference_fact_selectors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#1123) Northern Ireland's household control rolls its eleven districts' census cells forward to the nation. The hierarchy needs one geography for the row, so the national generator marks a roll-forward whose base operand moves the geography as a composed row (composed_from_level, with the row's own geography), the path region rows built from their authorities already take. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nerator (#1123) The generator forced a sum on dwp.uc.households_by_area_children_3plus for the May-snapshot spelling; the contract now declares the calendar-2025 monthly window sum, which the forced operation overrode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ow (#1123) DWP publishes each UC child-count cell as its own record set inside one release, raw file and table (PolicyEngine/chronicle#314). The window's shared-publication key no longer compares record sets; the per-cell series check still pins each cell to one record set across its months. The three-plus band's selector names its three cells, and the authored reference header lists rolled_forward_by_ratio. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The vendored resource keeps each row's concept at top level, not under observed_measure, so the drift found no SPREE row for any authority. The fixture now matches the vendored shape and a test drifts Hartlepool from the packaged resource. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…C3) The re-pinned DfI edition restates 2024-25 Translink receipts to the nearest £0.1m (49.6m Metro and Glider, 100.5m Ulsterbus), so the recomputed price reads the vendored rows' new values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
compile_uk_target_registry scanned the whole Chronicle feed once per reference; on the 927,239-fact feed the national parity receipt took over two hours. Facts are now indexed by source once, and a reference whose selectors all pin source_name scans only those sources' facts, in feed order. A selector without a source pin keeps the full scan, so the candidate facts are exactly those of the full scan (checked on the pinned feed for 68 references, the 28 ratio-operand references among them). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every coverage gap and undeclared hold it tolerated is closed, so the coverage check and the holds check now refuse any gap outright and the resource leaves the package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and higher rate taxpayers by area (#1123 C8b) Income Tax liabilities Table 2.2 counts taxpayers by marginal rate for each English region and nation; the two new national targets bind them on the region tier as the calendar-2025 window of HMRC's 2024-25 and 2025-26 projections, measured on the engine's tax_band. From 2024-25 HMRC's Scottish additional row is the advanced and top rates together, as the engine groups them. SPI 2023-24 Tables 3.14 and 3.15 give higher rate taxpayers by constituency and local authority. They bind for England, Wales and Northern Ireland (area_scope): Scotland's 2023-24 cells predate its advanced rate. Each region's or nation's Table 2.2 row controls its area cells through a per-geography bridge, the cells keep their 2023-24 shares (a declared control_rescaled hold), and at source the cells sum to within 1.2% of each 2023-24 row at both grains. The additional rate area cells are left unbound: rounded to the thousand and suppressed in 85 constituencies, they cannot sum to their controls. The area metric is appended to the local metric list, so no incumbent metric index moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1121 named the SPI amounts' ITL band index in the contract before this branch rebased, so C2 adds only the test that pins every reference's index to its contract target's; there is no user-visible fix to record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…change (#1123) National references: 1,267 active (the four nation household controls, twelve cells; the 24 ITL Table 2.2 marginal-rate cells). Local references: 21,788 active (the 919 higher rate taxpayer cells; the 16 council-tax band cells below the small-cell floor deferred; the UC child bands on the calendar-2025 window). The local census, the release input coverage manifest (the CGT conditioning resource's feed header moved with the re-pin), the uk-data target parity register and the three signed compile parity receipts are regenerated, the count pins follow, and the re-pin runbook gains the manifest step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The zero-target census edited microcosm.calibrate.solve, which the calibrate graph parity kernel, the spec-engine loader golden vector and the US seed-protocol and seed-map digests all hash: the calibrate parity fixture is re-pinned (tools/graph_parity_repin.py), the loader golden and EXPECTED_HASHES carry the new digests (the commit before #104 reproduces the old ones), and docs/evidence/spec-engine/us-f0-coverage.json is regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e calendar means (#1123) The full national compile refused two undeclared holds and five stale declarations. The Wales and Northern Ireland household controls resolve older bases (mid-2024, Census 2021) but rolled_forward_by_ratio carries them to 2025 by a ratio whose numerator is pinned there, so they are not held. The UC payment-distribution and Scotland youngest-child rows were November 2025 snapshots when the hold was declared; on the 942a1fa feed they bind the calendar-2025 mean of the monthly series, so the in_year_snapshot declaration no longer matches a hold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The joint local surface refused its own fan-out sum control: the synthetic control's signature carried filters at top level, and the reconciliation canonicalizes only a 'measurement' mapping. The test now runs the synthetic signature through that canonicalization. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Independent adversarial review (Claude Code) at
|
Part of #1123 (PR A: items 1, 2, 6 and 7; private rent is PR B). Addresses #104.
Why
The K=25 audit found that most local misses surviving the dense solve were on the target side. Local rows were never uprated, 509 national rows were held at an older period with no declared index, nothing refused an undeclared hold, and reconciliation across grains was opt-in. This PR makes the UK target surface one consistent, enforced surface.
What changes
uk/cross_grain_declarations.json: bridges, partitions, relations) or the compile refuses. The lattice gains a nation grain, rows that span several legs, and same-geography partitions. The national register is reconciled across UK, nation and region in both loaders, and the joint local pass refuses a national factor away from one.uk/uprating_holds.json, 15 entries,approved_bynull). The compile refuses an undeclared, expired or stale hold, nationally and locally.tax_band), and higher rate taxpayers bind by constituency and local authority in England, Wales and Northern Ireland (919 cells).uk/local_small_cell_rule.json, unsigned). A deferred cell still counts in its leg's control.rolled_forward_by_ratiovalue operation; both UK target kernels hash every module that decides a target value;tools/uk_target_surface_receipt.pywrites and diffs receipts of the compiled surface; the national compile scans only each reference's own sources (the national parity receipts drop from about 2h25m to under 20 minutes on the 927k-fact feed, with identical candidate facts).Value moves (receipt against the pre-change surface)
dwp.hb.amount_pension_age), 168 moved (149 population rows, the 10 household-composition rows, land, SLC repayments, ISC, public sector employment, DfI), 2 removed (top UC payment-distribution bands the 2025 series does not publish).Verification
tools/uk_target_surface_receipt.py --communalruns clean end to end on the pinned feed.tools/ci_test_plan.py verifyok. The failures are local-environment only and also fail at the base commit (pe-uk installed in the engine-free venv; a node package the Orrery parser tests need).microcosm.calibrate.solvemoves the loader golden vector, the US seed digests, the US coverage evidence and the calibrate graph parity pins; all are re-pinned as main does for solver edits.tax_bandmeasure is tested against fakes only here), and the dense re-solve, so the effect on fit is not yet measured.Departures from the plan
gates.json: closure fails closed at compile instead.Open decisions (María)
Before merge:
Can follow as issues:
After merge
🤖 Generated with Claude Code