Repository navigation
Port the uk-data wave to policyengine-uk 2.134.4: stamp duty, Move to UC, national home values (sixth #1095 PR) - #1308
Conversation
732131e to
306bc11
Compare
Independent adversarial review of this PR at 732131eRun with Claude Code on 16–17 October 2026 at 732131e: seven independent review lenses (correctness, method, generated artifacts, tests, claims against the diff, disclosure, a second fresh-eyes pass), then one to three skeptics per finding who tried to refute it; (k/n) is how many agreed. No builds and no feed-gated tests were run. The branch was force-pushed to 306bc11 during the review (policyengine-uk relocked to 2.134.4; the stage code is unchanged), so every finding below applies to 306bc11 except the 2.134.5 lock finding and the E1 nit; no tests were run at 306bc11. The force-push rewrote every commit from E1 on: the reviewed-head commits b355aad, e5bee83 and 21cb5e0 cited below are 34a5526, a6c9758 and 908f6a3 on the live head. Leave this PR in draft. The stages are faithful ports and the published-statistics arithmetic checks out exactly. What blocks it is in the code, not the method:
Separately, the frozen build will be refused by the release-blocking local compile-parity gate unless someone regenerates that receipt on the final head (X-s01, see Cross-PR). Blocking
Should fix
Minor
Cross-PR
Disclosure check
Tests and tools runAll runs were at 732131e unless noted. No tests were run at 306bc11.
Checked and found sound
Refuted during verification
Not covered
🤖 Generated with Claude Code |
On main after #1302: 25,327 compiled cells. The 18 Scottish BRMA rows are ledger-only by construction, and no signed value moves. The UC drift rationales now read their fixture/ours bands off the rows, and the support floor's deferred cells carry their own signed rationale. The release-blocking preflight gate uk_ledger_compile_parity_local_incumbent_2025 reads this receipt, so whichever of #1305, #1306, #1307 and #1308 lands last regenerates it on the combined tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
306bc11 to
8739557
Compare
…gets (#1095) Review of #1308: purchases_outside_sdlt_regions_weight was the raw weighted total of Scotland's and Wales's purchasers, and on the national spine the non-residential cell rests on fewer than ten source households; the fitted value, the shortfall, the event rate, the scale and the eligible owner weight were floats whatever their support. Every weighted field now goes through receipt_counts.small_cell_share on the distinct household_source_id values behind it, so one to nine sources read "<10"; the share at the stock cap appears only where neither the capped purchasers nor the rest rest on fewer than ten, and the capped support is an extreme_count band. Nothing was staged or uploaded with the old field. Tests: a cell of three outside sources and a fit region of five sources read "<10"; a capped side of two sources hides the share; the stage test now requires positive purchases and fitted transactions and value for both kinds (a nil target branch passed it before); and the packaged HMRC rows reproduce uk-data#573's household targets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1308: regional_home_values banded its capped cell on rows, and support copies from high_value_home_support repeat their source household, so a cell past ten rows could rest on fewer than ten sources. The cell is now counted on distinct household_source_id values (source_count, as the purchases stage counts them) and reported as sources_at_cap; the row totals go through receipt_counts.rounded_count. A test bands twelve copies of two households as "<10". No figure leaked: the cell is latent on the head spine, and the receipt is build output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…receipt (#1095) Review of #1308, with María's ruling of 2026-10-17: p, the share of a combination's drawn units that would claim anyway, pooled Great Britain and Northern Ireland, while DWP's Move to UC rates cover Great Britain only. p is now measured over Great Britain's units, and the probability applies to the combination's units in Northern Ireland too, as the Pension Credit take-up redraw does. Where p exceeds DWP's rate the probability is 0, and the receipt now flags the combination (rate_unreachable), never p itself, and only where ten or more source households carry it. Each combination's support is banded on distinct household_source_id values, not rows, since support copies repeat their source; the aggregates cover Great Britain only and go through receipt_counts.small_cell_share. The texts say that the share reaches DWP's rate in expectation where p is at most r, and declare the two limits uk-data#534 has: the drawn cohort includes units on the Pension Credit route, whose legacy benefits policyengine-uk does not close, and DWP's rates count every household sent a notice. The vendored resource now maps to the spine's 2024 build period. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1308: an operand that sums several cells was checked on its total match count only, so two facts for one listed member and none for another compiled, and a list of three members declared with a count of two passed when one was missing. An operand that sums cells now names them in exactly one list-valued selector field of expected_member_count distinct members, and each member must match exactly one fact. obr.sdlt compiles unchanged (each of its four HMRC record ids matches once in abf0921). Two tests cover the 2+0 case and the mismatched list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1095) Review of #1308, with María's ruling of 2026-10-17 to declare the HRAD allocation as uk-data#573 does: - "Receipts are checked against HMRC, never fitted" described a check that does not exist. The stage fits transactions and value only; households' SDLT is fitted at build level through obr.sdlt, and the texts now say so, and that uk-data#573's 30% higher-rates SDLT test is not carried over. - Households' HRAD is not published, so the allocation by individuals' shares of all Table 8a residential transactions is an assumption, as uk-data#573 says; its household lower bounds are 87,200 transactions and GBP 33.28bn. The texts declare that it probably overstates households' purchases, since nearly all receipts at or below the GBP 250,000 nil-rate band are surcharge and other buyers' receipts there imply more higher-rates transactions than their overall share. - obr.sdlt's notes declare two gaps it does not correct: Table 6b counts the higher rates before replacement-home refunds, which policyengine-uk never gives, while Tables 8a and 8b and the OBR line are net; and the engine's main-home purchase rate also counts the higher-rates transactions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1308: the engine freeze (María's ruling of 2026-10-16) was held by uv.lock alone; the UK extras accept any later release, and the spine build and the calibration only recorded the installed version. assert_installed_engine_is_pinned compares it with the version the engine pins were taken from and refuses a mismatch, at the spine build's engine and at the calibration's simulation factory. Tools that re-pin or regenerate reports keep their own engines, and an environment without policyengine-uk is left to the callers' own import errors. A test covers the match, a later release and the absent engine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Reply to the review. Thanks for it. It is addressed at Blocking
Should fix
Minor
Cross-PR
🤖 Generated with Claude Code |
Re-review of this PR at 8739557Run with Claude Code on 17–18 October 2026, after the rebase onto 1f1cfa6, the relock to policyengine-uk 2.134.4, the dropped N2, the six fix commits and the reply.
VerdictMost fixes hold, in code:
Tests: 1,244 engine-free and 214 engine tests pass. The engine run, on policyengine-uk 2.134.4, covers the whole engine-uk list including H2 parity. All the Two things block, and the savings-interest renewal is now time-critical. Blocking
Timeline: the savings-interest renewal (R0)
Low
Nits
First-round dispositions
Cross-PR
Disclosure checkNo figure in the 13 commit messages, the added lines, the body or the reply breaches the output rules. Every figure in the body is one of:
The "about 81%" non-residential fit rests on 30 or more source households. The body's disclosure line overstates the banding until the first Blocking item is fixed. Tests and tools run
🤖 Generated with Claude Code |
On main after #1307 and #1306: 27,267 rows, 23,004 of them compiled. The 18 Scottish BRMA rows are ledger-only by construction, and no signed value moves. The 40 cells of the support floor's two authorities, E06000053 and E09000001, carry the floor's own signed rationale; nothing else changes. The release-blocking preflight gate uk_ledger_compile_parity_local_incumbent_2025 reads this receipt, so whichever of #1305 and #1308 lands second regenerates it on the combined tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8739557 to
ca13808
Compare
…gets (#1095) Review of #1308: purchases_outside_sdlt_regions_weight was the raw weighted total of Scotland's and Wales's purchasers, and on the national spine the non-residential cell rests on fewer than ten source households; the fitted value, the shortfall, the event rate, the scale and the eligible owner weight were floats whatever their support. Every weighted field now goes through receipt_counts.small_cell_share on the distinct household_source_id values behind it, so one to nine sources read "<10"; the share at the stock cap appears only where neither the capped purchasers nor the rest rest on fewer than ten, and the capped support is an extreme_count band. Nothing was staged or uploaded with the old field. Tests: a cell of three outside sources and a fit region of five sources read "<10"; a capped side of two sources hides the share; the stage test now requires positive purchases and fitted transactions and value for both kinds (a nil target branch passed it before); and the packaged HMRC rows reproduce uk-data#573's household targets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1308: regional_home_values banded its capped cell on rows, and support copies from high_value_home_support repeat their source household, so a cell past ten rows could rest on fewer than ten sources. The cell is now counted on distinct household_source_id values (source_count, as the purchases stage counts them) and reported as sources_at_cap; the row totals go through receipt_counts.rounded_count. A test bands twelve copies of two households as "<10". No figure leaked: the cell is latent on the head spine, and the receipt is build output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…receipt (#1095) Review of #1308, with María's ruling of 2026-10-17: p, the share of a combination's drawn units that would claim anyway, pooled Great Britain and Northern Ireland, while DWP's Move to UC rates cover Great Britain only. p is now measured over Great Britain's units, and the probability applies to the combination's units in Northern Ireland too, as the Pension Credit take-up redraw does. Where p exceeds DWP's rate the probability is 0, and the receipt now flags the combination (rate_unreachable), never p itself, and only where ten or more source households carry it. Each combination's support is banded on distinct household_source_id values, not rows, since support copies repeat their source; the aggregates cover Great Britain only and go through receipt_counts.small_cell_share. The texts say that the share reaches DWP's rate in expectation where p is at most r, and declare the two limits uk-data#534 has: the drawn cohort includes units on the Pension Credit route, whose legacy benefits policyengine-uk does not close, and DWP's rates count every household sent a notice. The vendored resource now maps to the spine's 2024 build period. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1308: an operand that sums several cells was checked on its total match count only, so two facts for one listed member and none for another compiled, and a list of three members declared with a count of two passed when one was missing. An operand that sums cells now names them in exactly one list-valued selector field of expected_member_count distinct members, and each member must match exactly one fact. obr.sdlt compiles unchanged (each of its four HMRC record ids matches once in abf0921). Two tests cover the 2+0 case and the mismatched list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the national spine, the non-residential purchasers' stock falls short of HMRC's Table 8b individuals' value, so the property purchases stage refused to build (uk-data#573 refuses any shortfall). Per María's ruling of 2026-10-16, where the purchasers' stock cannot carry the value target every purchaser buys at its stock, the most the cap allows, and the receipt records the fit (value_fit, value_shortfall_share); the stage still refuses below 80% of the value (minimum_value_share). The selection, its stock exponent and HMRC's transactions are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1095) The national release solves the bound spine, which has no area, so its main_residence_value stayed the WAS-imputed value after the regional uprating; only the dense line read values off the assigned constituency's Land Registry distribution (uk.full.property.value, microcosm#1240). A new spine stage, regional_home_values, after the high-value home support copies, gives every England-and-Wales owner household with a value the one its region's uprated Land Registry distribution holds at the household's weighted mid-rank among the owner households of its FRS region, at the spine's weights. It ranks as the dense value node does (ties broken by an identity-keyed draw; the support copies spread over the top stratum by their own draws), reads the region level of uk/high_value_home_value_knots.json (Wales is W92000004), and moves property_wealth by the value's change. Scotland, Northern Ireland, renters and owners without a value keep theirs, and the council-tax band is untouched. The stage keeps the value it replaced in main_residence_value_rank_basis. The dense value node receives that column through its slice and ranks on it, so the dense line's values, wealth and bands do not change; it refuses if a declared column is missing, and a run_graph test pins both. The release boundary drops the column on both lines. The value reaches three national rows: obr.sdlt, through the main-home purchase value, and ons.land.land_value and ons.land.household_land_value, through property_wealth; corporate land value does not move. The region knots' tails run up to each region's largest sale, as the dense line's do; they are to be fixed on both lines together (María's ruling of 2026-10-17). The roster pins move to 44 stages; the coverage manifest, the H2 fixture and the stage JSON are regenerated, and no gate digest moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
policyengine-uk 2.128.0 (pe-uk#2119) moves a family off a legacy benefit DWP has closed and takes its other legacy awards away; the family then claims Universal Credit if would_claim_uc or would_claim_uc_at_legacy_closure is true, and the engine's default for the second, true, put every such family on Universal Credit. A new spine stage, uc_legacy_closure_take_up, after uc_capital_coherence and before pension_credit_take_up, ports uk-data#534 (a draft, at c67077a66; uk-data#492). A unit reporting Universal Credit, a unit whose would_claim_uc is true and a unit reporting none of the six legacy means-tested benefits keep the default. Every other unit is drawn at (r - p) / (1 - p), clipped to [0, 1]: r is DWP's claimed / (claimed + did not claim) for the unit's combination of legacy benefits (the all-households rate where DWP reports none), and p is the combination's share already claiming, at the spine's design weights (María's ruling of 2026-10-16; uk-data#534's share is unweighted). The rates are DWP's Stat-Xplore households sent a migration notice, notices July 2022 to June 2026, vendored from the pinned feed (dwp_move_to_uc_facts.json). Each unit reads an identity-keyed uniform. The flag joins the export surface's reviewed extra columns, so 5 of the 12 mirrored gate digests move; the policyengine-uk concept mapping binds it as a take-up input, so no take-up input is left at the engine's default. The roster pins move to 45 stages, and the coverage manifest, the H2 fixture, the stage JSON and the concept coverage report are regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gets (#1095) Review of #1308: purchases_outside_sdlt_regions_weight was the raw weighted total of Scotland's and Wales's purchasers, and on the national spine the non-residential cell rests on fewer than ten source households; the fitted value, the shortfall, the event rate, the scale and the eligible owner weight were floats whatever their support. Every weighted field now goes through receipt_counts.small_cell_share on the distinct household_source_id values behind it, so one to nine sources read "<10"; the share at the stock cap appears only where neither the capped purchasers nor the rest rest on fewer than ten, and the capped support is an extreme_count band. Nothing was staged or uploaded with the old field. Tests: a cell of three outside sources and a fit region of five sources read "<10"; a capped side of two sources hides the share; the stage test now requires positive purchases and fitted transactions and value for both kinds (a nil target branch passed it before); and the packaged HMRC rows reproduce uk-data#573's household targets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1308: regional_home_values banded its capped cell on rows, and support copies from high_value_home_support repeat their source household, so a cell past ten rows could rest on fewer than ten sources. The cell is now counted on distinct household_source_id values (source_count, as the purchases stage counts them) and reported as sources_at_cap; the row totals go through receipt_counts.rounded_count. A test bands twelve copies of two households as "<10". No figure leaked: the cell is latent on the head spine, and the receipt is build output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On main after #1307 and #1306: 27,267 rows, 23,004 of them compiled. The 18 Scottish BRMA rows are ledger-only by construction, and no signed value moves. The 40 cells of the support floor's two authorities, E06000053 and E09000001, carry the floor's own signed rationale; nothing else changes. The release-blocking preflight gate uk_ledger_compile_parity_local_incumbent_2025 reads this receipt, so whichever of #1305 and #1308 lands second regenerates it on the combined tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…receipt (#1095) Review of #1308, with María's ruling of 2026-10-17: p, the share of a combination's drawn units that would claim anyway, pooled Great Britain and Northern Ireland, while DWP's Move to UC rates cover Great Britain only. p is now measured over Great Britain's units, and the probability applies to the combination's units in Northern Ireland too, as the Pension Credit take-up redraw does. Where p exceeds DWP's rate the probability is 0, and the receipt now flags the combination (rate_unreachable), never p itself, and only where ten or more source households carry it. Each combination's support is banded on distinct household_source_id values, not rows, since support copies repeat their source; the aggregates cover Great Britain only and go through receipt_counts.small_cell_share. The texts say that the share reaches DWP's rate in expectation where p is at most r, and declare the two limits uk-data#534 has: the drawn cohort includes units on the Pension Credit route, whose legacy benefits policyengine-uk does not close, and DWP's rates count every household sent a notice. The vendored resource now maps to the spine's 2024 build period. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1308: an operand that sums several cells was checked on its total match count only, so two facts for one listed member and none for another compiled, and a list of three members declared with a count of two passed when one was missing. An operand that sums cells now names them in exactly one list-valued selector field of expected_member_count distinct members, and each member must match exactly one fact. obr.sdlt compiles unchanged (each of its four HMRC record ids matches once in abf0921). Two tests cover the 2+0 case and the mismatched list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1095) Review of #1308, with María's ruling of 2026-10-17 to declare the HRAD allocation as uk-data#573 does: - "Receipts are checked against HMRC, never fitted" described a check that does not exist. The stage fits transactions and value only; households' SDLT is fitted at build level through obr.sdlt, and the texts now say so, and that uk-data#573's 30% higher-rates SDLT test is not carried over. - Households' HRAD is not published, so the allocation by individuals' shares of all Table 8a residential transactions is an assumption, as uk-data#573 says; its household lower bounds are 87,200 transactions and GBP 33.28bn. The texts declare that it probably overstates households' purchases, since nearly all receipts at or below the GBP 250,000 nil-rate band are surcharge and other buyers' receipts there imply more higher-rates transactions than their overall share. - obr.sdlt's notes declare two gaps it does not correct: Table 6b counts the higher rates before replacement-home refunds, which policyengine-uk never gives, while Tables 8a and 8b and the OBR line are net; and the engine's main-home purchase rate also counts the higher-rates transactions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of #1308: the engine freeze (María's ruling of 2026-10-16) was held by uv.lock alone; the UK extras accept any later release, and the builds only recorded the installed version. assert_installed_engine_is_pinned compares it with the version the engine pins were taken from and refuses a mismatch. It runs in spine_build._rules_engine, which gives the spine build its engine and which prepare_full_build and prepare_national_build call before they solve, and at the start of the release-cut battery the certifier runs. The HMRC calibration helpers' default simulation factory, which no build path calls, checks too. The match is exact, so a post-release or a local build of the pinned version is refused as well. An importable policyengine-uk without distribution metadata is refused, since its version cannot be read; an environment without policyengine-uk is left to the callers' own import errors. The tools that regenerate committed surfaces do not check: CI's --check jobs and the H2 parity run them on the lock. Tests drive _rules_engine and the release-cut battery with a later release, and cover the match, a post-release, the absent engine and the missing metadata. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…count (#1095) Re-review of #1308: the regional_home_values and uc_legacy_closure_take_up nodes did not declare household_source_id, and the graph executor hands a stage only its declared cells. In a graph build both receipts fell back without a word, the Move to UC one to household ids and the home-value one to row counts, so support copies counted once each. Both nodes now declare the column, and both stages refuse a frame without it, as property_purchases does. New tests run each production node through the executor's input projection and UKStageKernel on twelve support copies of three source households: the Move to UC combination reads "<10" for the flag and the support, and the home-value cap cell reads "<10" (the test lowers the cap, which on the packaged knots no rank below 1 reaches). Before this change the same tests read True and "10-29". Stage-level tests now also fail if the stage stops passing Great Britain's units (a UK-wide p crosses the rate) or the source ids. The H2 graph fixture moves on the two nodes' inputs only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… as 0 (#1095) Re-review of #1308: the purchases receipt sized the uncapped side as the fit's sources less the capped side's, which is the number of sources with no capped row. Where uncapped rows copy capped sources, a synthetic probe showed the share shipping on a side of three sources, and the fitted weight times one less the share giving that side's weight back. Each side is now counted on its own distinct sources, and the share is also withheld when one to nine sources carry the fit, since at the stock cap every purchaser buys at its stock and a share of 1.0 beside "<10" would still describe it. Tests put twelve capped rows on two sources, three uncapped rows on copies of capped sources, and a stock-cap fit on five sources; the old logic and a rows-not-sources count each fail them. The three stages shared a copy of the same support band, which read "<10" for an empty cell beside a zero weight. receipt_counts.support_band now serves all three and reads "0" there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iod (#1095) Re-review of #1308: both stages declare mapped_build_period 2024 for their vendored facts, HMRC's 2024-25 Stamp Tax tables and DWP's cumulative Move to UC rates, but neither checked it, so a 2030 frame ran on them. Each now refuses a frame at any other period, as property_components does, and the declared period is the constant the check reads. A test per stage covers a 2030 frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Re-review of #1308: the H2 fixture never reached the stock-cap fit, the branch the national spine's non-residential purchases take. Its synthetic non-residential target now asks a ninth more than the purchasers' stock, so the stage buys at the stock in both executors; the synthetic frame holds no other-residential stock, so that kind is still not imputed there. A stage test now fits one kind at the stock beside one it scales, in one run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…residents' SDLT (#1095) Re-review of #1308: main gave the region rows' local-only reason as the national spine's main_residence_value being the WAS donor's, not a Land Registry placement (microcosm#1301). N1 makes that untrue: the national spine now reads the value off the regions' Land Registry knots. The rows stay local-only for now, and the reason is restated in the three notes, #1301's fragment, the calibration-lines docstring, the generator's signed rationale with the membership file it writes, and its pin: binding them on the national line would move the national registry and solve while the knots' tails still place more homes in the top bands than the derived stock, which is left to microcosm#1112's follow-up. obr.sdlt's note said non-resident individuals' surcharge stays inside the individuals' share. Their whole SDLT does, and no FRS household is non-resident, so that is now the first of three declared gaps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…des (#1095) Third look at #1308 (synthetic probes): two receipt fields together could give back a small side, as the stock-cap share could before 0d4a58c. In the purchases receipt the eligible owner weight less the fitted transactions, or that weight times one less the event rate, is the eligible owners who do not buy; both fields are now withheld when the buyers or the others rest on one to nine sources. In the Move to UC receipt the share claiming times Great Britain's drawn weight gives back either side; the share is now withheld when either side rests on one to nine sources. The Move to UC flag said on which side of DWP's rate p lies once ten sources carried p in total, so one unit claiming anyway decided it. It is now given only where neither side of p rests on one to nine sources, and a combination with no Great Britain unit, which has no p, reads None instead of "<10". support_band now reads an empty cell as the integer 0, as rounded_count and extreme_count do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Third look at #1308: the purchases price function and the Move to UC probability function still counted rows when no source ids were passed, and the second also fell back to a UK-wide p without Great Britain's units; a present but null household_source_id passed all three stages' refusals, reading 0 at the home-value cap or one source elsewhere. receipt_counts now carries the lineage: source_household_ids refuses a table without the column or with a null in it, and distinct_count counts a cell's sources. The three stages read their lineage through the first and count through the second, and both functions require their source ids, the Move to UC one Great Britain's units as well. The purchases stage's source-id wiring and its node's declaration had no engine-free test. A new test runs twelve support copies of three households through the stage and through the production node, and reads "<10" for the purchasers; passing no source ids or dropping the node's declaration now fails it. Each stage refuses a null lineage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Third look at #1308: the two stages' period checks were tested at 2030 only, so a check that refused only later years survived, and a 2023 frame would have run; a "2024-25" label failed with a bare int() error instead of the stage's message. Both stages now compare the frame's period label with "2024" and name the label they refuse, and their tests cover 2023, 2030 and "2024-25". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1095) Third look at #1308: the engine guard read the distribution's metadata, not the module that imports. A policyengine_uk copy earlier on sys.path passed it, as did a pinned dist-info beside other files, and a module without a spec raised find_spec's ValueError instead of the guard's error. The guard now checks, after the version, that policyengine_uk imports from the distribution's own __init__.py and that the file matches the distribution's RECORD; a module without a spec is refused by name. An editable install of the pinned version is refused too, as it is not the locked release. The guard also runs first in prepare_full_build and prepare_national_build, before any input is read, and in the certifier tool before it hashes either H5. A bound spine whose sidecar records another policyengine-uk version is refused; one that records none, or the synthetic "unavailable", is left to the preflight's stage roster. Tests cover the shadowing copy (patched and on sys.path), the stale record, the module without a spec, the +local build, the HMRC helpers' factory, both preparations and the bound spine; the engine tier checks that the locked install passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1095) Third look at #1308: once its non-residential target capped, the H2 fixture ran the stock-cap fit only, and not the scaled fit additional dwellings take on the national spine, because the synthetic frame held no other residential stock. The synthetic WAS donors put net rent on every other house as well as on buy-to-let, and with that no recipient drew any other house. Rent now goes with buy-to-let only, so most fixture households hold other residential stock: additional dwellings now fit their value with scaled band draws while non-residential purchases buy at the stock, in both executors. The WAS donor table, the asset-type facts sized to the frame and the purchase targets regenerate. tools/graph_uk_spine_fixture.py --check regenerates the fixture into a temporary directory and fails if any file differs from, or is missing in, the committed one; the fixture does not depend on where it is written, so the comparison is byte for byte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it (#1095) Third look at #1308: the purchases fragment still gave the stock-cap share's old withholding rule. It now says what the receipt does: the share appears only where the fit and both its sides rest on ten or more source households, the eligible weight and the event rate only where the buyers and the eligible owners who do not buy both do, an empty cell reads 0, and the stage refuses a frame without lineage or at another period. obr.sdlt's note now says that the window's 2025-26 months take 2024-25's payer split. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ca13808 to
8e92411
Compare
|
Reply to the third look. Thanks for it. It is addressed at
Low
Nits
Cross-PR
Tests
🤖 Generated with Claude Code |
… the engine (#1095) CI's engine-free jobs failed one test on 8e92411: test_a_gate_only_sign_off_rekeys_the_calibrated_gates_and_nothing_before. Its checkpoint_argv fixture stands in the engine (spine_build's _rules_engine and its provenance), and another fixture reads every absent distribution's version as "0+stand-in" so that kernels can hash their dependencies. The guard b76c9d0 now runs first in prepare_full_build, outside the stubbed _rules_engine, so in CI, where policyengine-uk is absent, it read "0+stand-in" and refused. The fixture now stands in the freeze check with the engine. Locally, with policyengine-uk's import and metadata both hidden as in CI, the test failed before this change and the file's six tests pass after it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On main after #1307 and #1306: 27,267 rows, 23,004 of them compiled. The 18 Scottish BRMA rows are ledger-only by construction, and no signed value moves. The 40 cells of the support floor's two authorities, E06000053 and E09000001, carry the floor's own signed rationale; nothing else changes. The release-blocking preflight gate uk_ledger_compile_parity_local_incumbent_2025 reads this receipt, so whichever of #1305 and #1308 lands second regenerates it on the combined tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r rule (#1262) On the rebase onto #1308, as the fourth review of #1305 lists: - engine_rent_diagnostics.py calls #1308's assert_installed_engine_is_pinned() after its work-tree guard and before it reads an H5, and reports the version that check returns. - docs/uk-full-build-graph.md counts the union's 45 spine stages, in both places it gave a count. - receipt_counts holds one carrier rule. distinct_carriers drops a missing key (None, NaN, NA or blank) and reads a key one way however it is spelled (1, 1.0 and "1" are one household), since an over-count is the unsafe direction for a floor. #1308's distinct_count takes the cell's weights and delegates to it, and the legacy closure, property purchase and regional home value receipts pass their weights: a source carried only by rows of zero weight no longer counts. A cohort of 24 sources, five of them weighted, now reads "<10", not "10-29". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sixth #1095 ports PR. It locks policyengine-uk 2.134.4, frozen for this PR (2.134.5 waits, see E1). It ports the stamp-duty purchases and the households' SDLT target (uk-data#573, uk-data#564). It gives the national line the regions' Land Registry home values (#1112) and draws the Move to Universal Credit claim flag at DWP's rates (uk-data#534). The rulings applied are María's of 16 and 17 October, and each commit names the one it applies.
Status: rebased on main after #1309, #1310, #1316 and #1318 merged. The commits from
b23f3415btof9d775f95address the first review of this PR, those fromfc88454cato752aa5672the re-review, and those from884983f7aon the third look;cd1342e35was amended for the re-review. N2, the high-value home OBR-leg fix, is no longer here: #1306 carries it. R0, the savings-interest renewal, is no longer here either: #1310 shipped the identical renewal to main. A1, the head before the re-review and the head at the re-review all pass the 7 calibrated-seam gates on 2.134.4. The receipts follow in an update of this body.Summary
Engine (E1, and the freeze)
additional_residential_property_purchasedandnon_residential_property_purchasedfrom formulas into inputs that default to nil. Until the data supplies them (S1 below), a purchasing household pays SDLT on its main home only.APPROVED_UV_LOCK_SHA256re-derive.unused_pension_annual_allowanceinput at nil. On the national spine that moves Universal Credit awards and income tax for households with pension contributions, and the solve fails the weight-ratio gate (see Measurement). 2.134.5 waits for a follow-up that supplies the unused allowance and checks the relief inputs.cd1342e35,b76c9d0cf). A policyengine-uk other than the one the engine pins were taken from is refused:prepare_full_buildandprepare_national_build, before any input is read, and again inspine_build._rules_engine, which gives the spine build its engine;policyengine_ukmust also import from that distribution's own files, and its__init__.pymust match the distribution's RECORD. So a copy earlier onsys.path, an editable install and a stale record beside other files are refused, as is an importable policyengine-uk without distribution metadata.--checkjobs and the H2 parity run them on the lock.Stamp duty (S1, S1b, S2)
property_purchases, imputes households' annual purchases of additional dwellings and of non-residential property (uk-data#573 at 07f6b460f, a draft; uk-data#556).obr.sdlt, and uk-data#573's 30% higher-rates SDLT test is not carried over.b23f3415b). The share at the stock cap is also withheld where the fit or either side of it rests on fewer than ten (08878cf4a). The eligible owner weight and the event rate are withheld where the buyers or the eligible owners who do not buy rest on fewer than ten, and an empty cell reads 0 (884983f7a).household_source_idor with a null in it (6d5507a0d). It also refuses a frame at any period label other than 2024, the period HMRC's 2024-25 tables are mapped to (a89288852,b1968c951).value_fit,value_shortfall_share) and refuses below 80% of the value. The selection, its stock exponent and HMRC's transactions are unchanged.obr.sdltbinds households' SDLT (uk-data#564).stamp_duty_land_taxand addscorporate_sdltbeside it.calendar_year_window_scaled_by_ratio, carries the rule: the window of the reference's own series times the quotient of two operands. Each operand pins its own period and names its members in one list, and each member matches exactly one fact (d8930322c).obr.sdltalone, from £15.03bn to £10.16bn.Home values (N1)
regional_home_values, runs after the high-value home support copies. It gives every England-and-Wales owner household with a value the one its region's uprated Land Registry distribution holds at the household's weighted mid-rank. It reads the region level ofuk/high_value_home_value_knots.jsonand movesproperty_wealthby the change. Scotland, Northern Ireland, renters and owners without a value keep theirs, and the council-tax band is untouched.main_residence_value_rank_basis, and the dense value node receives it through its slice and ranks on it. The node refuses a declared column that is missing, and arun_graphtest pins this. The release boundary drops the column on both lines.obr.sdlt, through the main-home purchase value, andons.land.land_valueandons.land.household_land_value, throughproperty_wealth.752aa5672). Main gave the reason that the national spine's value is the WAS donor's; N1 makes that untrue. The reason is now this: binding the rows on the national line would move the national registry and solve, while the knots' tails still place more homes in the top bands than the derived stock. That is left to the UK national release: main_residence_value keeps the WAS-imputed tail, so the surcharge's top bands stay empty #1112 follow-up.household_source_id, and the stage refuses a frame without it (fc88454ca).Move to Universal Credit (M1)
would_claim_uc_at_legacy_closureis drawn at DWP's Move to UC claim rates (uk-data#534 at c67077a66, a draft; uk-data#492).would_claim_ucor this flag is true. The flag's default put every such family on Universal Credit.uc_legacy_closure_take_up, runs afteruc_capital_coherence. It keeps the default for a unit reporting Universal Credit, a unit whosewould_claim_ucis true and a unit reporting no legacy benefit.884983f7a). Great Britain's share claiming is withheld where either side of it rests on fewer than ten (884983f7a).household_source_id, and the stage refuses a frame without it or with a null in it (fc88454ca,6d5507a0d). It also refuses a frame at any period label other than 2024 (a89288852,b1968c951).Deviations and open points
Measurement
Each arm builds its spine locally on feed abf0921 and runs the national role's calibration on it.
obr.sdltthere is the whole OBR line.obr.sdltwithin 0.2%. In the purchases stage, additional dwellings fit HMRC's value exactly, and non-residential purchases reach about 81% of HMRC's value at the stock cap, with transactions within 1%.obr.sdltto within 0.1%.obr.sdlt: from £9.70bn to £13.51bn at design weights, against a £10.16bn target; within 0.1% after the solve.ons.land.household_land_value: from £4.21tn to £5.09tn at design weights, against a £4.49tn target; within 0.02% after the solve on both arms.ons.land.land_value: misses by about 4.7% after the solve on both arms, so N1 does not cause the gap.ons.land.corporate_land_valuedoes not move.ca138083e, on main with UK local release: publish a K=15, 150,000-household dense dataset in at most two builds (#1301) #1307 and UK local surface before the Budget build: high-value homes OBR leg, per-vehicle fuel flag, Northern Ireland energy share and housekeeping (#1278) #1306, 2.134.4): loss 0.26816 to 0.00565; 98.4% of 1,215 rows within 10%; all 7 calibrated-seam gates pass. Main now holdsobr.ni_employerout as a diagnostic (pe-uk#2459), hence one row fewer.property_purchases, so the purchases fit is as before: additional dwellings fit HMRC's value exactly, and non-residential purchases reach about 81% at the stock cap.obr.sdltis within 0.4% and household land value within 0.1%. Total land value still misses by about 4.7%.Not in this PR
property_purchases,regional_home_valuesanduc_legacy_closure_take_upare deliberately ungated for now, andobr.sdltstays held byuk_target_fit.Disclosure
Checked against the UK Data Service rules before pushing: the commit messages, every added line, the fragments and this body. No record-level value, extreme, effective sample size, weight ratio or support count appears. The figures are published statistics or national weighted aggregates. The three stages' receipts band every support cell on its distinct source households, in a graph build as well as in a direct call (
fc88454ca), and withhold any pair of figures that would give back a side resting on fewer than ten (08878cf4a,884983f7a). The home-value receipt'srowsandrows_rewritten_by_regionare rounded row counts, support copies included, at region level.Tests
8e92411af: 3 of the 10 checks had passed, and none had failed.8e92411afon 2.134.4: CI's engine-uk job, 218 tests, all pass. CI's engine-free job in full, with policyengine-uk hidden as in CI, 19,827 tests with 158 skips, all pass except 3 that this machine cannot run as CI does: two Orrery parser tests need an npm package that is not installed here, and one refuses this Python 3.14.6.tools/ci_test_plan.py verifypasses, and so do the--checktools on 2.134.4: the uprating pins, the concept coverage, the coverage manifest and the new H2 fixture check.ca138083e, 180 passed with 2 skips, and the committed national and local surfaces regenerated byte-identically.🤖 Generated with Claude Code