Skip to content

Count the Pension Age Winter Heating Payment in household and HBAI income - #2047

Draft
MaxGhenis wants to merge 3 commits into
winter-heating-per-person-receiptfrom
pawhp-household-income
Draft

MaxGhenis wants to merge 3 commits into
winter-heating-per-person-receiptfrom
pawhp-household-income

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #2038 (winter-heating-per-person-receipt), which pays the Winter Fuel Payment in Scotland up to the 2023 qualifying week and the Pension Age Winter Heating Payment (PAWHP) per person from 2024. Merge after #2038.

Summary

#1000 (merged 5 December 2024) stopped paying winter_fuel_allowance in Scotland and added pawhp to gov_spending. It did not add it to household_benefits, hbai_household_net_income or hbai_benefits, which all count winter_fuel_allowance. Since then, Scottish pensioners' winter heating payment has been counted as government spending but never reached household net income or HBAI income. PolicyEngine/impact-iran-war-living-standards works around it (BENEFITS_OUTSIDE_HOUSEHOLD_BENEFITS = ["pawhp"]).

This PR adds pawhp next to winter_fuel_allowance in all three lists.

disable_simulated_benefits

Checking how the reform treats PAWHP showed that the reform could not run at all:

  • Simulation.__init__ never set tax_benefit_system.simulation. Core applies a reform by calling its apply() with the tax-benefit system as self, so the reform's self.simulation raised AttributeError. Core's own Simulation.__init__ sets the attribute, but PolicyEngine UK's __init__ replaces core's. It is now set before the structural reforms are applied. (adjust_budgets reads self.simulation the same way.)
  • Five list entries had no reported amount: attendance_allowance, dla_m, dla_sc, pip_dl and pip_m (ValueError: Variable attendance_allowance_reported does not exist). policyengine-uk-data converts their reported amounts into award categories (aa_category, …; add_disability_benefit_categories_from_reported_amounts), and the model pays the category's rate, so they already follow the reported award. They are dropped from the list. A code-health test now requires every listed benefit to have a _reported variable, and every simulated benefit with one to be listed or excluded with a reason. Maternity Allowance is excluded because it already adds maternity_allowance_reported.

Treatment of PAWHP. There is no pawhp_reported. The survey records one winter heating payment, FRS benefit code 62, which policyengine-uk-data loads into winter_fuel_allowance_reported for every respondent, Scotland included. Had winter_fuel_allowance stayed set from it while pawhp stayed simulated, Scottish households would have counted the payment twice now that both are household income. The reform now sets both from the one report, year by year. A Scottish household's report becomes its pawhp in the years PAWHP is paid (gov.social_security_scotland.pawhp.active, from the 2024 qualifying week) and its winter_fuel_allowance before then. Every other household's report stays winter_fuel_allowance. Each report is counted once, under the scheme that pays it, and the programme totals match the simulated definitions.

Note: the reform fires only when its parameter is set before the data load (Scenario(applied_before_data_load=True, ...)). Set through an ordinary reform= dict, it is silently ignored. That applies to every parameter-gated structural reform and is left to a follow-up.

Overlap with #2075. #2075 owns the generic repair of this reform: the tax_benefit_system.simulation line, the AA/DLA/PIP removal, and claimant-or-partner scoping of ESA, JSA and Income Support. This PR carries the same two generic fixes so it can run and test its winter heating treatment on its own stack. Whichever PR lands second keeps #2075's structure, takes winter_fuel_allowance out of its BENEFITS, and keeps the winter heating split here, so the one reported payment is counted once.

Cost-of-Living Payments

Unchanged. The pensioner payment (£300 in 2022-23 and 2023-24, £0 from 2024) is keyed on winter_fuel_allowance > 0, which this PR does not change. #2038 pays the Winter Fuel Payment in Scotland up to the 2023 week. Under the reported-benefits reform, Scottish reports stay winter_fuel_allowance in 2022 and 2023, when PAWHP is inactive. Evidence: a 2023 YAML case, a reform test (the pensioner payment moves with the report only in 2023), and the Enhanced FRS run (largest change in cost_of_living_support_payment in any household, 2024-30: £0).

