Repository navigation
Add the universal credit limb of the Housing Benefit passport - #2133
Merged
Merged
Conversation
SI 2006/213 Sch 4 para 12, Sch 5 para 4 and Sch 6 para 5 (NI: SR 2006/405 Sch 5 para 12, Sch 6 para 4 and Sch 7 para 5) disregard the earnings, whole income and whole capital of a claimant on universal credit, as for income support, income-based JSA and income-related ESA. 'On universal credit' means entitled, whether in payment or not (reg 2(3B)), read as Universal Credit before the benefit cap (is_uc_entitled). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hand-computed YAML cases for the passport and its in-force dates, a Hypothesis property file (definition, passport to maximum, metamorphic, differential against the Income Support limb, monotonic, dates, order independence), and would_claim_uc: false on existing fixtures that test a family not on Universal Credit. Docs and changelog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…assport # Conflicts: # policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income.py # policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_assessable_capital.py
This was referenced Oct 4, 2026
- Pin would_claim_uc: false on 18 more YAML cases whose families were UC-entitled, so the passport, not the rule under test, produced the expected value (found by rebuilding every case with the limb on and off). A mutation removing the earnings-disregard cap now fails them again. - housing_benefit_eligible.yaml: its absolute_error_margin of 5 voided the boolean checks, and two cases expecting True computed False; pin the continuing-award route and drop the margin. - test_means_test_income_properties checks both claim states, and the CTR pensioner property's invariant 6 pins families off Universal Credit. - The order-independence property compares Housing Benefit, Universal Credit, the benefit cap reduction and household net income, with a capped sentinel family on both benefits. - Explicit weekly hours in the hand-computed cases; docstring on the age condition; changelog wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…passport Resolve the Sch 4 para 12 return in the earnings disregard: keep #2123's para 18 amount and read housing_benefit_on_passporting_benefit; drop its sentence that the universal credit limb is not modelled. Pin would_claim_uc: false on the 19 para 18 YAML cases whose families were UC-entitled (13 failed, 6 passed only because the passport gave the expected value) and in the para 18 property fixture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
is_uc_eligible tests has_attained_state_pension_credit_qualifying_age since #1907, not State Pension age. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
marked this pull request as ready for review
October 4, 2026 18:00
Collaborator
Author
|
Hand-off to the UK hub. Head Everything verified at this head:
Pending: CI Test at 1d478e2. Land on green with a merge commit and
🤖 Generated with Claude Code |
Collaborator
Author
|
Merged by the UK hub, which took this PR on handoff from local_f51300bf, under the PE-UK merge-on-gates rule.
|
Closed
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2132
axiom: TheAxiomFoundation/rulespec-uk#375 queued
Summary
The working-age Housing Benefit passport now covers a claimant on Universal Credit, as the law requires. #1908 and #2000 implemented it for Income Support, income-based JSA and income-related ESA only.
housing_benefit_applicable_income_disregard, Sch 4 para 12): all earnings.housing_benefit_applicable_income, Sch 5 para 4): nil.housing_benefit_assessable_capital, Sch 6 para 5): nil. Tariff income is then nil and the capital limit is met.All three read a new variable,
housing_benefit_on_passporting_benefit. With nil income Housing Benefit is the maximum: the eligible rent less non-dependant deductions.No Enhanced FRS output changes, because the model does not yet pay Housing Benefit to a family on Universal Credit. The change takes effect when #1910 (or any other HB-alongside-UC route) lands; #1910 is open and on hold (d837).
Law
Read verbatim from legislation.gov.uk
/data.xmlon 2026-10-04.Change
gov.dwp.housing_benefit.means_test.universal_credit_passport, with boolean childrengreat_britain(true from 2013-10-28) andnorthern_ireland(true from 2018-05-08). Model years are read on 30 April, so the limb applies from 2014-15 in GB and 2019-20 in NI.housing_benefit_on_passporting_benefit(BenUnit, YEAR, bool). It is true whenin_receipt_of_income_support_jsa_ib_or_esa_irholds, or whenis_uc_entitledholds and the limb is in force for the household's country; Great Britain's date applies everywhere except Northern Ireland. It is the counterpart of rulespec-uk#375's plannedhb_on_passporting_benefit.docs/book/programs/gov/dwp/housing-benefit.ipynband the three variables' docstrings.Why
is_uc_entitledand notuniversal_credituniversal_creditis paid after the benefit cap, andbenefit_cap_reductiondepends on Housing Benefit. Reading it here creates a cycle; a negative control raisedCycleErroronhousing_benefit_pre_benefit_cap.is_uc_entitledisuniversal_credit_pre_benefit_cap > 0. That is UC before the cap and deductions, defined for a family that claims (would_claim_uc). It depends on no Housing Benefit variable:test_calculation_order_does_not_change_resultsgets the same results from five calculation orders.housing_benefit_pension_age_regulations_applyalready reads it for reg 5. It also matches "whether it is in payment or not": an award that the cap or deductions reduce to nil still counts. The law has no cycle at all: Housing Benefit counts as nil in the UC cap (UC Regs reg 80(2A)), and the HB cap does not apply to a claimant receiving UC (HB Regs reg 75F(1)(g)).Modelling choices and limitations
mainno family is both HB-eligible and UC-entitled. Pension-age claimants are not UC-eligible, and the continuing-award route requires not claiming UC. So the limb changes HB intermediates only, for families that get no Housing Benefit. Before this change their intermediates showed a means test that the law would not apply.housing_benefit.pyanduniversal_credit.pyboth subtract the wholebenefit_cap_reduction. Once Abolish working-age Housing Benefit from 1 July 2026 (NI 1 October 2026) #1910 lets both be paid, a capped family loses the reduction twice.Tests
New YAML
housing_benefit_universal_credit_passport.yaml, 9 cases hand-computed from the regulations (2025-26 amounts):These cases supply
housing_benefit_eligible(and a nil UC housing element), standing in for a specified or temporary accommodation claim, whichmaincannot reach.New YAML
housing_benefit_on_passporting_benefit.yaml, 12 cases on the variable: each limb, the pre-cap reading, and the in-force dates (GB 2013 and 2014, Wales 2018, NI 2018 and 2019, and later).Mutation check: with the limb switched off in the parameter, 13 of the 21 new YAML cases fail and the 8 controls pass.
Updated fixtures: existing fixtures that leave
would_claim_ucat its defaultTruedescribe low-income working-age families that are UC-entitled, so they are now passported.Each of these 46 cases tests a family not on UC, so each now sets
would_claim_uc: false; expected values are unchanged. A mutation that removes the cap on the earnings disregard fails the pinned cases again. The detector flagged 25 more cases and I left them alone. Some supply the passported inputs directly (applicable income, pre-cap HB or entitlement). Some test an eligibility route that pays no HB. Others are deliberate UC cases. Scripts and outputs are in~/reviews/uk-hb-uc-passport-2026-10-04/out/vacuity/.Add the Housing Benefit earnings disregard for specified or temporary accommodation (Sch 4 para 18, from 5 October 2026) #2123's fixtures, after merging it (para 18 disregard): 19 more cases in its YAML file were pinned the same way; 13 failed and 6 passed only by coincidence. Its property fixture is pinned too, as the coordination comment on Add the Housing Benefit earnings disregard for specified or temporary accommodation (Sch 4 para 18, from 5 October 2026) #2123 proposed. In the conflicting
return, Add the Housing Benefit earnings disregard for specified or temporary accommodation (Sch 4 para 18, from 5 October 2026) #2123's para 18 amount is kept, and its sentence saying the UC limb is not modelled is dropped.Pre-existing defect in
housing_benefit_eligible.yaml:absolute_error_margin: 5voided its boolean checks. Two cases expectingTrueactually computedFalse, because the defaultwould_claim_uc: Trueblocks the continuing-award route. The cases now pin that route and the margin is gone, so all four hold exactly.Python tests: five files now pin or vary the claim state.
test_lifetime_isa_capital.py,test_carer_support_payment.pyandtest_housing_benefit_earnings_disregard_properties.pyfailed.test_council_tax_reduction_pensioner_earnings_disregard_properties.py, the HB statutory-sum invariant relied on £20,000–£40,000 earnings to keep families off UC. It now pins them off.test_means_test_income_properties.pychecks its invariant in both claim states, so the HB means test is still exercised.An instrumented run logged every Python test in which the UC limb fires. No other test has assertions the limb touches.
Invariants (property-tested in
test_housing_benefit_universal_credit_passport_properties.py)For generated populations of renting families, with optional non-dependants, across GB and NI. Each family is not on UC, on a supplied UC amount (nil, one penny or more), or on UC the model computes, with or without a legacy benefit. Every population also includes one family passported by UC alone.
is_uc_entitledequals the latter.Mutation check: with the limb off, all six property tests fail.
Commands run
All at head 0d5e146 (
mainb1dd418, with #2123, merged in).uv run --frozen ruff format --check .anduv run --frozen ruff check .: clean.uv run --frozen policyengine-core test policyengine_uk/tests/policy -c policyengine_uk: 2,323 passed. Before the fixture pins, the passport alone failed 28 cases, plus 13 in Add the Housing Benefit earnings disregard for specified or temporary accommodation (Sch 4 para 18, from 5 October 2026) #2123's file, all fromwould_claim_ucleft at its defaultTrue.uv run --frozen pytest policyengine_uk/tests/ -n 6 --dist worksteal: 2,948 passed, 34 skipped, 7 xfailed, 0 failed.mainb1dd418 against 0d5e146, and earliermain2105816 against 407acbd. Both show exactly £0 (below).make documentation. CI's documentation build passes on this branch. The notebook edit is a single markdown line.Enhanced FRS impact
Real runs on a copy of the Enhanced FRS 2024-25 (sha256
e433e532…), 2024-25 to 2030-31. Scripts, logs and outputs are in~/reviews/uk-hb-uc-passport-2026-10-04/impact/.mainb1dd418 vs this branch (0d5e146); alsomain2105816 vs 407acbd andmain6ae9c5e vs e38e0cdmain6ae9c5e vs the same + e38e0cd (local merges)in_specified_or_temporary_accommodationset for working-age benefit units reporting both HB and UC (a sensitivity, not data)Only HB intermediates move, for 3.6k–3.8k records each year. Every one of those benefit units is UC-entitled, not on a legacy benefit, and not HB-eligible in either run. An independent check recomputed the stacked aggregates from the saved arrays and confirmed the merge resolution of #1910 with
main.Coordination
housing_benefit_applicable_income: 0input in its HB-alongside-UC YAML case, and its working-age property test passes (38 of 38) on the merged tree.returnline of the disregard. The merge in 0d5e146 resolves it the way the coordination comment proposed:where(on_passporting_benefit, net_earnings, standard + accommodation + additional);Axiom
TheAxiomFoundation/rulespec-uk#375 already plans
hb_on_passporting_benefit, with "claimant is on universal credit" as a supplied Judgment for Sch 4 para 12 and Sch 5 para 4 / Sch 6 para 5. An extension comment does the rest:Encoding is blocked for two reasons:
🤖 Generated with Claude Code