Repository navigation
Conversation
…im rates Every working-age legacy means-tested benefit has closed or is closing, but would_claim_uc is drawn without regard to legacy receipt: a legacy reporter that drew it false lost its legacy award and got no Universal Credit. Draw would_claim_uc_at_legacy_closure, which policyengine-uk reads once a legacy benefit the unit reports has closed, at DWP's Move to Universal Credit claim rates by combination of legacy benefits (Stat-Xplore table MtUC Households 3, notices to end of March 2026). Units reporting UC, and units with no legacy benefit, are true (the model default). The draw has its own seeded generator, so the seed=100 sequence behind every other flag is unchanged; SPI-synthetic rows are redrawn after stage 2 rewrites their receipt. would_claim_uc itself is untouched, so 2024-25 legacy receipt is unchanged. Fixes #492. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…keup Keeps main's take-up machinery (assign_reported_takeup, the pension-age UC exclusion, SPI-donor rules) and its hypothesis dependency and lock. create_frs and utils/takeup.py now share one reported_benunit_mask. Tests: the closure draw leaves the seed=100 stream alone (pinned on the synthetic fixture), and impute_income redraws the SPI copy's flag after stage 2 rewrites its receipt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
policyengine-uk claims at the closure when would_claim_uc OR the flag is true, so drawing the flag at DWP's rate r gave a share of p + (1 - p) r, where p is the cohort's would_claim_uc share (review r1 of #534: HB only 0.866 against 0.753). Units already claiming are now True, and the rest are drawn at (r - p) / (1 - p), clipped to [0, 1], with p computed per combination, unweighted, before calibration. The SPI copy uses its redrawn would_claim_uc. Tests: the closure-claim share (not the flag share) is within sampling error of r with would_claim_uc at 0 and 0.3; an exact characterisation of the conditional draw; the built-FRS cohort test checks the flag share among units not claiming anyway against the conditional rate (it needs a fresh build). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…edit Review r2 (P2): the conditional rate counted Pension Credit route families in p, although policyengine-uk never moves them to Universal Credit, so the units that do move claimed above DWP's rate (78.3% against 67.4% on a synthetic Child Tax Credit cohort). derive_moves_to_uc_at_legacy_closure follows policyengine-uk's closure rule with every closure in force, reading meets_pension_credit_age_conditions, is_mixed_age_couple and is_SP_age from the build's simulation; only those units are drawn and counted. Tests: the moved units' share is DWP's rate with Pension Credit route units alongside; a Universal Credit reporter does not set the rate (the r2 mutant survived the property test); the closure rule case by case. Both mutants fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Calibration fit against the base build: FAILOne of the builds produced no fit summary, so the fit could not be compared. See the workflow run. Income distribution and single ages: FAILThe report is missing for one build, so the distributions could not be compared. Aggregate impact of the data change: FAILBase dataset Checks: 0 broken by this change, 0 failing on both sides, 0 fixed. Moves flagged for review: 0 (thresholds in Failures
Notes
Moves flagged for reviewNone. Receipts by tax head
Spending by benefit and service
Caseloads
Government balance, each component counted once
Model totals
Components the model counts twice
Household income totals
Population
Poverty rates
People in poverty
Inequality
Mean household net income in each side's own deciles
Standard reform costings (change in gov_balance)
Standard reforms: change in each reform's target
Household net income by decileThe datasets hold different households, so households are not matched; compare the mean income in each side's own deciles. Counted-once checkCounted-once check not run: unknown reason. Provenance
Base dataset: Receipts by tax head: head levels
Spending by benefit and service: head levels
Caseloads: head levels
Government balance, each component counted once: head levels
Model totals: head levels
Components the model counts twice: head levels
Household income totals: head levels
Population: head levels
Poverty rates: head levels
People in poverty: head levels
Inequality: head levels
Mean household net income in each side's own deciles: head levels
Standard reform costings (change in gov_balance): head levels
Standard reforms: change in each reform's target: head levels
|
…Credit Review r3 (P2): the migration population was taken in the survey year only, and a unit outside it kept the default True. The model recomputes the Pension Credit route each year from ages held at survey values, so a mixed-age couple saved in 2024 loses the saving in 2025 and moves then, claiming with certainty. first_year_moved_to_uc evaluates the age conditions in each model year from 2025-26 to 2035-36; the draw is taken within each combination and first year, so every year's movers claim at DWP's rate. Reports are now the claimant's and partner's, as in policyengine-uk main, and Housing Benefit units move unless they meet the Pension Credit age conditions, as in PE-UK #1910 at 2d0e49417. Tests: each first-year group claims at the rate (pooling the groups fails it); a unit leaving the route in 2025 is drawn (r3's regression); the closure-rule table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #492
Summary
Every working-age legacy means-tested benefit has closed or is closing, but
would_claim_ucis drawn without regard to legacy receipt. A legacy reporter whose draw came out false lost its legacy award at the closure and got no Universal Credit.This PR draws
would_claim_uc_at_legacy_closure, which PolicyEngine/policyengine-uk#2119 (released in policyengine-uk 2.128.0) reads once DWP moves a family off its legacy benefits (legacy_benefits_closed). The draw uses DWP's Move to Universal Credit claim rates for the family's combination of legacy benefits.would_claim_ucis untouched, so 2024-25 legacy receipt is unchanged.Option (b), not (a)
The issue offered two routes. Only (b) keeps 2024-25 and 2025-26 legacy receipt intact:
extend_single_year_dataset).~would_claim_uc.Option (a), setting
would_claim_uctrue for legacy reporters, would therefore:Option (b) needs a model input and switch, which #2119 adds. This PR is the data half.
Change
parameters/take_up/uc_managed_migration.yaml: households sent a migration notice, by legacy benefit combination, from Stat-Xplore ("Households invited to Move to Universal Credit", table MtUC Households 3; GB, notices to end of March 2026, retrieved 2026-10-03). The table has all 19 combinations DWP reports and the all-households row. Rate = claimed / (claimed + did not claim); the 538 households still in progress are left out. The headline matches DWP's 12 May 2026 release: 1.82m notices, 87% claimed.utils/takeup.py:legacy_benefit_combinationandassign_uc_claim_at_legacy_closure. policyengine-uk claims at the closure whenwould_claim_ucor the flag is true. So the flag is drawn to make each combination's closure-claim share, among the units the model moves to Universal Credit, equal DWP's rate r in expectation:first_year_moved_to_uc, below); a family DWP sends to Pension Credit never reads the flag;would_claim_uc) are true;datasets/frs.py:derive_moves_to_uc_at_legacy_closurefollows policyengine-uk'slegacy_benefits_closedwith every closure in force, on the claimant's and partner's reports. Tax credit units move unless they are on Pension Credit or meet its age conditions without Working Tax Credit (SI 2019/167 art. 3A(1); SI 2014/1230 reg 60A). Income Support, income-based JSA and income-related ESA units move unless on Pension Credit or wholly over the qualifying age (reg 8(2B)). Housing Benefit units move unless they meet the Pension Credit age conditions (SI 2025/1148 art. 7(4)(a); reg 6A(4)-(5)), as in Abolish working-age Housing Benefit from 1 July 2026 (NI 1 October 2026) policyengine-uk#1910.first_year_moved_to_ucreadsmeets_pension_credit_age_conditionsandis_mixed_age_couplefrom the simulationcreate_frsalready builds, in each model year from 2025-26 to 2035-36. policyengine-uk holds ages at survey values and moves inferred birth dates with the year, so a unit can leave the Pension Credit route later, for example a mixed-age couple whose older member is then born too late for the SI 2019/37 saving. Limits: the model does not yet end income-related ESA (#1914), so ESA-only units are drawn but read nothing; HB units read the flag only once #1910 lands; the specified or temporary accommodation saving has no input in the data.create_frs: draws the flag with its own generator (seed 492), right afterwould_claim_uc. Theseed=100sequence behind every other take-up flag is unchanged.require_variablefails the build if policyengine-uk lacks the input.impute_income: the SPI-synthetic copy's flag is redrawn after stage 2, from its own receipt and its redrawnwould_claim_uc. Since main's SPI-donor rules zero those rows' legacy and Universal Credit reports, they all come out true.require_variableuntil the lock moves to 2.128.0 or later. That bump belongs to the batch relock, since it brings every policyengine-uk change since 2.122.2 into the build.(Every combination is in the YAML.)
Income-related ESA ordering (issue's note)
policyengine-uk has not modelled the income-related ESA end date. That is SI 2025/1148 art 3A, inserted by SI 2026/409, ending remaining awards on 1 July 2026 (NI: SR 2025/176 art 3A from 1 October 2026). PolicyEngine/policyengine-uk#1914 is open with no PR.
How #492 interacts:
legacy_benefits_closed.would_claim_ucis true) is untouched. It is #1914's job.Impact
To be re-measured. The figures below come from builds of pre-batch
main(b45c373), before the conditional draw (c67077a) and before the draw was limited to moved units by first year (49dd6f8, fcce07a). This PR will be rebuilt against currentmainwhen the UK hub's rebuild queue gives it a slot (row 6), and the table replaced.They come from seeded production builds (seed 0) of
mainand this branch, both on policyengine-uk #2119, which the branch'srequire_variableguard needs. Each cell shows the branch minusmain, with the placebo in brackets:mainrebuilt with calibration seed 1, which sizes reweighting noise.would_claim_uc, closure-claim Universal Credit falls from £0.84bn (48.7k units) to £0.78bn (41.8k) in 2025.main's build apart from the new column, sowould_claim_ucand every other take-up flag are unchanged. The base FRS file differs only by the new column.impute_wealthreads model outputs, includinghousehold_net_income, at policyengine-uk's default period, 2025, where the flag already applies (wealth.py:336,calculate_dataframewith no period). Regional property rescaling and energy calibration then spread that to most household rows; totals move by about 1e-5 relative.main's. Imputing from 2025 outputs for a 2024 dataset is an older issue.main's build, which was calibrated under #2119, #2119 alone gives Universal Credit +£0.83bn in 2025. On the released 1.57.4 it gives +£0.07bn. In both, most migrated families that do not drawwould_claim_ucget no Universal Credit. Onmain's build 54k of 87k are eligible; on 1.57.4, 9k of 63k, most of the rest having capital over £16,000. These are different builds; the cause of the difference was not isolated.Run files are in the session's review folder:
impact/aggregates.py,compare_columns.py,diag_closed.py, with outputss1_*,s2_*ands1_columns*.txt.Invariants
Tested in
policyengine_uk_data/tests/test_uc_managed_migration_takeup.py(Hypothesis plus built-dataset checks):would_claim_ucor the flag) is within 4 standard errors of r. This is tested on 20,000 synthetic units per combination, with 0% and 30% claiming anyway. On the built FRS, units the model moves and not claiming anyway, in unweighted groups of 30 or more by combination and first year, are tested against that group's conditional probability, (r − p) / (1 − p) clipped.By first year. With half of the 2025 movers claiming anyway and none of the 2027 movers, both the 2025 movers and all movers claim within 4 standard errors of r. Pooling the years fails this. A unit on the Pension Credit route in the survey year and off it from 2025 gets first year 2025 and is drawn (r3's regression).
derive_moves_to_uc_at_legacy_closurematches 14 hand-checked cases: tax credits with and without the Pension Credit route and reg 60A, Income Support for wholly pension-age and mixed-age couples, Pension Credit reporters, income-related ESA, and Housing Benefit for working-age, saved and unsaved mixed-age, and wholly pension-age families.The seeded builds also confirm that
would_claim_ucand every other take-up flag are identical tomainon FRS-original rows. A pinned check on the synthetic fixture fails if anything consumes the shared seed=100 generator before the later draws. A unit test checks the SPI-copy redraw. Both were mutation-checked.Limitations
Release
This is in the d833 uk-data batch. It is reviewed and approved on gates, not merged; it lands with the batch on Max's go, after the batch relocks policyengine-uk to 2.128.0 or later. PolicyEngine/policyengine-uk#1910 (working-age Housing Benefit abolition) was held for this PR (d837); since 22 October it prompts the same closure claim, with the flag's default (true) until this PR supplies it.
🤖 Generated with Claude Code