Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4bce9cc
Lift the benefit cap for ESA only when it includes the support component
MaxGhenis Oct 2, 2026
6764375
Lift the benefit cap only for the people HB reg 75F and UC reg 83 name
MaxGhenis Oct 2, 2026
c6cf647
Infer LCWRA from ESA only where the support component is missing
MaxGhenis Oct 2, 2026
b318950
Find a directly entered ESA award without the capital test or the inp…
MaxGhenis Oct 2, 2026
5213738
Merge legacy-award-readers-claimant-partner (#2027, 3803dda0d) into b…
MaxGhenis Oct 2, 2026
8303062
Count only the claimant's or couple's benefits in the benefit cap
MaxGhenis Oct 2, 2026
c6eabda
Leave the support component out of the capped-total cases
MaxGhenis Oct 2, 2026
19a13f2
Merge legacy-award-readers-claimant-partner (#2027, f551e542b) into b…
MaxGhenis Oct 2, 2026
e648b2d
Merge legacy-award-readers-claimant-partner (#2027, b8b4d7b50) into b…
MaxGhenis Oct 2, 2026
c2a8c35
Merge origin/main (e89b8abcf, with #2027, #2084, #1944, #1907, #1978,…
MaxGhenis Oct 10, 2026
a6100a7
Read limited capability for work in the work preparation group and th…
MaxGhenis Oct 10, 2026
a6a3073
Answer the after-merge review: entered UC elements, the scheme's coup…
MaxGhenis Oct 10, 2026
e756d1b
Answer review r2: the ESA head and the cap rate read the scheme's cou…
MaxGhenis Oct 10, 2026
285317c
Merge origin/main (97ef6e1af, with #2021, #2038, #2063, #2180, #2248,…
MaxGhenis Oct 11, 2026
2f341de
Answer review r3: include a UC element by the award, keep the rebalan…
MaxGhenis Oct 11, 2026
399da7f
Merge origin/main (6d89338ab, with #2251, #2270, #2288, #2294) into b…
MaxGhenis Oct 11, 2026
5d543f1
Answer review r5: keep a model value the model's only while it is the…
MaxGhenis Oct 11, 2026
e249a42
Give the rebuild-from-a-frame test's frame its identifiers
MaxGhenis Oct 11, 2026
d79a189
Merge origin/main (3f174d07e, with #1925 and #2349) into benefit-cap-…
MaxGhenis Oct 12, 2026
d4984fb
Answer review r6: forget a model value only when set_input stores one…
MaxGhenis Oct 12, 2026
1c946fa
Merge origin/main (1d3abecef) into benefit-cap-75f-claimant-partner
MaxGhenis Oct 13, 2026
298df66
Pin LCWRA entered as false, and calculate the cap in the readers inva…
MaxGhenis Oct 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog.d/benefit-cap-75f-claimant-partner.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Add `esa_includes_support_component`: whether a person's employment and support allowance includes the support component. Datasets and users can supply it; otherwise it is assumed for anyone with an ESA award of their own. Household calculations with ESA outside the support group should set it to false.
- Add `has_own_esa_award`: whether a person claims ESA themselves (a reported award, or a directly entered income-related award attributed to the claimant who heads the benefit unit).
- Add `uc_limited_capability_for_work`: limited capability for work, for the Universal Credit work allowance, reduced minimum age, work preparation group and the partner's exception to the childcare work condition.
7 changes: 7 additions & 0 deletions changelog.d/benefit-cap-75f-claimant-partner.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- Lift the benefit cap for employment and support allowance only where the claimant's or partner's allowance includes the support component (HB Regs 2006 reg 75F(1)(a); UC Regs 2013 reg 83(1)(a)), for contributory and income-related ESA alike. Any income-related ESA, and any member's contributory ESA, used to lift it. Without other information the support component is assumed for a person with their own allowance, as for about nine in ten ESA recipients in the Family Resources Survey.
- Lift the benefit cap for a disability or carer benefit only when the person the regulations name receives it: the claimant or partner for attendance allowance, industrial injuries benefit and armed forces compensation; also a child or young person they are responsible for, for disability living allowance; also a young person, for personal independence payment, Carer's Allowance and Carer Support Payment, and a Housing Benefit young person for armed forces independence payment. A 16-year-old who receives benefits in their own right or is looked after is not a young person here. The Universal Credit LCWRA and carer exceptions now need the element in the award: an element entered directly, or given by a reform's formula, lifts the cap when positive whatever the members' circumstances, and the element the model calculates lifts it unless only members who are not claimants have limited capability or caring responsibilities. This holds in 2026-2029 too: the Universal Credit rebalancing's rescaled LCWRA element is the model's own, not an entered one (`utils/supplied_inputs.set_model_value`). A partner who cannot be a joint claimant (UC Regs 2013 reg 3(3)) lifts no cap with their own benefits. For a family on Universal Credit the claimant and partner are the award's claimants (`is_uc_assessed_claimant`), otherwise Housing Benefit's claimant and partner; the two are the same unless `is_uc_claimant` is entered. A non-dependent adult's disability and carer benefits, ESA, disability flag and caring no longer lift the family's cap.
- Count armed forces independence payment towards the benefit cap exception.
- Take a person whose own ESA lacks the support component not to have limited capability for work-related activity (`uc_limited_capability_for_WRA`), whatever their disability flag, since that determination is what gives ESA the support component and Universal Credit takes it (UC Regs 2013 reg 40(1)(a)(ii)). Datasets flag every ESA recipient as disabled and the support component is assumed, so this changes nothing in the data until a dataset reports the ESA group.
- Give the Universal Credit work allowance and reduced minimum age their own limited capability for work test (`uc_limited_capability_for_work`, UC Regs 2013 regs 8(1)(a), 22, 39): limited capability for work-related activity or the disability flag, so an ESA recipient outside the support group keeps both. The work preparation group (Welfare Reform Act 2012 s.21(1)(a)) and the partner's exception to the childcare work condition (UC Regs 2013 reg 32(1)(b)(i)) read it too, so a claimant flagged as disabled without limited capability for work-related activity is in the work preparation group, outside the minimum income floor, and as a partner still lets a working claimant meet the childcare work condition.
- Count in the benefit cap only the welfare benefits the claimant or couple is entitled to (UC Regs 2013 reg 80(1); HB Regs 2006 reg 75A): the claimant's and partner's contributory ESA, JSA, incapacity benefit and SDA, their income-related ESA and income-based JSA (`claimant_or_partner_esa_income`, `claimant_or_partner_jsa_income`), and the family's Child Benefit, CTC, Income Support, Universal Credit and Housing Benefit. For a family on Universal Credit the couple is the award's claimants, including the other member of a couple where one member claims as a single person (UC Regs 2013 reg 78(2)); otherwise it is the claimant and partner. Another member's own award no longer reduces the family's Universal Credit or Housing Benefit; it still counts in household income.
- Give the benefit cap's single or couple limit by the award's claimants where the claimants of a family on Universal Credit are entered (`is_uc_claimant`) and are not Housing Benefit's claimant and partner (UC Regs 2013 reg 80A); otherwise `is_couple` decides, as before.
2 changes: 2 additions & 0 deletions changelog.d/benefit-cap-75f-claimant-partner.removed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Remove the unused benefit sums from `is_benefit_cap_exempt_earnings` and `is_benefit_cap_exempt_other`; the earnings and age exceptions keep their results and the armed forces and ESA exceptions move, limited to the claimant and partner, into `is_benefit_cap_exempt_health_disability`.
- Remove the input `uc_has_limited_capability_for_work`. The work preparation group reads `uc_limited_capability_for_work`, which datasets and users can set instead.
2 changes: 1 addition & 1 deletion docs/book/programs/gov/dwp/universal-credit.ipynb

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion policyengine_uk/scenarios/uc_reform.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from policyengine_uk.model_api import Scenario
from policyengine_uk import Microsimulation
from policyengine_uk.utils.supplied_inputs import set_model_value
import numpy as np


Expand Down Expand Up @@ -156,7 +157,10 @@ def add_universal_credit_reform(sim: Microsimulation):
current_health_element[has_health_element & is_post_2025_claimant] = (
new_claimant_health_element(year) * 12
) # Monthly amount * 12
sim.set_input("uc_LCWRA_element", year, current_health_element)
# Stored as an input, so that reforms keep it, but still the model's
# own element unless one was entered (the benefit cap's LCWRA
# exception reads entries differently).
set_model_value(sim, "uc_LCWRA_element", year, current_health_element)

# Standard allowance uplift is handled in the formula itself so user
# reforms to the base amount are applied before the uplift.
Expand Down
31 changes: 31 additions & 0 deletions policyengine_uk/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@
from policyengine_uk.utils.dependencies import get_variable_dependencies
from policyengine_uk.utils.supplied_inputs import (
SUPPLIED_INPUT_VARIABLES,
copy_model_values,
drop_missing_model_values,
drop_missing_supplied_inputs,
forget_model_values,
forget_replaced_model_values,
model_value_input_records,
)
from policyengine_uk.reforms import create_structural_reforms_from_parameters
from policyengine_uk.parameters.gov.simulation.labour_supply_responses.aliases import (
Expand Down Expand Up @@ -266,6 +271,32 @@ def delete_arrays(self, variable: str, period: Period = None) -> None:
# Inspect storage after core's deletion to honour period containment
# and the current branch, ancestor branches and default branch.
drop_missing_supplied_inputs(self, variable)
# A value stored later in a period whose model value (set_model_value)
# is gone is supplied, even one equal to the deleted value.
drop_missing_model_values(self, variable)

def build_from_populations(self, populations) -> None:
super().build_from_populations(populations)
# The new populations hold none of the values set_model_value stored,
# so an equal value a rebuild stores (from a situation or a frame) is
# supplied.
forget_model_values(self)

def set_input(self, variable_name: str, period, value) -> None:
# What is set here is supplied, even a value equal to the model's own,
# so forget the model value (set_model_value) of each period the call
# stores a value for, and only those. set_model_value records its
# value as the model's after this call.
records = model_value_input_records(self, variable_name)
try:
super().set_input(variable_name, period, value)
finally:
forget_replaced_model_values(self, variable_name, records)

def clone(self, *args, **kwargs) -> "Simulation":
new = super().clone(*args, **kwargs)
copy_model_values(self, new)
return new

def reset_calculations(self):
for variable in self.tax_benefit_system.variables:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,21 +338,40 @@
targeted_childcare_entitlement_eligible: true

# Benefit cap: no cap where "the claimant or the claimant's partner" receives
# ESA with the support component (HB Regs 2006 reg 75F(1)(a)). The model
# exempts any income-related ESA. This PR leaves the cap's reading of the
# benefit unit's ESA unchanged: scoping the exemption alone would cap the
# claimant with another member's own award in the capped sum
# (benefit_cap_reduction), which is out of scope here. The claimant's own
# award still exempts the family.
# ESA with the support component (HB Regs 2006 reg 75F(1)(a)).

- name: The claimant's own income-related ESA exempts the family from the benefit cap
- name: Another member's income-related ESA does not exempt the family from the benefit cap
period: 2025
input:
people:
claimant:
age: 40
is_claimant_or_partner: true
other:
age: 30
is_claimant_or_partner: false
current_education: NOT_IN_EDUCATION
esa_income_reported: 5_000
esa_includes_support_component: true
benunits:
benunit:
members: [claimant, other]
working_tax_credit: 0
households:
household:
members: [claimant, other]
output:
is_benefit_cap_exempt_health_disability: false

- name: The claimant's own income-related ESA with the support component exempts the family from the benefit cap
period: 2025
input:
people:
claimant:
age: 40
is_claimant_or_partner: true
esa_income_reported: 5_000
esa_includes_support_component: true
other:
age: 30
is_claimant_or_partner: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,35 @@
uc_limited_capability_for_WRA: [false, true]
uc_childcare_work_condition: true

- name: Reg. 32(1)(b)(i) - a partner whose own ESA lacks the support component still has limited capability for work
period: 2025
absolute_error_margin: 0
input:
people:
claimant:
age: 35
employment_income: 15_000
partner:
age: 34
is_disabled_for_benefits: true
has_own_esa_award: true
esa_includes_support_component: false
benunits:
benunit:
members: [claimant, partner]
households:
household:
members: [claimant, partner]
output:
# An ESA award without the support component means no limited capability
# for work-related activity (UC Regs 2013 reg. 40(1)(a)(ii)), but the
# partner still has limited capability for work (reg. 39), so limb (i)
# holds.
uc_limited_capability_for_WRA: [false, false]
uc_limited_capability_for_work: [false, true]
uc_unable_to_provide_childcare: [false, true]
uc_childcare_work_condition: true

- name: Reg. 32(1)(b)(ii) - ADM F7013 example, a partner caring full time for her disabled mother
period: 2025
absolute_error_margin: 0.01
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,24 @@
input:
age: 30
self_employment_income: 3_000
uc_has_limited_capability_for_work: true
uc_limited_capability_for_work: true
output:
uc_mif_applies: false

- name: Limited capability for work with an ESA award lacking the support component (section 21)
period: 2026
absolute_error_margin: 0
input:
age: 30
self_employment_income: 3_000
is_disabled_for_benefits: true
has_own_esa_award: true
esa_includes_support_component: false
output:
uc_limited_capability_for_WRA: false
uc_work_related_group_apart_from_earnings: WORK_PREPARATION
uc_mif_applies: false

- name: Pregnant claimant within 11 weeks of the expected week of confinement (section 19)
period: 2026
absolute_error_margin: 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@
absolute_error_margin: 0.01
input:
age: 19
uc_has_limited_capability_for_work: true
uc_limited_capability_for_work: true
output:
# 10.85 x 16 x 52.
uc_minimum_income_floor_gross: 9_027.20
Expand All @@ -296,7 +296,7 @@
absolute_error_margin: 0.01
input:
age: 20
uc_has_limited_capability_for_work: true
uc_limited_capability_for_work: true
output:
# "Alison's individual earnings threshold is 5.30 X 16 = 84.80, converted
# to a monthly amount - 84.80 x 52 / 12 = 367 per month": 367.47 before
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,17 +305,32 @@
period: 2026
input:
age: 30
uc_has_limited_capability_for_work: true
uc_limited_capability_for_work: true
output:
uc_work_related_group_apart_from_earnings: WORK_PREPARATION

- name: Section 21(1)(a) takes a claimant whose own ESA lacks the support component
period: 2026
input:
age: 30
is_disabled_for_benefits: true
has_own_esa_award: true
esa_includes_support_component: false
output:
# No limited capability for work-related activity (UC Regs 2013 reg.
# 40(1)(a)(ii)), so not section 19(2)(a), but limited capability for work
# (reg. 39), so section 21(1)(a).
uc_limited_capability_for_WRA: false
uc_limited_capability_for_work: true
uc_work_related_group_apart_from_earnings: WORK_PREPARATION

- name: Section 21 takes only a claimant who does not fall within section 19 or 20
period: 2026
input:
people:
parent:
age: 30
uc_has_limited_capability_for_work: true
uc_limited_capability_for_work: true
child:
age: 1
output:
Expand Down
24 changes: 24 additions & 0 deletions policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,30 @@
# Change = 14,753 - 22,020 = -7,267 per year.
benefit_cap: 14_753

- name: A 16-year-old outside education gives the family cap (UC reg 5(1)(a)).
period: 2025
input:
people:
claimant:
age: 30
is_benunit_head: true
is_parent: true
young_person:
age: 16
is_benunit_head: false
current_education: NOT_IN_EDUCATION
benunits:
family:
members: [claimant, young_person]
is_benefit_cap_exempt: false
households:
home:
members: [claimant, young_person]
region: SOUTH_EAST
output:
is_responsible_for_child_or_young_person_for_uc_or_housing_benefit: true
benefit_cap: 22_020

- name: A looked-after foster child does not give the claimant the family cap.
period: 2025
input:
Expand Down
Loading
Loading