Skip to content

Correct Pension Credit and pension-age Housing Benefit projections - #2092

Draft
anth-volk wants to merge 20 commits into
mainfrom
feature/pension-credit-2025-rates
Draft

anth-volk wants to merge 20 commits into
mainfrom
feature/pension-credit-2025-rates

Conversation

@anth-volk

@anth-volk anth-volk commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #2083
Fixes #2072
Fixes #2154
Fixes #2155
Fixes #2195

Changes

Merge main eb0b8087d821 (including #2576 and #2207) into Anthony Volk’s 52c96b111efe (merge head 9b0d406723d1), preserving his commits without rebasing or force-pushing. Four commits on top (head 000f16be9f7f) address the hub review of the merge head.

  • Keep Fix Pension Credit and pension-age Housing Benefit earnings uprating #2576’s dedicated earnings series, published Savings Credit maximum and forecast algebra: higher pension-age HB = guarantee + maximum Savings Credit.
  • Retain this PR’s common projection base derived from the seven enacted guarantee/threshold/protected-allowance paths. An incomplete rates update raises with every path and year; reform dates do not move the enacted base.
  • Generate independent threshold and single/lone-parent/couple allowance indices through standard uprating, retaining explicit reform anchors. Guarantee and phase-in reforms do not re-derive separately set thresholds or HB allowances. Below-guarantee and zero allowance/threshold reforms build; threshold residuals are non-negative.
  • Maximum Savings Credit reforms reach both forecasts, as they did on main. Each index reads the maximum from the reform’s tree as general uprating will project it: Core’s uprating runs on private copies of the maximum and of the index chain it is uprated by (gov.benefit_uprating_cpi, then September CPI). A reform from the base year or any later year, or to that index, therefore moves the projected threshold and higher allowances as it moves the maximum. A threshold or allowance whose own base-year value is reformed keeps projecting its own reformed gap. The base-year maximum fill for a rates update without one no longer overwrites a reform anchored before the base year.
  • Keep main’s existing non-negative earnings/CPI floors and post-2021 cohort link to earnings; remove the duplicate builders and overlapping changelog fragments.
  • Keep Correct historical Housing Benefit pensioner allowances #2207’s sourced 2015–19 history and all subsequent published rates, including 2025 HB £244.40/£366. The old PR’s 2015 placeholders were 2019 rates: those amounts remain at their correct 2019 dates. Add this PR’s dated GB/NI citations and regression cases.
  • Add the Pension Credit page’s Uprating section, link the State Pension page to it, and publish the notebook: myst.yml’s Programmes block now lists docs/book/programs/gov/dwp/pension-credit.ipynb, since notebooks are not pattern-matched. Retain Guarantee Credit boundary regressions.

The two existing builder modules retain main’s formulas and now generate the independent indices before general uprating. tax_benefit_system.py changes only their registration/order and unreformed context, including prior-law projections (archived W1 ownership).

Invariants and evidence

  • Unreformed, each projected higher HB allowance equals the projected guarantee plus the projected Savings Credit maximum in every forecast year: existing Fix Pension Credit and pension-age Housing Benefit earnings uprating #2576 properties plus test_unreformed_higher_allowances_equal_guarantee_plus_maximum_every_year.
  • No projected allowance, threshold or maximum is negative under valid finite non-negative monetary/rate reforms; any combination of guarantee, threshold, phase-in, allowance and maximum Savings Credit reforms builds: test_combined_reforms_build_and_never_project_negative_amounts (Hypothesis, zero/subnormal/large amounts, future anchors, and one to three maximum anchors per relationship from the year before the base).
  • Enacted amounts reproduce exactly in their years: test_forecasts_reproduce_each_enacted_value_exactly and existing published-rate properties. Independently rounded enacted HB amounts are preserved; the exact forecast identity applies after them.
  • The reconciled and frozen main builders agree wherever main’s behavior is retained: test_reconciled_builders_agree_with_main_where_its_behavior_is_retained (Hypothesis, positive and negative macro paths, unreformed), and test_maximum_reforms_reproduce_main_with_a_floored_threshold (Hypothesis, any path of maximum Savings Credit reforms before, at or after the base year, enacted phase-in paths and an optional reform to the maximum’s uprating index; equal to main apart from the threshold floor).
  • A maximum Savings Credit reform reaches both forecasts: test_maximum_reforms_reach_both_dependent_forecasts (the review’s zero-maximum oracle, base-year and 2028 anchors), test_maximum_reform_reaches_both_forecasts_through_every_route (reform dict, Scenario, structural Reform.from_dict class and a reprocessing simulation modifier; each forecast year’s threshold is the guarantee less the reformed maximum / phase-in and each allowance the guarantee plus it, and no Savings Credit is paid once the maximum is zero), and the own-base precedence cases test_a_reformed_own_base_takes_precedence_over_a_maximum_reform and test_a_reformed_own_base_keeps_its_index_beside_a_maximum_reform.
  • Lone-parent independence, below-guarantee/zero reforms, base-year updates and 2035 Guarantee Credit boundary continuity retain focused regressions.

Validation

Locked Python 3.13 environment, policyengine-core 3.33.2 (uv sync --frozen --extra dev). ruff format --check and ruff check pass.

Local runtime tests at 000f16be9f7f (foreground; at most two xdist workers; Hypothesis-heavy files under a 2 GB process-group memory cap, which made four runs single-process): test_pension_age_projection_invariants.py 13 passed, test_pension_credit_uprating.py 34, test_housing_benefit_protected_allowance_uprating.py 13, test_pension_credit_guarantee_reform_projections.py 51, test_savings_credit_threshold_projection.py 14 and test_process_time_reforms.py 71. Policy YAML: Pension Credit 320 cases, Housing Benefit 456, and 21 more in the three gov/dwp/housing_benefit_*.yaml files. The full Python suite was not run locally; CI is the evidence for it.

The new tests fail where they should. Against the 9b0d406723d1 builders, all nine new synthetic test nodes fail (the extended combined property, which checks only that building succeeds without negative amounts, passes there), and 11 of the 12 new public-route tests do; the base-year modifier case already passed there, because that route reads the reformed maximum as its own base. Without the fill guard, the differential fails on its pre-base-year anchor. Without the own-base fallback, all six synthetic precedence cases fail. With the review’s proposed September-CPI-only helper, 0 of 68 drawn differential cases without an index reform fail and 11 of 12 with one do.

Current-law forecasts do not move. Every threshold, maximum, higher HB allowance and forecast index from 2020 to 2040, on the main tree and its baseline copy (504 values), matches 9b0d406723d1: 496 are identical and the rest differ by at most 2.2e-16 relatively (one unit in the last place).

Docs: make documentation (the docs CI command) built 51 pages locally, including programs/gov/dwp/pension-credit.ipynb; the State Pension page’s link resolves as an internal cross-reference to its uprating heading. CI at 9b0d406723d1 built 50 pages, without it.

CI at 000f16be9f7f (Code changes run 40481780315): Lint, documentation, smoke-import (3.11–3.14), release-build, Changelog, Fix evidence and Docs changes pass, as do Python shards 2–5. Test / YAML passed all 5,111 policy YAML cases, then hit its 30-minute job limit while listing the Python tests; Python shard 1 reached 99% with no failures before its 75-minute limit. Test / Result therefore fails. Other PRs on current main hit the same limits (run 40424696088 passed on its third attempt after its YAML job was cancelled at 30 minutes); a re-run is the hub's call. At 9b0d406723d1, CI passed 4,911 policy YAML cases and audited all 10,201 collected Python tests: 10,138 passed, 61 allowlisted skips and 2 allowlisted xfails, including all six 13 October regressions; required Test / Result and Lint passed. No local microsimulation was run.

Aggregate impact at 000f16be9f7f passed: on certified enhanced_frs_2024_25.h5 (policyengine-uk-data 1.58.1), base main 159a5ec99d against this head merged with it, for 2025-26 to 2031-32, no household’s net income changes in any year, and receipts, government balance, poverty and inequality do not change. No check is broken; one VAT sign check fails on both sides. At 9b0d406723d1 the run timed out.

Keep this PR draft for the hub to dispatch its independent re-review; do not merge.

Reproducer: policyengine_uk/tests/test_pension_age_projection_invariants.py::test_combined_reforms_build_and_never_project_negative_amounts, policyengine_uk/tests/test_pension_age_projection_invariants.py::test_maximum_reforms_reach_both_dependent_forecasts

axiom: n/a: this reconciliation changes forecast generation, reform safeguards and regression coverage only; main already contains the enacted Pension Credit and Housing Benefit rates and legal uprating rules.

🤖 Generated with Claude Code

@vahid-ahmadi vahid-ahmadi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Anthony. Reviewed at 54e19d2: the fix looks right to me, with a few small points.

All four corrected 2025-26 values match SI 2025/295 art. 29 (£227.10 / £346.60; £198.27 / £314.34; NI SR 2025/64 gives the same), and the explicit 2026 entries match SI 2026/148. Tests pass locally (PC directory 106/106; PC + integration 207/207 when merged into current main), and six of the new tests fail on the old values.

  1. Overlap with #1925. Max's #1925 sets the same four values and makes the same entitledto_scenarios.yaml edit (4,023.20 / 18,048.66), so whichever merges second will conflict. #1925 currently has conflicts with main and this PR is green, so I'd suggest merging this one first and rebasing #1925 on it. That also keeps the tests here and closes #2083, which #1925 doesn't link.
  2. The savings credit tests don't catch the fix. savings_credit.yaml Case 3 passes on the old values too (MG − threshold is 28.83 under both sets), and Case 4 only moves by 1p. A case that separates them: a single person with £11,000 income gets 413.98 on the new values vs 577.78 on the old. A positive eligibility case just above the new threshold would help too; the current eligibility cases are all negative.
  3. Nit: the other 2025 amounts in art. 29 are still off on main (carer £46.40 vs 46.37, severe disability £82.90 vs 83.25, child £67.42 vs 67.60, child disability £36.54 vs 36.68, child severe disability £114.12 vs 114.54). #1925 covers these, so fine to leave them there.
  4. Nit, follow-up rather than this PR: from 2027 the minimum guarantee is uprated by gov.benefit_uprating_cpi (minimum_guarantee.yaml), but SSAA 1992 s.150A(2) requires an increase not less than earnings growth, so projected Pension Credit is understated in later years.
  5. Nit: the docs edit and its revert (b270272 / 54e19d2) could be squashed.

@anth-volk
anth-volk marked this pull request as ready for review October 5, 2026 14:36
@anth-volk
anth-volk requested a review from vahid-ahmadi October 5, 2026 14:36
@anth-volk anth-volk changed the title Correct 2025 Pension Credit rates Correct Pension Credit rates and Savings Credit projection Oct 5, 2026
@anth-volk
anth-volk marked this pull request as draft October 6, 2026 15:55
@anth-volk
anth-volk force-pushed the feature/pension-credit-2025-rates branch from b1097c2 to ddfd3f0 Compare October 6, 2026 16:47
@anth-volk anth-volk changed the title Correct Pension Credit rates and Savings Credit projection Correct Pension Credit and pension-age Housing Benefit projections Oct 6, 2026
@anth-volk

Copy link
Copy Markdown
Collaborator Author

Critical fixed

  • Consolidated Project Pension Credit guarantee with earnings #2146 and Project protected pension-age Housing Benefit by component #2156 into this PR so that the Pension Credit guarantee, Savings Credit threshold, and protected Housing Benefit allowances use compatible projections.
  • Replaced the provisional post-2026 Savings Credit threshold freeze with a threshold derived from the earnings-projected guarantee and CPI-projected maximum Savings Credit.
  • Added protected single, lone-parent, and couple Housing Benefit projections, removing the discontinuous downstream loss at the modeled Guarantee Credit boundary.

Should-address fixed

  • Reused the State Pension review's May-July earnings input through a separate non-negative earnings series; Pension Credit does not inherit the CPI or 2.5% parts of the triple lock.
  • Derived component bases from enacted parameters instead of hard-coding monetary values in Python.
  • Added direct official-source references, documentation, and Towncrier fragments.
  • Added focused tests for both relationship types, every forecast year, negative-growth floors, and deterministic 2035 boundary calculations.

Skipped by design

Verification

  • make format: passed.
  • Focused Python tests: 34 passed before and after rebasing on current main.
  • Altered and directly affected YAML tests: 22 passed before and after rebasing.
  • Towncrier draft build and staged diff checks: passed.
  • Independent plan verification: 5/5 items complete; no new issues found.

@anth-volk

Copy link
Copy Markdown
Collaborator Author

Addressed all three findings from the latest review:

  • The Savings Credit projection now creates separate SINGLE and COUPLE
    forecast indices. Standard parameter uprating consumes those indices; the
    builder no longer writes future values into either threshold parameter.
  • Regression tests confirm that pre-processing scenario changes, direct
    reforms, and future explicit source values remain exact. A future explicit
    value becomes the anchor for subsequent forecast years.
  • Savings Credit metadata now cites the GB and Northern Ireland prices-review
    provisions as well as the earnings-review provisions.
  • The protected pension-age Housing Benefit allowances now cite SI 2006/214,
    schedule 3 paragraph 1, and the enacted 2026 amounts in SI 2026/148,
    article 24(6) and schedule 6.

Validation: make format passed; 41 changed/directly affected Python tests
passed; 51 focused Pension Credit and Housing Benefit policy YAML cases passed;
the Towncrier draft and diff checks passed. The full package suite was not run,
following the requested focused-test scope.

@anth-volk
anth-volk force-pushed the feature/pension-credit-2025-rates branch from 0d45a0d to 8bc60ad Compare October 7, 2026 13:09
@anth-volk
anth-volk requested review from vahid-ahmadi and removed request for vahid-ahmadi October 7, 2026 13:09
@anth-volk

anth-volk commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator Author

@vahid-ahmadi I rebased this PR cleanly onto current main (base revision c85ffef; new PR revision 8bc60ad) and renewed your review request. Focused local validation passed: formatting/lint, 40 Python tests, and 51 YAML policy tests. I did not run the full test suite locally; GitHub checks are running against the rebased revision. Could you please re-review the latest revision?

@vahid-ahmadi vahid-ahmadi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Anthony. I re-reviewed at 8bc60ad. The earnings uprating of the guarantee, the derived Savings Credit threshold and the protected HB allowances all look right to me. The 2025-26 and 2026-27 enacted values are unchanged from the SIs, and the projected maximum Savings Credit follows September CPI exactly: £17.96 → £18.34 → £18.72 → £19.10 → £19.49 for a single person, 2026 to 2030.

Locally, the 40 changed Python tests pass, and so do 448 YAML cases (the Pension Credit, Housing Benefit and integration directories). From my last round:

  • the £11,000 savings credit case (413.98);
  • the positive eligibility case;
  • the s.150A earnings point.

Those are all in now, and the docs edit and its revert go away in the squash.

Should

  1. 2025-26 pension-age HB allowances are still on the old guarantee. housing_benefit/allowances/{single,lone_parent}/aged.yaml:11-12 hold 239.20 and couple/aged.yaml:11 holds 357.98, which is the old £221.85 / £338.61 guarantee plus maximum Savings Credit. DWP's 2025-26 rates are 244.40 / 366.00: £227.10 + £17.30, and £346.60 + £19.35 rounded up.

    • With the corrected guarantee, the protected allowance in 2025-26 sits only £12.10 (single) and £11.38 (couple) above it, instead of the maximum Savings Credit, so HB is understated by up to £5.20 a week for pension-age claimants just above Guarantee Credit.
    • Since this PR now owns the PC/HB relationship and edits these files, I'd fix the three 2025 values here and add one 2025 case. #1925 makes the same change, so it can drop those hunks when it rebases. It conflicts with this PR on minimum_guarantee.yaml, threshold.yaml, the three aged.yaml files and entitledto_scenarios.yaml either way.
  2. BASE_YEAR = 2026 is hard-coded in create_savings_credit_threshold_projection.py:22 and create_protected_pension_age_uprating.py:18.

    • When the 2027-28 rates go in, the 2027 values become the uprating anchor but the indices are still built from projected 2027 amounts. The maximum Savings Credit then no longer follows CPI from the enacted 2027 maximum, and the HB uplift drifts the same way.
    • Fix: take the base year as the latest year with an explicit value. Raise an error if the guarantee, threshold and aged allowances disagree on that year, so the next uprating update can't miss it.

Nits

  1. A guarantee reform doesn't move the derived threshold. With minimum_guarantee.SINGLE set to 260 in 2027, the threshold stays at 216.71, so the maximum Savings Credit jumps from £18.34 to £25.97, and the protected HB allowance doesn't move either. That's what main does too, and arguably right, since the threshold is set separately by SI. A sentence in the threshold description saying reforms to the guarantee don't re-derive the threshold or the HB allowance would save someone a surprise.
  2. Docs placement: the paragraph about Pension Credit and HB sits in docs/book/programs/gov/dwp/state-pension.md:213-220. A line or link from the Pension Credit page would make it easier to find.

Happy to approve once 1 is in. 2 could be a follow-up issue if you'd rather keep this PR tight.

@anth-volk

Copy link
Copy Markdown
Collaborator Author

@vahid-ahmadi The local parameter audit selected the protected pension-age Housing Benefit couple allowance and confirmed your item 1. Commit 9442ffb corrects all three 2025 rates: £244.40 for single/lone-parent claimants and £366.00 for couples. Each dated value now cites SI 2025/295 Schedule 6 and NI SR 2025/64 Schedule 6, plus DWP's published rates. The enacted 2026 amounts and projection builders are unchanged.

The three new parameter regressions fail on the old values. After the correction, the entire altered protected-allowance Python test file passes (10 cases), and the new YAML file passes both tapered-award cases. Formatting/lint, Towncrier preview and diff checks pass. No full suite or population simulation was run.

The cited audit is #2195. #1925 still owns the remaining historical rate corrections and can drop the overlapping three 2025 hunks when rebasing; #1913 still owns the cohort split. Your separate item 2 about the hard-coded projection base year remains unaddressed in this small rate correction, as do the two documentation nits. Could you re-review the rate correction?

@vahid-ahmadi

Copy link
Copy Markdown
Collaborator

Thanks Anthony, the rate correction is right. £244.40 and £366.00 match SI 2025/295 Schedule 6 and DWP's 2025-26 rates page, and at 9442ffb the protected-allowance and savings-credit tests pass (20), as do the Pension Credit and Housing Benefit YAML cases (437).

It needs a rebase before it can go in, though. #1925 merged on 11 October, and the branch now conflicts with main in seven files: the three aged.yaml files, minimum_guarantee.yaml, threshold.yaml, tax_benefit_system.py and entitledto_scenarios.yaml. Main already has 244.4 and 366 from #1925, but as bare values, so keep your cited versions when resolving.

The hard-coded BASE_YEAR is fine as a follow-up issue, as are the two docs nits. Happy to approve once it's rebased and CI is green.

🤖 Generated with Claude Code

@anth-volk
anth-volk force-pushed the feature/pension-credit-2025-rates branch from 9442ffb to 3c8b9bf Compare October 12, 2026 13:27
@anth-volk

Copy link
Copy Markdown
Collaborator Author

Rebased onto main e01f2f5ec after #1925 merged. I retained main's historical/current rates and the specific GB/NI citations, and integrated the projection builders into main's current date-aware parameter-processing code.

The 2035 boundary tests initially failed because their old fixed income values no longer crossed the updated forecast cutoff. They now locate the actual £1 step across that cutoff, including income-tax deductions; the assertions against discontinuous benefit losses are unchanged.

Validation: 43 Python tests and 24 YAML cases passed, sequentially. Formatting, lint, Towncrier draft, fix-evidence validation and whitespace checks passed. No full suite or CI polling. The 2026 projection-anchor follow-up remains unchanged.

@vahid-ahmadi vahid-ahmadi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Anthony. The conflict resolution itself looks right at 3c8b9bf: the cited 2025 values are kept on top of #1925's, the builders sit in main's process_parameter_tree, and the new bisection in the 2035 boundary test leaves the assertions unchanged. The 43 Python tests and 475 Pension Credit, Housing Benefit and integration YAML cases pass locally.

CI is red, though, and I can reproduce it: six tests fail in run 38255826272, all with ValueError: The protected Housing Benefit allowance cannot be lower than the corresponding Pension Credit guarantee. from create_protected_pension_age_uprating.py:37.

  • test_council_tax_reduction_pension_credit_properties.py: five tests (test_freeze_keeps_savings_credit_only_receipt_when_the_reform_adds_guarantee_credit, test_guarantee_credit_recipients_ctr_is_invariant_to_state_pension, test_guarantee_credit_recipients_ctr_is_invariant_to_savings, test_awards_follow_the_pension_credit_routes, test_not_claiming_pension_credit_never_raises_a_recipients_ctr)
  • test_savings_credit_only_receipt_properties.py::test_pension_credit_freeze_keeps_the_baseline_receipt

Both files landed on main on 11 October (6731dd7), so the focused run wouldn't have picked them up. Their CREDIT_REFORMS raise the 2026 minimum guarantee to £300 / £459. Now that the builders run inside the parameter processing, they see the reformed guarantee, which is above the enacted £256 / £383.35 protected allowance, so retained_uplift goes negative and the guard raises. A one-line check: minimum_guarantee.SINGLE at 300 from 2026 raises on this branch and gives £15,600 of Pension Credit on main. A reform from 2027 onwards doesn't raise, so it's only the base-year guard.

The guard makes sense for the enacted base, but under a reform it should fall back rather than stop the simulation, for example by flooring the uplift at zero or building the index from the baseline values. Either is fine by me, and it stays consistent with the earlier point that a guarantee reform doesn't re-derive the HB allowance. A test with a 2026 guarantee reform would cover it. Happy to approve once that's in and CI is green.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Aggregate impact on certified data: pass

Base 159a5ec99d against head f1e0c4a9f6, both on enhanced_frs_2024_25.h5 (policyengine-uk-data 1.58.1, sha256 056e80fd8e91), fiscal years 2025-26 to 2031-32. Each year is a full microsimulation run on each side.

Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 0 (thresholds in report_spec.yaml; a flag does not fail the job).

Workflow run and full report

Notes

  • In the years a figure from fewer than 10 records is suppressed (spending.disabled_students_allowance), every total containing it, and the decile incomes, are rebuilt from their other components, so no published total includes it. Those totals are marked †. A change is withheld where the two sides leave out different figures and can't be lined up.

Moves flagged for review

None.

Spending by benefit and service

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed

Caseloads

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed

Standard reform costings (change in gov_balance)

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise additional rate by 3pp 0 0 0 0 0 suppressed suppressed

Standard reforms: change in each reform's target

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise additional rate by 3pp: change in income_tax 0 0 0 0 0 suppressed suppressed

Household net income by decile

No household's net income changes in any year.

No change in any year: receipts by tax head; government balance, each component counted once; model totals; household income totals; population; poverty rates; people in poverty; inequality; mean household net income in each side's own deciles.

Checks failing on both base and head (1)
  • VAT excluding private school fees is never negative (sign:vat): 2025-26: 76 units below zero, a weighted £-53,889,156 in total; 2026-27: 76 units below zero, a weighted £-55,420,387 in total; 2027-28: 76 units below zero, a weighted £-56,618,038 in total
Counted-once check

Each of 91 components was raised by £1 per unit in 2026-27, and every head of its totals was read household by household: its own head must rise by exactly £1, no other tax head may move, no other benefit may rise, and the model's own total must move by what its heads moved. A head that breaks a rule is measured again twice: with every other head held at its baseline value, and with every other head held at its value after the first £1 while the component rises again. It fails where either still shows the response. 0 failed; 0 were skipped.

  • widowed_parents_allowance: pension_credit responds only through other heads in fewer than 10 households (other heads that moved there: carers_allowance; gov_spending; not a failure)
  • widowed_parents_allowance: pension_credit responds only through other heads in fewer than 10 households (other heads that moved there: carers_allowance; household_benefits; not a failure)
Provenance
Side Commit Runtime Environment hash Partition hash Time
base main 159a5ec99d uk 2.162.10 (files 3f1086bbbda8), core 3.33.12, py 3.13.18 aarch64 1cc494f91d0a 4f86ac4248d9 1865.78s
head #2092 merged into main f1e0c4a9f6 uk 2.162.10 (files e13e44cfb77c), core 3.33.12, py 3.13.18 aarch64 1cc494f91d0a 4f86ac4248d9 1885.96s

Dataset: hf://policyengine/policyengine-uk-data-private/enhanced_frs_2024_25.h5@2526edbbb687a3b54dbb11272e5389e4b62d2ac2, sha256 056e80fd8e9151d78f432a25cde863340baee81304c6216a37fb575ce8563784 (verified on both sides), policyengine-uk-data 1.58.1.

Receipts by tax head: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Final charge on cash interest in non-cash ISAs £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Lifetime ISA withdrawal charge £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Income tax £298.70bn £318.31bn £335.60bn £350.52bn £365.79bn £383.53bn £401.03bn
National Insurance (employee and self-employed) £52.37bn £54.71bn £56.48bn £58.15bn £60.22bn £62.11bn £64.44bn
National Insurance (employer) £150.59bn £156.99bn £161.74bn £166.13bn £173.24bn £178.59bn £185.29bn
Employer NI change borne by capital £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Employer NI change borne by consumers £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Capital gains tax £12.61bn £13.05bn £13.53bn £14.02bn £14.52bn £15.05bn £15.58bn
Student loan repayments £7.91bn £8.21bn £8.43bn £8.63bn £8.85bn £9.10bn £9.42bn
VAT excluding private school fees £249.37bn £255.28bn £261.81bn £267.87bn £274.12bn £280.56bn £286.81bn
Change in VAT from its baseline £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
VAT on private school fees £1.95bn £2.00bn £2.05bn £2.10bn £2.21bn £2.21bn £2.31bn
Change in private school VAT from its baseline £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Fuel duties £24.53bn £24.64bn £26.64bn £26.69bn £26.42bn £25.76bn £26.35bn
Alcohol duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Tobacco duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Vehicle excise duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Betting and gaming duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
TV licence fee £4.26bn £4.41bn £4.42bn £4.44bn £4.45bn £4.47bn £4.48bn
Council tax £49.21bn £51.88bn £54.97bn £58.29bn £61.55bn £64.92bn £68.49bn
High value council tax surcharge £0.00bn £0.00bn £0.00bn £0.48bn £0.49bn £0.50bn £0.51bn
Northern Ireland domestic rates £0.55bn £0.58bn £0.60bn £0.63bn £0.66bn £0.69bn £0.72bn
Business rates £34.68bn £38.04bn £38.88bn £39.76bn £43.07bn £43.13bn £44.57bn
Stamp duty land tax on the household's own purchases £6.90bn £7.39bn £7.81bn £8.21bn £8.62bn £9.02bn £9.42bn
Stamp duty land tax paid by companies £4.81bn £5.98bn £6.73bn £7.46bn £8.21bn £8.87bn £9.16bn
Land transaction tax (Wales) £0.07bn £0.08bn £0.09bn £0.09bn £0.10bn £0.10bn £0.11bn
Land and buildings transaction tax (Scotland) £0.31bn £0.32bn £0.34bn £0.35bn £0.37bn £0.38bn £0.40bn
Unspecified tax change borne by capital £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Unspecified tax change borne by consumers £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Wealth tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Wealth tax excluding the main residence £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Land value tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Carbon tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Spending by benefit and service: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Lifetime ISA bonus £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
State Pension £121.64bn £126.07bn £127.08bn £128.87bn £132.52bn £136.31bn £140.08bn
Pension Credit £6.66bn £7.04bn £7.32bn £7.46bn £7.60bn £7.77bn £7.94bn
Winter Fuel Payment £1.86bn £1.86bn £1.80bn £1.78bn £1.79bn £1.79bn £1.80bn
Pension Age Winter Heating Payment (Scotland) £0.19bn £0.20bn £0.20bn £0.21bn £0.21bn £0.22bn £0.22bn
Universal Credit £75.21bn £80.38bn £82.93bn £84.23bn £85.58bn £86.87bn £87.97bn
Housing Benefit £8.55bn £8.89bn £9.17bn £9.31bn £9.58bn £9.84bn £10.10bn
Council Tax Reduction £2.07bn £2.18bn £2.32bn £2.46bn £2.60bn £2.74bn £2.89bn
Employment and Support Allowance (income-related) £0.79bn £0.81bn £0.83bn £0.85bn £0.86bn £0.89bn £0.90bn
Employment and Support Allowance (contribution-based) £5.20bn £5.34bn £5.46bn £5.59bn £5.72bn £5.85bn £5.99bn
Jobseeker's Allowance (income-based) £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Jobseeker's Allowance (contribution-based) £0.29bn £0.30bn £0.30bn £0.31bn £0.32bn £0.32bn £0.33bn
Income Support £0.12bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Working Tax Credit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Child Tax Credit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Incapacity Benefit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Personal Independence Payment £26.94bn £28.06bn £29.13bn £29.83bn £30.54bn £31.26bn £31.96bn
Disability Living Allowance £7.49bn £7.80bn £8.10bn £8.29bn £8.49bn £8.69bn £8.88bn
Attendance Allowance £8.68bn £9.04bn £9.38bn £9.61bn £9.83bn £10.07bn £10.29bn
Carer's Allowance £4.48bn £4.67bn £4.84bn £4.96bn £5.08bn £5.19bn £5.31bn
Carer Support Payment (Scotland) £0.31bn £0.32bn £0.33bn £0.34bn £0.35bn £0.36bn £0.37bn
Carer's Allowance Supplement (Scotland) £0.00bn £0.04bn £0.04bn £0.04bn £0.04bn £0.04bn £0.04bn
Severe Disablement Allowance £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn
Industrial Injuries Disablement Benefit £0.50bn £0.52bn £0.53bn £0.54bn £0.55bn £0.57bn £0.58bn
Armed Forces Compensation Scheme £0.61bn £0.62bn £0.64bn £0.65bn £0.67bn £0.68bn £0.70bn
War Widow's or Widower's Pension £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Child Benefit £16.16bn £16.83bn £17.43bn £17.85bn £18.26bn £18.69bn £19.10bn
Scottish Child Payment £0.49bn £0.52bn £0.54bn £0.55bn £0.56bn £0.57bn £0.56bn
Two Child Limit Payment (Scotland, counterfactual) £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Healthy Start vouchers £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn
Maternity Allowance £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Statutory Maternity Pay £3.30bn £3.39bn £3.47bn £3.55bn £3.63bn £3.72bn £3.80bn
Statutory Paternity Pay £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Statutory Sick Pay £1.12bn £1.15bn £1.18bn £1.21bn £1.24bn £1.27bn £1.29bn
Sure Start Maternity Grant £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn
Bereavement Support Payment £0.67bn £0.69bn £0.70bn £0.72bn £0.73bn £0.75bn £0.77bn
Widowed Parent's Allowance £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Tax-Free Childcare £0.55bn £0.56bn £0.57bn £0.58bn £0.59bn £0.61bn £0.59bn
Extended childcare entitlement £2.99bn £3.50bn £3.60bn £3.67bn £3.77bn £3.85bn £3.94bn
Universal childcare entitlement £2.05bn £2.23bn £2.28bn £2.34bn £2.40bn £2.47bn £2.52bn
Targeted childcare entitlement £0.50bn £0.51bn £0.52bn £0.53bn £0.54bn £0.56bn £0.52bn
Childcare Offer for Wales £0.26bn £0.28bn £0.28bn £0.29bn £0.30bn £0.30bn £0.31bn
Care to Learn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Childcare Grant £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Parents' Learning Allowance £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn
Adult Dependants' Grant £0.06bn £0.06bn £0.04bn £0.04bn £0.04bn £0.04bn £0.04bn
Travel Grant £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed
16 to 19 Bursary Fund £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
NHS spending £203.83bn £204.54bn £205.13bn £205.79bn £206.49bn £207.23bn £207.73bn
State education spending £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn
Transport subsidy spending £25.05bn £25.13bn £25.21bn £25.29bn £25.37bn £25.46bn £25.53bn
Cost-of-living support payment £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Energy Bills Rebate £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Energy Price Guarantee subsidy £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Basic income £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Disability basic income £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Other public spending budget change £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Caseloads: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Lifetime ISA bonus 0k 0k 0k 0k 0k 0k 0k
State Pension 11,261k 11,158k 10,849k 10,746k 10,783k 10,822k 10,848k
Pension Credit 1,485k 1,483k 1,480k 1,461k 1,462k 1,464k 1,460k
Winter Fuel Payment 7,842k 7,793k 7,494k 7,407k 7,432k 7,459k 7,477k
Pension Age Winter Heating Payment (Scotland) 803k 803k 803k 800k 803k 806k 808k
Universal Credit 6,334k 6,341k 6,366k 6,347k 6,345k 6,333k 6,298k
Housing Benefit 1,249k 1,247k 1,250k 1,234k 1,238k 1,240k 1,239k
Council Tax Reduction 3,175k 3,198k 3,209k 3,229k 3,244k 3,257k 3,264k
Employment and Support Allowance (income-related) 71k 71k 71k 71k 71k 72k 72k
Employment and Support Allowance (contribution-based) 719k 721k 723k 726k 728k 731k 732k
Jobseeker's Allowance (income-based) 0k 0k 0k 0k 0k 0k 0k
Jobseeker's Allowance (contribution-based) 67k 67k 67k 68k 68k 68k 68k
Income Support 25k 0k 0k 0k 0k 0k 0k
Working Tax Credit 0k 0k 0k 0k 0k 0k 0k
Child Tax Credit 0k 0k 0k 0k 0k 0k 0k
Incapacity Benefit 0k 0k 0k 0k 0k 0k 0k
Personal Independence Payment 3,566k 3,579k 3,589k 3,601k 3,613k 3,626k 3,635k
Disability Living Allowance 1,291k 1,295k 1,299k 1,303k 1,308k 1,312k 1,316k
Attendance Allowance 1,694k 1,700k 1,705k 1,711k 1,716k 1,723k 1,727k
Carer's Allowance 1,034k 1,038k 1,041k 1,044k 1,048k 1,051k 1,053k
Carer Support Payment (Scotland) 71k 71k 72k 72k 72k 72k 73k
Carer's Allowance Supplement (Scotland) 0k 71k 72k 72k 72k 72k 73k
Severe Disablement Allowance 4k 4k 4k 4k 4k 4k 4k
Industrial Injuries Disablement Benefit 137k 137k 138k 138k 139k 139k 140k
Armed Forces Compensation Scheme 93k 93k 94k 94k 94k 95k 95k
War Widow's or Widower's Pension 0k 0k 0k 0k 0k 0k 0k
Child Benefit 8,102k 8,128k 8,142k 8,165k 8,191k 8,216k 8,232k
Scottish Child Payment 350k 352k 353k 357k 354k 355k 342k
Two Child Limit Payment (Scotland, counterfactual) 0k 0k 0k 0k 0k 0k 0k
Healthy Start vouchers 199k 200k 201k 201k 202k 203k 203k
Maternity Allowance 0k 0k 0k 0k 0k 0k 0k
Statutory Maternity Pay 332k 334k 335k 336k 337k 338k 339k
Statutory Paternity Pay 0k 0k 0k 0k 0k 0k 0k
Statutory Sick Pay 181k 182k 182k 183k 183k 184k 185k
Sure Start Maternity Grant 26k 26k 26k 26k 26k 27k 27k
Bereavement Support Payment 98k 98k 98k 99k 99k 99k 100k
Widowed Parent's Allowance 0k 0k 0k 0k 0k 0k 0k
Tax-Free Childcare 1,014k 1,024k 1,024k 1,027k 1,027k 1,031k 1,013k
Extended childcare entitlement 796k 801k 806k 809k 815k 819k 820k
Universal childcare entitlement 1,071k 1,074k 1,077k 1,080k 1,086k 1,096k 1,097k
Targeted childcare entitlement 165k 156k 154k 155k 155k 156k 144k
Childcare Offer for Wales 39k 39k 39k 39k 39k 39k 39k
Care to Learn 0k 0k 0k 0k 0k 0k 0k
Childcare Grant 0k 0k 0k 0k 0k 0k 0k
Parents' Learning Allowance 98k 98k 99k 99k 99k 100k 100k
Adult Dependants' Grant 18k 18k 12k 12k 12k 13k 13k
Travel Grant 0k 0k 0k 0k 0k 0k 0k
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed
16 to 19 Bursary Fund 0k 0k 0k 0k 0k 0k 0k
Cost-of-living support payment 0k 0k 0k 0k 0k 0k 0k
Energy Bills Rebate 0k 0k 0k 0k 0k 0k 0k
Energy Price Guarantee subsidy 0k 0k 0k 0k 0k 0k 0k
Basic income 0k 0k 0k 0k 0k 0k 0k
Disability basic income 0k 0k 0k 0k 0k 0k 0k
Government balance, each component counted once: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Government receipts, each component once £898.82bn £941.86bn £980.11bn £1,013.84bn £1,052.89bn £1,089.01bn £1,129.10bn
Government spending £528.96bn † £543.83bn † £551.68bn † £558.06bn † £566.57bn † £575.31bn † £583.37bn †
Government balance, each receipt once £369.86bn † £398.03bn † £428.43bn † £455.78bn † £486.32bn † £513.70bn † £545.72bn †
Benefits in household_benefits but not in gov_spending £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Model totals: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
gov_tax £898.82bn £941.86bn £980.11bn £1,013.84bn £1,052.89bn £1,089.01bn £1,129.10bn
household_tax £496.91bn £527.60bn £554.51bn £577.74bn £603.32bn £627.65bn £654.69bn
pre_budget_change_household_tax £489.00bn £519.39bn £546.08bn £568.63bn £593.98bn £618.05bn £644.75bn
gov_spending £528.96bn † £543.83bn † £551.68bn † £558.06bn † £566.57bn † £575.31bn † £583.37bn †
household_benefits £528.96bn † £543.83bn † £551.68bn † £558.06bn † £566.57bn † £575.31bn † £583.37bn †
gov_balance £369.86bn † £398.03bn † £428.43bn † £455.78bn † £486.32bn † £513.70bn † £545.72bn †
Household income totals: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Household market income £1,651.49bn £1,713.03bn £1,764.05bn £1,812.97bn £1,862.19bn £1,918.39bn £1,985.28bn
Household net income £1,644.43bn † £1,688.69bn † £1,719.55bn † £1,750.61bn † £1,781.67bn † £1,821.07bn † £1,867.39bn †
Household net income (HBAI definition) £1,457.30bn £1,504.48bn £1,537.58bn £1,569.36bn £1,602.66bn £1,640.48bn £1,687.04bn
Population: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
People 69,224k 69,466k 69,668k 69,891k 70,128k 70,381k 70,550k
Benefit units 35,100k 35,223k 35,325k 35,438k 35,558k 35,686k 35,772k
Households 31,037k 31,145k 31,236k 31,336k 31,442k 31,555k 31,631k
Poverty rates: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Absolute poverty, before housing costs: everyone 16.0% 15.1% 15.4% 15.5% 15.6% 15.7% 15.7%
Absolute poverty, before housing costs: children 24.0% 21.1% 21.4% 21.3% 21.5% 21.8% 21.8%
Absolute poverty, before housing costs: working-age adults 13.8% 13.5% 13.9% 14.1% 14.1% 14.2% 14.2%
Absolute poverty, before housing costs: pensioners 13.7% 13.2% 13.1% 13.4% 13.5% 13.5% 13.5%
Absolute poverty, after housing costs: everyone 21.4% 20.8% 21.3% 21.5% 21.7% 21.9% 22.2%
Absolute poverty, after housing costs: children 33.0% 30.7% 31.1% 31.1% 31.4% 31.7% 32.3%
Absolute poverty, after housing costs: working-age adults 19.8% 19.6% 20.2% 20.5% 20.7% 20.8% 21.1%
Absolute poverty, after housing costs: pensioners 12.7% 12.4% 12.3% 12.5% 12.8% 12.9% 13.0%
Relative poverty, before housing costs: everyone 17.2% 16.4% 16.5% 16.4% 16.2% 16.3% 16.5%
Relative poverty, before housing costs: children 26.2% 23.5% 23.4% 23.1% 22.7% 22.8% 23.1%
Relative poverty, before housing costs: working-age adults 14.6% 14.4% 14.7% 14.7% 14.6% 14.7% 14.8%
Relative poverty, before housing costs: pensioners 15.0% 14.8% 14.1% 14.1% 14.0% 14.0% 14.2%
Relative poverty, after housing costs: everyone 21.6% 21.0% 21.1% 21.2% 21.3% 21.4% 21.8%
Relative poverty, after housing costs: children 33.3% 31.2% 30.8% 30.7% 31.0% 31.0% 31.5%
Relative poverty, after housing costs: working-age adults 19.9% 19.8% 20.1% 20.3% 20.4% 20.5% 20.7%
Relative poverty, after housing costs: pensioners 12.9% 12.5% 12.1% 11.9% 11.8% 11.9% 12.8%
People in poverty: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Absolute poverty, before housing costs: everyone 11,049k 10,466k 10,736k 10,860k 10,960k 11,054k 11,081k
Absolute poverty, before housing costs: children 3,580k 3,171k 3,211k 3,220k 3,261k 3,305k 3,317k
Absolute poverty, before housing costs: working-age adults 5,845k 5,746k 6,022k 6,123k 6,169k 6,209k 6,219k
Absolute poverty, before housing costs: pensioners 1,625k 1,550k 1,504k 1,517k 1,530k 1,539k 1,545k
Absolute poverty, after housing costs: everyone 14,847k 14,422k 14,808k 14,994k 15,242k 15,402k 15,638k
Absolute poverty, after housing costs: children 4,937k 4,609k 4,671k 4,685k 4,757k 4,814k 4,912k
Absolute poverty, after housing costs: working-age adults 8,406k 8,359k 8,732k 8,898k 9,024k 9,114k 9,240k
Absolute poverty, after housing costs: pensioners 1,504k 1,454k 1,404k 1,411k 1,460k 1,475k 1,486k
Relative poverty, before housing costs: everyone 11,896k 11,403k 11,468k 11,487k 11,391k 11,468k 11,652k
Relative poverty, before housing costs: children 3,918k 3,523k 3,519k 3,491k 3,435k 3,458k 3,516k
Relative poverty, before housing costs: working-age adults 6,202k 6,136k 6,338k 6,400k 6,368k 6,415k 6,515k
Relative poverty, before housing costs: pensioners 1,776k 1,744k 1,611k 1,597k 1,588k 1,595k 1,621k
Relative poverty, after housing costs: everyone 14,957k 14,597k 14,698k 14,825k 14,951k 15,042k 15,351k
Relative poverty, after housing costs: children 4,976k 4,677k 4,630k 4,634k 4,690k 4,712k 4,794k
Relative poverty, after housing costs: working-age adults 8,443k 8,455k 8,680k 8,847k 8,916k 8,977k 9,092k
Relative poverty, after housing costs: pensioners 1,537k 1,465k 1,388k 1,344k 1,346k 1,354k 1,464k
Inequality: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Gini coefficient, equivalised HBAI income before housing costs 0.3085 0.3059 0.3065 0.3071 0.3075 0.3081 0.3094
Mean household net income in each side's own deciles: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Decile 1: mean household_net_income per household £17,781 † £17,906 † £17,832 † £17,905 † £18,142 † £18,339 † £18,482 †
Decile 2: mean household_net_income per household £27,963 † £28,917 † £28,654 † £28,906 † £29,096 † £29,646 † £30,235 †
Decile 3: mean household_net_income per household £34,381 † £35,463 † £36,065 † £36,509 † £36,851 † £37,338 † £38,043 †
Decile 4: mean household_net_income per household £38,785 † £40,713 † £41,873 † £42,413 † £43,144 † £43,906 † £44,167 †
Decile 5: mean household_net_income per household £43,788 † £44,626 † £45,606 † £46,288 † £47,327 † £48,285 † £49,710 †
Decile 6: mean household_net_income per household £48,210 † £49,525 † £50,736 † £51,914 † £52,203 † £53,174 † £54,203 †
Decile 7: mean household_net_income per household £56,219 † £57,224 † £57,764 † £58,010 † £59,040 † £60,061 † £61,765 †
Decile 8: mean household_net_income per household £69,150 † £70,533 † £71,800 † £73,075 † £74,092 † £75,540 † £77,427 †
Decile 9: mean household_net_income per household £79,490 † £81,421 † £82,828 † £84,135 † £85,539 † £87,053 † £89,944 †
Decile 10: mean household_net_income per household £117,581 † £120,296 † £121,933 † £124,163 † £126,045 † £128,342 † £131,110 †
Standard reform costings (change in gov_balance): head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise basic rate by 1pp £6.47bn £6.76bn £6.67bn £6.86bn £7.06bn £7.35bn £7.55bn
Raise higher rate by 1pp £3.69bn £3.98bn £4.03bn £4.29bn £4.46bn £4.78bn £4.91bn
Raise personal allowance by ~800GBP/year £-3.82bn £-3.94bn £-4.01bn £-4.09bn £-4.18bn £-4.18bn £-2.00bn
Raise child benefit by 25GBP/week per additional child £-1.99bn £-1.75bn £-1.56bn £-1.46bn £-1.35bn £-1.23bn £-1.12bn
Reduce Universal Credit taper rate to 20% £-20.39bn £-20.96bn £-21.38bn £-21.61bn £-21.87bn £-22.17bn £-22.60bn
Raise Class 1 main employee NICs rate to 10% £11.02bn £11.42bn £11.77bn £12.07bn £12.37bn £12.75bn £13.07bn
Raise VAT standard rate by 2pp £24.89bn £25.55bn £26.14bn £26.75bn £27.39bn £28.03bn £28.66bn
Raise additional rate by 3pp £2.96bn £3.21bn £3.26bn £3.46bn £3.65bn suppressed suppressed
Raise the Universal Credit standard allowance by 10% £-3.05bn £-3.18bn £-3.30bn £-3.34bn £-3.38bn £-3.42bn £-3.48bn
Raise the State Pension by 10% £-7.79bn £-8.02bn £-8.01bn £-8.12bn £-8.43bn £-8.54bn £-8.87bn
Standard reforms: change in each reform's target: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise basic rate by 1pp: change in income_tax £6.61bn £6.91bn £6.83bn £7.03bn £7.24bn £7.47bn £7.73bn
Raise higher rate by 1pp: change in income_tax £3.69bn £3.99bn £4.04bn £4.25bn £4.47bn £4.74bn £4.93bn
Raise personal allowance by ~800GBP/year: change in income_tax £-3.95bn £-4.08bn £-4.17bn £-4.24bn £-4.34bn £-4.41bn £-1.76bn
Raise child benefit by 25GBP/week per additional child: change in child_benefit £2.33bn £2.14bn £1.95bn £1.84bn £1.73bn £1.62bn £1.50bn
Reduce Universal Credit taper rate to 20%: change in universal_credit £20.39bn £20.95bn £21.37bn £21.62bn £21.87bn £22.22bn £22.61bn
Raise Class 1 main employee NICs rate to 10%: change in national_insurance £11.28bn £11.74bn £12.06bn £12.37bn £12.69bn £13.03bn £13.49bn
Raise VAT standard rate by 2pp: change in vat £24.69bn £25.35bn £25.94bn £26.54bn £27.17bn £27.81bn £28.43bn
Raise additional rate by 3pp: change in income_tax £2.96bn £3.21bn £3.26bn £3.46bn £3.66bn suppressed suppressed
Raise the Universal Credit standard allowance by 10%: change in universal_credit £3.05bn £3.18bn £3.29bn £3.36bn £3.43bn £3.48bn £3.52bn
Raise the State Pension by 10%: change in state_pension £10.68bn £11.18bn £11.31bn £11.55bn £12.00bn £12.44bn £12.94bn

MaxGhenis and others added 3 commits October 23, 2026 03:05
Resolves the conflicts with #2557 (September CPI uprating), which moved the
standard minimum guarantee, Savings Credit threshold and pension-age HB
allowances onto lagged CPI "until its own basis is modelled". This branch is
that basis, so its projection indices replace the lagged-CPI placeholder, with
a comment on each allowance and the guarantee saying why. Keeps main's NI scale
validation ahead of the generation steps in process_parameter_tree, and both
sides' Savings Credit YAML cases.

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

Review items on #2092 (vahid-ahmadi, 8 and 13 October):

- BASE_YEAR = 2026 is gone from both builders. The base year is the last
  year with an explicit value for the standard minimum guarantee, the Savings
  Credit threshold and the three protected HB allowances, read from the
  enacted tree; they must all reach the same year, or processing raises, so a
  rates update that misses one fails instead of indexing from a projection.
- The protected-allowance guard was reachable: any reform that set a
  base-year allowance below its guarantee (or to zero) raised ValueError while
  the tree was built. Such an allowance keeps no retained Savings Credit, so
  it is now projected whole with the guarantee's earnings path, continuously
  with an allowance equal to its guarantee. Only an enacted allowance below its
  guarantee still raises. intermediate_projection is no longer needed and is
  removed from tax_benefit_system.py, which shrinks this PR's footprint there.
- The same class of failure in the threshold builder: a zero phase-in rate
  raised, and a zero threshold divided by zero. The threshold now uses the
  enacted phase-in rate, as it already used the unreformed guarantee, and a
  zero base stays zero.
- Lone parents get their own index, so a reform to the single allowance no
  longer moves the lone-parent projection.

Tests: boundary cases at, 1p below and 1p above the guarantee; zero-base and
phase-in reforms; base-year derivation and an incomplete rates update. Pins
for 2027 move with main's published September 2026 CPI (3.5%, replacing the
2.116% forecast).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1913's aged_from_cutoff allowances equal the Pension Credit standard minimum
guarantee in every enacted year (SI 2006/214 Sch 3 para 1(1)(c) and (2)(c)).
On main both were on lagged CPI, so they stayed equal; once this branch moves
the guarantee to the statutory earnings floor, the allowances would have
drifted below it from 2027. They now use the same index, and a test checks
they are equal in every year to 2040. #2557's lagged-CPI test now checks
that these amounts have left lagged CPI.

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

