From 0943de73ef1b487ddd32b4a6020805d498bfd2fc Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 30 Sep 2026 07:54:42 -0400 Subject: [PATCH 1/5] Abolish working-age Housing Benefit from 1 July 2026 (NI 1 October 2026) SI 2025/1148 art 7, inserted by SI 2026/409, brings WRA 2012 s.33(1)(d) into force on 1 July 2026 for every Housing Benefit award not already terminated. It saves claimants within SI 2014/1230 reg 6A(2)-(5) (specified or temporary accommodation, pension age, protected mixed-age couples), income-related ESA appointee cases and prisoners until release. Northern Ireland: SR 2025/176 art 7, inserted by SR 2026/115, from 1 October 2026. gov.dwp.housing_benefit.working_age_awards_payable holds 1/0 per jurisdiction (great_britain, northern_ireland). PE-UK samples gov.* parameters at 30 April of each fiscal year, which would have left all of 2026-27 unaffected, so the parameter is day-weighted with fiscal_year_blend: 2026 takes 86/365 in GB and 178/365 in NI, and 0 from 2027. housing_benefit_payable_share is 1 for any benefit unit with a member over State Pension age and the country share otherwise; the continuing-award route needs it above 0 and the pre-cap award is scaled by it. Also make housing_benefit_eligible.yaml's boolean checks non-vacuous (margin 0, would_claim_uc false), and update the docs, programs.yaml and the take-up description. Co-Authored-By: Claude Opus 5.5 --- ...olish-working-age-housing-benefit.fixed.md | 1 + .../programs/gov/dwp/housing-benefit.ipynb | 16 +- .../gov/dwp/housing_benefit/takeup.yaml | 3 +- .../working_age_awards_payable.yaml | 40 +++ policyengine_uk/programs.yaml | 2 +- .../housing_benefit_eligible.yaml | 16 +- ...housing_benefit_working_age_abolition.yaml | 309 ++++++++++++++++++ ..._housing_benefit_pension_age_properties.py | 5 +- ...enefit_working_age_abolition_properties.py | 222 +++++++++++++ .../housing_benefit_eligible.py | 18 +- .../housing_benefit_payable_share.py | 43 +++ .../housing_benefit_pre_benefit_cap.py | 5 +- 12 files changed, 665 insertions(+), 15 deletions(-) create mode 100644 changelog.d/abolish-working-age-housing-benefit.fixed.md create mode 100644 policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml create mode 100644 policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_working_age_abolition.yaml create mode 100644 policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py create mode 100644 policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py diff --git a/changelog.d/abolish-working-age-housing-benefit.fixed.md b/changelog.d/abolish-working-age-housing-benefit.fixed.md new file mode 100644 index 0000000000..d45d0ee235 --- /dev/null +++ b/changelog.d/abolish-working-age-housing-benefit.fixed.md @@ -0,0 +1 @@ +- Abolish working-age Housing Benefit awards outside specified and temporary accommodation from 1 July 2026 in Great Britain (SI 2025/1148 art. 7, inserted by SI 2026/409) and 1 October 2026 in Northern Ireland (SR 2025/176 art. 7, inserted by SR 2026/115). The new `gov.dwp.housing_benefit.working_age_awards_payable` parameter is day-weighted across each fiscal year, so 2026-27 pays a working-age continuing award for 86/365 of the year in Great Britain and 178/365 in Northern Ireland, and from 2027-28 there is none. Families with a member over State Pension age keep their awards (the pension-age and mixed-age savings). diff --git a/docs/book/programs/gov/dwp/housing-benefit.ipynb b/docs/book/programs/gov/dwp/housing-benefit.ipynb index 4f928b173d..cb21032538 100644 --- a/docs/book/programs/gov/dwp/housing-benefit.ipynb +++ b/docs/book/programs/gov/dwp/housing-benefit.ipynb @@ -7,7 +7,7 @@ "source": [ "# Housing Benefit\n", "\n", - "Housing Benefit is a means-tested DWP benefit that helps people on a low income with their rent. It is a legacy benefit that is being phased out: new working-age claimants are routed to the Universal Credit housing element instead, and a managed migration of the remaining HB caseload to UC is in progress. Housing Benefit continues to operate for two groups — pensioners and tenants of supported/temporary accommodation — and PolicyEngine models it alongside the UC housing element so reform analyses can move between the two systems cleanly.\n", + "Housing Benefit is a means-tested DWP benefit that helps people on a low income with their rent. It is a legacy benefit: new working-age claimants have been routed to the Universal Credit housing element for years, and the remaining working-age awards outside specified and temporary accommodation were abolished on 1 July 2026 in Great Britain and 1 October 2026 in Northern Ireland. Housing Benefit continues for pensioners, protected mixed-age couples and tenants of specified (supported) or temporary accommodation, and PolicyEngine models it alongside the UC housing element so reform analyses can move between the two systems cleanly.\n", "\n", "Parameters live in `policyengine_uk/parameters/gov/dwp/housing_benefit/` and the formulas in `policyengine_uk/variables/gov/dwp/housing_benefit/`." ] @@ -24,7 +24,19 @@ "- **Pension age**: every adult in the benunit has reached the qualifying age for State Pension Credit (State Pension age). New Housing Benefit claims are open to them ([SI 2014/1230 reg. 6A(4)](https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A)), and Universal Credit is not, so neither a previous claim nor `would_claim_uc` matters.\n", "- **Continuing award**: any other benunit was **already claiming** Housing Benefit (proxied by a positive `housing_benefit_reported`) and is **not claiming Universal Credit**. Working-age and mixed-age families cannot make new claims; they claim the Universal Credit housing element instead. For mixed-age couples the reported award stands in for the saving for couples who were entitled to Housing Benefit or Pension Credit on 14 May 2019 ([SI 2019/37 art. 4](https://www.legislation.gov.uk/uksi/2019/37/article/4)).\n", "\n", - "The pension-age test counts the same benunit members as Universal Credit's working-age test, and the continuing-award route requires not claiming Universal Credit, so no benunit receives both. Working-age awards outside specified or temporary accommodation ended on 1 July 2026 in Great Britain ([SI 2025/1148 art. 7](https://www.legislation.gov.uk/uksi/2025/1148/article/7)); PolicyEngine does not yet model that. Claims for specified or temporary accommodation, which remain open at any age, are not modelled.\n", + "The pension-age test counts the same benunit members as Universal Credit's working-age test, and the continuing-award route requires not claiming Universal Credit, so no benunit receives both.\n", + "\n", + "### Abolition of working-age awards\n", + "\n", + "Working-age Housing Benefit awards outside specified or temporary accommodation were abolished from **1 July 2026** in Great Britain ([SI 2025/1148 art. 7](https://www.legislation.gov.uk/uksi/2025/1148/article/7), inserted by [SI 2026/409](https://www.legislation.gov.uk/uksi/2026/409/made)) and **1 October 2026** in Northern Ireland ([SR 2025/176 art. 7](https://www.legislation.gov.uk/nisr/2025/176/article/7), inserted by SR 2026/115). Claimants within [SI 2014/1230 reg. 6A(2)-(5)](https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A) keep their awards: pension age, mixed-age couples protected since 14 May 2019, and specified or temporary accommodation. So do prisoners until their Universal Credit restriction ends and some income-related ESA claimants with an appointee.\n", + "\n", + "The dates live in `gov.dwp.housing_benefit.working_age_awards_payable` (`great_britain` and `northern_ireland`), with 1 while working-age awards are payable and 0 after. PolicyEngine UK evaluates a year's parameters as one value for the fiscal year starting 6 April. By default that value is the one in force on 30 April, which would have left the whole of 2026-27 unaffected. This parameter is instead day-weighted across the fiscal year (`fiscal_year_blend`), so 2026 (2026-27) takes the share of the year before the abolition: 86/365 in Great Britain (6 April to 30 June) and 178/365 in Northern Ireland (6 April to 30 September). From 2027 it is 0.\n", + "\n", + "`housing_benefit_payable_share` applies this per benunit. It is 1 for any benunit with a member over State Pension age, which stands in for the pension-age and protected mixed-age savings. In law a mixed-age couple is saved only if the older member claims under the pension-age rules and the couple has been entitled since 14 May 2019 ([SI 2019/37 art. 4](https://www.legislation.gov.uk/uksi/2019/37/article/4)), which the data cannot show. For other benunits it is the share for their country. A working-age continuing award stays eligible while the share is above 0, and `housing_benefit_pre_benefit_cap` pays the entitlement times the share. A working-age continuing award in 2026 is therefore paid for the part of the year before the abolition, and there is none from 2027.\n", + "\n", + "What happens to the families whose awards end is set by `would_claim_uc`, the Universal Credit take-up input. Families that claim Universal Credit already receive it instead of Housing Benefit in every year. Families that do not claim it lose Housing Benefit and receive no Universal Credit. In the Enhanced FRS that leaves too many former claimants with neither. DWP's Move to Universal Credit statistics show that 97% of households sent a migration notice while on Housing Benefit and another legacy benefit claimed Universal Credit (98% of those also on income-related ESA), and 75% of Housing Benefit-only households did. The dataset's take-up draw puts about 72% of working-age Housing Benefit reporters on Universal Credit. Transitional protection, which went only to households that claimed after a migration notice, is not modelled.\n", + "\n", + "The savings PolicyEngine cannot observe are not modelled: specified and temporary accommodation (open to new claims at any age, including alongside Universal Credit), prisoners, and the ESA appointee saving. The Enhanced FRS therefore has no working-age Housing Benefit from 2027. DWP's Spring 2026 forecast for Great Britain is £1.64bn of temporary-accommodation and £3.94bn of supported-accommodation Housing Benefit in 2026-27, across all ages.\n", "\n", "Take-up is handled by `would_claim_housing_benefit`, which is true for benunits that report Housing Benefit and, when no reported benefits are supplied (as in household calculations), for everyone. In the Enhanced FRS take-up therefore follows reported claims." ] diff --git a/policyengine_uk/parameters/gov/dwp/housing_benefit/takeup.yaml b/policyengine_uk/parameters/gov/dwp/housing_benefit/takeup.yaml index 26c847e978..c5d60a37c8 100644 --- a/policyengine_uk/parameters/gov/dwp/housing_benefit/takeup.yaml +++ b/policyengine_uk/parameters/gov/dwp/housing_benefit/takeup.yaml @@ -1,7 +1,8 @@ description: Share of eligible Housing Benefit recipients that participate. Not used by the model, which takes Housing Benefit take-up from reported claims in survey data and assumes full take-up in household calculations. Pension-age families can make new - claims; working-age families only continue existing awards. + claims; working-age families only continued existing awards, until those were + abolished on 1 July 2026 in Great Britain and 1 October 2026 in Northern Ireland. metadata: economy: false label: Housing benefit take-up rate diff --git a/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml b/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml new file mode 100644 index 0000000000..91c7ce942d --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml @@ -0,0 +1,40 @@ +description: Whether working-age Housing Benefit awards outside specified and temporary accommodation are still payable (1) or abolished (0). Housing Benefit was abolished for these awards from 1 July 2026 in Great Britain and 1 October 2026 in Northern Ireland; awards of claimants over the qualifying age for State Pension Credit, protected mixed-age couples and claimants in specified or temporary accommodation are saved. Each fiscal year takes the day-weighted average, which is the share of that year for which such awards stayed payable. +great_britain: + description: Whether working-age Housing Benefit awards outside specified and temporary accommodation are still payable in Great Britain. Section 33(1)(d) of the Welfare Reform Act 2012 came into force for them on 1 July 2026. + values: + 1988-04-01: 1 + 2026-07-01: 0 + metadata: + label: Working-age Housing Benefit awards payable, Great Britain + unit: /1 + period: year + fiscal_year_blend: true + reference: + - title: SI 2025/1148 art. 7 (inserted by SI 2026/409 art. 2(6)) + href: https://www.legislation.gov.uk/uksi/2025/1148/article/7 + - title: SI 2026/409, The Welfare Reform Act 2012 (Commencement No. 35) (Abolition of Benefits) (Amendment) Order 2026 + href: https://www.legislation.gov.uk/uksi/2026/409/made + - title: Welfare Reform Act 2012 s. 33(1)(d) + href: https://www.legislation.gov.uk/ukpga/2012/5/section/33 + - title: DWP HB circular A6/2026, closure of working age Housing Benefit + href: https://www.gov.uk/government/publications/housing-benefit-adjudication-circulars-2026/a62026-closure-of-working-age-housing-benefit +northern_ireland: + description: Whether working-age Housing Benefit awards outside specified and temporary accommodation are still payable in Northern Ireland. Article 39(1)(d) of the Welfare Reform (Northern Ireland) Order 2015 came into operation for them on 1 October 2026. + values: + 1988-04-01: 1 + 2026-10-01: 0 + metadata: + label: Working-age Housing Benefit awards payable, Northern Ireland + unit: /1 + period: year + fiscal_year_blend: true + reference: + - title: SR 2025/176 art. 7 (inserted by SR 2026/115) + href: https://www.legislation.gov.uk/nisr/2025/176/article/7 + - title: SR 2026/115 + href: https://www.legislation.gov.uk/nisr/2026/115/made + - title: Welfare Reform (Northern Ireland) Order 2015 art. 39(1)(d) + href: https://www.legislation.gov.uk/nisi/2015/2006/article/39 +metadata: + label: Working-age Housing Benefit awards payable + economy: false diff --git a/policyengine_uk/programs.yaml b/policyengine_uk/programs.yaml index f83e4acdd9..bb4a00ee85 100644 --- a/policyengine_uk/programs.yaml +++ b/policyengine_uk/programs.yaml @@ -227,7 +227,7 @@ programs: variable: housing_benefit parameter_prefix: gov.dwp.housing_benefit verified_start_year: 2022 - notes: Legacy benefit being replaced by Universal Credit + notes: Legacy benefit replaced by Universal Credit. Working-age awards were abolished on 1 July 2026 (GB) and 1 October 2026 (NI); pension-age and protected mixed-age awards continue. Specified and temporary accommodation awards, which continue at any age, are not modelled. - id: income_support name: Income Support diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_eligible.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_eligible.yaml index b00f14f60d..2c7a4d8488 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_eligible.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_eligible.yaml @@ -1,39 +1,47 @@ +# Continuing-award route for a working-age family (age defaults to 40) in +# 2021, before working-age awards were abolished: a reported award, no +# Universal Credit claim, and social housing or the Local Housing Allowance. + - name: Social housing eligible period: 2021 - absolute_error_margin: 5 + absolute_error_margin: 0 input: in_social_housing: true housing_benefit_reported: 1 + would_claim_uc: false LHA_eligible: false output: housing_benefit_eligible: true - name: LHA eligible period: 2021 - absolute_error_margin: 5 + absolute_error_margin: 0 input: in_social_housing: false housing_benefit_reported: 1 + would_claim_uc: false LHA_eligible: true output: housing_benefit_eligible: true - name: Not already enrolled period: 2021 - absolute_error_margin: 5 + absolute_error_margin: 0 input: in_social_housing: false housing_benefit_reported: 0 + would_claim_uc: false LHA_eligible: true output: housing_benefit_eligible: false - name: Not eligible period: 2021 - absolute_error_margin: 5 + absolute_error_margin: 0 input: in_social_housing: false housing_benefit_reported: 1 + would_claim_uc: false LHA_eligible: false output: housing_benefit_eligible: false diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_working_age_abolition.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_working_age_abolition.yaml new file mode 100644 index 0000000000..d6790c9dd5 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_working_age_abolition.yaml @@ -0,0 +1,309 @@ +# Abolition of working-age Housing Benefit outside specified and temporary +# accommodation: 1 July 2026 in Great Britain (SI 2025/1148 art. 7, inserted +# by SI 2026/409) and 1 October 2026 in Northern Ireland (SR 2025/176 art. 7, +# inserted by SR 2026/115). Pension-age claimants and protected mixed-age +# couples keep their awards (art. 7(4)(a), SI 2014/1230 reg 6A(4)-(5)). +# +# Each fiscal year runs 6 April to 5 April (365 days in 2026-27). A GB award +# is payable for the 86 days from 6 April to 30 June 2026 and an NI award for +# the 178 days from 6 April to 30 September 2026, so the 2026 amount is the +# annual entitlement times 86/365 or 178/365. Every case below fixes +# housing_benefit_applicable_income at 0, so entitlement equals the £5,200 +# rent (£100 a week). + +- name: GB working-age continuing award is paid in full before the abolition year + period: 2025 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit: 5_200 + +- name: GB working-age continuing award is paid until 30 June 2026 (86 of 365 days) + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + # 86 / 365 + housing_benefit_payable_share: 0.235616 + housing_benefit_eligible: true + housing_benefit_entitlement: 5_200 + # 5,200 * 86 / 365 + housing_benefit: 1_225.21 + +- name: GB working-age continuing award has ended by 2027 + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 0 + housing_benefit_eligible: false + housing_benefit: 0 + universal_credit: 0 + +- name: Scotland follows the Great Britain date + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_HA + region: SCOTLAND + output: + housing_benefit_payable_share: 0.235616 + housing_benefit: 1_225.21 + +- name: Wales follows the Great Britain date + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: WALES + output: + housing_benefit_payable_share: 0.235616 + housing_benefit: 1_225.21 + +- name: NI working-age continuing award is paid until 30 September 2026 (178 of 365 days) + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + # 178 / 365 + housing_benefit_payable_share: 0.487671 + housing_benefit_eligible: true + # 5,200 * 178 / 365 + housing_benefit: 2_535.89 + +- name: NI working-age continuing award has ended by 2027 + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + housing_benefit_payable_share: 0 + housing_benefit_eligible: false + housing_benefit: 0 + +- name: Working-age couple with a continuing award loses it from 2027 + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 45 + housing_benefit_reported: 1 + partner: + age: 43 + benunits: + benunit: + members: [claimant, partner] + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant, partner] + rent: 5_200 + tenure_type: RENT_FROM_HA + region: NORTH_WEST + output: + housing_benefit_payable_share: 0 + housing_benefit_eligible: false + housing_benefit: 0 + +- name: Protected mixed-age couple keeps its award after the abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + older: + age: 70 + housing_benefit_reported: 1 + younger: + age: 60 + benunits: + benunit: + members: [older, younger] + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [older, younger] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit: 5_200 + +- name: Pensioner with an 18 year old dependant keeps a continuing award after the abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 70 + is_parent: true + housing_benefit_reported: 1 + dependant: + age: 18 + current_education: UPPER_SECONDARY + benunits: + benunit: + members: [claimant, dependant] + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant, dependant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit: 5_200 + +- name: Pension-age new claim is unaffected by the abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 75 + benunits: + benunit: + members: [claimant] + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit: 5_200 + +- name: Working-age family claiming Universal Credit gets no Housing Benefit in any year + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + would_claim_uc: true + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_eligible: false + housing_benefit: 0 diff --git a/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py b/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py index 37672b3c3d..92c6ffaaca 100644 --- a/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py +++ b/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py @@ -4,7 +4,10 @@ partner, has reached the qualifying age for State Pension Credit (SI 2014/1230 reg 6A(4)); Universal Credit is not available to them (Welfare Reform Act 2012 s.4(1)(b)). Other families keep Housing Benefit only while they continue an -existing award and do not claim Universal Credit. +existing award and do not claim Universal Credit. YEAR is 2026-27, when +working-age awards are still payable for part of the year, so eligibility +follows the same rule; test_housing_benefit_working_age_abolition_properties.py +covers the abolition itself. Invariants, for any generated population of families: diff --git a/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py b/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py new file mode 100644 index 0000000000..d6f6794859 --- /dev/null +++ b/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py @@ -0,0 +1,222 @@ +"""Property-based tests for the abolition of working-age Housing Benefit. + +Working-age Housing Benefit awards outside specified and temporary +accommodation were abolished from 1 July 2026 in Great Britain (SI 2025/1148 +art. 7, inserted by SI 2026/409) and 1 October 2026 in Northern Ireland +(SR 2025/176 art. 7, inserted by SR 2026/115). Claimants over the qualifying +age for State Pension Credit and protected mixed-age couples keep their awards +(art. 7(4)(a) and SI 2014/1230 reg 6A(4)-(5)). + +Invariants, for any generated population of families: + +1. Parameter: each fiscal year's working_age_awards_payable value equals the + share of 6 April to 5 April falling before the statutory date, counted + independently here from the two dates (1 before 2026-27, 0 after). +2. Bounds: 0 <= housing_benefit_payable_share <= 1, and it is 1 for every + family with a member over State Pension age. +3. Abolition: from 2027 no family without a member over State Pension age + receives Housing Benefit, in any country or claim mode. +4. Differential against the pre-abolition rule: with working-age awards kept + payable by a reform, Housing Benefit is what the continuing-award rule paid + before. Under current law, Housing Benefit before the benefit cap is + exactly that amount times the country's payable share for a family with no + member over State Pension age, and exactly the same amount for every other + family (the cap applies afterwards, so the final amount is compared by 5). +5. Monotonic: the abolition never raises anyone's Housing Benefit and never + changes Universal Credit (families on the continuing-award route do not + claim it). +6. Structural (unchanged): no family gets both Housing Benefit and Universal + Credit, and Housing Benefit stays within 0 and the rent. +""" + +import datetime + +import numpy as np +import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_uk import CountryTaxBenefitSystem, Simulation + +PROPERTY_SETTINGS = settings( + max_examples=10, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +ABOLITION_DATES = { + "great_britain": datetime.date(2026, 7, 1), + "northern_ireland": datetime.date(2026, 10, 1), +} +PARAMETER = "gov.dwp.housing_benefit.working_age_awards_payable" +KEEP_WORKING_AGE_AWARDS = { + f"{PARAMETER}.{jurisdiction}": {"2015-01-01.2040-12-31": 1} + for jurisdiction in ABOLITION_DATES +} +REGIONS = ["LONDON", "NORTH_EAST", "SCOTLAND", "WALES", "NORTHERN_IRELAND"] +TENURES = ["RENT_FROM_COUNCIL", "RENT_FROM_HA", "RENT_PRIVATELY", "OWNED_OUTRIGHT"] +# State Pension age is 66 until the rise to 67 phases in from 2026-27; 68 and +# over is unambiguously pension age and 60 and under unambiguously working age. +PENSION_AGE = st.integers(68, 100) +WORKING_AGE = st.integers(18, 60) +SHAPES = { + "single_pension": [("claimant", PENSION_AGE)], + "mixed_age": [("claimant", PENSION_AGE), ("partner", WORKING_AGE)], + "single_working": [("claimant", WORKING_AGE)], + "couple_working": [("claimant", WORKING_AGE), ("partner", WORKING_AGE)], + "pension_with_dependant": [ + ("claimant", PENSION_AGE), + ("dependant", st.integers(18, 19)), + ], +} +money = st.floats(0, 20_000, allow_nan=False, allow_infinity=False) + + +def fiscal_year_share_before(date, year): + """Share of the fiscal year starting 6 April `year` falling before `date`.""" + start = datetime.date(year, 4, 6) + end = datetime.date(year + 1, 4, 6) + before = (min(max(date, start), end) - start).days + return before / (end - start).days + + +@st.composite +def families(draw): + shape = draw(st.sampled_from(sorted(SHAPES))) + return dict( + shape=shape, + members=[(role, draw(age)) for role, age in SHAPES[shape]], + region=draw(st.sampled_from(REGIONS)), + tenure=draw(st.sampled_from(TENURES)), + rent=draw(money), + earnings=draw(st.one_of(st.just(0.0), money)), + savings=draw(st.one_of(st.just(0.0), st.floats(0, 20_000))), + would_claim_uc=draw(st.booleans()), + hb_reported=draw(st.sampled_from([0.0, 1.0])), + ) + + +def situation(units, year, claims_all): + people, benunits, households = {}, {}, {} + for i, unit in enumerate(units): + names = [] + for j, (role, age) in enumerate(unit["members"]): + name = f"p{i}_{j}" + person = {"age": {year: age}} + if role == "claimant": + person["employment_income"] = {year: unit["earnings"]} + person["is_parent"] = {year: unit["shape"] == "pension_with_dependant"} + if unit["hb_reported"]: + person["housing_benefit_reported"] = {year: unit["hb_reported"]} + if role == "dependant": + person["current_education"] = {year: "UPPER_SECONDARY"} + people[name] = person + names.append(name) + benunits[f"b{i}"] = { + "members": names, + "would_claim_uc": {year: unit["would_claim_uc"]}, + # claims_all_entitled_benefits sums reported benefits across the + # whole simulation, so set it per family. + "claims_all_entitled_benefits": {year: claims_all}, + } + households[f"h{i}"] = { + "members": names, + "region": {year: unit["region"]}, + "rent": {year: unit["rent"]}, + "tenure_type": {year: unit["tenure"]}, + "savings": {year: unit["savings"]}, + } + return {"people": people, "benunits": benunits, "households": households} + + +VARIABLES = [ + "housing_benefit", + "housing_benefit_pre_benefit_cap", + "housing_benefit_eligible", + "housing_benefit_payable_share", + "universal_credit", + "benunit_rent", +] + + +def calculate(units, year, claims_all, reform=None): + sim = Simulation(situation=situation(units, year, claims_all), reform=reform) + return {v: np.asarray(sim.calculate(v, year)) for v in VARIABLES} + + +def any_over_pension_age(unit): + return any(age >= 68 for _, age in unit["members"]) + + +def jurisdiction(unit): + return ( + "northern_ireland" if unit["region"] == "NORTHERN_IRELAND" else "great_britain" + ) + + +def check_structural(values): + hb = values["housing_benefit"] + assert np.all(hb >= 0) + assert np.all(hb <= values["benunit_rent"] + 0.01) + assert not np.any((hb > 0) & (values["universal_credit"] > 0)) + + +@pytest.mark.parametrize("year", range(2015, 2041)) +def test_payable_share_is_the_day_share_before_the_statutory_date(year): + parameter = ( + CountryTaxBenefitSystem() + .parameters(str(year)) + .gov.dwp.housing_benefit.working_age_awards_payable + ) + for name, date in ABOLITION_DATES.items(): + expected = fiscal_year_share_before(date, year) + assert getattr(parameter, name) == pytest.approx(expected, abs=1e-12) + + +def test_day_shares_match_the_hand_counted_days(): + # 6 April to 30 June 2026 is 86 days; 6 April to 30 September is 178. + assert fiscal_year_share_before(ABOLITION_DATES["great_britain"], 2026) == 86 / 365 + assert ( + fiscal_year_share_before(ABOLITION_DATES["northern_ireland"], 2026) == 178 / 365 + ) + + +@PROPERTY_SETTINGS +@given( + st.lists(families(), min_size=1, max_size=25), + st.sampled_from([2025, 2026, 2027, 2030]), + st.booleans(), +) +def test_abolition_matches_the_pre_abolition_rule_scaled_by_the_payable_share( + units, year, claims_all +): + current = calculate(units, year, claims_all) + kept = calculate(units, year, claims_all, reform=KEEP_WORKING_AGE_AWARDS) + check_structural(current) + check_structural(kept) + share = current["housing_benefit_payable_share"] + assert np.all((share >= 0) & (share <= 1)) + assert np.all(kept["housing_benefit_payable_share"] == 1) + pre_cap = "housing_benefit_pre_benefit_cap" + for i, unit in enumerate(units): + if any_over_pension_age(unit): + assert share[i] == 1, unit + expected_share = 1 + assert current["housing_benefit"][i] == pytest.approx( + kept["housing_benefit"][i], abs=0.01 + ), unit + else: + expected_share = fiscal_year_share_before( + ABOLITION_DATES[jurisdiction(unit)], year + ) + assert share[i] == pytest.approx(expected_share, abs=1e-9), unit + if year >= 2027: + assert current["housing_benefit"][i] == 0, unit + assert not current["housing_benefit_eligible"][i], unit + assert current[pre_cap][i] == pytest.approx( + kept[pre_cap][i] * expected_share, abs=0.01 + ), unit + assert current["housing_benefit"][i] <= kept["housing_benefit"][i] + 0.01 + assert current["universal_credit"][i] == pytest.approx( + kept["universal_credit"][i], abs=0.01 + ), unit diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_eligible.py b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_eligible.py index 9cab415763..60842f59cd 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_eligible.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_eligible.py @@ -11,14 +11,18 @@ class housing_benefit_eligible(Variable): "can make a new claim; Universal Credit is not available to it. Any " "other family keeps Housing Benefit only while it continues an " "existing award (reported Housing Benefit) and does not claim " - "Universal Credit. Claims for specified or temporary accommodation " - "are not modelled." + "Universal Credit. Working-age awards ended on 1 July 2026 in Great " + "Britain and 1 October 2026 in Northern Ireland, so from then only " + "families with a member over State Pension age continue one. Claims " + "for specified or temporary accommodation are not modelled." ) definition_period = YEAR reference = ( "https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A", "https://www.legislation.gov.uk/uksi/2014/1230/regulation/8", "https://www.legislation.gov.uk/uksi/2019/37/article/4", + "https://www.legislation.gov.uk/uksi/2025/1148/article/7", + "https://www.legislation.gov.uk/nisr/2025/176/article/7", "https://www.legislation.gov.uk/ukpga/2012/5/section/4", "https://www.legislation.gov.uk/nisr/2016/226/regulation/4A", ) @@ -42,11 +46,15 @@ def formula(benunit, period, parameters): # claim it (reg 8(2A)), so this route also rules out receiving # Universal Credit. For mixed-age couples the reported award stands # in for the SI 2019/37 art. 4 saving (reg 6A(5)). Working-age awards - # outside specified or temporary accommodation ended on 1 July 2026 - # in Great Britain (SI 2025/1148 art. 7); that is not modelled. + # outside specified or temporary accommodation were abolished from + # 1 July 2026 in Great Britain (SI 2025/1148 art. 7) and 1 October + # 2026 in Northern Ireland (SR 2025/176 art. 7); a family with no + # member over State Pension age continues one only for the part of + # the year before that date (housing_benefit_payable_share). already_claiming = add(benunit, period, ["housing_benefit_reported"]) > 0 claiming_uc = benunit("would_claim_uc", period) - continuing_award = already_claiming & ~claiming_uc + still_payable = benunit("housing_benefit_payable_share", period) > 0 + continuing_award = already_claiming & ~claiming_uc & still_payable social = benunit.any(person("in_social_housing", period)) lha_eligible = benunit("LHA_eligible", period) any_over_SP_age = benunit.any(sp_age) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py new file mode 100644 index 0000000000..6fb3deeffe --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py @@ -0,0 +1,43 @@ +from policyengine_uk.model_api import * + + +class housing_benefit_payable_share(Variable): + value_type = float + entity = BenUnit + label = "Share of the year Housing Benefit is payable" + documentation = ( + "Share of the year for which this family's Housing Benefit award stays " + "payable. Working-age awards outside specified and temporary " + "accommodation were abolished from 1 July 2026 in Great Britain and " + "1 October 2026 in Northern Ireland, so for a family with no member " + "over State Pension age this is the share of the year before that " + "date. Families with a member over State Pension age keep their award " + "(the pension-age and mixed-age savings), so theirs is 1." + ) + definition_period = YEAR + unit = "/1" + reference = ( + "https://www.legislation.gov.uk/uksi/2025/1148/article/7", + "https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A", + "https://www.legislation.gov.uk/nisr/2025/176/article/7", + ) + + def formula(benunit, period, parameters): + payable = parameters(period).gov.dwp.housing_benefit.working_age_awards_payable + country = benunit.household("country", period) + working_age_share = where( + country == country.possible_values.NORTHERN_IRELAND, + payable.northern_ireland, + payable.great_britain, + ) + # SI 2025/1148 art. 7(4)(a) saves claimants within SI 2014/1230 + # reg 6A(2)-(5). Reg 6A(4) (pension age) and 6A(5) (mixed-age couples + # saved by SI 2019/37 art. 4) are the savings the model can see: any + # member over State Pension age keeps the award running. In law a + # mixed-age couple is saved only if the older member claims under the + # pension-age regulations and the couple has been entitled since + # 14 May 2019, which the data cannot show. Specified and temporary + # accommodation (reg 6A(2)), prisoners (art. 7(2)) and the income- + # related ESA appointee saving (art. 7(4)(b)) have no inputs. + any_over_SP_age = benunit.any(benunit.members("is_SP_age", period)) + return where(any_over_SP_age, 1, working_age_share) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_pre_benefit_cap.py b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_pre_benefit_cap.py index 5d54f3e83d..073c18810c 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_pre_benefit_cap.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_pre_benefit_cap.py @@ -11,8 +11,11 @@ class housing_benefit_pre_benefit_cap(Variable): def formula(benunit, period, parameters): eligible = benunit("housing_benefit_eligible", period) would_claim = benunit("would_claim_housing_benefit", period) + # A working-age award abolished part-way through the year is paid + # for the part before the abolition date. + payable_share = benunit("housing_benefit_payable_share", period) return where( eligible & would_claim, - benunit("housing_benefit_entitlement", period), + benunit("housing_benefit_entitlement", period) * payable_share, 0, ) From 1a664216a72d2fea5974efb69bb9f9cc33288ba4 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 30 Sep 2026 08:30:19 -0400 Subject: [PATCH 2/5] Test working-age continuing awards directly for each year and jurisdiction A mutation check showed the mixed-population differential missed two breakages (no proration; NI given the GB date), because random populations rarely hold a working-age continuing award in 2026. The new property test builds only those families, for 2025-2027 in GB and NI, and fails on both mutations. Also drop dataset-specific take-up shares from the docs page. Co-Authored-By: Claude Opus 5.5 --- .../programs/gov/dwp/housing-benefit.ipynb | 2 +- ...enefit_working_age_abolition_properties.py | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/docs/book/programs/gov/dwp/housing-benefit.ipynb b/docs/book/programs/gov/dwp/housing-benefit.ipynb index cb21032538..ef6e4d149e 100644 --- a/docs/book/programs/gov/dwp/housing-benefit.ipynb +++ b/docs/book/programs/gov/dwp/housing-benefit.ipynb @@ -34,7 +34,7 @@ "\n", "`housing_benefit_payable_share` applies this per benunit. It is 1 for any benunit with a member over State Pension age, which stands in for the pension-age and protected mixed-age savings. In law a mixed-age couple is saved only if the older member claims under the pension-age rules and the couple has been entitled since 14 May 2019 ([SI 2019/37 art. 4](https://www.legislation.gov.uk/uksi/2019/37/article/4)), which the data cannot show. For other benunits it is the share for their country. A working-age continuing award stays eligible while the share is above 0, and `housing_benefit_pre_benefit_cap` pays the entitlement times the share. A working-age continuing award in 2026 is therefore paid for the part of the year before the abolition, and there is none from 2027.\n", "\n", - "What happens to the families whose awards end is set by `would_claim_uc`, the Universal Credit take-up input. Families that claim Universal Credit already receive it instead of Housing Benefit in every year. Families that do not claim it lose Housing Benefit and receive no Universal Credit. In the Enhanced FRS that leaves too many former claimants with neither. DWP's Move to Universal Credit statistics show that 97% of households sent a migration notice while on Housing Benefit and another legacy benefit claimed Universal Credit (98% of those also on income-related ESA), and 75% of Housing Benefit-only households did. The dataset's take-up draw puts about 72% of working-age Housing Benefit reporters on Universal Credit. Transitional protection, which went only to households that claimed after a migration notice, is not modelled.\n", + "What happens to the families whose awards end is set by `would_claim_uc`, the Universal Credit take-up input. Families that claim Universal Credit already receive it instead of Housing Benefit in every year. Families that do not claim it lose Housing Benefit and receive no Universal Credit. The Enhanced FRS take-up draw does not look at legacy receipt, so it leaves too many former claimants with neither. DWP's Move to Universal Credit statistics show that 97% of households sent a migration notice while on Housing Benefit and another legacy benefit claimed Universal Credit (98% of those also on income-related ESA), and 75% of Housing Benefit-only households did. Transitional protection, which went only to households that claimed after a migration notice, is not modelled.\n", "\n", "The savings PolicyEngine cannot observe are not modelled: specified and temporary accommodation (open to new claims at any age, including alongside Universal Credit), prisoners, and the ESA appointee saving. The Enhanced FRS therefore has no working-age Housing Benefit from 2027. DWP's Spring 2026 forecast for Great Britain is £1.64bn of temporary-accommodation and £3.94bn of supported-accommodation Housing Benefit in 2026-27, across all ages.\n", "\n", diff --git a/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py b/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py index d6f6794859..4337bef3d4 100644 --- a/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py +++ b/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py @@ -22,6 +22,8 @@ exactly that amount times the country's payable share for a family with no member over State Pension age, and exactly the same amount for every other family (the cap applies afterwards, so the final amount is compared by 5). + Mixed populations rarely hold a 2026 working-age continuing award, so a + targeted test builds only those, for each year and jurisdiction. 5. Monotonic: the abolition never raises anyone's Housing Benefit and never changes Universal Credit (families on the continuing-award route do not claim it). @@ -220,3 +222,46 @@ def test_abolition_matches_the_pre_abolition_rule_scaled_by_the_payable_share( assert current["universal_credit"][i] == pytest.approx( kept["universal_credit"][i], abs=0.01 ), unit + + +@st.composite +def working_age_continuing_awards(draw, region): + """A working-age family continuing an award: reported HB, no UC claim.""" + shape = draw(st.sampled_from(["single_working", "couple_working"])) + return dict( + shape=shape, + members=[(role, draw(age)) for role, age in SHAPES[shape]], + region=region, + tenure=draw(st.sampled_from(TENURES[:3])), + rent=draw(st.floats(1_000, 20_000, allow_nan=False, allow_infinity=False)), + earnings=draw(st.one_of(st.just(0.0), money)), + savings=draw(st.one_of(st.just(0.0), st.floats(0, 15_000))), + would_claim_uc=False, + hb_reported=1.0, + ) + + +@pytest.mark.parametrize("year", [2025, 2026, 2027]) +@pytest.mark.parametrize("region", ["LONDON", "NORTHERN_IRELAND"]) +@settings(PROPERTY_SETTINGS, max_examples=5) +@given(data=st.data()) +def test_working_age_continuing_award_is_paid_for_the_payable_share(year, region, data): + # Every family here continues an award, and the first has no earnings, so + # at least one has a positive entitlement in every example. + units = data.draw( + st.lists(working_age_continuing_awards(region), min_size=1, max_size=8) + ) + units[0]["earnings"] = 0.0 + current = calculate(units, year, claims_all=False) + kept = calculate(units, year, claims_all=False, reform=KEEP_WORKING_AGE_AWARDS) + pre_cap = "housing_benefit_pre_benefit_cap" + assert kept[pre_cap][0] > 0, units[0] + expected_share = fiscal_year_share_before( + ABOLITION_DATES[jurisdiction(units[0])], year + ) + assert np.allclose(current["housing_benefit_payable_share"], expected_share) + assert np.allclose(current[pre_cap], kept[pre_cap] * expected_share, atol=0.01) + assert np.all( + current["housing_benefit_eligible"] + == (kept["housing_benefit_eligible"] & (expected_share > 0)) + ) From cab0828970898f44b48f2aa767aae1d7c8166074 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 30 Sep 2026 08:43:03 -0400 Subject: [PATCH 3/5] Mark Housing Benefit partial and note the unknown-country fallback Review follow-up: specified and temporary accommodation HB is not modelled, so programs.yaml lists Housing Benefit as partial. Comment that Great Britain's date applies to an unknown country, and use Stat-Xplore's own "Employment and Support Allowance" label in the docs. Co-Authored-By: Claude Opus 5.5 --- docs/book/programs/gov/dwp/housing-benefit.ipynb | 2 +- policyengine_uk/programs.yaml | 2 +- .../gov/dwp/housing_benefit/housing_benefit_payable_share.py | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/book/programs/gov/dwp/housing-benefit.ipynb b/docs/book/programs/gov/dwp/housing-benefit.ipynb index ef6e4d149e..f9c086e7d8 100644 --- a/docs/book/programs/gov/dwp/housing-benefit.ipynb +++ b/docs/book/programs/gov/dwp/housing-benefit.ipynb @@ -34,7 +34,7 @@ "\n", "`housing_benefit_payable_share` applies this per benunit. It is 1 for any benunit with a member over State Pension age, which stands in for the pension-age and protected mixed-age savings. In law a mixed-age couple is saved only if the older member claims under the pension-age rules and the couple has been entitled since 14 May 2019 ([SI 2019/37 art. 4](https://www.legislation.gov.uk/uksi/2019/37/article/4)), which the data cannot show. For other benunits it is the share for their country. A working-age continuing award stays eligible while the share is above 0, and `housing_benefit_pre_benefit_cap` pays the entitlement times the share. A working-age continuing award in 2026 is therefore paid for the part of the year before the abolition, and there is none from 2027.\n", "\n", - "What happens to the families whose awards end is set by `would_claim_uc`, the Universal Credit take-up input. Families that claim Universal Credit already receive it instead of Housing Benefit in every year. Families that do not claim it lose Housing Benefit and receive no Universal Credit. The Enhanced FRS take-up draw does not look at legacy receipt, so it leaves too many former claimants with neither. DWP's Move to Universal Credit statistics show that 97% of households sent a migration notice while on Housing Benefit and another legacy benefit claimed Universal Credit (98% of those also on income-related ESA), and 75% of Housing Benefit-only households did. Transitional protection, which went only to households that claimed after a migration notice, is not modelled.\n", + "What happens to the families whose awards end is set by `would_claim_uc`, the Universal Credit take-up input. Families that claim Universal Credit already receive it instead of Housing Benefit in every year. Families that do not claim it lose Housing Benefit and receive no Universal Credit. The Enhanced FRS take-up draw does not look at legacy receipt, so it leaves too many former claimants with neither. DWP's Move to Universal Credit statistics show that 97% of households sent a migration notice while on Housing Benefit and another legacy benefit claimed Universal Credit (98% of those also on Employment and Support Allowance), and 75% of Housing Benefit-only households did. Transitional protection, which went only to households that claimed after a migration notice, is not modelled.\n", "\n", "The savings PolicyEngine cannot observe are not modelled: specified and temporary accommodation (open to new claims at any age, including alongside Universal Credit), prisoners, and the ESA appointee saving. The Enhanced FRS therefore has no working-age Housing Benefit from 2027. DWP's Spring 2026 forecast for Great Britain is £1.64bn of temporary-accommodation and £3.94bn of supported-accommodation Housing Benefit in 2026-27, across all ages.\n", "\n", diff --git a/policyengine_uk/programs.yaml b/policyengine_uk/programs.yaml index bb4a00ee85..411da55735 100644 --- a/policyengine_uk/programs.yaml +++ b/policyengine_uk/programs.yaml @@ -222,7 +222,7 @@ programs: full_name: Housing Benefit category: Benefits agency: DWP - status: complete + status: partial coverage: UK variable: housing_benefit parameter_prefix: gov.dwp.housing_benefit diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py index 6fb3deeffe..1da4a97ff1 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py @@ -25,6 +25,8 @@ class housing_benefit_payable_share(Variable): def formula(benunit, period, parameters): payable = parameters(period).gov.dwp.housing_benefit.working_age_awards_payable country = benunit.household("country", period) + # Great Britain's date applies everywhere but Northern Ireland, + # including an unknown country. working_age_share = where( country == country.possible_values.NORTHERN_IRELAND, payable.northern_ireland, From bd9272a88ee1198eacd2a2da9cb5abf30e4be01c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 30 Sep 2026 08:46:28 -0400 Subject: [PATCH 4/5] Use lowercase parameter labels, per the model-structure convention Co-Authored-By: Claude Opus 5.5 --- .../gov/dwp/housing_benefit/working_age_awards_payable.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml b/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml index 91c7ce942d..916f4e4f3b 100644 --- a/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml +++ b/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml @@ -5,7 +5,7 @@ great_britain: 1988-04-01: 1 2026-07-01: 0 metadata: - label: Working-age Housing Benefit awards payable, Great Britain + label: Working-age housing benefit awards payable in Great Britain unit: /1 period: year fiscal_year_blend: true @@ -24,7 +24,7 @@ northern_ireland: 1988-04-01: 1 2026-10-01: 0 metadata: - label: Working-age Housing Benefit awards payable, Northern Ireland + label: Working-age housing benefit awards payable in Northern Ireland unit: /1 period: year fiscal_year_blend: true @@ -36,5 +36,5 @@ northern_ireland: - title: Welfare Reform (Northern Ireland) Order 2015 art. 39(1)(d) href: https://www.legislation.gov.uk/nisi/2015/2006/article/39 metadata: - label: Working-age Housing Benefit awards payable + label: Working-age housing benefit awards payable economy: false From 0d7df47d52a938e8dd58509721d616857e85c561 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 2 Oct 2026 15:52:14 -0400 Subject: [PATCH 5/5] Keep Housing Benefit for specified or temporary accommodation after abolition Adds the benefit-unit input in_specified_or_temporary_accommodation (default False; SI 2025/1148 art 7 and TP Regs reg 6A(2), NI equivalents): such awards stay payable after 1 July 2026 (NI 1 October 2026), new claims remain open, and the UC housing costs element excludes that rent. Other saved classes (pension-age switchers, ESA(IR) appointees, restricted prisoners) are documented as not modelled. The parameter description now says how reforms dated inside a year are read. With the input absent every existing result is unchanged. Addresses Vahid's items 2-3 and the Codex P1 on bd9272a8. Item 1 (data migration onto UC) is held for Max (d837). Co-Authored-By: Claude Opus 5.5 --- ...olish-working-age-housing-benefit.added.md | 1 + .../programs/gov/dwp/housing-benefit.ipynb | 27 +- .../working_age_awards_payable.yaml | 14 +- policyengine_uk/programs.yaml | 2 +- ...housing_benefit_working_age_abolition.yaml | 240 ++++++++++++++++++ ..._housing_benefit_pension_age_properties.py | 16 +- ...enefit_working_age_abolition_properties.py | 156 +++++++++--- .../housing_benefit_eligible.py | 40 +-- .../housing_benefit_payable_share.py | 30 ++- ...in_specified_or_temporary_accommodation.py | 33 +++ .../uc_housing_costs_element.py | 19 +- 11 files changed, 506 insertions(+), 72 deletions(-) create mode 100644 changelog.d/abolish-working-age-housing-benefit.added.md create mode 100644 policyengine_uk/variables/gov/dwp/housing_benefit/in_specified_or_temporary_accommodation.py diff --git a/changelog.d/abolish-working-age-housing-benefit.added.md b/changelog.d/abolish-working-age-housing-benefit.added.md new file mode 100644 index 0000000000..e815895034 --- /dev/null +++ b/changelog.d/abolish-working-age-housing-benefit.added.md @@ -0,0 +1 @@ +- Added a specified or temporary accommodation input to preserve working-age Housing Benefit, permit new claims alongside Universal Credit and exclude the corresponding Universal Credit housing costs. diff --git a/docs/book/programs/gov/dwp/housing-benefit.ipynb b/docs/book/programs/gov/dwp/housing-benefit.ipynb index da87969bc8..8ba8479429 100644 --- a/docs/book/programs/gov/dwp/housing-benefit.ipynb +++ b/docs/book/programs/gov/dwp/housing-benefit.ipynb @@ -7,7 +7,7 @@ "source": [ "# Housing Benefit\n", "\n", - "Housing Benefit is a means-tested DWP benefit that helps people on a low income with their rent. It is a legacy benefit: new working-age claimants have been routed to the Universal Credit housing element for years, and the remaining working-age awards outside specified and temporary accommodation were abolished on 1 July 2026 in Great Britain and 1 October 2026 in Northern Ireland. Housing Benefit continues for pensioners, protected mixed-age couples and tenants of specified (supported) or temporary accommodation, and PolicyEngine models it alongside the UC housing element so reform analyses can move between the two systems cleanly.\n", + "Housing Benefit is a means-tested DWP benefit that helps people on a low income with their rent. It is a legacy benefit: most new working-age claimants use the Universal Credit housing element, and working-age awards were abolished on 1 July 2026 in Great Britain and 1 October 2026 in Northern Ireland, subject to statutory savings. Housing Benefit continues for pensioners, protected mixed-age couples, tenants of specified or temporary accommodation and other saved cases. PolicyEngine models the accommodation saving through an explicit input; its coverage of the saved cases and accommodation-specific award rules remains partial.\n", "\n", "Parameters live in `policyengine_uk/parameters/gov/dwp/housing_benefit/` and the formulas in `policyengine_uk/variables/gov/dwp/housing_benefit/`." ] @@ -19,24 +19,31 @@ "source": [ "## Eligibility\n", "\n", - "`housing_benefit_eligible` requires the benunit to be either in **social housing** or eligible for the **Local Housing Allowance** rate (broadly, private renters), with assessable capital at or below the working-age (or pension-age) capital limit, and to reach Housing Benefit by one of two routes:\n", + "`housing_benefit_eligible` requires the benunit to be either in **social housing** or eligible for the **Local Housing Allowance** rate (broadly, private renters), with assessable capital at or below the working-age (or pension-age) capital limit, and to reach Housing Benefit by one of three routes:\n", "\n", - "- **Pension age**: every adult in the benunit has reached the qualifying age for State Pension Credit (State Pension age). New Housing Benefit claims are open to them ([SI 2014/1230 reg. 6A(4)](https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A)), and Universal Credit is not, so neither a previous claim nor `would_claim_uc` matters.\n", - "- **Continuing award**: any other benunit was **already claiming** Housing Benefit (proxied by a positive `housing_benefit_reported`) and is **not claiming Universal Credit**. Working-age and mixed-age families cannot make new claims; they claim the Universal Credit housing element instead. For mixed-age couples the reported award stands in for the saving for couples who were entitled to Housing Benefit or Pension Credit on 14 May 2019 ([SI 2019/37 art. 4](https://www.legislation.gov.uk/uksi/2019/37/article/4)).\n", + "- **Pension age**: every benunit member aged 18 or older has reached the qualifying age for State Pension Credit (State Pension age). This is the model's approximation of the claimant and partner age conditions in [SI 2014/1230 reg. 6A(4)](https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A); neither a previous claim nor `would_claim_uc` is required on this route.\n", + "- **Specified or temporary accommodation**: `in_specified_or_temporary_accommodation` is a boolean benunit input, defaulting to `False`. When supplied as `True`, it permits new Housing Benefit claims without a reported previous award, under [SI 2014/1230 reg. 6A(2)](https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A) and [SR 2016/226 reg. 4A(2)](https://www.legislation.gov.uk/nisr/2016/226/regulation/4A). The input refers to the statutory definitions: [GB specified accommodation](https://www.legislation.gov.uk/uksi/2013/376/schedule/1/paragraph/3A) and [temporary accommodation](https://www.legislation.gov.uk/uksi/2013/376/schedule/1/paragraph/3B), and their [NI specified](https://www.legislation.gov.uk/nisr/2016/216/schedule/1/paragraph/4) and [temporary](https://www.legislation.gov.uk/nisr/2016/216/schedule/1/paragraph/4A) equivalents. It is not inferred from tenure or a general supported-housing label.\n", + "- **Continuing award**: any other benunit was **already claiming** Housing Benefit (proxied by a positive `housing_benefit_reported`), is **not claiming Universal Credit**, and has a positive `housing_benefit_payable_share`. For mixed-age couples the reported award stands in for the saving for couples who were entitled to Housing Benefit or Pension Credit on 14 May 2019 ([SI 2019/37 art. 4](https://www.legislation.gov.uk/uksi/2019/37/article/4)).\n", "\n", - "The pension-age test counts the same benunit members as Universal Credit's working-age test, and the continuing-award route requires not claiming Universal Credit, so no benunit receives both.\n", + "The accommodation route permits Housing Benefit alongside Universal Credit: these Housing Benefit awards do not terminate on a UC claim ([SI 2014/1230 reg. 8(3)](https://www.legislation.gov.uk/uksi/2014/1230/regulation/8); [NI reg. 6(3)](https://www.legislation.gov.uk/nisr/2016/226/regulation/6)). When the input is `True`, PolicyEngine excludes the UC housing costs element, as the corresponding rent payments are excluded from UC housing costs ([SI 2013/376 Schedule 1 para. 3(h)-(i)](https://www.legislation.gov.uk/uksi/2013/376/schedule/1/paragraph/3); [NI Schedule 1 para. 3(e)-(ea)](https://www.legislation.gov.uk/nisr/2016/216/schedule/1/paragraph/3)). Existing rental, capital, entitlement and Housing Benefit take-up checks still apply. The input does not implement the special eligible-rent or benefit-cap treatment of these accommodation types.\n", "\n", "### Abolition of working-age awards\n", "\n", - "Working-age Housing Benefit awards outside specified or temporary accommodation were abolished from **1 July 2026** in Great Britain ([SI 2025/1148 art. 7](https://www.legislation.gov.uk/uksi/2025/1148/article/7), inserted by [SI 2026/409](https://www.legislation.gov.uk/uksi/2026/409/made)) and **1 October 2026** in Northern Ireland ([SR 2025/176 art. 7](https://www.legislation.gov.uk/nisr/2025/176/article/7), inserted by SR 2026/115). Claimants within [SI 2014/1230 reg. 6A(2)-(5)](https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A) keep their awards: pension age, mixed-age couples protected since 14 May 2019, and specified or temporary accommodation. So do prisoners until their Universal Credit restriction ends and some income-related ESA claimants with an appointee.\n", + "Working-age Housing Benefit awards were abolished from **1 July 2026** in Great Britain ([SI 2025/1148 art. 7](https://www.legislation.gov.uk/uksi/2025/1148/article/7), inserted by [SI 2026/409](https://www.legislation.gov.uk/uksi/2026/409/made)) and **1 October 2026** in Northern Ireland ([SR 2025/176 art. 7](https://www.legislation.gov.uk/nisr/2025/176/article/7), inserted by [SR 2026/115](https://www.legislation.gov.uk/nisr/2026/115/made)), subject to these savings:\n", "\n", - "The dates live in `gov.dwp.housing_benefit.working_age_awards_payable` (`great_britain` and `northern_ireland`), with 1 while working-age awards are payable and 0 after. PolicyEngine UK evaluates a year's parameters as one value for the fiscal year starting 6 April. By default that value is the one in force on 30 April, which would have left the whole of 2026-27 unaffected. This parameter is instead day-weighted across the fiscal year (`fiscal_year_blend`), so 2026 (2026-27) takes the share of the year before the abolition: 86/365 in Great Britain (6 April to 30 June) and 178/365 in Northern Ireland (6 April to 30 September). From 2027 it is 0.\n", + "- Article 7(4)(a) saves the cases in [SI 2014/1230 reg. 6A(2)-(5)](https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A) and [NI reg. 4A(2)-(5)](https://www.legislation.gov.uk/nisr/2016/226/regulation/4A): specified or temporary accommodation; the qualifying final UC assessment period when the claimant reaches Pension Credit age and the relevant Decisions and Appeals provision applies, with entitlement from reaching that age; pension-age single claimants, couples where both have reached that age and polygamous marriages where all have; and mixed-age couples whose 2019 saving remains effective.\n", + "- Article 7(4)(b) saves an award where the claimant was entitled to income-related ESA immediately before the abolition date and the article 3A(2) saving applies: an appointee was appointed immediately before that date, or the Secretary of State (NI: Department) determined in the preceding six months that an appointee was likely to be needed, whether or not one was appointed. Article 3A(3) still permits termination following a UC claim or failure to claim by the migration deadline.\n", + "- Article 7(2) delays abolition where, immediately before the abolition date, the claimant was prevented from receiving UC by regulation 19(1)(b) (prisoners) or (c) (serving a sentence of imprisonment while detained in hospital). The appointed abolition date is then the day after the last day that restriction applies; this is not a general saving for anyone who becomes a prisoner later.\n", "\n", - "`housing_benefit_payable_share` applies this per benunit. It is 1 for any benunit with a member over State Pension age, which stands in for the pension-age and protected mixed-age savings. In law a mixed-age couple is saved only if the older member claims under the pension-age rules and the couple has been entitled since 14 May 2019 ([SI 2019/37 art. 4](https://www.legislation.gov.uk/uksi/2019/37/article/4)), which the data cannot show. For other benunits it is the share for their country. A working-age continuing award stays eligible while the share is above 0, and `housing_benefit_pre_benefit_cap` pays the entitlement times the share. A working-age continuing award in 2026 is therefore paid for the part of the year before the abolition, and there is none from 2027.\n", + "The dates live in `gov.dwp.housing_benefit.working_age_awards_payable` (`great_britain` and `northern_ireland`), with 1 while working-age awards are payable and 0 after. PolicyEngine UK evaluates a year's parameters as one value for the fiscal year starting 6 April. By default that value is the one in force on 30 April, which would have left the whole of 2026-27 unaffected. This parameter is instead day-weighted across the fiscal year (`fiscal_year_blend`), so 2026 (2026-27) takes the share of the year before the abolition: 86/365 in Great Britain (6 April to 30 June) and 178/365 in Northern Ireland (6 April to 30 September). From 2027 it is 0. Annual formulas read the converted annual value at the start of the model year; later values do not switch a YEAR formula partway through that year.\n", "\n", - "What happens to the families whose awards end is set by `would_claim_uc`, the Universal Credit take-up input. Families that claim Universal Credit already receive it instead of Housing Benefit in every year. Families that do not claim it lose Housing Benefit and receive no Universal Credit. The Enhanced FRS take-up draw does not look at legacy receipt, so it leaves too many former claimants with neither. DWP's Move to Universal Credit statistics show that 97% of households sent a migration notice while on Housing Benefit and another legacy benefit claimed Universal Credit (98% of those also on Employment and Support Allowance), and 75% of Housing Benefit-only households did. Transitional protection, which went only to households that claimed after a migration notice, is not modelled.\n", + "`Simulation.apply_parameter_changes` applies reform dictionaries to raw parameters before fiscal-year conversion. A bare-year key such as `2026` covers the entire fiscal year and can set its share to 1. A date key such as `2026-07-01` is a one-day override that is then blended: setting GB to 1 for that date changes its 2026 share from 86/365 to 87/365. A range applies over its stated dates and is also blended. A dated change made directly to already-converted parameters after the model year's start is ignored by that year's annual formula; it is not the same as a reform dictionary.\n", "\n", - "The savings PolicyEngine cannot observe are not modelled: specified and temporary accommodation (open to new claims at any age, including alongside Universal Credit), prisoners, and the ESA appointee saving. The Enhanced FRS therefore has no working-age Housing Benefit from 2027. DWP's Spring 2026 forecast for Great Britain is £1.64bn of temporary-accommodation and £3.94bn of supported-accommodation Housing Benefit in 2026-27, across all ages.\n", + "`housing_benefit_payable_share` applies this per benunit. It is 1 when `in_specified_or_temporary_accommodation` is `True`, in every model year. It is also 1 for any benunit with a member over State Pension age, which stands in for the pension-age and protected mixed-age savings. In law a mixed-age couple is saved only if the older member claims under the pension-age rules and the couple's 2019 saving remains effective ([SI 2019/37 art. 4](https://www.legislation.gov.uk/uksi/2019/37/article/4)), which the data cannot show. For other benunits it is the share for their country. A working-age continuing award stays eligible while the share is above 0, and `housing_benefit_pre_benefit_cap` pays the entitlement times the share. An unprotected working-age continuing award in 2026 is therefore paid for the part of the year before the abolition, and there is none from 2027.\n", + "\n", + "What happens to the families whose awards end is set by `would_claim_uc`, the Universal Credit take-up input. Families without the accommodation input that claim Universal Credit already receive it instead of Housing Benefit in every year. Families that do not claim it lose Housing Benefit and receive no Universal Credit. The Enhanced FRS take-up draw does not look at legacy receipt, so it leaves too many former claimants with neither. DWP's Move to Universal Credit statistics show that 97% of households sent a migration notice while on Housing Benefit and another legacy benefit claimed Universal Credit (98% of those also on Employment and Support Allowance), and 75% of Housing Benefit-only households did. Transitional protection, which went only to households that claimed after a migration notice, is not modelled.\n", + "\n", + "No current dataset supplies `in_specified_or_temporary_accommodation`, so its default of `False` leaves existing dataset outputs unchanged. The other savings requiring histories or decisions remain unmodelled: the qualifying final UC assessment-period case, the ESA appointee or likely-needed-appointee determination, and the restricted prisoner cases. The pension-age and protected mixed-age routes remain approximations. The Enhanced FRS therefore still has no working-age Housing Benefit from 2027. Accommodation-specific eligible-rent and benefit-cap rules also remain gaps even when the accommodation input is supplied. DWP's Spring 2026 forecast for Great Britain is £1.64bn of temporary-accommodation and £3.94bn of supported-accommodation Housing Benefit in 2026-27, across all ages.\n", "\n", "Take-up is handled by `would_claim_housing_benefit`, which is true for benunits that report Housing Benefit and, when no reported benefits are supplied (as in household calculations), for everyone. In the Enhanced FRS take-up therefore follows reported claims." ] diff --git a/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml b/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml index 916f4e4f3b..492d2f921f 100644 --- a/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml +++ b/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml @@ -1,4 +1,16 @@ -description: Whether working-age Housing Benefit awards outside specified and temporary accommodation are still payable (1) or abolished (0). Housing Benefit was abolished for these awards from 1 July 2026 in Great Britain and 1 October 2026 in Northern Ireland; awards of claimants over the qualifying age for State Pension Credit, protected mixed-age couples and claimants in specified or temporary accommodation are saved. Each fiscal year takes the day-weighted average, which is the share of that year for which such awards stayed payable. +description: >- + Whether working-age Housing Benefit awards outside specified and temporary + accommodation are still payable (1) or abolished (0). Housing Benefit was + abolished for these awards from 1 July 2026 in Great Britain and 1 October + 2026 in Northern Ireland; statutory savings are described in + housing_benefit_payable_share. Annual formulas read parameters once, at the + start of the model year, after fiscal-year conversion. Parameters normally + sample 30 April, but these numeric flags use fiscal_year_blend to take the + day-weighted average from 6 April to 5 April. An in-year value applied after + that conversion is ignored for that model year. Simulation reform dictionaries + are applied before conversion: a date-only key overrides just one day and is + blended, while a bare year overrides the whole fiscal year. Use whole-year + values or a date range for annual reform scenarios. great_britain: description: Whether working-age Housing Benefit awards outside specified and temporary accommodation are still payable in Great Britain. Section 33(1)(d) of the Welfare Reform Act 2012 came into force for them on 1 July 2026. values: diff --git a/policyengine_uk/programs.yaml b/policyengine_uk/programs.yaml index b3d1f504ea..9a69b1832e 100644 --- a/policyengine_uk/programs.yaml +++ b/policyengine_uk/programs.yaml @@ -227,7 +227,7 @@ programs: variable: housing_benefit parameter_prefix: gov.dwp.housing_benefit verified_start_year: 2022 - notes: Legacy benefit replaced by Universal Credit. Working-age awards were abolished on 1 July 2026 (GB) and 1 October 2026 (NI); pension-age and protected mixed-age awards continue. Specified and temporary accommodation awards, which continue at any age, are not modelled. + notes: Legacy benefit replaced by Universal Credit. Working-age abolition applies from 1 July 2026 (GB) and 1 October 2026 (NI). An explicit specified-or-temporary-accommodation input preserves payable awards and new claims alongside UC without UC rent support; datasets do not supply it. Pension-age and protected mixed-age savings are approximated. Other historical savings and accommodation-specific eligible-rent and benefit-cap rules are not modelled. - id: income_support name: Income Support diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_working_age_abolition.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_working_age_abolition.yaml index d6790c9dd5..20d4c50240 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_working_age_abolition.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_working_age_abolition.yaml @@ -73,6 +73,7 @@ benunits: benunit: members: [claimant] + in_specified_or_temporary_accommodation: false would_claim_uc: false housing_benefit_applicable_income: 0 households: @@ -170,6 +171,7 @@ benunits: benunit: members: [claimant] + in_specified_or_temporary_accommodation: false would_claim_uc: false housing_benefit_applicable_income: 0 households: @@ -307,3 +309,241 @@ output: housing_benefit_eligible: false housing_benefit: 0 + +- name: GB protected working-age continuing award is paid in full after abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit_entitlement: 5_200 + housing_benefit: 5_200 + +- name: NI protected working-age continuing award is paid in full after abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit_entitlement: 5_200 + housing_benefit: 5_200 + +- name: GB protected accommodation permits a working-age new claim after abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + claims_all_entitled_benefits: true + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit_entitlement: 5_200 + housing_benefit: 5_200 + +- name: NI protected accommodation permits a working-age new claim after abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + claims_all_entitled_benefits: true + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit_entitlement: 5_200 + housing_benefit: 5_200 + +- name: GB unprotected working-age new claim receives no award after abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: false + claims_all_entitled_benefits: true + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 0 + housing_benefit_eligible: false + housing_benefit: 0 + +- name: NI unprotected working-age new claim receives no award after abolition + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: false + claims_all_entitled_benefits: true + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + housing_benefit_payable_share: 0 + housing_benefit_eligible: false + housing_benefit: 0 + +- name: Protected accommodation still respects the working-age capital limit + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + claims_all_entitled_benefits: true + would_claim_uc: false + housing_benefit_applicable_income: 0 + households: + household: + members: [claimant] + rent: 5_200 + savings: 20_000 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: false + housing_benefit: 0 + +- name: GB protected accommodation receives Housing Benefit alongside non-housing UC + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + would_claim_uc: true + housing_benefit_applicable_income: 0 + # Fix the non-housing allowance to isolate the rent-cost routing. + uc_standard_allowance: 5_000 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: LONDON + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit: 5_200 + uc_housing_costs_element: 0 + universal_credit: 5_000 + +- name: NI protected accommodation receives Housing Benefit alongside non-housing UC + period: 2027 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + housing_benefit_reported: 1 + benunits: + benunit: + members: [claimant] + in_specified_or_temporary_accommodation: true + would_claim_uc: true + housing_benefit_applicable_income: 0 + # Fix the non-housing allowance to isolate the rent-cost routing. + uc_standard_allowance: 5_000 + households: + household: + members: [claimant] + rent: 5_200 + tenure_type: RENT_FROM_COUNCIL + region: NORTHERN_IRELAND + output: + housing_benefit_payable_share: 1 + housing_benefit_eligible: true + housing_benefit: 5_200 + uc_housing_costs_element: 0 + universal_credit: 5_000 diff --git a/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py b/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py index 92c6ffaaca..602aba232b 100644 --- a/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py +++ b/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py @@ -1,9 +1,10 @@ """Property-based tests for who can claim Housing Benefit. -New Housing Benefit claims are barred except where the claimant, and any -partner, has reached the qualifying age for State Pension Credit (SI 2014/1230 -reg 6A(4)); Universal Credit is not available to them (Welfare Reform Act 2012 -s.4(1)(b)). Other families keep Housing Benefit only while they continue an +These tests omit the accommodation input, so its default False applies. +For this route, new Housing Benefit claims are barred except where the +claimant, and any partner, has reached the qualifying age for State Pension +Credit (SI 2014/1230 reg 6A(4)); the model approximates this with members aged +18 or over. Other families keep Housing Benefit only while they continue an existing award and do not claim Universal Credit. YEAR is 2026-27, when working-age awards are still payable for part of the year, so eligibility follows the same rule; test_housing_benefit_working_age_abolition_properties.py @@ -28,9 +29,10 @@ 5. Metamorphic: a wholly pension-age family's Housing Benefit is non-increasing in private pension income. -A pensioner with an 18 or 19 year old dependant counts as having a working-age -adult for Housing Benefit, Pension Credit and Universal Credit alike, so the -mutual exclusion holds for that shape too. +The model's HB age approximation counts an 18 or 19 year old dependant among +members aged 18 or over. UC uses its own claimant definition, so that HB +approximation does not establish UC eligibility. Accommodation protection and +the HB/non-housing-UC route are tested in the abolition property file. """ import numpy as np diff --git a/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py b/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py index 4337bef3d4..cd9e6a2216 100644 --- a/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py +++ b/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py @@ -13,22 +13,27 @@ share of 6 April to 5 April falling before the statutory date, counted independently here from the two dates (1 before 2026-27, 0 after). 2. Bounds: 0 <= housing_benefit_payable_share <= 1, and it is 1 for every - family with a member over State Pension age. -3. Abolition: from 2027 no family without a member over State Pension age - receives Housing Benefit, in any country or claim mode. + family with a member over State Pension age or the protected-accommodation + input, in every model year. +3. Abolition: from 2027 no family without a member over State Pension age or + the protected-accommodation input receives Housing Benefit, in any country + or claim mode. 4. Differential against the pre-abolition rule: with working-age awards kept payable by a reform, Housing Benefit is what the continuing-award rule paid before. Under current law, Housing Benefit before the benefit cap is exactly that amount times the country's payable share for a family with no - member over State Pension age, and exactly the same amount for every other - family (the cap applies afterwards, so the final amount is compared by 5). + member over State Pension age or protected-accommodation input, and exactly + the same amount for every other family (the cap applies afterwards, so the + final amount is compared by 5). Mixed populations rarely hold a 2026 working-age continuing award, so a targeted test builds only those, for each year and jurisdiction. 5. Monotonic: the abolition never raises anyone's Housing Benefit and never - changes Universal Credit (families on the continuing-award route do not - claim it). -6. Structural (unchanged): no family gets both Housing Benefit and Universal - Credit, and Housing Benefit stays within 0 and the rent. + changes Universal Credit. +6. Structural: only protected-accommodation families can get both Housing + Benefit and Universal Credit, their UC housing costs element is zero, + and Housing Benefit stays within 0 and the rent. +7. Default: omitting the protected-accommodation input produces the same + Housing Benefit and Universal Credit outputs as setting it to False. """ import datetime @@ -95,38 +100,47 @@ def families(draw): savings=draw(st.one_of(st.just(0.0), st.floats(0, 20_000))), would_claim_uc=draw(st.booleans()), hb_reported=draw(st.sampled_from([0.0, 1.0])), + in_specified_or_temporary_accommodation=draw(st.booleans()), ) def situation(units, year, claims_all): + years = [year] if isinstance(year, int) else list(year) + + def annual(value): + return {year: value for year in years} + people, benunits, households = {}, {}, {} for i, unit in enumerate(units): names = [] for j, (role, age) in enumerate(unit["members"]): name = f"p{i}_{j}" - person = {"age": {year: age}} + person = {"age": annual(age)} if role == "claimant": - person["employment_income"] = {year: unit["earnings"]} - person["is_parent"] = {year: unit["shape"] == "pension_with_dependant"} + person["employment_income"] = annual(unit["earnings"]) + person["is_parent"] = annual(unit["shape"] == "pension_with_dependant") if unit["hb_reported"]: - person["housing_benefit_reported"] = {year: unit["hb_reported"]} + person["housing_benefit_reported"] = annual(unit["hb_reported"]) if role == "dependant": - person["current_education"] = {year: "UPPER_SECONDARY"} + person["current_education"] = annual("UPPER_SECONDARY") people[name] = person names.append(name) benunits[f"b{i}"] = { "members": names, - "would_claim_uc": {year: unit["would_claim_uc"]}, + "would_claim_uc": annual(unit["would_claim_uc"]), + "in_specified_or_temporary_accommodation": annual( + unit["in_specified_or_temporary_accommodation"] + ), # claims_all_entitled_benefits sums reported benefits across the # whole simulation, so set it per family. - "claims_all_entitled_benefits": {year: claims_all}, + "claims_all_entitled_benefits": annual(claims_all), } households[f"h{i}"] = { "members": names, - "region": {year: unit["region"]}, - "rent": {year: unit["rent"]}, - "tenure_type": {year: unit["tenure"]}, - "savings": {year: unit["savings"]}, + "region": annual(unit["region"]), + "rent": annual(unit["rent"]), + "tenure_type": annual(unit["tenure"]), + "savings": annual(unit["savings"]), } return {"people": people, "benunits": benunits, "households": households} @@ -137,6 +151,7 @@ def situation(units, year, claims_all): "housing_benefit_eligible", "housing_benefit_payable_share", "universal_credit", + "uc_housing_costs_element", "benunit_rent", ] @@ -156,20 +171,30 @@ def jurisdiction(unit): ) -def check_structural(values): +def check_structural(values, units): hb = values["housing_benefit"] + protected = np.array( + [unit["in_specified_or_temporary_accommodation"] for unit in units] + ) assert np.all(hb >= 0) assert np.all(hb <= values["benunit_rent"] + 0.01) - assert not np.any((hb > 0) & (values["universal_credit"] > 0)) + assert not np.any((hb > 0) & (values["universal_credit"] > 0) & ~protected) + assert np.all(values["uc_housing_costs_element"][protected] == 0) + + +@pytest.fixture(scope="module") +def baseline_tax_benefit_system(): + """Share immutable baseline parameters across the annual-value checks.""" + return CountryTaxBenefitSystem() @pytest.mark.parametrize("year", range(2015, 2041)) -def test_payable_share_is_the_day_share_before_the_statutory_date(year): - parameter = ( - CountryTaxBenefitSystem() - .parameters(str(year)) - .gov.dwp.housing_benefit.working_age_awards_payable - ) +def test_payable_share_is_the_day_share_before_the_statutory_date( + year, baseline_tax_benefit_system +): + parameter = baseline_tax_benefit_system.parameters( + str(year) + ).gov.dwp.housing_benefit.working_age_awards_payable for name, date in ABOLITION_DATES.items(): expected = fiscal_year_share_before(date, year) assert getattr(parameter, name) == pytest.approx(expected, abs=1e-12) @@ -183,6 +208,35 @@ def test_day_shares_match_the_hand_counted_days(): ) +def test_dated_reform_is_blended_but_year_reform_covers_the_whole_fiscal_year(): + unit = dict( + shape="single_working", + members=[("claimant", 40)], + region="LONDON", + tenure="RENT_FROM_COUNCIL", + rent=5_200, + earnings=0, + savings=0, + would_claim_uc=False, + hb_reported=1, + in_specified_or_temporary_accommodation=False, + ) + parameter = f"{PARAMETER}.great_britain" + one_day = Simulation( + situation=situation([unit], 2026, claims_all=False), + reform={parameter: {"2026-07-01": 1}}, + ) + whole_year = Simulation( + situation=situation([unit], 2026, claims_all=False), + reform={parameter: {"2026": 1}}, + ) + # The single-date override adds one payable day to the baseline 86 days. + assert one_day.calculate("housing_benefit_payable_share", 2026)[0] == pytest.approx( + (86 + 1) / 365 + ) + assert whole_year.calculate("housing_benefit_payable_share", 2026)[0] == 1 + + @PROPERTY_SETTINGS @given( st.lists(families(), min_size=1, max_size=25), @@ -194,14 +248,17 @@ def test_abolition_matches_the_pre_abolition_rule_scaled_by_the_payable_share( ): current = calculate(units, year, claims_all) kept = calculate(units, year, claims_all, reform=KEEP_WORKING_AGE_AWARDS) - check_structural(current) - check_structural(kept) + check_structural(current, units) + check_structural(kept, units) share = current["housing_benefit_payable_share"] assert np.all((share >= 0) & (share <= 1)) assert np.all(kept["housing_benefit_payable_share"] == 1) pre_cap = "housing_benefit_pre_benefit_cap" for i, unit in enumerate(units): - if any_over_pension_age(unit): + if ( + any_over_pension_age(unit) + or unit["in_specified_or_temporary_accommodation"] + ): assert share[i] == 1, unit expected_share = 1 assert current["housing_benefit"][i] == pytest.approx( @@ -238,6 +295,7 @@ def working_age_continuing_awards(draw, region): savings=draw(st.one_of(st.just(0.0), st.floats(0, 15_000))), would_claim_uc=False, hb_reported=1.0, + in_specified_or_temporary_accommodation=False, ) @@ -265,3 +323,39 @@ def test_working_age_continuing_award_is_paid_for_the_payable_share(year, region current["housing_benefit_eligible"] == (kept["housing_benefit_eligible"] & (expected_share > 0)) ) + + +@pytest.mark.parametrize("region", ["LONDON", "NORTHERN_IRELAND"]) +@settings(PROPERTY_SETTINGS, max_examples=5) +@given(data=st.data()) +def test_protected_working_age_award_is_payable_in_full_in_every_year(region, data): + units = data.draw( + st.lists(working_age_continuing_awards(region), min_size=1, max_size=8) + ) + for unit in units: + unit["in_specified_or_temporary_accommodation"] = True + years = range(2015, 2041) + sim = Simulation(situation=situation(units, years, claims_all=False)) + for year in years: + share = sim.calculate("housing_benefit_payable_share", year) + assert np.all(share == 1), (year, region, units) + + +@settings(PROPERTY_SETTINGS, max_examples=5) +@given( + st.lists(families(), min_size=1, max_size=8), + st.sampled_from([2025, 2026, 2027, 2030]), + st.booleans(), +) +def test_omitted_accommodation_input_matches_explicit_false(units, year, claims_all): + for unit in units: + unit["in_specified_or_temporary_accommodation"] = False + explicit = calculate(units, year, claims_all) + omitted = situation(units, year, claims_all) + for benunit in omitted["benunits"].values(): + benunit.pop("in_specified_or_temporary_accommodation") + sim = Simulation(situation=omitted) + for variable in VARIABLES: + np.testing.assert_array_equal( + sim.calculate(variable, year), explicit[variable], err_msg=variable + ) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_eligible.py b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_eligible.py index 60842f59cd..40f75db6a8 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_eligible.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_eligible.py @@ -8,13 +8,19 @@ class housing_benefit_eligible(Variable): documentation = ( "Whether this family can receive Housing Benefit. A family in which " "every adult has reached the qualifying age for State Pension Credit " - "can make a new claim; Universal Credit is not available to it. Any " - "other family keeps Housing Benefit only while it continues an " - "existing award (reported Housing Benefit) and does not claim " - "Universal Credit. Working-age awards ended on 1 July 2026 in Great " + "can make a new claim; Universal Credit is not available to it. " + "in_specified_or_temporary_accommodation also opens the new-claim " + "route at any age, including alongside Universal Credit, and " + "preserves existing awards. Other families keep Housing Benefit " + "only while they continue an " + "existing award (reported Housing Benefit) and do not claim " + "Universal Credit. Unprotected working-age awards ended on 1 July 2026 in Great " "Britain and 1 October 2026 in Northern Ireland, so from then only " - "families with a member over State Pension age continue one. Claims " - "for specified or temporary accommodation are not modelled." + "families with a member over State Pension age or the accommodation " + "input continue one. Other unmodelled statutory savings are listed " + "in housing_benefit_payable_share's documentation. Rental, capital " + "and take-up rules still apply; accommodation-specific eligible rent " + "and benefit-cap rules are not modelled." ) definition_period = YEAR reference = ( @@ -30,15 +36,13 @@ class housing_benefit_eligible(Variable): def formula(benunit, period, parameters): person = benunit.members sp_age = person("is_SP_age", period) - # New claims are barred except where the claimant, and any partner, + # Apart from the accommodation exception, new claims are barred + # except where the claimant, and any partner, # has reached the qualifying age for State Pension Credit - # (SI 2014/1230 reg 6A(4); NI: SR 2016/226 reg 4A(4)). Every adult in - # the benefit unit stands in for the claimant and partner, as in - # is_uc_eligible and is_pension_credit_eligible, so a pensioner with - # an 18 or 19 year old dependant is routed to Universal Credit. - # Because is_uc_eligible needs a working-age adult, no family on this - # route receives Universal Credit; change the three together. - adult = person("is_adult", period) + # (SI 2014/1230 reg 6A(4); NI: SR 2016/226 reg 4A(4)). The current HB + # approximation counts members aged 18 or over, so an 18 or 19 year + # old dependant prevents this pension-age route. + adult = person("age", period) >= 18 adult_count = benunit.sum(adult) pension_age = (adult_count > 0) & (benunit.sum(adult & sp_age) == adult_count) # Working-age and mixed-age families (since 15 May 2019) claim @@ -55,6 +59,12 @@ def formula(benunit, period, parameters): claiming_uc = benunit("would_claim_uc", period) still_payable = benunit("housing_benefit_payable_share", period) > 0 continuing_award = already_claiming & ~claiming_uc & still_payable + # Reg 6A(2) permits new accommodation claims irrespective of UC; + # reg 8(3) preserves accommodation HB when UC is claimed. + # NI equivalents: SR 2016/226 regs 4A(2) and 6(3). + protected_accommodation = benunit( + "in_specified_or_temporary_accommodation", period + ) social = benunit.any(person("in_social_housing", period)) lha_eligible = benunit("LHA_eligible", period) any_over_SP_age = benunit.any(sp_age) @@ -66,7 +76,7 @@ def formula(benunit, period, parameters): hb_capital.working_age.limit, ) return ( - (pension_age | continuing_award) + (pension_age | continuing_award | protected_accommodation) & (social | lha_eligible) & (capital <= limit) ) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py index 1da4a97ff1..3e47058165 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py @@ -7,12 +7,26 @@ class housing_benefit_payable_share(Variable): label = "Share of the year Housing Benefit is payable" documentation = ( "Share of the year for which this family's Housing Benefit award stays " - "payable. Working-age awards outside specified and temporary " + "payable. Unprotected working-age awards outside specified and temporary " "accommodation were abolished from 1 July 2026 in Great Britain and " "1 October 2026 in Northern Ireland, so for a family with no member " "over State Pension age this is the share of the year before that " - "date. Families with a member over State Pension age keep their award " - "(the pension-age and mixed-age savings), so theirs is 1." + "date, unless in_specified_or_temporary_accommodation is True. " + "That input preserves a share of 1 in every year. Families with a " + "member over State Pension age also have a share of 1 (the model's " + "pension-age and protected mixed-age approximation). Other saved " + "working-age awards are not modelled: qualifying HB claims during the " + "final UC assessment period on reaching pension-credit age, with " + "entitlement from reaching that age and the relevant Decisions and " + "Appeals Schedule 1 paragraph 26 applying (reg 6A(3); NI reg 4A(3)); " + "income-related " + "ESA entitlement immediately before abolition with an appointee then, " + "or a determination in the preceding six months that one was likely " + "needed (art 7(4)(b) and 3A(2)); and the delayed termination for " + "claimants excluded from UC immediately before abolition under UC " + "reg 19(1)(b) or (c), until the day after the last day of that " + "exclusion (art 7(2)). These need award history, decisions or dates " + "which no inputs supply." ) definition_period = YEAR unit = "/1" @@ -39,7 +53,11 @@ def formula(benunit, period, parameters): # mixed-age couple is saved only if the older member claims under the # pension-age regulations and the couple has been entitled since # 14 May 2019, which the data cannot show. Specified and temporary - # accommodation (reg 6A(2)), prisoners (art. 7(2)) and the income- - # related ESA appointee saving (art. 7(4)(b)) have no inputs. + # accommodation is supplied explicitly under reg 6A(2) (NI reg 4A(2)). + # The other savings described in the variable documentation have no + # inputs for their history and date conditions. any_over_SP_age = benunit.any(benunit.members("is_SP_age", period)) - return where(any_over_SP_age, 1, working_age_share) + protected_accommodation = benunit( + "in_specified_or_temporary_accommodation", period + ) + return where(any_over_SP_age | protected_accommodation, 1, working_age_share) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/in_specified_or_temporary_accommodation.py b/policyengine_uk/variables/gov/dwp/housing_benefit/in_specified_or_temporary_accommodation.py new file mode 100644 index 0000000000..ba5d006b60 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/in_specified_or_temporary_accommodation.py @@ -0,0 +1,33 @@ +from policyengine_uk.model_api import * + + +class in_specified_or_temporary_accommodation(Variable): + value_type = bool + entity = BenUnit + label = "In specified or temporary accommodation for Housing Benefit" + documentation = ( + "Whether this family's housing falls within the statutory specified " + "or temporary accommodation definitions for Housing Benefit. GB " + "Universal Credit (Transitional Provisions) Regulations 2014 reg " + "6A(2), with the definitions in reg 2, refers to Universal Credit " + "Regulations 2013 Schedule 1 paragraphs 3A(2)-(5) and 3B. The NI " + "equivalents are Transitional Provisions Regulations 2016 regs 2 " + "and 4A(2), and Universal Credit Regulations 2016 Schedule 1 " + "paragraphs 4(2)-(5) and 4A. This is a supplied status, not inferred " + "from tenure or a generic supported-housing label. It protects " + "working-age awards from abolition and permits new Housing Benefit " + "claims, subject to the model's other entitlement and take-up rules. " + "It defaults to False when absent from household or dataset inputs." + ) + definition_period = YEAR + default_value = False + reference = ( + "https://www.legislation.gov.uk/uksi/2014/1230/regulation/6A/2", + "https://www.legislation.gov.uk/uksi/2014/1230/regulation/2", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/1/paragraph/3A", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/1/paragraph/3B", + "https://www.legislation.gov.uk/nisr/2016/226/regulation/4A/2", + "https://www.legislation.gov.uk/nisr/2016/226/regulation/2", + "https://www.legislation.gov.uk/nisr/2016/216/schedule/1/paragraph/4", + "https://www.legislation.gov.uk/nisr/2016/216/schedule/1/paragraph/4A", + ) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/uc_housing_costs_element.py b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/uc_housing_costs_element.py index ac1fbaf119..e37b995a04 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/uc_housing_costs_element.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/uc_housing_costs_element.py @@ -7,6 +7,16 @@ class uc_housing_costs_element(Variable): label = "Universal Credit housing costs element" definition_period = YEAR unit = GBP + documentation = ( + "Universal Credit support for rent, excluding payments for specified " + "or temporary accommodation when " + "in_specified_or_temporary_accommodation is supplied as True. " + "Those payments can instead be covered by Housing Benefit." + ) + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/1/paragraph/3", + "https://www.legislation.gov.uk/nisr/2016/216/schedule/1/paragraph/3", + ) def formula(benunit, period, parameters): tenure_type = benunit.value_from_first_person( @@ -28,4 +38,11 @@ def formula(benunit, period, parameters): default=0, ) non_dependent_deductions = benunit("uc_non_dep_deductions", period) - return max_(max_housing_costs - non_dependent_deductions, 0) + protected_accommodation = benunit( + "in_specified_or_temporary_accommodation", period + ) + return where( + protected_accommodation, + 0, + max_(max_housing_costs - non_dependent_deductions, 0), + )