Invariants

  1. Income identity. For every household, switching PAWHP off lowers household_benefits, household_gross_income and hbai_benefits by exactly its pawhp, and household_net_income and hbai_household_net_income by its pawhp less the change in its income tax. That change is zero today; the term keeps the property true once the winter fuel payment charge (ITEPA 2003 s.681I) is modelled in income tax. Switching PAWHP off changes neither winter_fuel_allowance nor cost_of_living_support_payment. Tested as a Hypothesis differential property over households in every country, 2023-27, at float32 precision. On the Enhanced FRS, the base-to-branch change equals pawhp for every household in every year 2024-30, within 2 float32 ulps of the stored values. The only records more than 1p off have household income of at least £65k, where a float32 ulp is at least £0.0078.
  2. No spending change. gov_spending, gov_balance, pawhp, winter_fuel_allowance and cost_of_living_support_payment are unchanged in every Enhanced FRS household, 2024-30 (largest absolute change: 0).
  3. List consistency (code health). Every gov_spending item is in household_benefits except other_public_spending_budget_change, a contrib budget lever. Both winter heating payments appear exactly once in household_benefits, the HBAI net income list, hbai_benefits and gov_spending.
  4. Reported mode. In each of the reform's years, winter_fuel_allowance + pawhp equals the household's reported winter heating payment (conservation). The report is pawhp exactly where the household is in Scotland and PAWHP is paid (placement). household_benefits and hbai_household_net_income exceed those of the same dataset with no report by the report plus the pensioner Cost-of-Living Payment it brings (differential). Tested with Hypothesis over datasets in every region starting 2022-25, under Simulation and Microsimulation.

Tests

  • tests/policy/baseline/household/income/winter_heating_payments_in_income.yaml: 5 cases.
    • A Scottish single pensioner in 2025 and a couple in 2026 (shared amounts), and a Pension Credit recipient in 2024. On the base, household_benefits, household_net_income, hbai_benefits and hbai_household_net_income miss the PAWHP, so all three cases fail there.
    • Two controls: an English pensioner in 2025 (Winter Fuel Payment), and a Scottish pensioner in 2023 (Winter Fuel Payment and the £300 pensioner Cost-of-Living Payment, no PAWHP).
  • tests/code_health/test_household_income_lists.py: invariant 3.
  • tests/test_pawhp_household_income_properties.py: invariant 1, plus a non-vacuity case.
  • tests/test_disable_simulated_benefits_winter_heating.py: the reform runs under Simulation and Microsimulation, its list matches the reported variables, and invariant 4.
  • Mutation check: all 9 mutants killed on 27f5fdb0f (no Hypothesis shrinking): pawhp dropped from each of the three lists, listed twice, the reform putting every report in winter_fuel_allowance, double counting Scotland, splitting a year late, Simulation not setting tax_benefit_system.simulation, and attendance_allowance back in the reform's list.
  • Full suites: the Code changes workflow (lint, smoke imports on Python 3.11-3.14, make test), dispatched on this branch because PR checks run only against main: run 37005793386 on 27f5fdb0f: all jobs passed (Test, Lint, smoke imports on Python 3.11-3.14).

Impact (Enhanced FRS)

One real microsimulation per package state on a private copy of enhanced_frs_2024_25.h5 (sha256 e433e532b17b…). Base: #2038 head 071d5170b. Branch: ff5f5a98f; later commits change only tests and the name of the reform's list constant. Aggregates only. The change moves no spending, so the whole effect is income and poverty.

Year PAWHP (£m) Change in household net income (£m) Change in HBAI net income, BHC and AHC (£m) Households gaining (thousands, all in Scotland)
2024 33.8 +33.8 +33.8 131
2025 201.0 +201.0 +201.0 842
2026 209.5 +209.5 +209.5 845
2027 214.5 +214.5 +214.5 848
2028 219.6 +219.6 +219.6 852
2029 225.0 +225.0 +225.0 855
2030 230.5 +230.5 +230.5 859

Absolute poverty (HBAI line, 60% of the FYE 2025 median held constant in real terms; percentage points, people in thousands):

Year All, BHC All, AHC Pensioners, BHC Pensioners, AHC Scottish pensioners, BHC Scottish pensioners, AHC
2024 +0.000 (+0.0k) -0.001 (-0.5k) +0.000 (+0.0k) -0.004 (-0.5k) +0.000 (+0.0k) -0.052 (-0.5k)
2025 -0.003 (-2.2k) -0.018 (-12.5k) -0.018 (-2.2k) -0.104 (-12.5k) -0.215 (-2.2k) -1.210 (-12.5k)
2026 -0.001 (-0.8k) -0.004 (-3.0k) -0.006 (-0.8k) -0.024 (-3.0k) -0.073 (-0.8k) -0.285 (-3.0k)
2027 -0.001 (-0.8k) -0.001 (-0.9k) -0.005 (-0.6k) -0.008 (-0.9k) -0.061 (-0.6k) -0.091 (-0.9k)
2028 -0.002 (-1.6k) -0.001 (-0.9k) -0.012 (-1.4k) -0.008 (-0.9k) -0.136 (-1.4k) -0.091 (-0.9k)
2029 -0.003 (-2.3k) -0.001 (-0.6k) -0.015 (-1.8k) -0.005 (-0.6k) -0.172 (-1.8k) -0.058 (-0.6k)
2030 -0.003 (-2.1k) -0.001 (-0.8k) -0.015 (-1.8k) -0.007 (-0.8k) -0.173 (-1.8k) -0.080 (-0.8k)