Copy link
Copy Markdown
Collaborator

Hand-off: review items addressed and main merged (backlog job B, 23 Oct 2026)

Head: 52c96b111, three commits on top of edef32cfa: a merge of origin/main at c65bced64 (after #2557), then two fixes. No rebase or force-push.

What I changed

  • Merge: seven conflicts resolved. Uprate benefits by the previous September's CPI #2557 had moved the guarantee, threshold and pension-age HB allowances to lagged CPI "until its own basis is modelled"; this PR's indices replace that placeholder. Main's NI scale validation stays ahead of the generation steps. Both sides' Savings Credit YAML cases are kept.
  • BASE_YEAR = 2026 → derived (pension_age_projection_base.py): the base is the last year with an explicit value for the guarantee (2), threshold (2) and protected allowances (3), read from the enacted tree. If they disagree, processing raises with the list of years.
  • Guard: reachable, now fixed. Any reform that set a base-year allowance below its guarantee, or to 0, raised ValueError while the tree was built (I checked single.aged at 200, 237.99 and 0). Now only an inconsistent enacted pair raises. A reformed allowance below its guarantee is projected by the guarantee's earnings path, which is continuous with allowance = guarantee. The intermediate_projection plumbing is no longer needed and is gone, which shrinks this PR's footprint in tax_benefit_system.py. That file's W1 owner is archived; the remaining change only registers the builders and passes unreformed.
  • Same failure in the threshold builder: a phase-in rate of 0 raised, and a threshold of 0 divided by zero. The threshold now uses the enacted phase-in rate, and a zero base stays zero.
  • Lone parents have their own index, so a single-allowance reform no longer moves the lone-parent projection.
  • Post-2021 cohort: Split pension-age Housing Benefit allowances by when State Pension age was attained #1913's aged_from_cutoff allowances equal the guarantee in every enacted year. They now project by the guarantee's earnings index; otherwise this merge would have made them diverge from 2027.
  • Boundary and base-year tests: reformed allowance 1p below, at and 1p above £238, and £100 below; zero-base reforms; phase-in 0 pays no Savings Credit and leaves projections unchanged; base year 2026; a full 2027-28 rates entry moves the base and the 2028 maximum then follows CPI from the 2027 maximum; a partial entry raises.
  • 2027 pins moved only because main now has the published September 2026 CPI (3.5%).

Tests (locked env, core 3.33.2)

  • 108 passed: the projection files, September CPI, triple lock, inventory and cycle tests.
  • 48 passed: HB cohort properties and the applicable-amount cover property.
  • 11 passed: the six tests that failed CI on 13 Oct, plus inventory and cycle tests.
  • Published rates and parameter descriptions: 1,360 passed, 0 failed.
  • YAML: 83 files, 950 passed.
  • uvx ruff format --check . passes, and the fix-evidence checker passes locally.

Open items

  1. CI on 52c96b111 is pending.
  2. @vahid-ahmadi, could you re-review? Your 8 Oct item 2 and the 13 Oct guard point are addressed as above (your CHANGES_REQUESTED still stands). One behaviour change needs your eye: a phase-in reform no longer re-derives the threshold. Previously, a phase-in change from 2027 moved the threshold to keep the CPI-projected maximum; now the maximum moves with the rate, consistent with your nit 3 for guarantee reforms.
  3. The aggregate impact will differ from the 20 Oct run (Pension Credit +£257m to +£486m, 2027-28 to 2031-32): main's CPI input and the post-2021-cohort projection are new. The hub's aggregate-impact run at this head will show the change. I ran no microsimulation.
  4. Overlap with Correct historical Housing Benefit pensioner allowances #2207: both edit the three aged.yaml files (Correct historical Housing Benefit pensioner allowances #2207 adds 2015-19 history; this PR adds 2025 citations and the uprating line). Whichever lands second needs a small merge that keeps both.
  5. Docs nit 4 (a link from the Pension Credit page, pension-credit.ipynb) is not done; it's a small follow-up.
  6. Independent review: the hub's to dispatch at this head.

@MaxGhenis

Copy link
Copy Markdown
Collaborator

UK hub (Claude session, for Max): overlap with #2576. The hub should have spotted this before building #2576, and didn't.

#2576 ("Fix Pension Credit and pension-age Housing Benefit earnings uprating") follows from #2557's review. It is independently approved and in the merge queue. It implements much of this PR's projection design:

  • the standard minimum guarantee projected by earnings (s.150A);
  • the Savings Credit threshold derived from it;
  • the higher pension-age HB allowances composed as the guarantee plus the Savings Credit maximum (A3/2021);
  • a guard for a zero phase-in.

The two PRs share 12 files, including minimum_guarantee.yaml, threshold.yaml, all six aged*.yaml and tax_benefit_system.py.

Plan: let #2576 land, because it's Budget-critical and already green and approved. Then the hub reconciles this PR onto main, keeping what #2576 doesn't have and dropping what duplicates it:

Where the two implementations differ, the hub will list each difference here before choosing. @anth-volk, @vahid-ahmadi: say if you'd rather this PR's version win on a particular point. The backlog build's 52c96b111 is kept as the starting point.

@MaxGhenis

Copy link
Copy Markdown
Collaborator

Hand-off: #2092 reconciled onto main

Head: 9b0d406723d172e8a07c0ef42d57fd010251b977 on feature/pension-credit-2025-rates.
Merge parents: Anthony’s 52c96b111efe3205feae501aafce869977dba02c and main eb0b8087d821b92d4faa75cf51a92d15aa2a3c45. Anthony’s history is preserved; one merge commit added, no rebase or force-push.

Resolution

Kept #2576’s dedicated earnings series, published maximum Savings Credit and exact unreformed higher-HB forecast identity. Integrated this PR’s reform-safe independent indices into main’s existing builder modules; removed the duplicate builder files and 2072/2083/2154 changelog fragments. Retained derived enacted base validation, non-negative threshold residuals, zero/below-guarantee reform fallbacks, lone-parent independence, later-cohort equality and boundary regressions. W1’s tax_benefit_system.py changes are limited to builder registration/order and passing unreformed context.

All main published values stay exact. All PR published values stay at their original dates except three obsolete 2015 HB placeholders corrected by #2207; their amounts remain at the proper 2019 dates. 2025 HB values were already correct on main (£244.40/£366); retained PR’s dated GB/NI citations and test cases.

Checks and counts

  • uvx ruff format --check .: pass (1,999 files checked).
  • uvx ruff check .: pass.
  • Fix-evidence checker against the full committed SHA: pass, 11 changed executable test files.
  • Static parsing: 23 changed source/doc files including 9 YAML files, AST and notebook JSON; 156 explicit parameter value records checked against both sides, with the three documented Correct historical Housing Benefit pensioner allowances #2207 date corrections.
  • git diff --check, committed ancestry and Git bundle verification: pass. Worktree source changes are committed.
  • Runtime tests: 0 executed, 0 collected. Gate attempts at 04:56, 04:59 and 05:04 EDT had reservation exit 0 but free disk 33.89/33.83/33.81 GB, below 55 GB. First load1 was 123.56 (also above 110); last was 73.14. No test process started. Commands were prepared with -n 2, foreground and BLAS threads 1.

Not run: all projection Python files (including #2576’s test_pension_credit_uprating.py and the new Hypothesis/differential file); test_september_cpi_uprating.py; test_triple_lock_statutory_inputs.py; test_process_time_reforms.py including inventory/cycle coverage; test_council_tax_reduction_pension_credit_properties.py and test_savings_credit_only_receipt_properties.py (including all six 13 October failures); Pension Credit, Housing Benefit and integration YAML directories, including #2576’s pension_age_earnings_uprating.yaml and council_tax_reduction_pension_age_personal_allowance.yaml. Also not run: rendered docs build, full suite, mutation tests or microsimulation. Runtime validation and impact are pending CI at this head; old aggregate reports are not evidence for this merge.

Vahid’s points

Point Status after merge Regression evidence (not run locally; CI pending)
8 Oct item 1: 2025–26 HB rates Already fixed on main, retained exactly with PR citations; static rate audit passes test_protected_allowances_match_enacted_2025_amounts; protected_pension_age_rates.yaml
8 Oct item 2: derived base year Retained: seven enacted paths must agree; error lists paths and years; reforms do not reset the base test_projection_base_year_is_the_last_enacted_year; test_next_year_rates_move_the_projection_base; test_rates_update_that_misses_a_parameter_raises
13 Oct guard and six CI failures Addressed in code: guarantee-only reforms do not change HB; below-guarantee/zero allowance and threshold reforms build test_2026_guarantee_reform_pays_the_reformed_credit_without_rebuilding_hb; test_completed_joint_reform_below_the_guarantee_projects_with_the_guarantee; test_zero_base_reform_builds_and_keeps_the_zero; combined-reform Hypothesis test; the original two failing-property files
Nit 3: guarantee reforms leave threshold/HB separately set Explicit sentence retained in threshold.yaml and README; phase-in reforms are independent too test_phase_in_reform_changes_savings_credit_not_the_projections and guarantee reform properties
Nit 4: docs placement Completed: Pension Credit notebook has its own Uprating section and link; State Pension page links there Notebook JSON/markdown diff checked; no rendered docs build

The CHANGES_REQUESTED review remains open. Do not mark ready or merge. Hub to undraft for CI and dispatch the independent review. axiom: needed is retained; Axiom parity still needs maintainer follow-up.

Workspace/recovery

The assigned checkout contains only the build brief and its .git file is read-only, pointing outside the writable workspace. Original .git and prompt.md were preserved. All real repository operations use workspace-local .job/git (git --git-dir=.job/git --work-tree=.). .job/reconciled.bundle is verified and contains the reconciled branch; it requires ancestors available from the original PR branch. The remote branch is the canonical delivery. The hub must use this local Git directory or bundle if its generic salvage sees the original brief checkout instead.

#2092 reconciliation decisions (written before resolution)

Sides: main eb0b8087d821 (includes #2576 and #2207), PR 52c96b111efe.
Paths below are relative to policyengine_uk/ unless stated. Line numbers refer to those snapshots.

Main file:line PR file:line Main PR Keep / reason
parameters/gov/economic_assumptions/create_earnings_uprating.py:37 parameters/gov/dwp/state_pension/triple_lock/create_triple_lock.py:154 Dedicated non-negative earnings series, unrounded compounded index Duplicate statutory_earnings_floor in triple-lock builder Main series and precision (already floors earnings); SSAA 1992 s150A. Drop duplicate series.
parameters/gov/dwp/pension_credit/savings_credit/create_savings_credit_threshold.py:50 parameters/gov/dwp/pension_credit/create_savings_credit_threshold_projection.py:65 Derive after uprating from separately published maximum Generate threshold index from penny-rounded guarantee/threshold gap Main published maximum and algebra; retain PR reform independence and explicit anchors. Differential tests cover shared domain, including positive and negative macro inputs.
parameters/gov/dwp/pension_credit/savings_credit/create_savings_credit_threshold.py:61 parameters/gov/dwp/pension_credit/create_savings_credit_threshold_projection.py:56 Zero phase-in retains prior threshold Uses enacted phase-in, isolates all phase-in reforms Retain PR independence for separately enacted thresholds and HB; never divide by a reformed zero rate. Nit 3.
parameters/gov/dwp/housing_benefit/allowances/create_pension_age_allowances.py:29 parameters/gov/dwp/housing_benefit/allowances/create_protected_pension_age_uprating.py:39 Forecast guarantee + published CPI-projected Savings Credit maximum Forecast guarantee + enacted HB-minus-guarantee retained uplift Main exact forecast identity, DWP A3/2021 paras 3–5. Enacted amounts remain exact even when independently rounded.
parameters/gov/dwp/housing_benefit/allowances/create_pension_age_allowances.py:29 parameters/gov/dwp/housing_benefit/allowances/create_protected_pension_age_uprating.py:159 Reforms re-derive HB, no per-allowance index Independent indices, below-guarantee fallback, zero stays zero Keep PR extras: own lone-parent index, independent guarantee reforms, non-negative projections and below-guarantee reform buildability.
parameters/gov/dwp/pension_credit/savings_credit/create_savings_credit_threshold.py:23 parameters/gov/dwp/pension_credit/pension_age_projection_base.py:14 Each parameter extends from own last value Common latest enacted year, error enumerates seven years PR base validation on enacted tree; reform dates must not move enacted base.
parameters/gov/dwp/pension_credit/savings_credit/maximum.yaml:12 parameters/gov/dwp/pension_credit/create_savings_credit_threshold_projection.py:49 September CPI already has a non-negative floor Also floors CPI at zero Keep main's existing floor and published maximum; add a floor only to the derived threshold residual.
parameters/gov/dwp/housing_benefit/allowances/{single,lone_parent,couple}/aged.yaml:9 same paths:8 #2207 adds 2015–19 history; #1925 already has 244.40/366 in 2025 Dated GB/NI 2025 citations, forecast description Keep main’s cited 2015–19 history and all later rates, add PR citations; SI 2025/295 Sch 6 and DWP published rates. PR’s 2015 placeholders (181/270.60) are 2019 rates: retain those amounts at their correct 2019 dates, with 2015 166.05/248.30 from SI 2015/457 Sch 7. #2207 explicitly corrects these dates.
parameters/gov/dwp/housing_benefit/allowances/{single,lone_parent,couple}/aged_from_cutoff.yaml:13 same paths:13 Earnings uprating statutory_earnings_floor Main earnings series; same guarantee path. SI 2021/188 and DWP A3/2021 para 5.
parameters/gov/dwp/pension_credit/guarantee_credit/minimum_guarantee.yaml:1 same path:1 earnings_uprating; published history statutory_earnings_floor; dated 2025 citations Main earnings route and all values plus PR citations/forecast description.
parameters/gov/dwp/pension_credit/savings_credit/threshold.yaml:1 same path:1 Post-uprating builder, published history Generated indices, explicit independence sentence/citations Keep main algebra with PR reform/anchor safety; retain independence sentence and citations.
tax_benefit_system.py:31,213 same path:33,215 Registers main builders after general uprating Registers PR builders in generation, passes unreformed into prior-law projections Integrate only required builder registration/context; retain W1 orchestration and other main changes.
tests/policy/baseline/gov/dwp/pension_credit/guarantee_credit/standard_minimum_guarantee.yaml:28 same path:28 Additional 2025 and projected earnings cases Additional 2025 cases Keep distinct cases and all main earnings cases, remove duplicate cases.
tests/test_september_cpi_uprating.py:240 same path:240 #2576 excludes pension-age paths PR excludes same paths, mentions later cohort Main checks plus explicit later-cohort exclusion.
tests/test_pension_credit_uprating.py:1 tests/test_housing_benefit_protected_allowance_uprating.py:1; tests/test_pension_credit_guarantee_reform_projections.py:1; tests/test_savings_credit_threshold_projection.py:1 Forecast algebra and published-rate regressions Forecast pins, reform/base/continuity regressions Main forecast expectations; retain distinct PR regressions, add Hypothesis invariants and differential comparison.
parameters/gov/dwp/pension_credit/README.md:1 docs/book/programs/gov/dwp/state-pension.md:260 Dedicated PC projection documentation Paragraph on State Pension page Keep main PC explanation, add reform/base rules and link from pension-credit.ipynb (nit 4).
changelog.d/pension-credit-earnings-uprating.fixed.md:1 changelog.d/{2072,2083,2092,2154}.fixed.md:1 Describes guarantee/threshold/HB forecast already merged Four overlapping fragments Drop 2072/2083/2154; rewrite 2092 for remaining floors, reform safety, base validation and continuity only.

Sources: SSAA s150A, SPCA s3, DWP A3/2021, 2025 rates.

The DWP published couple maximum in 2025 is £19.36 (formula £19.356), not £19.35. Published HB £366 is preserved without reconstructing its rounding.

Static preservation check

All main explicit parameter values are preserved exactly. All PR explicit values are preserved at the same dates except the three obsolete 2015-04-01 HB placeholders replaced by #2207. Their amounts (£181 single/lone parent, £270.60 couple) are retained at their correct 2019 dates. The 2015 values (£166.05/£248.30) and intervening years keep #2207’s statutory citations. A literal same-date union is impossible for those three conflicting placeholders; this follows the explicit instruction to keep #2207’s history.

@MaxGhenis

Copy link
Copy Markdown
Collaborator

UK hub review of the reconcile head 9b0d40672: changes requested. This was an independent review, by GPT-6.1 Sol. Required CI passes: 4,911 YAML cases, and 10,138 of 10,201 collected Python tests passed, with 61 skips and 2 xfails, all allowlisted. The review also checked the merge resolution against every row of the DIFFERENCES table, and checked the enacted rates against legislation.gov.uk and DWP's rate tables. It found no legal or rate errors. Four findings remain:

  • P2: reforms to the maximum Savings Credit no longer reach the forecasts. create_savings_credit_threshold.py and create_pension_age_allowances.py take the maximum from the unreformed tree, so a reform to savings_credit.maximum doesn't move the projected threshold or the higher pension-age HB allowances. Main does pass it through. Example: with the single maximum set to 0 from April 2026, main gives a 2027 threshold and higher HB allowance of £247.282, equal to the guarantee. This head gives £216.301 and £265.8706, so a positive Savings Credit could still be paid. These figures are derived from the source and haven't been run yet.
  • P2: the Pension Credit notebook isn't published. The new link from the State Pension page points to pension-credit.ipynb#uprating, but myst.yml doesn't list the notebook, and the docs build doesn't build it.
  • P3: two body lines are stale. One is the runtime-evidence paragraph; CI has now run, and Aggregate impact timed out with no report. The other is the axiom: needed line, a placeholder meant for external contributors.

Next: the hub will push the fixes on top of 9b0d40672 as one commit, with no force-push, and then re-review. That happens after the host's disk hold lifts and the hub's Budget items run first. Vahid's CHANGES_REQUESTED stays open until then.

MaxGhenis and others added 4 commits October 27, 2026 00:53
Both forecast builders now read the maximum Savings Credit from the
reform's tree, uprated as general uprating will project it (Core's own
rule on private copies of the maximum and its uprating chain), so a
reform to the maximum from the base year or any later year moves the
projected Savings Credit threshold and higher pension-age HB allowances,
as it did on main. The unreformed guarantee and phase-in context and the
own-base reform fallback are unchanged.

Adds the hub review's zero-maximum regression, which failed at 9b0d406.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The combined Hypothesis property now draws a path of one to three maximum
anchors for each relationship, from the year before the base onwards. A
new differential property checks that any path of maximum reforms, with
enacted phase-in paths and an optional reform to the maximum's uprating
index, reproduces main's post-uprating builders, apart from the threshold
floor. Own-base threshold and allowance reforms keep their fallback when
combined with a maximum reform, while the other amounts follow it.

The base-year maximum fill now also requires the enacted maximum to lack
the base year, so it never overwrites a reform anchored before it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A zero single maximum from the base year or from 2028, through the reform
dict, a Scenario, a structural Reform.from_dict class and a reprocessing
simulation modifier, makes the single threshold and the single and
lone-parent higher allowances equal the guarantee from then on, so no
Savings Credit is paid; every forecast year reads the maximum the reform
leaves, and couples are unchanged. A 2026 threshold or allowance change
beside the maximum reform keeps its own index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Register docs/book/programs/gov/dwp/pension-credit.ipynb in myst.yml's
Programmes block: notebooks are not pattern-matched, so the State Pension
page's link to its Uprating section pointed at an unpublished page. The
local docs build now builds 51 pages, including Pension Credit, and
resolves that link as an internal cross-reference to its uprating heading.

The notebook, the Pension Credit README and the threshold and maximum
descriptions now say that a maximum Savings Credit reform moves the
projected threshold and higher allowances, unless their own base-year
value is reformed.

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

Copy link
Copy Markdown
Collaborator

Picking up the hub review's findings on 9b0d40672 for the UK hub (see the comment above). @anth-volk, reply here if you're mid-change and I'll stop.

@MaxGhenis

Copy link
Copy Markdown
Collaborator

Ready for hub review

Head: 000f16be9f7f6050ebf847d53f2af3f909ea0ee6 on feature/pension-credit-2025-rates: four commits on top of the reconcile merge 9b0d406723d1. It was a fast-forward push, with no rebase or force-push, and Anthony's commits are untouched. The PR stays a draft. Vahid's 8 October CHANGES_REQUESTED and the 13 October follow-up are still open; I didn't dismiss them.

Findings and fixes

P2-1: maximum Savings Credit reforms now reach both forecasts.

  • policyengine_uk/parameters/gov/dwp/pension_credit/savings_credit/create_savings_credit_threshold.py:94 adds uprated_values (with _propagated_uprating at :67). It uprates private copies of the maximum, and of each index its uprating reads, with Core's own uprate_parameters. Each forecast year therefore gets exactly the value the maximum parameter will have after general uprating, every explicit or reform anchor included.
  • :122 adds projected_maximum_values, the per-year maximum floored at zero.
  • :177: the threshold index uses maxima[year] when given.
  • :408: the maxima are computed before the own-base comparison, and :415 drops them in the changed-own-base branch, so the own-base fallback is unchanged.
  • policyengine_uk/parameters/gov/dwp/housing_benefit/allowances/create_pension_age_allowances.py:90 makes the retained component maxima[year] when given; :259 and :264 mirror the threshold builder.
  • The unreformed guarantee and phase-in context are untouched.
  • Current law is unchanged. All 504 threshold, maximum, higher-HB and forecast-index values for 2020–2040, on the main tree and its baseline copy, match 9b0d40672: 496 are bit-identical and the rest differ by at most 2.2e-16 relative (one ulp).
  • create_savings_credit_threshold.py:400 adds a fill guard. The base-year maximum fill, for a rates update without a maximum, now also requires the enacted maximum to lack the base year, so it no longer overwrites a reform anchored before the base year. The review didn't ask for this; the new differential found it. Dated routes never hit it, because they always restate a later value; direct updates (synthetic tree, modifiers) did.

P2-2: the Pension Credit page is published.

  • myst.yml:41 registers docs/book/programs/gov/dwp/pension-credit.ipynb in Programmes, exactly as the review gave it.
  • Locally, make documentation (the docs CI command) built 51 pages, including programs/gov/dwp/pension-credit.ipynb; CI at 9b0d40672 built 50.
  • The State Pension link resolves as an internal crossReference to /pension-credit#uprating (kind heading, no error).
  • The notebook's Uprating section, the Pension Credit README (:44), threshold.yaml:7 and the maximum.yaml description now state the maximum-reform rule.

P3-1 and P3-2: PR body. I replaced the stale Validation paragraphs with the review's wording, updated for what I ran here, and kept CI at 9b0d40672 labelled as such. The axiom: line is now the review's n/a scope line, verbatim. The body claims no encoding and no parity result.

I added no changelog fragment. Relative to main this restores main's response to maximum reforms, and 2092.fixed.md ("safe under reforms") already covers it.

From the review's proposal

Taken:

  • the zero-maximum test, verbatim (test_pension_age_projection_invariants.py:165);
  • the mechanism of Core's anchor rules on a private clone;
  • an optional maxima threaded through both _projection_parameter functions into the index helpers, using the proposed replacement expressions at both index lines;
  • maxima computed before each own-base comparison and set to None in the changed-own-base branch;
  • the HB module importing the helper;
  • the myst.yml block, the evidence text and the axiom line.

Changed:

  1. Uprating chain. The proposal uprates a copy of the maximum directly by the September CPI index (uprating: index). Mine copies the maximum with its propagated uprating metadata and follows its real chain: gov.benefit_uprating_cpi, then gov.economic_assumptions.indices.september_cpi_uprating. The forecasts then read exactly what the maximum parameter becomes, including after a reform to its uprating index, and they honour any rounding or cadence metadata. The two agree for maximum reforms alone. With the proposal's helper swapped in, the new differential passed 68 of 68 drawn cases without an index reform and failed 11 of 12 with one.
  2. Helper signature. projected_maximum_values(parameters, relationship, base_year, final_year) reads the maximum from the tree and floors it at zero, so both builders call it identically.
  3. The fill guard above.
  4. HB index description. The generated HB index's description no longer quotes a fixed £ Savings Credit component when that component follows the maximum, because the figure would be wrong under a maximum reform.

Tests

All local runs were foreground, with at most two xdist workers. Hypothesis-heavy files ran under run_capped.py --rss-gb 2. Two runs at -n 2 hit that group cap, so I reran them single-process (-n 0) under the same cap; nothing ran uncapped.

Suite Result at 000f16be
test_pension_age_projection_invariants.py 13 passed (4 existing, 9 new nodes)
test_pension_credit_uprating.py 34 passed
test_housing_benefit_protected_allowance_uprating.py 13 passed
test_pension_credit_guarantee_reform_projections.py 51 passed (39 existing, 12 new), in four capped chunks
test_savings_credit_threshold_projection.py 14 passed
test_process_time_reforms.py 71 passed
Pension Credit YAML directory 320 passed
Housing Benefit YAML directory (finance/benefit/family/housing_benefit) 456 passed
gov/dwp/housing_benefit_*.yaml (3 files) 21 passed

New tests:

  • the review's zero-maximum oracle at 2026 and 2028 anchors (:165);
  • own-base precedence for the threshold, single and lone-parent allowances, at both anchors (:201, 6 nodes);
  • a differential against main's frozen builders (:407, Hypothesis). It covers any path of one to three maximum anchors from 2025, enacted phase-in paths and an optional reform to the maximum's uprating index, and requires equality with main apart from the threshold floor;
  • the combined property (:145) now draws maximum paths (:79–:115);
  • the public routes, in test_pension_credit_guarantee_reform_projections.py:521. It runs the reform dict, Scenario(parameter_changes), structural Reform.from_dict and a reprocessing simulation_modifier, each at 2026 and 2028 anchors. It checks the identity threshold = max(0, guarantee − maximum/phase-in) and allowance = guarantee + maximum in every year 2027–2040, a zero maximum and threshold equal to the guarantee from the anchor, unchanged couples, unchanged prior law before the anchor on the dated routes, and no Savings Credit paid once the maximum is zero (the baseline pays some);
  • the own-base index beside a maximum reform, through the reform dict and Reform.from_dict (:598).

Mutation checks:

  • Against the 9b0d40672 builders, all 9 new synthetic nodes fail, and 11 of the 12 public-route nodes do. The base-year modifier case already passed there, because that route has no separate unreformed tree.
  • Without the fill guard, the differential fails.
  • Without the own-base fallback, all 6 precedence cases fail.

Did not run locally:

  • the full Python suite (about 10k tests);
  • the other Pension Credit property files (test_september_cpi_uprating.py, test_triple_lock_statutory_inputs.py, test_council_tax_reduction_pension_credit_properties.py, test_savings_credit_only_receipt_properties.py);
  • any microsimulation or aggregate impact.

CI is the evidence for those.

CI at 000f16be (Code changes run 40481780315):

  • Lint, documentation, smoke imports (3.11–3.14), the release build, Changelog, Fix evidence and Docs changes pass, and so do Python shards 2–5.
  • The Docs changes build lists 52 pages, including programs/gov/dwp/pension-credit.ipynb. The extra page beyond my local 51 is main's new assumptions/isa-inputs-and-capital-flows.md, from the synthetic merge.
  • Test / YAML passed all 5,111 policy YAML cases. It then hit its 30-minute job limit while listing the Python tests (cancelled).
  • Python 1/5 reached 99% with no failures and then hit its 75-minute limit (cancelled).
  • So Test / Result fails, and the PR is not mergeable on CI yet. Both cancellations are time limits. Other PRs on current main hit the same: run 40424696088's YAML job was cancelled at 30 minutes and passed on attempt 3, and recent YAML jobs take 22–29 minutes and shards 63–74. My new tests add at most about 30 summed seconds to any shard.
  • Per notices 17 and 20, I didn't re-run; a re-run of the two cancelled jobs is the hub's call.

Aggregate impact passed (at 9b0d40672 it timed out):

  • base: current main 159a5ec99d; head: this branch merged with it; data: certified enhanced_frs_2024_25.h5 (policyengine-uk-data 1.58.1); years 2025-26 to 2031-32.
  • No household's net income changes in any year, and receipts, government balance, poverty and inequality don't move.
  • 0 checks broken; one VAT sign check fails on both sides.

Other checks:

  • ruff format --check and ruff check pass.
  • main's check_fix_pr.py (identical to the copy on this branch) passes against the new body: "Fix PR has valid reproducer evidence. Changed executable tests: 11."
  • Main has moved 167 commits since the merge base eb0b8087. None touches a file this PR changes, and git merge-tree against main 159a5ec9 is clean. Main pins policyengine-core 3.33.12, and the Core uprating module, metadata propagation and Parameter.clone sources are byte-identical in 3.33.0, 3.33.2 and 3.33.12.

Worked example: zero maximum

Synthetic tree, CI-pinned path [(0.039, 0.035)], maximum.SINGLE = 0 from April 2026, 2027-28 weekly amounts:

Amount Review's main figure 9b0d40672 000f16be
Standard minimum guarantee (single) £247.282 £247.282 £247.282
Maximum Savings Credit (single) £0 £0 £0
Savings Credit threshold (single) £247.282 £216.301 (216.300933) £247.282 (247.282000)
Higher HB, single and lone parent £247.282 £265.8706 (265.870640) £247.282 (247.282000)

The "before" column reproduces the review's figures for this head exactly, and the "after" column matches its main figures. The real model gives the same 2027 values through all four public routes. Unreformed, the 2027 figures are unchanged at £216.300933 and £265.870640.

Notes for the re-review

  • Precedence is the review's spec: a threshold or allowance whose own base-year value is reformed keeps its own gap rather than following a maximum reform.
  • Dated routes, own-base change. Through these routes, a 2026-only own-base change keeps that index unchanged. Once the change ends, the dated routes restore prior law under the reform's other changes (prior_law_projection), so the parameter then follows the maximum reform. This is observed and tested (:598).
  • Modifier route, bounded direct update. Core uprates only from the last explicit value, so the years before the anchor keep the earlier explicit maximum. The forecasts follow that maximum by identity, as main's post-uprating builders did.

🤖 Generated with Claude Code

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