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/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 3fa1f88ec0..6250e40d4d 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: 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,12 +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. 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 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 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", + "- 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", + "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", + "In a `Simulation` reform dictionary, a bare-year key such as `2026` covers the entire fiscal year and can set its share to 1, and a date range applies over its stated dates. A key dated inside the year, such as `2026-07-01`, leaves that model year's share unchanged (86/365 for Great Britain in 2026), so use whole-year values or a range for annual reform scenarios.\n", + "\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/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..99f735cafe --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/housing_benefit/working_age_awards_payable.yaml @@ -0,0 +1,51 @@ +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. In a Simulation reform + dictionary a bare year overrides the whole fiscal year, while a key dated + inside the year (for example 2026-07-01) leaves that model year's value + unchanged. 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: + 1988-04-01: 1 + 2026-07-01: 0 + metadata: + label: Working-age housing benefit awards payable in 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 in 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 46d253f103..9a69b1832e 100644 --- a/policyengine_uk/programs.yaml +++ b/policyengine_uk/programs.yaml @@ -222,12 +222,12 @@ programs: full_name: Housing Benefit category: Benefits agency: DWP - status: complete + status: partial coverage: UK 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 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_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..20d4c50240 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_working_age_abolition.yaml @@ -0,0 +1,549 @@ +# 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] + in_specified_or_temporary_accommodation: false + 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] + in_specified_or_temporary_accommodation: false + 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 + +- 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 147b4e3fe9..17fda0d37b 100644 --- a/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py +++ b/policyengine_uk/tests/test_housing_benefit_pension_age_properties.py @@ -1,10 +1,14 @@ """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 -existing award and do not claim Universal Credit. +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 +covers the abolition itself. Invariants, for any generated population of families: @@ -29,6 +33,8 @@ Housing Benefit, Pension Credit and Universal Credit alike (the dependant is not a claimant or partner), so that family can claim Housing Benefit and not Universal Credit, and the mutual exclusion holds for that shape too. +Accommodation protection and the HB/non-housing-UC route are tested in the +abolition property file. Every member's is_claimant_or_partner is set from its generated role, as the FRS supplies it. Without that input an 18- or 19-year-old partner of a 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..dc61874c36 --- /dev/null +++ b/policyengine_uk/tests/test_housing_benefit_working_age_abolition_properties.py @@ -0,0 +1,362 @@ +"""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 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 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. +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 + +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])), + 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": annual(age)} + if role == "claimant": + person["employment_income"] = annual(unit["earnings"]) + person["is_parent"] = annual(unit["shape"] == "pension_with_dependant") + if unit["hb_reported"]: + person["housing_benefit_reported"] = annual(unit["hb_reported"]) + if role == "dependant": + person["current_education"] = annual("UPPER_SECONDARY") + people[name] = person + names.append(name) + benunits[f"b{i}"] = { + "members": names, + "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": annual(claims_all), + } + households[f"h{i}"] = { + "members": names, + "region": annual(unit["region"]), + "rent": annual(unit["rent"]), + "tenure_type": annual(unit["tenure"]), + "savings": annual(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", + "uc_housing_costs_element", + "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, 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) & ~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, 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) + + +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 + ) + + +def test_dated_reform_is_ignored_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}}, + ) + # A key dated inside the year leaves the model year's share at the + # baseline 86 days: it is not applied. + assert one_day.calculate("housing_benefit_payable_share", 2026)[0] == pytest.approx( + 86 / 365 + ) + assert whole_year.calculate("housing_benefit_payable_share", 2026)[0] == 1 + + +@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, 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) + or unit["in_specified_or_temporary_accommodation"] + ): + 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 + + +@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, + in_specified_or_temporary_accommodation=False, + ) + + +@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)) + ) + + +@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 6c65db61b8..c89ea7f499 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,18 +8,27 @@ class housing_benefit_eligible(Variable): documentation = ( "Whether this family can receive Housing Benefit. A family whose " "claimant and any partner have 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. Claims for specified or temporary accommodation " - "are not modelled." + "Pension Credit 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 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 = ( "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", ) @@ -27,7 +36,8 @@ 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)). This is the # same claimant-and-partner set is_uc_eligible and @@ -42,11 +52,21 @@ 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 + # 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) @@ -58,7 +78,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 new file mode 100644 index 0000000000..3e47058165 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_payable_share.py @@ -0,0 +1,63 @@ +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. 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, 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" + 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) + # 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, + 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 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)) + 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/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, ) 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), + )