Relative poverty (60% of the contemporary median):

Year All, BHC All, AHC Pensioners, BHC Pensioners, AHC Children, AHC Working age, AHC
2024 -0.000 (-0.1k) +0.000 (+0.0k) -0.001 (-0.1k) +0.000 (+0.0k) +0.000 (+0.0k) +0.000 (+0.0k)
2025 -0.031 (-21.3k) -0.016 (-10.8k) -0.175 (-21.2k) -0.087 (-10.6k) +0.000 (+0.0k) -0.001 (-0.3k)
2026 -0.110 (-76.9k) -0.003 (-1.8k) -0.633 (-76.9k) -0.072 (-8.8k) +0.015 (+2.3k) +0.011 (+4.7k)
2027 -0.109 (-76.5k) +0.012 (+8.4k) -0.627 (-76.3k) +0.017 (+2.0k) +0.019 (+2.8k) +0.008 (+3.6k)
2028 -0.015 (-10.9k) +0.089 (+63.1k) -0.087 (-10.7k) +0.010 (+1.3k) +0.193 (+29.6k) +0.075 (+32.2k)
2029 -0.012 (-8.2k) +0.002 (+1.2k) -0.067 (-8.2k) -0.015 (-1.8k) +0.007 (+1.0k) +0.005 (+2.0k)
2030 -0.071 (-50.6k) -0.105 (-74.9k) -0.410 (-50.6k) -0.599 (-73.9k) +0.000 (+0.0k) -0.002 (-1.0k)

Relative poverty moves in large, irregular steps because the distribution is lumpy near the line. A diagnostic on the run counted 58k to 161k Scottish people (by year) with equivalised income just below the base BHC line (within the largest equivalised PAWHP gain, about £9 to £10 a week), across only 30 to 44 distinct income values. A payment of about £4 a week moves whole blocks across: 77k Scottish pensioners in 2026 and 2027. On the AHC basis, the PAWHP gains also raise the median by £0.02 to £1.00 a week (most in 2027 and 2028), and the higher line pulls people elsewhere into relative poverty. In 2028 that is 64k people, 63.8k of them outside Scotland, mostly children and working-age adults. The BHC median does not move. The absolute measures, whose line is fixed, are the cleaner read of the effect: Scottish pensioner AHC poverty falls 1.21 points (12.5k people) in 2025, when PAWHP first reaches everyone of pensionable age.

Axiom

axiom: n/a: aggregation only (PAWHP's place in household and HBAI income lists) plus a contrib reform; PAWHP itself is TheAxiomFoundation/rulespec-uk#420 queued (from #2038)

Follow-ups

  • PolicyEngine/impact-iran-war-living-standards adds pawhp on top of household_benefits unconditionally. Once its policyengine bundle reaches a release with this PR, the PAWHP change would be counted twice there. A follow-up makes the adjustment conditional.
  • gov_spending omits scottish_child_payment, healthy_start_vouchers and two_child_limit_payment, which household_benefits counts. hbai_benefits omits scottish_child_payment and council_tax_benefit. A follow-up will verify these and reconcile them.
  • Parameter-gated structural reforms are silently ignored when their parameter is set after the data load. A follow-up also covers adjust_budgets, which this PR's tax_benefit_system.simulation fix lets run for the first time under the current Simulation.

🤖 Generated with Claude Code

MaxGhenis and others added 3 commits October 2, 2026 07:42
pawhp was in gov_spending but not in household_benefits,
hbai_household_net_income or hbai_benefits, so once the Winter Fuel
Payment stopped covering Scotland (2024 qualifying week), Scottish
pensioners' winter heating payment fell out of household net income and
HBAI income. Add it to the three lists next to winter_fuel_allowance.

disable_simulated_benefits could not run: Simulation never set
tax_benefit_system.simulation (core's __init__ does, this one replaces
it), and the reform named five _reported variables that no longer exist
(AA, DLA and PIP follow the reported award through their categories).
Set the attribute, drop the five, and count the one reported winter
heating payment (FRS code 62, Scotland included) once: as pawhp in
Scotland while PAWHP is paid, as winter_fuel_allowance otherwise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Matches the constant name used by the parallel disable-simulated-benefits
rework, and keeps the two branches from adding the same test file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Net measures fall by pawhp less the change in the household's income tax
(zero today), so the property still holds once the winter fuel payment
charge (ITEPA 2003 s.681I) is modelled in income tax. Compare at float32
precision: a 1p tolerance failed on a household with net income of about
150,000, where one float32 step is 1.6p.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant