From c2f0a209a07de7c96d13706648d1c05bb69a085b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 1 Oct 2026 09:19:36 -0400 Subject: [PATCH 01/12] Give the UC LCWRA and carer elements and the work allowance to claimants only The LCWRA element is for 'the fact that a claimant has limited capability for work and work-related activity' (UC Regs 2013 reg. 27(1)), the carer element is included 'where a claimant has regular and substantial caring responsibilities' (reg. 29(1)), and the work allowance turns on whether 'a single claimant' or 'joint claimants' have limited capability (reg. 22(1)(b)(i)). All three read every member of the benefit unit, so a disabled child gave the claimant the LCWRA element on top of the disabled child addition, and a qualifying young person who was a carer gave the claimant the carer element. The benefit cap exception for limited capability now tests that the LCWRA element is in the award (reg. 83(1)(a)) rather than any member's limited capability. A child's disability living allowance, a qualifying young person's personal independence payment and their entitlement to Carer's Allowance still exempt the award (reg. 83(1)(f), (g), (i)). Co-Authored-By: Claude Opus 5.5 --- .../carer_element/uc_carer_element.yaml | 154 +++++++++++++++++- .../uc_LCWRA_element.yaml | 139 ++++++++++++++++ .../is_uc_work_allowance_eligible.yaml | 89 ++++++++++ .../work_allowance/uc_work_allowance.yaml | 23 +++ ...enefit_cap_exempt_whose_circumstances.yaml | 110 +++++++++++++ ...is_benefit_cap_exempt_health_disability.py | 6 +- .../carer_element/uc_carer_element.py | 29 +++- .../limited_work_ability/uc_LCWRA_element.py | 23 ++- .../is_uc_work_allowance_eligible.py | 10 +- 9 files changed, 570 insertions(+), 13 deletions(-) create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_exempt_whose_circumstances.yaml diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml index 9b341354d..4b80fb19f 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml @@ -1,15 +1,155 @@ -- name: No carers, no element - period: 2022 +# Universal Credit Regulations 2013 reg. 29(1): "An award of universal credit +# is to include an amount ("the carer element") specified in the table in +# regulation 36 where a claimant has regular and substantial caring +# responsibilities for a severely disabled person". Reg. 30(1)(a) and (2): a +# person has those responsibilities if they satisfy the conditions for +# entitlement to a carer's allowance, "whether or not the person has made a +# claim for a carer's allowance" (35 hours a week: SSCBA 1992 s. 70). +# Reg. 36 table, carer element: £201.68 a month in 2025-26 (£2,420.16 a year) +# and £209.34 in 2026-27 (£2,512.08). + +- name: No carer, no carer element + period: 2026 absolute_error_margin: 0 input: - benunit_has_carer: false + age: 40 + care_hours: 0 output: uc_carer_element: 0 -- name: Carer gets element +- name: A single claimant caring 35 hours a week has the carer element, 2025-26 period: 2025 - absolute_error_margin: 100 + absolute_error_margin: 0.01 input: - benunit_has_carer: true + age: 40 + care_hours: 35 output: - uc_carer_element: 2417 + is_uc_assessed_claimant: true + uc_carer_element: 2_420.16 # 201.68 * 12 + +- name: A single claimant caring 35 hours a week has the carer element, 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + care_hours: 35 + output: + uc_carer_element: 2_512.08 # 209.34 * 12 + +- name: A claimant caring 34 hours a week has no carer element + period: 2026 + absolute_error_margin: 0 + input: + age: 40 + care_hours: 34 + output: + uc_carer_element: 0 + +- name: The carer element does not need a Carer's Allowance claim (reg. 30(2)) + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + care_hours: 35 + would_claim_carers_allowance: false + output: + carers_allowance: 0 + uc_carer_element: 2_512.08 + +- name: A qualifying young person who is a carer gives the claimant no carer element + period: 2026 + absolute_error_margin: 0 + input: + people: + parent: + age: 40 + is_parent: true + young_person: + age: 17 + current_education: UPPER_SECONDARY + care_hours: 40 + benunits: + benunit: + members: [parent, young_person] + output: + is_uc_assessed_claimant: [true, false] + is_carer_for_benefits: [false, true] + uc_carer_element: 0 + +- name: A qualifying young person receiving Carer's Allowance gives the claimant no carer element + period: 2026 + absolute_error_margin: 0 + input: + people: + parent: + age: 40 + is_parent: true + young_person: + age: 17 + current_education: UPPER_SECONDARY + carers_allowance_reported: 4_000 + benunits: + benunit: + members: [parent, young_person] + output: + is_uc_assessed_claimant: [true, false] + is_carer_for_benefits: [false, true] + uc_carer_element: 0 + +- name: The claimant's own caring still counts when a qualifying young person also cares + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + is_parent: true + care_hours: 35 + young_person: + age: 17 + current_education: UPPER_SECONDARY + care_hours: 40 + benunits: + benunit: + members: [parent, young_person] + output: + uc_carer_element: 2_512.08 + +- name: Joint claimants, one of them a carer, have one carer element + period: 2026 + absolute_error_margin: 0.01 + input: + people: + carer: + age: 40 + care_hours: 35 + partner: + age: 38 + benunits: + benunit: + members: [carer, partner] + output: + is_uc_assessed_claimant: [true, true] + uc_carer_element: 2_512.08 + +- name: A third member flagged as a claimant is not a claimant, and their caring gives no carer element + period: 2026 + absolute_error_margin: 0 + input: + people: + claimant: + age: 50 + is_uc_claimant: true + partner: + age: 48 + is_uc_claimant: true + adult_child: + age: 22 + is_uc_claimant: true + care_hours: 40 + benunits: + benunit: + members: [claimant, partner, adult_child] + output: + is_uc_assessed_claimant: [true, true, false] + uc_carer_element: 0 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/child_element/disability/limited_work_ability/uc_LCWRA_element.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/child_element/disability/limited_work_ability/uc_LCWRA_element.yaml index d06e4aac7..82357dff5 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/child_element/disability/limited_work_ability/uc_LCWRA_element.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/child_element/disability/limited_work_ability/uc_LCWRA_element.yaml @@ -1,3 +1,15 @@ +# Universal Credit Regulations 2013 reg. 27(1): "An award of universal credit +# is to include an amount in respect of the fact that a claimant has limited +# capability for work and work-related activity ("the LCWRA element")." +# Reg. 36 table, LCWRA element: £423.27 a month in 2025-26 (£5,079.24 a year); +# in 2026-27, £217.26 for a claimant "other than a pre-2026 claimant, a severe +# conditions criteria claimant or a claimant who is terminally ill" (£2,607.12 +# a year). These tests run the formula, which uses that 2026-27 amount; a +# simulation then sets protected amounts for existing claimants. +# A disabled child or qualifying young person has the "additional amount for +# disabled child or qualifying young person" instead (reg. 24(2)): lower rate +# £164.79 a month in 2026-27 (£1,977.48 a year). + - name: Ineligible period: 2022 absolute_error_margin: 0 @@ -23,3 +35,130 @@ members: person output: uc_LCWRA_element: 2_607.12 + +- name: A single claimant with LCWRA has the LCWRA element, 2025-26 + period: 2025 + absolute_error_margin: 0.01 + input: + age: 40 + uc_limited_capability_for_WRA: true + output: + is_uc_assessed_claimant: true + uc_LCWRA_element: 5_079.24 # 423.27 * 12 + +- name: A disabled child gives the claimant no LCWRA element, only the disabled child addition + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + is_parent: true + child: + age: 10 + is_disabled_for_benefits: true + benunits: + benunit: + members: [parent, child] + output: + is_uc_assessed_claimant: [true, false] + uc_limited_capability_for_WRA: [false, true] + uc_LCWRA_element: 0 + uc_individual_disabled_child_element: [0, 1_977.48] # 164.79 * 12 + uc_disability_elements: 1_977.48 + +- name: A child flagged with limited capability gives the claimant no LCWRA element + period: 2026 + absolute_error_margin: 0 + input: + people: + parent: + age: 40 + is_parent: true + child: + age: 10 + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [parent, child] + output: + uc_LCWRA_element: 0 + uc_disability_elements: 0 + +- name: A disabled qualifying young person gives the claimant no LCWRA element + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + is_parent: true + young_person: + age: 17 + current_education: UPPER_SECONDARY + is_disabled_for_benefits: true + benunits: + benunit: + members: [parent, young_person] + output: + is_uc_assessed_claimant: [true, false] + uc_LCWRA_element: 0 + uc_disability_elements: 1_977.48 + +- name: A disabled parent of a disabled child has the LCWRA element and the disabled child addition + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + is_parent: true + is_disabled_for_benefits: true + child: + age: 10 + is_disabled_for_benefits: true + benunits: + benunit: + members: [parent, child] + output: + uc_LCWRA_element: 2_607.12 + uc_disability_elements: 4_584.60 # 2,607.12 + 1,977.48 + +- name: Joint claimants, one of them with LCWRA, have the LCWRA element + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + uc_limited_capability_for_WRA: true + partner: + age: 38 + benunits: + benunit: + members: [claimant, partner] + output: + is_uc_assessed_claimant: [true, true] + uc_LCWRA_element: 2_607.12 + +- name: A third member flagged as a claimant is not a claimant, and their limited capability gives no LCWRA element + period: 2026 + absolute_error_margin: 0 + input: + people: + claimant: + age: 50 + is_uc_claimant: true + partner: + age: 48 + is_uc_claimant: true + adult_child: + age: 22 + is_uc_claimant: true + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [claimant, partner, adult_child] + output: + is_uc_assessed_claimant: [true, true, false] + uc_LCWRA_element: 0 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/work_allowance/is_uc_work_allowance_eligible.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/work_allowance/is_uc_work_allowance_eligible.yaml index 471036058..4c062a41f 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/work_allowance/is_uc_work_allowance_eligible.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/work_allowance/is_uc_work_allowance_eligible.yaml @@ -116,3 +116,92 @@ output: is_child_or_qualifying_young_person_for_universal_credit: [false, false] is_uc_work_allowance_eligible: false + +# Reg. 22(1)(b)(i): no work allowance "where a single claimant does not have, +# or neither of joint claimants has, responsibility for a child or qualifying +# young person or limited capability for work". + +- name: Case 8, a single claimant with limited capability has a work allowance. + period: 2026 + input: + age: 40 + uc_limited_capability_for_WRA: true + output: + is_uc_work_allowance_eligible: true + +- name: Case 9, joint claimants where one has limited capability have a work allowance. + period: 2026 + input: + people: + person1: + age: 40 + uc_limited_capability_for_WRA: false + person2: + age: 38 + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [person1, person2] + output: + is_uc_work_allowance_eligible: true + +- name: Case 10, a nonqualifying 17-year-old's limited capability does not create a work allowance. + period: 2026 + input: + people: + person1: + age: 40 + is_uc_claimant: true + uc_limited_capability_for_WRA: false + person2: + age: 17 + is_uc_claimant: false + is_in_non_advanced_education: false + is_in_approved_training: false + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [person1, person2] + output: + is_uc_assessed_claimant: [true, false] + is_child_or_qualifying_young_person_for_universal_credit: [false, false] + is_uc_work_allowance_eligible: false + uc_work_allowance: 0 + +- name: Case 11, a third flagged claimant's limited capability does not create a work allowance. + period: 2026 + input: + people: + person1: + age: 50 + is_uc_claimant: true + person2: + age: 48 + is_uc_claimant: true + person3: + age: 22 + is_uc_claimant: true + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [person1, person2, person3] + output: + is_uc_assessed_claimant: [true, true, false] + is_uc_work_allowance_eligible: false + +- name: Case 12, a looked-after child's limited capability does not create a work allowance. + period: 2026 + input: + people: + person1: + age: 40 + is_parent: true + person2: + age: 5 + is_looked_after_by_local_authority: true + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [person1, person2] + output: + is_uc_work_allowance_eligible: false diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/work_allowance/uc_work_allowance.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/work_allowance/uc_work_allowance.yaml index 6d1c8f1f2..73c3a7705 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/work_allowance/uc_work_allowance.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/work_allowance/uc_work_allowance.yaml @@ -6,3 +6,26 @@ uc_limited_capability_for_WRA: true output: uc_work_allowance: 4_924 + +# Reg. 22(2) table, 2026-27: higher work allowance £710.00 a month (no housing +# costs element), lower work allowance £427.00 (with one). + +- name: Higher work allowance for a claimant with limited capability and no housing costs element, 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + uc_housing_costs_element: 0 + uc_limited_capability_for_WRA: true + output: + uc_work_allowance: 8_520 # 710 * 12 + +- name: Lower work allowance for a claimant with limited capability and a housing costs element, 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + uc_housing_costs_element: 1 + uc_limited_capability_for_WRA: true + output: + uc_work_allowance: 5_124 # 427 * 12 diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_exempt_whose_circumstances.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_exempt_whose_circumstances.yaml new file mode 100644 index 000000000..1c4813c25 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_exempt_whose_circumstances.yaml @@ -0,0 +1,110 @@ +# Universal Credit Regulations 2013 reg. 83(1): "The benefit cap does not apply +# in relation to any assessment period where— (a) the LCWRA element is included +# in the award of universal credit or the claimant is receiving an employment +# and support allowance that includes the support component; ... (f) a +# claimant, or a child or qualifying young person for whom a claimant is +# responsible, is receiving disability living allowance; ... (g) a claimant, +# or a qualifying young person for whom a claimant is responsible, is +# receiving personal independence payment; ... (i) a claimant, or a qualifying +# young person for whom a claimant is responsible, is entitled to carer's +# allowance; ... (j) the carer element is included in the award of universal +# credit". +# The LCWRA element and the carer element are included only for a claimant +# (regs 27(1) and 29(1)). + +- name: A claimant with LCWRA is exempt, the LCWRA element being in the award (reg. 83(1)(a)) + period: 2026 + input: + people: + parent: + age: 40 + is_parent: true + uc_limited_capability_for_WRA: true + child: + age: 10 + benunits: + benunit: + members: [parent, child] + output: + is_benefit_cap_exempt_health_disability: true + +- name: A disabled child not receiving disability living allowance does not exempt the award + period: 2026 + input: + people: + parent: + age: 40 + is_parent: true + child: + age: 10 + is_disabled_for_benefits: true + benunits: + benunit: + members: [parent, child] + output: + uc_LCWRA_element: 0 + dla: [0, 0] + is_benefit_cap_exempt_health_disability: false + +- name: A child receiving disability living allowance exempts the award (reg. 83(1)(f)) + period: 2026 + input: + people: + parent: + age: 40 + is_parent: true + child: + age: 10 + dla_sc_category: MIDDLE + benunits: + benunit: + members: [parent, child] + output: + uc_LCWRA_element: 0 + is_benefit_cap_exempt_health_disability: true + +- name: A qualifying young person receiving personal independence payment exempts the award (reg. 83(1)(g)) + period: 2026 + input: + people: + parent: + age: 40 + is_parent: true + young_person: + age: 17 + current_education: UPPER_SECONDARY + pip_dl_category: STANDARD + benunits: + benunit: + members: [parent, young_person] + output: + uc_LCWRA_element: 0 + is_benefit_cap_exempt_health_disability: true + +- name: A qualifying young person entitled to Carer's Allowance exempts the award (reg. 83(1)(i)) without giving it the carer element + period: 2026 + input: + people: + parent: + age: 40 + is_parent: true + young_person: + age: 17 + current_education: UPPER_SECONDARY + care_hours: 40 + benunits: + benunit: + members: [parent, young_person] + output: + uc_carer_element: 0 + is_benefit_cap_exempt_health_disability: true + +- name: A claimant who is a carer is exempt, the carer element being in the award (reg. 83(1)(j)) + period: 2026 + input: + age: 40 + care_hours: 35 + would_claim_carers_allowance: false + output: + carers_allowance: 0 + is_benefit_cap_exempt_health_disability: true diff --git a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py index 69d9205c9..3bf4c2d27 100644 --- a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py +++ b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py @@ -15,8 +15,10 @@ def formula(benunit, period, parameters): has_pensioner = benunit.any(over_pension_age) # UC-specific exemptions - # Limited capability for work and work-related activity - has_lcwra = benunit.any(person("uc_limited_capability_for_WRA", period)) + # Limited capability for work and work-related activity: "the LCWRA + # element is included in the award" (UC Regs 2013 reg. 83(1)(a)), + # which it is only for a claimant (reg. 27(1)). + has_lcwra = benunit("uc_LCWRA_element", period) > 0 # Carer element in UC indicates caring for someone with disability gets_uc_carer_element = benunit("uc_carer_element", period) > 0 diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element.py b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element.py index 917d46bcb..0426b240d 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element.py @@ -5,10 +5,35 @@ class uc_carer_element(Variable): value_type = float entity = BenUnit label = "Universal Credit carer element" + documentation = ( + "The amount an award includes because a claimant has regular and " + "substantial caring responsibilities for a severely disabled person. " + "Only a claimant's caring counts: a child or qualifying young person " + "who is a carer does not give the claimants the element." + ) definition_period = YEAR unit = GBP - defined_for = "benunit_has_carer" + reference = [ + dict( + title="Welfare Reform Act 2012 s. 12(2)(c)", + href="https://www.legislation.gov.uk/ukpga/2012/5/section/12", + ), + dict( + title="Universal Credit Regulations 2013 reg. 29(1)", + href="https://www.legislation.gov.uk/uksi/2013/376/regulation/29", + ), + dict( + title="Universal Credit Regulations 2013 reg. 30", + href="https://www.legislation.gov.uk/uksi/2013/376/regulation/30", + ), + ] def formula(benunit, period, parameters): p = parameters(period).gov.dwp.universal_credit.elements.carer - return p.amount * MONTHS_IN_YEAR + person = benunit.members + # Reg. 29(1): the element is included "where a claimant has regular + # and substantial caring responsibilities for a severely disabled + # person" (reg. 30: `is_carer_for_benefits`). + claimant = person("is_uc_assessed_claimant", period) + carer = claimant & person("is_carer_for_benefits", period) + return benunit.any(carer) * p.amount * MONTHS_IN_YEAR diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_LCWRA_element.py b/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_LCWRA_element.py index 6460d3aaa..01811ad5e 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_LCWRA_element.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_LCWRA_element.py @@ -5,11 +5,32 @@ class uc_LCWRA_element(Variable): value_type = float entity = BenUnit label = "Universal Credit limited capability for work-related-activity element" + documentation = ( + "The amount an award includes because a claimant has limited " + "capability for work and work-related activity. Only a claimant's " + "limited capability counts: a disabled child or qualifying young " + "person gives the award the disabled child addition instead " + "(`uc_individual_disabled_child_element`)." + ) definition_period = YEAR unit = GBP + reference = [ + dict( + title="Welfare Reform Act 2012 s. 12(2)(b)", + href="https://www.legislation.gov.uk/ukpga/2012/5/section/12", + ), + dict( + title="Universal Credit Regulations 2013 reg. 27(1)", + href="https://www.legislation.gov.uk/uksi/2013/376/regulation/27", + ), + ] def formula(benunit, period, parameters): p = parameters(period).gov.dwp.universal_credit.elements.disabled - limited_capability = benunit.members("uc_limited_capability_for_WRA", period) + person = benunit.members + # Reg. 27(1): the element is "in respect of the fact that a claimant + # has limited capability for work and work-related activity". + claimant = person("is_uc_assessed_claimant", period) + limited_capability = claimant & person("uc_limited_capability_for_WRA", period) person_amounts = limited_capability * p.amount return benunit.sum(person_amounts) * MONTHS_IN_YEAR diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/work_allowance/is_uc_work_allowance_eligible.py b/policyengine_uk/variables/gov/dwp/universal_credit/work_allowance/is_uc_work_allowance_eligible.py index 28307abe9..b9e45febc 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/work_allowance/is_uc_work_allowance_eligible.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/work_allowance/is_uc_work_allowance_eligible.py @@ -10,7 +10,15 @@ class is_uc_work_allowance_eligible(Variable): def formula(benunit, period, parameters): person = benunit.members - has_LCWRA = benunit.any(person("uc_limited_capability_for_WRA", period)) + # Reg. 22(1)(b)(i): there is no work allowance "where a single + # claimant does not have, or neither of joint claimants has, + # responsibility for a child or qualifying young person or limited + # capability for work". A dependant's limited capability is not the + # claimant's. + has_LCWRA = benunit.any( + person("is_uc_assessed_claimant", period) + & person("uc_limited_capability_for_WRA", period) + ) has_children = benunit.any( person("is_child_or_qualifying_young_person_for_universal_credit", period) & ~person("is_uc_claimant", period) From 87b68c7a714be533a30f355ffa0dc26ae1f68d27 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 1 Oct 2026 09:20:00 -0400 Subject: [PATCH 02/12] Include one LCWRA element where both joint claimants have limited capability UC Regs 2013 reg. 27(4)(a): 'In the case of joint claimants, where each of them has limited capability for work and work-related activity, the award is only to include one LCWRA element'. The formula summed an element for each member. The Universal Credit rebalancing modifier already sets one amount per award for 2026 to 2029, so this changes 2025 and 2030 onwards. Co-Authored-By: Claude Opus 5.5 --- .../uc_LCWRA_element.yaml | 39 +++++++++++++++++++ .../limited_work_ability/uc_LCWRA_element.py | 11 ++++-- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/child_element/disability/limited_work_ability/uc_LCWRA_element.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/child_element/disability/limited_work_ability/uc_LCWRA_element.yaml index 82357dff5..94ac72cab 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/child_element/disability/limited_work_ability/uc_LCWRA_element.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/child_element/disability/limited_work_ability/uc_LCWRA_element.yaml @@ -162,3 +162,42 @@ output: is_uc_assessed_claimant: [true, true, false] uc_LCWRA_element: 0 + +# Reg. 27(4)(a): "In the case of joint claimants, where each of them has +# limited capability for work and work-related activity, the award is only to +# include one LCWRA element". + +- name: Joint claimants who both have LCWRA have one LCWRA element, 2025-26 + period: 2025 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + uc_limited_capability_for_WRA: true + partner: + age: 38 + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [claimant, partner] + output: + is_uc_assessed_claimant: [true, true] + uc_LCWRA_element: 5_079.24 # 423.27 * 12, once + +- name: Joint claimants who both have LCWRA have one LCWRA element, 2026-27 + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + uc_limited_capability_for_WRA: true + partner: + age: 38 + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [claimant, partner] + output: + uc_LCWRA_element: 2_607.12 # 217.26 * 12, once diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_LCWRA_element.py b/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_LCWRA_element.py index 01811ad5e..eacac442e 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_LCWRA_element.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_LCWRA_element.py @@ -10,7 +10,8 @@ class uc_LCWRA_element(Variable): "capability for work and work-related activity. Only a claimant's " "limited capability counts: a disabled child or qualifying young " "person gives the award the disabled child addition instead " - "(`uc_individual_disabled_child_element`)." + "(`uc_individual_disabled_child_element`). Joint claimants who both " + "have limited capability have one element." ) definition_period = YEAR unit = GBP @@ -20,7 +21,7 @@ class uc_LCWRA_element(Variable): href="https://www.legislation.gov.uk/ukpga/2012/5/section/12", ), dict( - title="Universal Credit Regulations 2013 reg. 27(1)", + title="Universal Credit Regulations 2013 reg. 27(1) and (4)", href="https://www.legislation.gov.uk/uksi/2013/376/regulation/27", ), ] @@ -32,5 +33,7 @@ def formula(benunit, period, parameters): # has limited capability for work and work-related activity". claimant = person("is_uc_assessed_claimant", period) limited_capability = claimant & person("uc_limited_capability_for_WRA", period) - person_amounts = limited_capability * p.amount - return benunit.sum(person_amounts) * MONTHS_IN_YEAR + # Reg. 27(4)(a): "In the case of joint claimants, where each of them + # has limited capability for work and work-related activity, the + # award is only to include one LCWRA element". + return benunit.any(limited_capability) * p.amount * MONTHS_IN_YEAR From 84bb078087b7b446a5e3b3d30c7b2b8669c0508b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 1 Oct 2026 09:21:41 -0400 Subject: [PATCH 03/12] Give each joint claimant who is a carer the carer element, and none to a carer with the LCWRA element UC Regs 2013 reg. 29(2): 'an award is to include the carer element for both joint claimants if they both qualify for it, but only if they are not caring for the same severely disabled person'. The model does not record who is cared for; it already pays each of two carers Carer's Allowance, so it takes them to care for different people. Reg. 29(4): a claimant who is a carer and has limited capability for work and work-related activity has only the LCWRA element, unless that element is included for the other joint claimant. The formula included both elements for the same claimant. Co-Authored-By: Claude Opus 5.5 --- .../carer_element/uc_carer_element.yaml | 156 ++++++++++++++++++ .../carer_element/uc_carer_element.py | 31 +++- 2 files changed, 184 insertions(+), 3 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml index 4b80fb19f..6fdee2f78 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml @@ -153,3 +153,159 @@ output: is_uc_assessed_claimant: [true, true, false] uc_carer_element: 0 + +# Reg. 29(2): "In the case of joint claimants, an award is to include the +# carer element for both joint claimants if they both qualify for it, but only +# if they are not caring for the same severely disabled person." The model +# does not record who is cared for and takes two carers to care for different +# people. + +- name: Joint claimants who are both carers have two carer elements + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + care_hours: 35 + partner: + age: 38 + care_hours: 35 + benunits: + benunit: + members: [claimant, partner] + output: + uc_carer_element: 5_024.16 # 209.34 * 12 * 2 + +# Reg. 29(4): "Where an amount would, apart from this paragraph, be included +# in an award in relation to a claimant by virtue of paragraphs (1) to (3), +# and the claimant has limited capability for work and work-related activity +# (and, in the case of joint claimants, the LCWRA element has not been +# included in respect of the other claimant), only the LCWRA element may be +# included in respect of the claimant." Reg. 27(4)(a): joint claimants who +# both have limited capability have one LCWRA element. + +- name: A single claimant who is a carer with LCWRA has the LCWRA element only + period: 2026 + absolute_error_margin: 0.01 + input: + age: 40 + care_hours: 35 + uc_limited_capability_for_WRA: true + output: + uc_carer_element: 0 + uc_LCWRA_element: 2_607.12 + +- name: A carer with LCWRA whose partner has neither has the LCWRA element only + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + care_hours: 35 + uc_limited_capability_for_WRA: true + partner: + age: 38 + benunits: + benunit: + members: [claimant, partner] + output: + uc_carer_element: 0 + uc_LCWRA_element: 2_607.12 + +- name: A carer whose partner has LCWRA - the award has both elements + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + care_hours: 35 + partner: + age: 38 + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [claimant, partner] + output: + uc_carer_element: 2_512.08 + uc_LCWRA_element: 2_607.12 + +- name: A carer with LCWRA whose partner has LCWRA keeps the carer element, the LCWRA element being the partner's + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + care_hours: 35 + uc_limited_capability_for_WRA: true + partner: + age: 38 + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [claimant, partner] + output: + uc_carer_element: 2_512.08 + uc_LCWRA_element: 2_607.12 + +- name: A carer with LCWRA whose partner is a carer - LCWRA element for one, carer element for the other + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + care_hours: 35 + uc_limited_capability_for_WRA: true + partner: + age: 38 + care_hours: 35 + benunits: + benunit: + members: [claimant, partner] + output: + uc_carer_element: 2_512.08 + uc_LCWRA_element: 2_607.12 + +- name: Joint claimants who are both carers with LCWRA have one LCWRA element and one carer element + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + care_hours: 35 + uc_limited_capability_for_WRA: true + partner: + age: 38 + care_hours: 35 + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [claimant, partner] + output: + uc_carer_element: 2_512.08 + uc_LCWRA_element: 2_607.12 + +- name: A qualifying young person's limited capability does not displace the claimant's carer element + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + is_parent: true + care_hours: 35 + young_person: + age: 17 + current_education: UPPER_SECONDARY + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [parent, young_person] + output: + uc_carer_element: 2_512.08 + uc_LCWRA_element: 0 diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element.py b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element.py index 0426b240d..a50887d45 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element.py @@ -9,7 +9,13 @@ class uc_carer_element(Variable): "The amount an award includes because a claimant has regular and " "substantial caring responsibilities for a severely disabled person. " "Only a claimant's caring counts: a child or qualifying young person " - "who is a carer does not give the claimants the element." + "who is a carer does not give the claimants the element. Joint " + "claimants who are both carers have an element each; the model does " + "not record who is cared for, and takes two carers to care for " + "different people, as it does when it pays each of them Carer's " + "Allowance. A claimant who is a carer and has limited capability for " + "work and work-related activity has the LCWRA element instead, unless " + "that element is included for the other joint claimant." ) definition_period = YEAR unit = GBP @@ -19,7 +25,7 @@ class uc_carer_element(Variable): href="https://www.legislation.gov.uk/ukpga/2012/5/section/12", ), dict( - title="Universal Credit Regulations 2013 reg. 29(1)", + title="Universal Credit Regulations 2013 reg. 29(1), (2) and (4)", href="https://www.legislation.gov.uk/uksi/2013/376/regulation/29", ), dict( @@ -36,4 +42,23 @@ def formula(benunit, period, parameters): # person" (reg. 30: `is_carer_for_benefits`). claimant = person("is_uc_assessed_claimant", period) carer = claimant & person("is_carer_for_benefits", period) - return benunit.any(carer) * p.amount * MONTHS_IN_YEAR + limited_capability = claimant & person("uc_limited_capability_for_WRA", period) + # Reg. 29(2): "an award is to include the carer element for both joint + # claimants if they both qualify for it, but only if they are not + # caring for the same severely disabled person". + carers_without_limited_capability = benunit.sum(carer & ~limited_capability) + # Reg. 29(4): where the carer "has limited capability for work and + # work-related activity (and, in the case of joint claimants, the + # LCWRA element has not been included in respect of the other + # claimant), only the LCWRA element may be included in respect of the + # claimant". Where both joint claimants have limited capability the + # award has one LCWRA element (reg. 27(4)), for one of them, which + # leaves a carer element for the other. + carers_with_limited_capability = benunit.sum(carer & limited_capability) + claimants_with_limited_capability = benunit.sum(limited_capability) + carers_keeping_the_carer_element = min_( + carers_with_limited_capability, + max_(claimants_with_limited_capability - 1, 0), + ) + elements = carers_without_limited_capability + carers_keeping_the_carer_element + return elements * p.amount * MONTHS_IN_YEAR From 672e1e5556af1d7783f022a9e1dcd067798c19a4 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 1 Oct 2026 09:23:47 -0400 Subject: [PATCH 04/12] Exempt an award from the benefit cap only for a claimant's ESA, industrial injuries benefit, Attendance Allowance and armed forces payments UC Regs 2013 reg. 83(1)(a) to (e) name 'the claimant' or 'a claimant' for ESA with the support component, industrial injuries benefit, attendance allowance and armed forces compensation payments, where reg. 83(1)(f) to (ia) add 'a child or qualifying young person for whom a claimant is responsible' for disability living allowance and 'a qualifying young person' for personal independence payment and Carer's Allowance. HB Regs 2006 reg. 75F(1) draws the same lines. The exemptions summed every member of the benefit unit for all of them. Co-Authored-By: Claude Opus 5.5 --- ...enefit_cap_exempt_whose_circumstances.yaml | 153 ++++++++++++++++++ ...is_benefit_cap_exempt_health_disability.py | 55 +++++-- .../gov/dwp/is_benefit_cap_exempt_other.py | 11 +- 3 files changed, 208 insertions(+), 11 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_exempt_whose_circumstances.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_exempt_whose_circumstances.yaml index 1c4813c25..db818b406 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_exempt_whose_circumstances.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_exempt_whose_circumstances.yaml @@ -108,3 +108,156 @@ output: carers_allowance: 0 is_benefit_cap_exempt_health_disability: true + +# Reg. 83(1)(a) to (e): "the claimant is receiving an employment and support +# allowance that includes the support component"; "(b) a claimant is receiving +# industrial injuries benefit; (c) a claimant is receiving attendance +# allowance; ... (e) a claimant is receiving a payment under article 15(1)(c) +# or article 29(1)(a) of the Armed Forces and Reserve Forces (Compensation +# Scheme) Order 2011". These name the claimant alone, so a dependant's payment +# does not exempt the award. Housing Benefit Regulations 2006 reg. 75F(1)(a) to +# (d) say "the claimant or the claimant's partner". + +- name: A claimant receiving industrial injuries benefit exempts the award (reg. 83(1)(b)) + period: 2026 + input: + people: + parent: + age: 40 + is_parent: true + iidb_reported: 3_000 + young_person: + age: 17 + current_education: UPPER_SECONDARY + benunits: + benunit: + members: [parent, young_person] + output: + is_benefit_cap_exempt_health_disability: true + +- name: A qualifying young person receiving industrial injuries benefit does not exempt the award + period: 2026 + input: + people: + parent: + age: 40 + is_parent: true + young_person: + age: 17 + current_education: UPPER_SECONDARY + iidb_reported: 3_000 + benunits: + benunit: + members: [parent, young_person] + output: + is_uc_assessed_claimant: [true, false] + is_benefit_cap_exempt_health_disability: false + is_benefit_cap_exempt: false + +- name: A claimant receiving contributory ESA exempts the award (reg. 83(1)(a)) + period: 2026 + input: + people: + parent: + age: 40 + is_parent: true + esa_contrib_reported: 5_000 + young_person: + age: 17 + current_education: UPPER_SECONDARY + benunits: + benunit: + members: [parent, young_person] + output: + is_benefit_cap_exempt_health_disability: true + is_benefit_cap_exempt_other: true + +- name: A dependant receiving contributory ESA does not exempt the award + period: 2026 + input: + people: + parent: + age: 40 + is_parent: true + young_person: + age: 17 + current_education: UPPER_SECONDARY + esa_contrib_reported: 5_000 + benunits: + benunit: + members: [parent, young_person] + output: + is_benefit_cap_exempt_health_disability: false + is_benefit_cap_exempt_other: false + is_benefit_cap_exempt: false + +- name: A claimant receiving an armed forces compensation payment exempts the award (reg. 83(1)(e)) + period: 2026 + input: + people: + parent: + age: 40 + is_parent: true + afcs_reported: 5_000 + young_person: + age: 17 + current_education: UPPER_SECONDARY + benunits: + benunit: + members: [parent, young_person] + output: + is_benefit_cap_exempt_other: true + +- name: A dependant receiving an armed forces compensation payment does not exempt the award + period: 2026 + input: + people: + parent: + age: 40 + is_parent: true + young_person: + age: 17 + current_education: UPPER_SECONDARY + afcs_reported: 5_000 + benunits: + benunit: + members: [parent, young_person] + output: + is_benefit_cap_exempt_other: false + +- name: A partner receiving industrial injuries benefit exempts the award, each joint claimant being a claimant + period: 2026 + input: + people: + claimant: + age: 40 + partner: + age: 38 + iidb_reported: 3_000 + benunits: + benunit: + members: [claimant, partner] + output: + is_uc_assessed_claimant: [true, true] + is_benefit_cap_exempt_health_disability: true + +- name: A third member flagged as a claimant receiving personal independence payment, not being a claimant or qualifying young person, does not exempt the award + period: 2026 + input: + people: + claimant: + age: 50 + is_uc_claimant: true + partner: + age: 48 + is_uc_claimant: true + adult_child: + age: 22 + is_uc_claimant: true + pip_dl_category: STANDARD + benunits: + benunit: + members: [claimant, partner, adult_child] + output: + is_uc_assessed_claimant: [true, true, false] + is_benefit_cap_exempt_health_disability: false diff --git a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py index 3bf4c2d27..04504d86a 100644 --- a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py +++ b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py @@ -34,15 +34,34 @@ def formula(benunit, period, parameters): earnings_threshold = 10_152 meets_earnings_test = uc_earned >= earnings_threshold - # Disability and carer benefits that exempt from cap - QUAL_PERSONAL_BENEFITS = [ + # Disability and carer benefits that exempt from cap. Whose receipt + # exempts the award depends on the benefit (UC Regs 2013 reg. 83(1)); + # HB Regs 2006 reg. 75F(1) draws the same lines between the claimant + # or partner and a child or young person. + claimant = person("is_uc_assessed_claimant", period) + child_or_young_person = person( + "is_child_or_qualifying_young_person_for_universal_credit", period + ) + young_person = child_or_young_person & person( + "is_qualifying_young_person_for_universal_credit", period + ) + # "a claimant is receiving" (reg. 83(1)(b), (c)) + QUAL_CLAIMANT_BENEFITS = [ "attendance_allowance", + "iidb", # Industrial injuries disability benefit + ] + # "a claimant, or a qualifying young person for whom a claimant is + # responsible" (reg. 83(1)(g), (i), (ia)) + QUAL_CLAIMANT_OR_YOUNG_PERSON_BENEFITS = [ "carers_allowance", "carer_support_payment", - "dla", # Disability Living Allowance (includes components) "pip_dl", # PIP daily living component "pip_m", # PIP mobility component - "iidb", # Industrial injuries disability benefit + ] + # "a claimant, or a child or qualifying young person for whom a + # claimant is responsible" (reg. 83(1)(f)) + QUAL_CLAIMANT_OR_CHILD_BENEFITS = [ + "dla", # Disability Living Allowance (includes components) ] # ESA and Working Tax Credit @@ -51,14 +70,32 @@ def formula(benunit, period, parameters): "working_tax_credit", # If getting WTC, likely working enough ] - qualifying_personal_benefits = add(benunit, period, QUAL_PERSONAL_BENEFITS) + qualifying_personal_benefits = ( + add_for_members(benunit, period, QUAL_CLAIMANT_BENEFITS, claimant) + + add_for_members( + benunit, + period, + QUAL_CLAIMANT_OR_YOUNG_PERSON_BENEFITS, + claimant | young_person, + ) + + add_for_members( + benunit, + period, + QUAL_CLAIMANT_OR_CHILD_BENEFITS, + claimant | child_or_young_person, + ) + ) qualifying_benunit_benefits = add(benunit, period, QUAL_BENUNIT_BENEFITS) - # Check for Armed Forces Compensation Scheme payments - afcs = benunit("afcs", period) > 0 + # Check for Armed Forces Compensation Scheme payments: "a claimant is + # receiving" (reg. 83(1)(e)) + afcs = add_for_members(benunit, period, ["afcs"], claimant) > 0 - # ESA contribution-based with support component - esa_support_component = benunit("esa_contrib", period) > 0 + # ESA contribution-based with support component: "the claimant is + # receiving" (reg. 83(1)(a)) + esa_support_component = ( + add_for_members(benunit, period, ["esa_contrib"], claimant) > 0 + ) return ( has_lcwra diff --git a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_other.py b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_other.py index 0fb154750..11f3ba164 100644 --- a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_other.py +++ b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_other.py @@ -51,10 +51,17 @@ def formula(benunit, period, parameters): qualifying_personal_benefits = add(benunit, period, QUAL_PERSONAL_BENEFITS) qualifying_benunit_benefits = add(benunit, period, QUAL_BENUNIT_BENEFITS) + # These exempt the award where "a claimant is receiving" them (UC Regs + # 2013 reg. 83(1)(a), (e); HB Regs 2006 reg. 75F(1)(a), (d): "the + # claimant or the claimant's partner"), not where a dependant is. + claimants = benunit.members("is_uc_assessed_claimant", period) + # Check for Armed Forces Compensation Scheme payments - afcs = benunit("afcs", period) > 0 + afcs = add_for_members(benunit, period, ["afcs"], claimants) > 0 # ESA contribution-based with support component - esa_support_component = benunit("esa_contrib", period) > 0 + esa_support_component = ( + add_for_members(benunit, period, ["esa_contrib"], claimants) > 0 + ) return has_pensioner | afcs | esa_support_component From 0ef77e453a8bd2ea7f662d7bd05aa4e92b0522ef Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 1 Oct 2026 09:52:54 -0400 Subject: [PATCH 05/12] Pay the disability basic income for the LCWRA element only to a claimant with limited capability The contrib reform pays anyone 'receiving' the Universal Credit LCWRA element. Now that the element is the claimant's (UC Regs 2013 reg. 27(1)), a child's or qualifying young person's limited capability flag no longer qualifies them. Co-Authored-By: Claude Opus 5.5 --- .../disability_basic_income.yaml | 18 ++++++++++++++++++ .../disability_basic_income.py | 9 +++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/contrib/disability_basic_income/disability_basic_income.yaml b/policyengine_uk/tests/policy/baseline/contrib/disability_basic_income/disability_basic_income.yaml index 4592e250b..fe96d0e36 100644 --- a/policyengine_uk/tests/policy/baseline/contrib/disability_basic_income/disability_basic_income.yaml +++ b/policyengine_uk/tests/policy/baseline/contrib/disability_basic_income/disability_basic_income.yaml @@ -70,3 +70,21 @@ is_disabled_for_benefits: true output: disability_basic_income: 5_200 + +- name: Disability basic income excludes a child's LCWRA flag, the LCWRA element being a claimant's + period: 2025 + absolute_error_margin: 0.01 + input: + gov.contrib.disability_basic_income.amount: 100 + people: + parent: + age: 40 + is_parent: true + child: + age: 10 + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [parent, child] + output: + disability_basic_income: [0, 0] diff --git a/policyengine_uk/variables/contrib/disability_basic_income/disability_basic_income.py b/policyengine_uk/variables/contrib/disability_basic_income/disability_basic_income.py index 10e7aaeed..0723a7fd0 100644 --- a/policyengine_uk/variables/contrib/disability_basic_income/disability_basic_income.py +++ b/policyengine_uk/variables/contrib/disability_basic_income/disability_basic_income.py @@ -13,8 +13,13 @@ def formula(person, period, parameters): amount = parameters(period).gov.contrib.disability_basic_income.amount receives_dla = person("dla", period) > 0 receives_pip = person("pip", period) > 0 - receives_uc_lcwra = person("uc_limited_capability_for_WRA", period) & ( - person.benunit("universal_credit", period) > 0 + # The LCWRA element is a claimant's (UC Regs 2013 reg. 27(1)): a + # child's or qualifying young person's limited capability does not + # put it in the award. + receives_uc_lcwra = ( + person("is_uc_assessed_claimant", period) + & person("uc_limited_capability_for_WRA", period) + & (person.benunit("universal_credit", period) > 0) ) eligible = receives_dla | receives_pip | receives_uc_lcwra return eligible * amount * WEEKS_IN_YEAR From 3d6759859a31b0b6e96480b0bef29f812153122f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 1 Oct 2026 09:53:55 -0400 Subject: [PATCH 06/12] Test that a dependant's disability or caring never gives the claimants an element, against DWP's table of combinations Hypothesis properties: a dependant's disability, limited capability, caring or Carer's Allowance never changes the LCWRA element, the carer element or the work allowance; the elements match DWP's ADM F6037 table for all sixteen combinations of two claimants' carer and LCWRA status, with amounts written from the reg. 36 table; at most one LCWRA element and one carer element per claimant; a claimant's limited capability never lowers the elements; member order does not matter; and a dependant's ESA, industrial injuries benefit or armed forces compensation payment never exempts an award from the benefit cap, while a child's DLA always does. Towncrier fragment for the change. Co-Authored-By: Claude Opus 5.5 --- .../uc-claimant-only-elements.fixed.md | 1 + .../test_uc_claimant_elements_properties.py | 427 ++++++++++++++++++ 2 files changed, 428 insertions(+) create mode 100644 changelog.d/uc-claimant-only-elements.fixed.md create mode 100644 policyengine_uk/tests/test_uc_claimant_elements_properties.py diff --git a/changelog.d/uc-claimant-only-elements.fixed.md b/changelog.d/uc-claimant-only-elements.fixed.md new file mode 100644 index 000000000..d710fdf38 --- /dev/null +++ b/changelog.d/uc-claimant-only-elements.fixed.md @@ -0,0 +1 @@ +Give the Universal Credit LCWRA element, carer element and work allowance on the claimants' circumstances only (UC Regs 2013 regs 27(1), 29(1) and 22(1)(b)): a disabled child gave the claimant the LCWRA element on top of the disabled child addition, and a child or qualifying young person who was a carer gave the claimant the carer element. Joint claimants who both have limited capability now have one LCWRA element (reg. 27(4)), each joint claimant who is a carer has a carer element, and a carer who has the LCWRA element does not also have the carer element (reg. 29(2) and (4)). A dependant's contributory ESA, industrial injuries benefit, Attendance Allowance or armed forces compensation payment no longer exempts the award from the benefit cap (reg. 83(1)(a) to (e)); a child's Disability Living Allowance and a qualifying young person's PIP or Carer's Allowance still do. The disability basic income contrib reform's LCWRA route follows: only a claimant with limited capability qualifies. diff --git a/policyengine_uk/tests/test_uc_claimant_elements_properties.py b/policyengine_uk/tests/test_uc_claimant_elements_properties.py new file mode 100644 index 000000000..c25421eef --- /dev/null +++ b/policyengine_uk/tests/test_uc_claimant_elements_properties.py @@ -0,0 +1,427 @@ +"""Property-based tests: Universal Credit elements turn on the claimants. + +UC Regs 2013 reg. 27(1) includes the LCWRA element "in respect of the fact +that a claimant has limited capability for work and work-related activity"; +reg. 29(1) includes the carer element "where a claimant has regular and +substantial caring responsibilities for a severely disabled person"; and reg. +22(1)(b)(i) gives no work allowance "where a single claimant does not have, +or neither of joint claimants has, responsibility for a child or qualifying +young person or limited capability for work". A child's or qualifying young +person's disability or caring is not the claimant's. The benefit cap +exceptions in reg. 83(1)(a) to (e) likewise name "the claimant" or "a +claimant" for ESA, industrial injuries benefit, attendance allowance and +armed forces compensation payments. + +Invariants, for any generated population of single claimants and couples +with up to four dependants aged 0 to 19, with or without rent, in England, +Wales and Scotland: + +1. A dependant's disability or caring never gives the claimants an LCWRA + element, a carer element or a work allowance. Flagging every dependant as + disabled, as having limited capability, as caring 35 hours or more or as + receiving Carer's Allowance leaves `uc_LCWRA_element`, `uc_carer_element`, + `is_uc_work_allowance_eligible` and `uc_work_allowance` exactly as they + are when no dependant is. +2. Differential against DWP's published table. The Advice for Decision + Making, chapter F6, paragraph F6037 tabulates which of the carer and LCWRA + amounts an award includes for each combination a single claimant or joint + claimants can have. The model includes the same number of each element for + all sixteen combinations, whatever the dependants' circumstances. The + carer element amount is written here from the reg. 36 table (£201.68 a + month in 2025-26, £209.34 in 2026-27), as is the 2025-26 LCWRA amount + (£423.27), not read from the model. +3. Bounds: an award has at most one LCWRA element (reg. 27(4)), at most one + carer element for each claimant (reg. 29(2)), and at most as many of the + two elements together as it has claimants (reg. 29(4)). +4. Monotonicity: a claimant coming to have limited capability never lowers + the two elements' total (the LCWRA element is the larger), and never takes + a work allowance away. +5. Order does not matter: entering the people of every family in reverse + leaves the elements and the work allowance unchanged. +6. Benefit cap: a dependant's contributory ESA, industrial injuries benefit + or armed forces compensation payment never changes whether the award is + exempt from the cap. Their ESA and industrial injuries benefit change + neither the cap reduction nor Universal Credit. (An armed forces + compensation payment can: `is_severely_disabled_for_benefits` treats it as + a severe disability, which gives a child the higher disabled child + addition.) A child's disability living allowance always exempts the award + (reg. 83(1)(f)). + +Marriage Allowance is switched off throughout, as in the other Universal +Credit property tests. +""" + +import numpy as np +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_uk import Simulation + +PROPERTY_SETTINGS = settings( + max_examples=15, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +YEARS = [2025, 2026] +TENURES = ["RENT_FROM_COUNCIL", "RENT_PRIVATELY", "OWNED_OUTRIGHT"] +REGIONS = ["LONDON", "NORTH_EAST", "WALES", "SCOTLAND"] +EDUCATION = ["UPPER_SECONDARY", "TERTIARY", "NOT_IN_EDUCATION"] +# Reg. 36 table, a month: carer element and (2025-26) LCWRA element. +CARER_ELEMENT = {2025: 201.68, 2026: 209.34} +LCWRA_ELEMENT_2025 = 423.27 +ELEMENT_VARIABLES = [ + "uc_LCWRA_element", + "uc_carer_element", + "is_uc_work_allowance_eligible", + "uc_work_allowance", +] +CAP_VARIABLES = [ + "is_benefit_cap_exempt", + "benefit_cap_reduction", + "universal_credit", +] +# What a dependant's disability or caring looks like in the inputs. +DEPENDANT_CIRCUMSTANCES = { + "is_disabled_for_benefits": st.booleans(), + "uc_limited_capability_for_WRA": st.booleans(), + "care_hours": st.sampled_from([0.0, 20.0, 35.0, 60.0]), + "carers_allowance_reported": st.sampled_from([0.0, 4_000.0]), +} +# Benefits that exempt an award from the cap only when a claimant has them. +DEPENDANT_CLAIMANT_ONLY_BENEFITS = { + "esa_contrib_reported": st.sampled_from([0.0, 5_000.0]), + "iidb_reported": st.sampled_from([0.0, 3_000.0]), + "afcs_reported": st.sampled_from([0.0, 6_000.0]), +} + +# DWP Advice for Decision Making, chapter F6, paragraph F6037: the additional +# amounts each claimant qualifies for ("C" carer, "L" LCWRA) and the amounts +# the award then includes, as (LCWRA elements, carer elements). The table +# lists twelve combinations; the other four follow by swapping the claimants +# or are the case where neither qualifies for either. +F6037 = { + ("C", ""): (0, 1), + ("L", ""): (1, 0), + ("CL", ""): (1, 0), + ("C", "C"): (0, 2), + ("L", "L"): (1, 0), + ("", "C"): (0, 1), + ("C", "L"): (1, 1), + ("", "L"): (1, 0), + ("CL", "C"): (1, 1), + ("CL", "L"): (1, 1), + ("C", "CL"): (1, 1), + ("CL", "CL"): (1, 1), +} + + +def published_elements(first, second=""): + """(LCWRA elements, carer elements) from the F6037 table.""" + if not first and not second: + return (0, 0) + if (first, second) in F6037: + return F6037[(first, second)] + return F6037[(second, first)] + + +def code(claimant): + return ("C" if claimant["care_hours"] >= 35 else "") + ( + "L" if claimant["uc_limited_capability_for_WRA"] else "" + ) + + +@st.composite +def dependants(draw, minimum=0, maximum=4): + members = [] + for _ in range(draw(st.integers(minimum, maximum))): + age = draw(st.integers(0, 19)) + member = dict(age=age) + for variable, strategy in DEPENDANT_CIRCUMSTANCES.items(): + member[variable] = draw(strategy) + for variable, strategy in DEPENDANT_CLAIMANT_ONLY_BENEFITS.items(): + member[variable] = draw(strategy) + if age >= 16: + # At 18 and 19 only a qualifying young person is a dependant + # (reg. 5(1)); anyone else that age is an adult whom the + # calculator treats as a claimant. + member["current_education"] = ( + draw(st.sampled_from(EDUCATION)) if age < 18 else "UPPER_SECONDARY" + ) + member["age_started_or_accepted_current_education_or_training"] = draw( + st.integers(16, min(age, 18)) + ) + if age == 19: + member[ + "is_before_universal_credit_qualifying_young_person_terminal_date" + ] = True + members.append(member) + return members + + +@st.composite +def families(draw, capped=False): + claimants = [ + dict( + age=draw(st.integers(25, 64)), + care_hours=draw(st.sampled_from([0.0, 20.0, 35.0, 60.0])), + uc_limited_capability_for_WRA=draw(st.booleans()), + would_claim_carers_allowance=draw(st.booleans()), + ) + for _ in range(draw(st.integers(1, 2))) + ] + if capped: + # Claimants with nothing that exempts them, several children and a + # high council rent: over the cap unless a dependant exempts them. + for claimant in claimants: + claimant.update(care_hours=0.0, uc_limited_capability_for_WRA=False) + return dict( + claimants=claimants, + dependants=draw(dependants(minimum=3)), + tenure="RENT_FROM_COUNCIL", + rent=draw(st.floats(12_000, 30_000)), + region=draw(st.sampled_from(REGIONS)), + ) + return dict( + claimants=claimants, + dependants=draw(dependants()), + tenure=draw(st.sampled_from(TENURES)), + rent=draw(st.one_of(st.just(0.0), st.floats(0, 20_000))), + region=draw(st.sampled_from(REGIONS)), + ) + + +populations = st.lists(families(), min_size=1, max_size=6) +capped_populations = st.lists(families(capped=True), min_size=1, max_size=6) + + +def situation( + units, year, clear=(), reverse=False, claimant_overrides=None, dependant_dla=False +): + """One simulation holding every family. + + ``clear`` names dependant inputs to reset to their defaults. With + ``reverse`` the people of each family are entered, and listed in their + benefit unit and household, in reverse order. ``claimant_overrides`` + replaces inputs of every claimant. With ``dependant_dla`` the first + dependant of each family receives the middle-rate care component of + disability living allowance. + """ + people, benunits, households = {}, {}, {} + for i, unit in enumerate(units): + members = [] + for j, claimant in enumerate(unit["claimants"]): + values = {**claimant, **(claimant_overrides or {})} + person = {k: {year: v} for k, v in values.items()} + person["is_parent"] = {year: bool(unit["dependants"])} + members.append((f"p{i}_{j}", person)) + for k, dependant in enumerate(unit["dependants"]): + person = { + key: {year: v} for key, v in dependant.items() if key not in clear + } + if dependant_dla and k == 0: + person["dla_sc_category"] = {year: "MIDDLE"} + members.append((f"d{i}_{k}", person)) + if reverse: + members = members[::-1] + names = [] + for name, person in members: + person["would_claim_marriage_allowance"] = {year: False} + people[name] = person + names.append(name) + benunits[f"b{i}"] = {"members": names} + households[f"h{i}"] = { + "members": names, + "rent": {year: unit["rent"]}, + "tenure_type": {year: unit["tenure"]}, + "region": {year: unit["region"]}, + } + return {"people": people, "benunits": benunits, "households": households} + + +def benunit_values(sim, year, variables): + return {v: np.asarray(sim.calculate(v, year)) for v in variables} + + +def assert_same(a, b, units): + for v in a: + np.testing.assert_allclose( + a[v].astype(float), b[v].astype(float), atol=0.01, err_msg=f"{v}: {units}" + ) + + +@PROPERTY_SETTINGS +@given(units=populations, year=st.sampled_from(YEARS)) +def test_dependants_disability_or_caring_never_gives_claimants_an_element(units, year): + with_circumstances = Simulation(situation=situation(units, year)) + without = Simulation( + situation=situation(units, year, clear=tuple(DEPENDANT_CIRCUMSTANCES)) + ) + assert_same( + benunit_values(with_circumstances, year, ELEMENT_VARIABLES), + benunit_values(without, year, ELEMENT_VARIABLES), + units, + ) + + +@PROPERTY_SETTINGS +@given(units=populations, year=st.sampled_from(YEARS)) +def test_elements_match_the_published_table_of_combinations(units, year): + sim = Simulation(situation=situation(units, year)) + lcwra = np.asarray(sim.calculate("uc_LCWRA_element", year)) + carer = np.asarray(sim.calculate("uc_carer_element", year)) + work_allowance = np.asarray(sim.calculate("is_uc_work_allowance_eligible", year)) + responsible = np.asarray( + sim.map_result( + np.asarray( + sim.calculate( + "is_child_or_qualifying_young_person_for_universal_credit", year + ) + ).astype(float), + "person", + "benunit", + ) + ) + for i, unit in enumerate(units): + lcwra_elements, carer_elements = published_elements( + *[code(c) for c in unit["claimants"]] + ) + np.testing.assert_allclose( + carer[i], + carer_elements * CARER_ELEMENT[year] * 12, + atol=0.01, + err_msg=str(unit), + ) + assert (lcwra[i] > 0) == (lcwra_elements == 1), unit + if year == 2025: + np.testing.assert_allclose( + lcwra[i], + lcwra_elements * LCWRA_ELEMENT_2025 * 12, + atol=0.01, + err_msg=str(unit), + ) + # Reg. 22(1)(b)(i): a work allowance needs a claimant with limited + # capability or responsibility for a child or qualifying young person. + claimant_limited = any( + c["uc_limited_capability_for_WRA"] for c in unit["claimants"] + ) + assert work_allowance[i] == (claimant_limited or responsible[i] > 0), unit + + +@PROPERTY_SETTINGS +@given(units=populations, year=st.sampled_from(YEARS)) +def test_an_award_has_at_most_one_element_for_each_claimant(units, year): + sim = Simulation(situation=situation(units, year)) + lcwra = np.asarray(sim.calculate("uc_LCWRA_element", year)) > 0 + carer_elements = np.rint( + np.asarray(sim.calculate("uc_carer_element", year)) / (CARER_ELEMENT[year] * 12) + ) + claimants = np.array([len(unit["claimants"]) for unit in units]) + assert np.all(carer_elements <= claimants), units + assert np.all(lcwra.astype(int) + carer_elements <= claimants), units + if year == 2025: + amounts = np.asarray(sim.calculate("uc_LCWRA_element", year)) + one_element = LCWRA_ELEMENT_2025 * 12 + assert np.all( + np.isclose(amounts, 0, atol=0.01) + | np.isclose(amounts, one_element, atol=0.01) + ), units + + +@PROPERTY_SETTINGS +@given(units=populations, year=st.sampled_from(YEARS)) +def test_a_claimants_limited_capability_never_lowers_the_elements(units, year): + variables = ["uc_LCWRA_element", "uc_carer_element", "uc_work_allowance"] + as_given = benunit_values( + Simulation(situation=situation(units, year)), year, variables + ) + all_limited = benunit_values( + Simulation( + situation=situation( + units, year, claimant_overrides=dict(uc_limited_capability_for_WRA=True) + ) + ), + year, + variables, + ) + total_given = as_given["uc_LCWRA_element"] + as_given["uc_carer_element"] + total_limited = all_limited["uc_LCWRA_element"] + all_limited["uc_carer_element"] + assert np.all(total_limited >= total_given - 0.01), units + assert np.all( + all_limited["uc_work_allowance"] >= as_given["uc_work_allowance"] - 0.01 + ), units + + +@PROPERTY_SETTINGS +@given(units=populations, year=st.sampled_from(YEARS)) +def test_member_order_does_not_change_the_elements(units, year): + # The 2026 LCWRA amount is drawn per benefit unit by the rebalancing + # modifier, and does not depend on the order of the unit's members. + forward = Simulation(situation=situation(units, year)) + backward = Simulation(situation=situation(units, year, reverse=True)) + assert_same( + benunit_values(forward, year, ELEMENT_VARIABLES), + benunit_values(backward, year, ELEMENT_VARIABLES), + units, + ) + + +@PROPERTY_SETTINGS +@given(units=capped_populations, year=st.sampled_from(YEARS)) +def test_dependants_claimant_only_benefits_never_change_the_benefit_cap(units, year): + # Disability and caring are cleared too, so that the only difference + # between the runs is the dependants' claimant-only benefits. + cleared = tuple(DEPENDANT_CIRCUMSTANCES) + with_all = Simulation(situation=situation(units, year, clear=cleared)) + with_esa_and_iidb = Simulation( + situation=situation(units, year, clear=cleared + ("afcs_reported",)) + ) + without = Simulation( + situation=situation( + units, year, clear=cleared + tuple(DEPENDANT_CLAIMANT_ONLY_BENEFITS) + ) + ) + assert_same( + benunit_values(with_all, year, ["is_benefit_cap_exempt"]), + benunit_values(without, year, ["is_benefit_cap_exempt"]), + units, + ) + # An armed forces compensation payment also makes a child severely + # disabled for benefits, which changes the maximum amount, so only ESA + # and industrial injuries benefit are held to the whole award. + assert_same( + benunit_values(with_esa_and_iidb, year, CAP_VARIABLES), + benunit_values(without, year, CAP_VARIABLES), + units, + ) + + +@PROPERTY_SETTINGS +@given(units=capped_populations, year=st.sampled_from(YEARS)) +def test_a_childs_disability_living_allowance_exempts_the_award(units, year): + everything = tuple(DEPENDANT_CIRCUMSTANCES) + tuple( + DEPENDANT_CLAIMANT_ONLY_BENEFITS + ) + without = Simulation(situation=situation(units, year, clear=everything)) + with_dla = Simulation( + situation=situation(units, year, clear=everything, dependant_dla=True) + ) + exempt = np.asarray(with_dla.calculate("is_benefit_cap_exempt", year)) + assert np.all(exempt), units + assert not np.any(with_dla.calculate("uc_LCWRA_element", year)), units + # The control: the same families without the allowance are not all exempt + # by some other route. + not_exempt = ~np.asarray(without.calculate("is_benefit_cap_exempt", year)) + assert np.all(not_exempt), units + + +def test_published_table_covers_all_sixteen_combinations(): + # Every combination of two claimants' carer and LCWRA status resolves, + # and swapping the claimants gives the same award. + codes = ["", "C", "L", "CL"] + for first in codes: + for second in codes: + assert published_elements(first, second) == published_elements( + second, first + ) + assert len(F6037) == 12 From 90a238677373faac22d85f094b06d5402cf99841 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 1 Oct 2026 10:01:04 -0400 Subject: [PATCH 07/12] Give the DLA property's allowance to a child under 16, and hold only ESA and IIB to the whole award The capped families' first dependant could be a 16-year-old outside non-advanced education, whom the model does not treat as a child or qualifying young person, so their DLA rightly did not exempt the award (reg. 83(1)(f)). A dependant's armed forces compensation payment makes them severely disabled for benefits, which changes the disabled child addition, so it is held only to the exemption flag. Co-Authored-By: Claude Opus 5.5 --- .../tests/test_uc_claimant_elements_properties.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/policyengine_uk/tests/test_uc_claimant_elements_properties.py b/policyengine_uk/tests/test_uc_claimant_elements_properties.py index c25421eef..a5c135842 100644 --- a/policyengine_uk/tests/test_uc_claimant_elements_properties.py +++ b/policyengine_uk/tests/test_uc_claimant_elements_properties.py @@ -132,10 +132,12 @@ def code(claimant): @st.composite -def dependants(draw, minimum=0, maximum=4): +def dependants(draw, minimum=0, maximum=4, first_child=False): + """Up to ``maximum`` dependants aged 0 to 19. With ``first_child`` the + first is a child under 16 (WRA 2012 s. 40), whatever their education.""" members = [] - for _ in range(draw(st.integers(minimum, maximum))): - age = draw(st.integers(0, 19)) + for i in range(draw(st.integers(minimum, maximum))): + age = draw(st.integers(0, 15 if first_child and i == 0 else 19)) member = dict(age=age) for variable, strategy in DEPENDANT_CIRCUMSTANCES.items(): member[variable] = draw(strategy) @@ -177,7 +179,7 @@ def families(draw, capped=False): claimant.update(care_hours=0.0, uc_limited_capability_for_WRA=False) return dict( claimants=claimants, - dependants=draw(dependants(minimum=3)), + dependants=draw(dependants(minimum=3, first_child=True)), tenure="RENT_FROM_COUNCIL", rent=draw(st.floats(12_000, 30_000)), region=draw(st.sampled_from(REGIONS)), @@ -204,8 +206,8 @@ def situation( ``reverse`` the people of each family are entered, and listed in their benefit unit and household, in reverse order. ``claimant_overrides`` replaces inputs of every claimant. With ``dependant_dla`` the first - dependant of each family receives the middle-rate care component of - disability living allowance. + dependant of each family, a child under 16 in the capped families, + receives the middle-rate care component of disability living allowance. """ people, benunits, households = {}, {}, {} for i, unit in enumerate(units): From ffa53b28132f9c95be2340674363656fe53ed457 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 06:15:49 -0400 Subject: [PATCH 08/12] Drop the benefit cap from the changelog fragment The cap exceptions are now #2076's and #2089's. Co-Authored-By: Claude Opus 5.5 --- changelog.d/uc-claimant-only-elements.fixed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/uc-claimant-only-elements.fixed.md b/changelog.d/uc-claimant-only-elements.fixed.md index d710fdf38..5a9758fb5 100644 --- a/changelog.d/uc-claimant-only-elements.fixed.md +++ b/changelog.d/uc-claimant-only-elements.fixed.md @@ -1 +1 @@ -Give the Universal Credit LCWRA element, carer element and work allowance on the claimants' circumstances only (UC Regs 2013 regs 27(1), 29(1) and 22(1)(b)): a disabled child gave the claimant the LCWRA element on top of the disabled child addition, and a child or qualifying young person who was a carer gave the claimant the carer element. Joint claimants who both have limited capability now have one LCWRA element (reg. 27(4)), each joint claimant who is a carer has a carer element, and a carer who has the LCWRA element does not also have the carer element (reg. 29(2) and (4)). A dependant's contributory ESA, industrial injuries benefit, Attendance Allowance or armed forces compensation payment no longer exempts the award from the benefit cap (reg. 83(1)(a) to (e)); a child's Disability Living Allowance and a qualifying young person's PIP or Carer's Allowance still do. The disability basic income contrib reform's LCWRA route follows: only a claimant with limited capability qualifies. +Give the Universal Credit LCWRA element, carer element and work allowance on the claimants' circumstances only (UC Regs 2013 regs 27(1), 29(1) and 22(1)(b)): a disabled child gave the claimant the LCWRA element on top of the disabled child addition, a child or qualifying young person who was a carer gave the claimant the carer element, and a dependant's limited capability gave a work allowance. Joint claimants who both have limited capability now have one LCWRA element (reg. 27(4)), each joint claimant who is a carer has a carer element, and a carer who has the LCWRA element does not also have the carer element (reg. 29(2) and (4)). The disability basic income contrib reform's LCWRA route follows: only a claimant with limited capability qualifies. From d721bcec8e517d1fabaf2bf9dcaa51c3863056ec Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 06:20:04 -0400 Subject: [PATCH 09/12] Attribute the UC carer element only to a claimant who is a carer receives_uc_carer_element (the DfE extended childcare tests read it for parents) flagged every carer in a benefit unit whose award included the element. The element is a claimant's (UC Regs 2013 reg. 29(1)), so a child or qualifying young person who also cares does not receive it. Co-Authored-By: Claude Opus 5.5 --- .../carer_element/uc_carer_element.yaml | 24 +++++++++++++++++++ .../receives_uc_carer_element.py | 19 ++++++++++----- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml index 6fdee2f78..e43469a8f 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml @@ -309,3 +309,27 @@ output: uc_carer_element: 2_512.08 uc_LCWRA_element: 0 + +# receives_uc_carer_element attributes the award's carer element to the +# claimant who is the carer, never to a dependant who also cares. + +- name: The claimant carer receives the carer element; a qualifying young person who also cares does not + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + is_parent: true + care_hours: 35 + young_person: + age: 17 + current_education: UPPER_SECONDARY + care_hours: 40 + benunits: + benunit: + members: [parent, young_person] + output: + is_carer_for_benefits: [true, true] + uc_carer_element: 2_512.08 # 209.34 * 12, once + receives_uc_carer_element: [true, false] diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/receives_uc_carer_element.py b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/receives_uc_carer_element.py index 4ef487bd6..c175b2568 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/receives_uc_carer_element.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/receives_uc_carer_element.py @@ -6,12 +6,16 @@ class receives_uc_carer_element(Variable): entity = Person label = "receives the Universal Credit carer element" documentation = ( - "Whether this person is a carer in a benefit unit whose Universal " - "Credit award includes the carer element (Universal Credit " - "Regulations 2013 reg 29). The carer element is a benefit-unit " + "Whether this person is a claimant who is a carer, in a benefit unit " + "whose Universal Credit award includes the carer element (Universal " + "Credit Regulations 2013 reg 29). The carer element is a benefit-unit " "amount; this attributes it to the carer, and requires an award, " - "since uc_carer_element is calculated for any benefit unit with a " - "carer." + "since uc_carer_element is calculated whether or not there is one. " + "The element is for a claimant (reg 29(1)), so a child or qualifying " + "young person who cares does not receive it. Where both joint " + "claimants are carers with limited capability for work and " + "work-related activity, the award has one carer element (reg 29(4)) " + "and the model does not say whose, so it attributes it to both." ) definition_period = YEAR reference = "https://www.legislation.gov.uk/uksi/2013/376/regulation/29" @@ -21,4 +25,7 @@ def formula(person, period, parameters): award_includes_carer_element = (benunit("universal_credit", period) > 0) & ( benunit("uc_carer_element", period) > 0 ) - return person("is_carer_for_benefits", period) & award_includes_carer_element + claimant_carer = person("is_uc_single_or_joint_claimant", period) & person( + "is_carer_for_benefits", period + ) + return claimant_carer & award_includes_carer_element From 5ffbc621104d1b7715e396aef2867ed88005e593 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 08:09:14 -0400 Subject: [PATCH 10/12] Allocate the UC carer element to claimants: one for carers of the same person, none for the claimant the LCWRA element is included for Review round 2 (GPT-6.1 Sol, salvaged report at d721bcec8) found three defects in the carer element: - Reg 29(3): two joint claimants caring for the same severely disabled person had two carer elements. The model records this as partners_care_for_same_severely_disabled_person (the legacy carer premium reads it); the element now reads it too, and the elder is taken where the couple would elect. - Reg 29(4) suppressed the carer element on the LCWRA flag even when the award included no LCWRA element (a nil element, as in the reg 28 waiting period, where ADM F6034 keeps the carer element). It now applies only when the LCWRA element is included, in respect of the claimant it is included for: one who is not a carer if there is one, otherwise the elder. - receives_uc_carer_element attributed the element to both members where a carer with LCWRA had a partner who was the carer; it now reads the new person-level uc_carer_element_included_for_claimant, which uc_carer_element sums. Tests: same-person, nil-LCWRA and attribution YAML cases; a 2021 extended childcare case where the attribution decided the partner exclusion; property tests for same-person caring and for one LCWRA element whether one or both claimants have limited capability (rate-free, so it covers 2026's per-award rebalanced amounts); and a deterministic run of all twenty single and joint carer/LCWRA combinations against ADM F6037. Changelog as a bullet. Co-Authored-By: Claude Opus 5.5 --- .../uc-claimant-only-elements.fixed.md | 2 +- .../carer_element/uc_carer_element.yaml | 134 +++++++++++++++++- ..._childcare_entitlement_work_condition.yaml | 33 +++++ .../test_uc_claimant_elements_properties.py | 130 +++++++++++++++-- .../receives_uc_carer_element.py | 24 ++-- .../carer_element/uc_carer_element.py | 45 +----- .../uc_carer_element_included_for_claimant.py | 72 ++++++++++ 7 files changed, 372 insertions(+), 68 deletions(-) create mode 100644 policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element_included_for_claimant.py diff --git a/changelog.d/uc-claimant-only-elements.fixed.md b/changelog.d/uc-claimant-only-elements.fixed.md index 5a9758fb5..2f89a9e1b 100644 --- a/changelog.d/uc-claimant-only-elements.fixed.md +++ b/changelog.d/uc-claimant-only-elements.fixed.md @@ -1 +1 @@ -Give the Universal Credit LCWRA element, carer element and work allowance on the claimants' circumstances only (UC Regs 2013 regs 27(1), 29(1) and 22(1)(b)): a disabled child gave the claimant the LCWRA element on top of the disabled child addition, a child or qualifying young person who was a carer gave the claimant the carer element, and a dependant's limited capability gave a work allowance. Joint claimants who both have limited capability now have one LCWRA element (reg. 27(4)), each joint claimant who is a carer has a carer element, and a carer who has the LCWRA element does not also have the carer element (reg. 29(2) and (4)). The disability basic income contrib reform's LCWRA route follows: only a claimant with limited capability qualifies. +- Give the Universal Credit LCWRA element, carer element and work allowance on the claimants' own circumstances (UC Regs 2013 regs 27(1), 29(1), 22(1)(b)), not a dependant's: a disabled child no longer adds the LCWRA element, and a child or qualifying young person who cares no longer adds the carer element. The work allowance still counts the other member of a single claim by a member of a couple (reg 22(3)). Joint claimants who both have limited capability get one LCWRA element (reg 27(4)). Each claimant who is a carer gets a carer element, but carers of the same severely disabled person get one between them (reg 29(2)-(3)). A carer with the LCWRA element does not also get the carer element (reg 29(4)). `receives_uc_carer_element` and the disability basic income reform follow the claimant each element is included for. diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml index e43469a8f..66cbdbbef 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/carer_element/uc_carer_element.yaml @@ -156,11 +156,13 @@ # Reg. 29(2): "In the case of joint claimants, an award is to include the # carer element for both joint claimants if they both qualify for it, but only -# if they are not caring for the same severely disabled person." The model -# does not record who is cared for and takes two carers to care for different -# people. +# if they are not caring for the same severely disabled person." Reg. 29(3): +# where two persons care for the same severely disabled person, the award +# "may only include the carer element in respect of one them". +# partners_care_for_same_severely_disabled_person records which; caring hours +# alone cannot show who is cared for, so it defaults to the same person. -- name: Joint claimants who are both carers have two carer elements +- name: Joint claimants who are both carers for different people have two carer elements period: 2026 absolute_error_margin: 0.01 input: @@ -174,9 +176,49 @@ benunits: benunit: members: [claimant, partner] + partners_care_for_same_severely_disabled_person: false output: + uc_carer_element_included_for_claimant: [true, true] uc_carer_element: 5_024.16 # 209.34 * 12 * 2 +- name: Joint claimants who care for the same person have one carer element (reg. 29(3)) + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + care_hours: 35 + partner: + age: 38 + care_hours: 35 + benunits: + benunit: + members: [claimant, partner] + partners_care_for_same_severely_disabled_person: true + output: + # The model takes the elder where the couple would elect. + uc_carer_element_included_for_claimant: [true, false] + uc_carer_element: 2_512.08 # 209.34 * 12, once + +- name: Two carers by hours alone are taken to care for the same person + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + care_hours: 35 + partner: + age: 38 + care_hours: 35 + benunits: + benunit: + members: [claimant, partner] + output: + partners_care_for_same_severely_disabled_person: true + uc_carer_element: 2_512.08 + # Reg. 29(4): "Where an amount would, apart from this paragraph, be included # in an award in relation to a claimant by virtue of paragraphs (1) to (3), # and the claimant has limited capability for work and work-related activity @@ -333,3 +375,87 @@ is_carer_for_benefits: [true, true] uc_carer_element: 2_512.08 # 209.34 * 12, once receives_uc_carer_element: [true, false] + +# The LCWRA element is withheld during the reg. 28 relevant period; DWP's +# Advice for Decision Making F6034 note: "an amount for the carer element can +# be included in that claimant's UC award" meanwhile. A nil LCWRA element +# entered for the award stands for that. + +- name: A carer with LCWRA whose award has no LCWRA element keeps the carer element + period: 2025 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + care_hours: 35 + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [claimant] + uc_LCWRA_element: 0 + output: + uc_carer_element: 2_420.16 # 201.68 * 12 + +# receives_uc_carer_element follows the claimant the element is included for. + +- name: A carer with LCWRA and a partner who is a carer - the element is the partner's + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + care_hours: 35 + uc_limited_capability_for_WRA: true + partner: + age: 38 + care_hours: 35 + benunits: + benunit: + members: [claimant, partner] + output: + uc_carer_element_included_for_claimant: [false, true] + receives_uc_carer_element: [false, true] + uc_carer_element: 2_512.08 + +- name: Joint carers who both have LCWRA - the LCWRA element is the elder's, the carer element the younger's + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 40 + care_hours: 35 + uc_limited_capability_for_WRA: true + partner: + age: 38 + care_hours: 35 + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [claimant, partner] + output: + uc_carer_element_included_for_claimant: [false, true] + receives_uc_carer_element: [false, true] + uc_carer_element: 2_512.08 + +- name: A carer with LCWRA whose partner has LCWRA but does not care - the LCWRA element is the partner's + period: 2026 + absolute_error_margin: 0.01 + input: + people: + claimant: + age: 38 + care_hours: 35 + uc_limited_capability_for_WRA: true + partner: + age: 40 + uc_limited_capability_for_WRA: true + benunits: + benunit: + members: [claimant, partner] + output: + uc_carer_element_included_for_claimant: [true, false] + receives_uc_carer_element: [true, false] + uc_carer_element: 2_512.08 diff --git a/policyengine_uk/tests/policy/baseline/gov/dfe/extended_childcare_entitlement/extended_childcare_entitlement_work_condition.yaml b/policyengine_uk/tests/policy/baseline/gov/dfe/extended_childcare_entitlement/extended_childcare_entitlement_work_condition.yaml index 39860f164..c9aadf70c 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dfe/extended_childcare_entitlement/extended_childcare_entitlement_work_condition.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dfe/extended_childcare_entitlement/extended_childcare_entitlement_work_condition.yaml @@ -538,3 +538,36 @@ output: carers_allowance: [0, 0, 0] extended_childcare_entitlement_work_condition: [true, false, false] + +# Reg 29(4): where a working parent who cares also has LCWRA and the other +# parent cares too, the award's LCWRA element is the working parent's and its +# carer element the other parent's. Before December 2022 the partner exclusion +# (SI 2016/1257 reg 9(3)) applied to a working partner receiving the carer +# element; the working parent does not receive it, so both meet the condition. +- name: Carer with LCWRA working, partner the carer on the UC award - the carer element is the partner's (2021) + period: 2021 + input: + people: + parent1: + age: 40 + is_parent: true + is_carer_for_benefits: true + uc_limited_capability_for_WRA: true + employment_income: 20_000 + adjusted_net_income: 20_000 + minimum_wage: 10 + parent2: + age: 38 + is_parent: true + is_carer_for_benefits: true + minimum_wage: 10 + child: + age: 3 + benunits: + benunit: + members: [parent1, parent2, child] + universal_credit: 3_000 + uc_carer_element: 2_400 + output: + receives_uc_carer_element: [false, true, false] + extended_childcare_entitlement_work_condition: [true, true, false] diff --git a/policyengine_uk/tests/test_uc_claimant_elements_properties.py b/policyengine_uk/tests/test_uc_claimant_elements_properties.py index 3194913ce..d695d4677 100644 --- a/policyengine_uk/tests/test_uc_claimant_elements_properties.py +++ b/policyengine_uk/tests/test_uc_claimant_elements_properties.py @@ -23,14 +23,21 @@ 2. Differential against DWP's published table. The Advice for Decision Making, chapter F6, paragraph F6037 tabulates which of the carer and LCWRA amounts an award includes for each combination a single claimant or joint - claimants can have. The model includes the same number of each element for - all sixteen combinations, whatever the dependants' circumstances. The - carer element amount is written here from the reg. 36 table (£201.68 a - month in 2025-26, £209.34 in 2026-27), as is the 2025-26 LCWRA amount - (£423.27), not read from the model. + claimants can have, for carers of different people. The model includes + the same number of each element for all sixteen combinations, whatever the + dependants' circumstances, both in the generated populations and in a + deterministic run of every combination. Where joint claimants care for + the same severely disabled person (reg. 29(3)) it includes at most one + carer element: the table's number, capped at one. The carer element + amount is written here from the reg. 36 table (£201.68 a month in + 2025-26, £209.34 in 2026-27), as is the 2025-26 LCWRA amount (£423.27), + not read from the model. 3. Bounds: an award has at most one LCWRA element (reg. 27(4)), at most one carer element for each claimant (reg. 29(2)), and at most as many of the - two elements together as it has claimants (reg. 29(4)). + two elements together as it has claimants (reg. 29(4)). Giving both + claimants limited capability gives exactly the LCWRA amount that giving it + to one does, in 2026 too, where the rebalancing modifier sets the amount + per award. 4. Monotonicity: a claimant coming to have limited capability never lowers the two elements' total (the LCWRA element is the larger), and never takes a work allowance away. @@ -158,19 +165,31 @@ def families(draw): populations = st.lists(families(), min_size=1, max_size=6) -def situation(units, year, clear=(), reverse=False, claimant_overrides=None): +def situation( + units, + year, + clear=(), + reverse=False, + claimant_overrides=None, + first_claimant_overrides=None, + same_person=False, +): """One simulation holding every family. ``clear`` names dependant inputs to reset to their defaults. With ``reverse`` the people of each family are entered, and listed in their benefit unit and household, in reverse order. ``claimant_overrides`` - replaces inputs of every claimant. + replaces inputs of every claimant, ``first_claimant_overrides`` those of + the first. ``same_person`` says whether the claimants care for the same + severely disabled person. """ people, benunits, households = {}, {}, {} for i, unit in enumerate(units): members = [] for j, claimant in enumerate(unit["claimants"]): values = {**claimant, **(claimant_overrides or {})} + if j == 0: + values.update(first_claimant_overrides or {}) person = {k: {year: v} for k, v in values.items()} person["is_parent"] = {year: bool(unit["dependants"])} members.append((f"p{i}_{j}", person)) @@ -186,7 +205,10 @@ def situation(units, year, clear=(), reverse=False, claimant_overrides=None): person["would_claim_marriage_allowance"] = {year: False} people[name] = person names.append(name) - benunits[f"b{i}"] = {"members": names} + benunits[f"b{i}"] = { + "members": names, + "partners_care_for_same_severely_disabled_person": {year: same_person}, + } households[f"h{i}"] = { "members": names, "rent": {year: unit["rent"]}, @@ -224,7 +246,7 @@ def test_dependants_disability_or_caring_never_gives_claimants_an_element(units, @PROPERTY_SETTINGS @given(units=populations, year=st.sampled_from(YEARS)) def test_elements_match_the_published_table_of_combinations(units, year): - sim = Simulation(situation=situation(units, year)) + sim = Simulation(situation=situation(units, year, same_person=False)) lcwra = np.asarray(sim.calculate("uc_LCWRA_element", year)) carer = np.asarray(sim.calculate("uc_carer_element", year)) work_allowance = np.asarray(sim.calculate("is_uc_work_allowance_eligible", year)) @@ -265,6 +287,43 @@ def test_elements_match_the_published_table_of_combinations(units, year): assert work_allowance[i] == (claimant_limited or responsible[i] > 0), unit +@PROPERTY_SETTINGS +@given(units=populations, year=st.sampled_from(YEARS)) +def test_carers_of_the_same_person_have_at_most_one_carer_element(units, year): + sim = Simulation(situation=situation(units, year, same_person=True)) + carer = np.asarray(sim.calculate("uc_carer_element", year)) + for i, unit in enumerate(units): + _, carer_elements = published_elements(*[code(c) for c in unit["claimants"]]) + np.testing.assert_allclose( + carer[i], + min(carer_elements, 1) * CARER_ELEMENT[year] * 12, + atol=0.01, + err_msg=str(unit), + ) + + +@PROPERTY_SETTINGS +@given(units=populations, year=st.sampled_from(YEARS)) +def test_both_claimants_limited_capability_gives_one_lcwra_element(units, year): + one = Simulation( + situation=situation( + units, + year, + claimant_overrides=dict(uc_limited_capability_for_WRA=False), + first_claimant_overrides=dict(uc_limited_capability_for_WRA=True), + ) + ) + both = Simulation( + situation=situation( + units, year, claimant_overrides=dict(uc_limited_capability_for_WRA=True) + ) + ) + amount_one = np.asarray(one.calculate("uc_LCWRA_element", year)) + amount_both = np.asarray(both.calculate("uc_LCWRA_element", year)) + assert np.all(amount_one > 0), units + np.testing.assert_allclose(amount_both, amount_one, atol=0.01, err_msg=str(units)) + + @PROPERTY_SETTINGS @given(units=populations, year=st.sampled_from(YEARS)) def test_an_award_has_at_most_one_element_for_each_claimant(units, year): @@ -333,3 +392,54 @@ def test_published_table_covers_all_sixteen_combinations(): second, first ) assert len(F6037) == 12 + + +def claimant_with(status, age): + return dict( + age=age, + care_hours=35.0 if "C" in status else 0.0, + uc_limited_capability_for_WRA="L" in status, + ) + + +def test_model_matches_the_published_table_for_every_combination(): + # A deterministic run of every single and joint combination, for carers + # of different people and of the same person, in 2025-26. + year = 2025 + codes = ["", "C", "L", "CL"] + combinations = [(first,) for first in codes] + [ + (first, second) for first in codes for second in codes + ] + for same_person in [False, True]: + units = [ + dict( + claimants=[ + claimant_with(status, 45 - 5 * j) for j, status in enumerate(c) + ], + dependants=[], + tenure="OWNED_OUTRIGHT", + rent=0.0, + region="NORTH_EAST", + ) + for c in combinations + ] + sim = Simulation(situation=situation(units, year, same_person=same_person)) + lcwra = np.asarray(sim.calculate("uc_LCWRA_element", year)) + carer = np.asarray(sim.calculate("uc_carer_element", year)) + for i, c in enumerate(combinations): + lcwra_elements, carer_elements = published_elements(*c) + if same_person: + carer_elements = min(carer_elements, 1) + context = str((c, same_person)) + np.testing.assert_allclose( + lcwra[i], + lcwra_elements * LCWRA_ELEMENT_2025 * 12, + atol=0.01, + err_msg=context, + ) + np.testing.assert_allclose( + carer[i], + carer_elements * CARER_ELEMENT[year] * 12, + atol=0.01, + err_msg=context, + ) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/receives_uc_carer_element.py b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/receives_uc_carer_element.py index c175b2568..31c9fa508 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/receives_uc_carer_element.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/receives_uc_carer_element.py @@ -6,16 +6,14 @@ class receives_uc_carer_element(Variable): entity = Person label = "receives the Universal Credit carer element" documentation = ( - "Whether this person is a claimant who is a carer, in a benefit unit " - "whose Universal Credit award includes the carer element (Universal " - "Credit Regulations 2013 reg 29). The carer element is a benefit-unit " - "amount; this attributes it to the carer, and requires an award, " - "since uc_carer_element is calculated whether or not there is one. " - "The element is for a claimant (reg 29(1)), so a child or qualifying " - "young person who cares does not receive it. Where both joint " - "claimants are carers with limited capability for work and " - "work-related activity, the award has one carer element (reg 29(4)) " - "and the model does not say whose, so it attributes it to both." + "Whether this person is a claimant in respect of whom the Universal " + "Credit award includes the carer element (Universal Credit " + "Regulations 2013 reg 29; `uc_carer_element_included_for_claimant`), " + "on an award. The carer element is a benefit-unit amount; this " + "attributes it to the claimant it is included for, and requires an " + "award, since uc_carer_element is calculated whether or not there is " + "one. A child or qualifying young person who cares does not receive " + "it, nor does a carer who has only the LCWRA element (reg 29(4))." ) definition_period = YEAR reference = "https://www.legislation.gov.uk/uksi/2013/376/regulation/29" @@ -25,7 +23,7 @@ def formula(person, period, parameters): award_includes_carer_element = (benunit("universal_credit", period) > 0) & ( benunit("uc_carer_element", period) > 0 ) - claimant_carer = person("is_uc_single_or_joint_claimant", period) & person( - "is_carer_for_benefits", period + included_for_this_claimant = person( + "uc_carer_element_included_for_claimant", period ) - return claimant_carer & award_includes_carer_element + return included_for_this_claimant & award_includes_carer_element diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element.py b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element.py index 272f20548..8acb565b4 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element.py @@ -7,16 +7,9 @@ class uc_carer_element(Variable): label = "Universal Credit carer element" documentation = ( "The amount an award includes because a claimant has regular and " - "substantial caring responsibilities for a severely disabled person. " - "Only a claimant's caring counts (`is_uc_single_or_joint_claimant`): a " - "child or qualifying young person who is a carer does not give the " - "claimants the element, nor does a partner who cannot be a joint " - "claimant (reg. 3(3)). Joint claimants who are both carers have an " - "element each; the model does not record who is cared for, and takes " - "two carers to care for different people. A claimant who is a carer " - "and has limited capability for work and work-related activity has " - "the LCWRA element instead, unless that element is included for the " - "other joint claimant." + "substantial caring responsibilities for a severely disabled person: " + "the reg. 36 amount for each claimant in respect of whom the element " + "is included (`uc_carer_element_included_for_claimant`)." ) definition_period = YEAR unit = GBP @@ -26,13 +19,9 @@ class uc_carer_element(Variable): href="https://www.legislation.gov.uk/ukpga/2012/5/section/12", ), dict( - title="Universal Credit Regulations 2013 reg. 29(1), (2) and (4)", + title="Universal Credit Regulations 2013 reg. 29", href="https://www.legislation.gov.uk/uksi/2013/376/regulation/29", ), - dict( - title="Universal Credit Regulations 2013 reg. 30", - href="https://www.legislation.gov.uk/uksi/2013/376/regulation/30", - ), dict( title="Universal Credit Regulations 2013 reg. 36(3)", href="https://www.legislation.gov.uk/uksi/2013/376/regulation/36", @@ -41,29 +30,5 @@ class uc_carer_element(Variable): def formula(benunit, period, parameters): p = parameters(period).gov.dwp.universal_credit.elements.carer - person = benunit.members - # Reg. 29(1): the element is included "where a claimant has regular - # and substantial caring responsibilities for a severely disabled - # person" (reg. 30: `is_carer_for_benefits`). - claimant = person("is_uc_single_or_joint_claimant", period) - carer = claimant & person("is_carer_for_benefits", period) - limited_capability = claimant & person("uc_limited_capability_for_WRA", period) - # Reg. 29(2): "an award is to include the carer element for both joint - # claimants if they both qualify for it, but only if they are not - # caring for the same severely disabled person". - carers_without_limited_capability = benunit.sum(carer & ~limited_capability) - # Reg. 29(4): where the carer "has limited capability for work and - # work-related activity (and, in the case of joint claimants, the - # LCWRA element has not been included in respect of the other - # claimant), only the LCWRA element may be included in respect of the - # claimant". Where both joint claimants have limited capability the - # award has one LCWRA element (reg. 27(4)), for one of them, which - # leaves a carer element for the other. - carers_with_limited_capability = benunit.sum(carer & limited_capability) - claimants_with_limited_capability = benunit.sum(limited_capability) - carers_keeping_the_carer_element = min_( - carers_with_limited_capability, - max_(claimants_with_limited_capability - 1, 0), - ) - elements = carers_without_limited_capability + carers_keeping_the_carer_element + elements = add(benunit, period, ["uc_carer_element_included_for_claimant"]) return elements * p.amount * MONTHS_IN_YEAR diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element_included_for_claimant.py b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element_included_for_claimant.py new file mode 100644 index 000000000..44b98612c --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element_included_for_claimant.py @@ -0,0 +1,72 @@ +from policyengine_uk.model_api import * + + +class uc_carer_element_included_for_claimant(Variable): + value_type = bool + entity = Person + label = "Universal Credit carer element included in respect of this claimant" + documentation = ( + "Whether the Universal Credit award includes the carer element in " + "respect of this person. Only a claimant (`is_uc_single_or_joint_claimant`) " + "who has regular and substantial caring responsibilities for a " + "severely disabled person (`is_carer_for_benefits`) qualifies, so a " + "child or qualifying young person who cares does not. A claimant in " + "respect of whom the award's LCWRA element is included has only that " + "element. The LCWRA element is one per award; where it is included " + "(`uc_LCWRA_element` positive) and more than one claimant has limited " + "capability for work and work-related activity, the model takes it " + "to be included in respect of one who is not a carer if there is " + "one, and otherwise the elder, so the other keeps the carer element. " + "Where it is not included (an award of nil, as in the reg. 28 " + "waiting period), a carer's limited capability does not displace the " + "carer element. Joint claimants who both qualify have an element " + "each unless they care for the same severely disabled person " + "(`partners_care_for_same_severely_disabled_person`); then only one " + "is included, which they elect or the Secretary of State decides, " + "and the model takes the elder." + ) + definition_period = YEAR + reference = [ + dict( + title="Welfare Reform Act 2012 s. 12(2)(c)", + href="https://www.legislation.gov.uk/ukpga/2012/5/section/12", + ), + dict( + title="Universal Credit Regulations 2013 reg. 29(1) to (4)", + href="https://www.legislation.gov.uk/uksi/2013/376/regulation/29", + ), + dict( + title="Universal Credit Regulations 2013 reg. 27(4)", + href="https://www.legislation.gov.uk/uksi/2013/376/regulation/27", + ), + dict( + title="Universal Credit Regulations 2013 reg. 30", + href="https://www.legislation.gov.uk/uksi/2013/376/regulation/30", + ), + ] + + def formula(person, period, parameters): + benunit = person.benunit + age = person("age", period) + # Reg. 29(1): "where a claimant has regular and substantial caring + # responsibilities for a severely disabled person" (reg. 30). + claimant = person("is_uc_single_or_joint_claimant", period) + carer = claimant & person("is_carer_for_benefits", period) + limited_capability = claimant & person("uc_limited_capability_for_WRA", period) + # Reg. 29(4): a carer with limited capability "(and, in the case of + # joint claimants, the LCWRA element has not been included in respect + # of the other claimant)" has "only the LCWRA element". The award has + # one LCWRA element (reg. 27(4)); in respect of whom it is included + # matters only when it is included. + lcwra_included = benunit("uc_LCWRA_element", period) > 0 + lcwra_rank = person.get_rank( + benunit, where(carer, 1_000, 0) - age, condition=limited_capability + ) + lcwra_claimant = limited_capability & lcwra_included & (lcwra_rank == 0) + qualifies = carer & ~lcwra_claimant + # Reg. 29(2): both joint claimants "if they both qualify for it, but + # only if they are not caring for the same severely disabled person"; + # reg. 29(3): otherwise one, by election or the Secretary of State. + same_person = benunit("partners_care_for_same_severely_disabled_person", period) + carer_rank = person.get_rank(benunit, -age, condition=qualifies) + return qualifies & (~same_person | (carer_rank == 0)) From 5f18bda1e35b1886a1adfa0c3b9d02b9b14ff164 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 22 Oct 2026 00:03:50 -0400 Subject: [PATCH 11/12] Drop the 'still read every member' reasons from the capability docstrings; reword the changelog's contrib sentence The LCWRA element and the work allowance now read only claimants, so the two docstrings' stated reason for not inferring capability from a person's own ESA no longer holds; the inference itself is unchanged. The changelog's disability basic income sentence now says what the route tests (review r3 P3). Co-Authored-By: Claude Opus 5.5 --- changelog.d/uc-claimant-only-elements.fixed.md | 2 +- .../limited_work_ability/uc_limited_capability_for_WRA.py | 6 ++---- .../limited_work_ability/uc_limited_capability_for_work.py | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/changelog.d/uc-claimant-only-elements.fixed.md b/changelog.d/uc-claimant-only-elements.fixed.md index 2f89a9e1b..a5be4573b 100644 --- a/changelog.d/uc-claimant-only-elements.fixed.md +++ b/changelog.d/uc-claimant-only-elements.fixed.md @@ -1 +1 @@ -- Give the Universal Credit LCWRA element, carer element and work allowance on the claimants' own circumstances (UC Regs 2013 regs 27(1), 29(1), 22(1)(b)), not a dependant's: a disabled child no longer adds the LCWRA element, and a child or qualifying young person who cares no longer adds the carer element. The work allowance still counts the other member of a single claim by a member of a couple (reg 22(3)). Joint claimants who both have limited capability get one LCWRA element (reg 27(4)). Each claimant who is a carer gets a carer element, but carers of the same severely disabled person get one between them (reg 29(2)-(3)). A carer with the LCWRA element does not also get the carer element (reg 29(4)). `receives_uc_carer_element` and the disability basic income reform follow the claimant each element is included for. +- Give the Universal Credit LCWRA element, carer element and work allowance on the claimants' own circumstances (UC Regs 2013 regs 27(1), 29(1), 22(1)(b)), not a dependant's: a disabled child no longer adds the LCWRA element, and a child or qualifying young person who cares no longer adds the carer element. The work allowance still counts the other member of a single claim by a member of a couple (reg 22(3)). Joint claimants who both have limited capability get one LCWRA element (reg 27(4)). Each claimant who is a carer gets a carer element, but carers of the same severely disabled person get one between them (reg 29(2)-(3)). A carer with the LCWRA element does not also get the carer element (reg 29(4)). `receives_uc_carer_element` follows the claimant the carer element is included for, and the disability basic income reform's LCWRA route needs a claimant with limited capability on a UC award. diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_limited_capability_for_WRA.py b/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_limited_capability_for_WRA.py index 8016ba9a6..cb195c4ca 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_limited_capability_for_WRA.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_limited_capability_for_WRA.py @@ -16,10 +16,8 @@ class uc_limited_capability_for_WRA(Variable): "does not include the support component (esa_includes_support_" "component) is taken not to have it, whatever the flag, though someone " "still in the ESA assessment phase may yet be found to. The model does " - "not infer it the other way, from the support component alone: the " - "Universal Credit LCWRA element and work allowance still read every " - "member of the benefit unit, not only the claimants. Users can set it " - "directly." + "not infer it the other way, from the support component alone. Users " + "can set it directly." ) definition_period = YEAR reference = ( diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_limited_capability_for_work.py b/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_limited_capability_for_work.py index f7e27613a..7be3da869 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_limited_capability_for_work.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_limited_capability_for_work.py @@ -17,8 +17,7 @@ class uc_limited_capability_for_work(Variable): "work-related activity but not for work. Universal Credit also takes " "an ESA determination of limited capability for work (reg 39(1)(a)), " "so any ESA award of the person's own would imply it; the model does " - "not infer that yet, because the work allowance still reads every " - "member of the benefit unit, not only the claimants." + "not infer that." ) definition_period = YEAR reference = ( From aea059fe8840c2858d928b6d40e8a4c126619435 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 22 Oct 2026 00:46:09 -0400 Subject: [PATCH 12/12] Keep the benefit cap's tests and #2084's flag-only rule with claimant-only elements - The elements, their attribution and the contrib reform read is_uc_assessed_claimant less other_member_of_single_claim, the set #2084 used for them, rather than is_uc_single_or_joint_claimant. The two differ only where an ineligible-partner flag marks no single claim (both members, or a lone adult, flagged), which leaves the rules alone (test_a_flag_marking_no_single_claim_leaves_cap_and_lha_rules_alone). - #2076's benefit cap tests built a positive model-owned LCWRA element from a non-claimant's limited capability, which the formula no longer gives. They now store one with set_model_value (as the rebalancing stores what it rescales) where they test that such an element is not an entry and does not lift the cap; where they test the formula, they expect the claimant-only result. Two YAML cases enter uc_carer_element instead of setting benunit_has_carer, and the comment above element_included no longer says the model's formula gives elements for any member. Co-Authored-By: Claude Opus 5.5 --- .../benefit_cap_specified_benefits.yaml | 16 ++-- ...nefit_cap_specified_benefits_properties.py | 82 ++++++++++++++----- .../disability_basic_income.py | 6 +- ...uc_benefit_cap_exempt_specified_benefit.py | 15 ++-- .../uc_carer_element_included_for_claimant.py | 10 ++- .../limited_work_ability/uc_LCWRA_element.py | 10 ++- 6 files changed, 99 insertions(+), 40 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_specified_benefits.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_specified_benefits.yaml index 082ac4d35..9327b3d86 100644 --- a/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_specified_benefits.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_specified_benefits.yaml @@ -1575,7 +1575,7 @@ is_housing_benefit_benefit_cap_exempt_specified_benefit: false benefit_cap_reduction: 0 -- name: A carer element the family's entered carer flag gives lifts the cap, though no member is flagged +- name: A carer element entered for the award lifts the cap, though no member is flagged as a carer period: 2025 input: people: @@ -1584,20 +1584,20 @@ benunits: family: members: [claimant] - benunit_has_carer: true + uc_carer_element: 2_420.16 universal_credit_pre_benefit_cap: 18_000 households: home: members: [claimant] region: NORTH_EAST output: - # The award includes the carer element (reg 83(1)(j)), and no member who - # is not a claimant has caring responsibilities to give it instead. + # The award includes the carer element (reg 83(1)(j)): an element + # entered for the award is included when it is positive. is_uc_benefit_cap_exempt_specified_benefit: true is_housing_benefit_benefit_cap_exempt_specified_benefit: false benefit_cap_reduction: 0 -- name: A nil carer element is not included in the award, though the claimant cares +- name: A nil carer element entered for the award is not included, though the claimant cares period: 2025 input: people: @@ -1607,15 +1607,15 @@ benunits: family: members: [claimant] - benunit_has_carer: false + uc_carer_element: 0 universal_credit_pre_benefit_cap: 18_000 households: home: members: [claimant] region: NORTH_EAST output: - # Reg 83(1)(j) asks whether the award includes the element; the - # family's entered flag gives none. 18,000 - 14,753. + # Reg 83(1)(j) asks whether the award includes the element; the entered + # award includes none. 18,000 - 14,753. uc_carer_element: 0 is_uc_benefit_cap_exempt_specified_benefit: false is_housing_benefit_benefit_cap_exempt_specified_benefit: false diff --git a/policyengine_uk/tests/test_benefit_cap_specified_benefits_properties.py b/policyengine_uk/tests/test_benefit_cap_specified_benefits_properties.py index 9648177f4..6c6745dff 100644 --- a/policyengine_uk/tests/test_benefit_cap_specified_benefits_properties.py +++ b/policyengine_uk/tests/test_benefit_cap_specified_benefits_properties.py @@ -629,10 +629,20 @@ def test_an_element_a_reform_gives_is_decided_by_its_value(element): assert positive.calculate(element, YEAR)[0] == 6_000 assert positive.calculate(UC_SPECIFIED, YEAR)[0] # A reform's amount that equals the model's own (here, what the model's - # formula gives for the other member's circumstance) is still the - # reform's. + # formula gives for a claimant's circumstance; it gives nothing for the + # other member's, regs 27(1), 29(1)) is still the reform's. + assert ( + Simulation( + situation=situation([([claimant, other], {"working_tax_credit": 0})]) + ).calculate(element, YEAR)[0] + == 0 + ) + claimant_with_circumstance = blank(role="claimant", age=40) + claimant_with_circumstance[circumstance] = True model_amount = Simulation( - situation=situation([([claimant, other], {"working_tax_credit": 0})]) + situation=situation( + [([claimant_with_circumstance, other], {"working_tax_credit": 0})] + ) ).calculate(element, YEAR)[0] assert model_amount > 0 same = Simulation( @@ -662,18 +672,27 @@ def test_an_element_a_reform_gives_is_decided_by_its_value(element): def test_the_rebalancing_adds_no_entry_and_keeps_one(): """From 2026 the Universal Credit rebalancing rescales uc_LCWRA_element (scenarios/uc_reform.py). An element it rescales from the model's own - formula is not an entry, so the other member's limited capability still - does not lift the cap; an element entered for the award stays entered.""" + formula is not an entry: the claimant's limited capability gives one that + lifts the cap, and the other member's gives none (reg 27(1)); an element + entered for the award stays entered.""" year = 2026 claimant, other = blank(role="claimant", age=40), blank(role="other", age=30) + for holder in ["claimant", "other"]: + claimant_flag, other_flag = dict(claimant), dict(other) + (claimant_flag if holder == "claimant" else other_flag)[ + "uc_limited_capability_for_WRA" + ] = True + calculated = Simulation( + situation=situation( + [([claimant_flag, other_flag], {"working_tax_credit": 0})], year + ) + ) + positive = calculated.calculate("uc_LCWRA_element", year)[0] > 0 + assert positive == (holder == "claimant") + benunit = calculated.populations["benunit"] + assert supplied_input(benunit, "uc_LCWRA_element", period(year)) is None + assert bool(calculated.calculate(UC_SPECIFIED, year)[0]) == positive other["uc_limited_capability_for_WRA"] = True - calculated = Simulation( - situation=situation([([claimant, other], {"working_tax_credit": 0})], year) - ) - assert calculated.calculate("uc_LCWRA_element", year)[0] > 0 - benunit = calculated.populations["benunit"] - assert supplied_input(benunit, "uc_LCWRA_element", period(year)) is None - assert not calculated.calculate(UC_SPECIFIED, year)[0] entered = Simulation( situation=situation( @@ -691,9 +710,15 @@ def test_the_rebalancing_adds_no_entry_and_keeps_one(): # stored for the period, so an entry equal to it is still an entry. +# The model's own amount stored below for a family whose only member with +# limited capability is not a claimant: twelve months at the 2026-27 rate. +MODEL_OWNED_ELEMENT = 217.26 * 12 + + def element_situation(year, element=None): - """A claimant and a non-claimant adult with limited capability: the - model's formula gives the element, which does not lift the cap.""" + """A claimant and a non-claimant adult with limited capability. The + model's formula gives no element for the other member's limited + capability (reg 27(1)); ``model_owned`` stores one as the model's own.""" claimant, other = blank(role="claimant", age=40), blank(role="other", age=30) other["uc_limited_capability_for_WRA"] = True inputs = {"working_tax_credit": 0} @@ -702,6 +727,19 @@ def element_situation(year, element=None): return situation([([claimant, other], inputs)], year) +def model_owned(year): + """A simulation of ``element_situation`` for ``year`` holding a positive + element stored as the model's own (set_model_value, as the rebalancing + stores the amounts it rescales) although only the non-claimant has + limited capability. Such an element is not included in the award, so it + does not lift the cap; an entry of the same amount is, and does.""" + simulation = Simulation(situation=element_situation(year)) + set_model_value( + simulation, "uc_LCWRA_element", year, np.array([MODEL_OWNED_ELEMENT]) + ) + return simulation + + def is_entered(simulation, year): benunit = simulation.populations["benunit"] return supplied_input(benunit, "uc_LCWRA_element", period(year)) is not None @@ -753,9 +791,9 @@ def test_an_entry_equal_to_the_model_value_is_an_entry(enter): even one equal to the amount the rebalancing stored as the model's in the same simulation, however it is entered.""" year = 2026 - without_entry = Simulation(situation=element_situation(year)) + without_entry = model_owned(year) assert not without_entry.calculate(UC_SPECIFIED, year)[0] - simulation = Simulation(situation=element_situation(year)) + simulation = model_owned(year) model_amount = float(simulation.calculate("uc_LCWRA_element", year)[0]) assert model_amount > 0 assert not is_entered(simulation, year) @@ -776,7 +814,7 @@ def test_an_entry_equal_to_the_model_value_is_an_entry(enter): def test_clones_and_branches_keep_their_own_record_of_model_values(): year = 2026 - simulation = Simulation(situation=element_situation(year)) + simulation = model_owned(year) model_amount = float(simulation.calculate("uc_LCWRA_element", year)[0]) clone = simulation.clone() @@ -802,7 +840,10 @@ def test_clones_and_branches_keep_their_own_record_of_model_values(): @pytest.fixture(scope="module") def rebalanced(): year = 2026 - simulation = Simulation(situation=element_situation(year)) + # The situation gives the non-claimant limited capability for 2026 only, + # so the model's own element is stored for that year; later years keep + # the formula's nil. + simulation = model_owned(year) return simulation, float(simulation.calculate("uc_LCWRA_element", year)[0]) @@ -963,7 +1004,10 @@ def test_internal_lcwra_wrapper_keeps_the_model_claimant_filter( applied_before_data_load=True, ), ) - assert sim.calculate("uc_LCWRA_element", year)[0] > 0 + # The model's formula gives the element only for a claimant (reg 27(1)). + assert (sim.calculate("uc_LCWRA_element", year)[0] > 0) == ( + holder_role == "claimant" + ) assert not is_entered(sim, year) assert bool(sim.calculate(UC_SPECIFIED, year)[0]) == (holder_role == "claimant") add_universal_credit_reform(sim) diff --git a/policyengine_uk/variables/contrib/disability_basic_income/disability_basic_income.py b/policyengine_uk/variables/contrib/disability_basic_income/disability_basic_income.py index 1c6410ba1..20bb5fab8 100644 --- a/policyengine_uk/variables/contrib/disability_basic_income/disability_basic_income.py +++ b/policyengine_uk/variables/contrib/disability_basic_income/disability_basic_income.py @@ -1,4 +1,7 @@ from policyengine_uk.model_api import * +from policyengine_uk.utils.uc_work_related_requirements import ( + other_member_of_single_claim, +) class disability_basic_income(Variable): @@ -17,7 +20,8 @@ def formula(person, period, parameters): # child's or qualifying young person's limited capability does not # put it in the award. receives_uc_lcwra = ( - person("is_uc_single_or_joint_claimant", period) + person("is_uc_assessed_claimant", period) + & ~other_member_of_single_claim(person, period) & person("uc_limited_capability_for_WRA", period) & (person.benunit("universal_credit", period) > 0) ) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/benefit_cap/is_uc_benefit_cap_exempt_specified_benefit.py b/policyengine_uk/variables/gov/dwp/universal_credit/benefit_cap/is_uc_benefit_cap_exempt_specified_benefit.py index ddbd0abac..a6acb7f36 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/benefit_cap/is_uc_benefit_cap_exempt_specified_benefit.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/benefit_cap/is_uc_benefit_cap_exempt_specified_benefit.py @@ -163,14 +163,13 @@ def received_by(benefits, members): # award. An element entered for the award is included exactly when it # is positive, whatever the members' circumstances (an entry of nil # means the award does not include it). An element a reform's formula - # gives is included when positive. The model's own formula still - # gives the element for any member's limited capability for work and - # work-related activity or caring responsibilities, but the element is - # for "a claimant" (regs 27(1), 29(1)). So the element it gives is - # included when positive unless the only members with the - # circumstance are not claimants. A circumstance entered for the - # family alone (benunit_has_carer) names no member, and a nil amount - # (benunit_has_carer entered as false, or a nil rate) is not included. + # gives is included when positive. The element is for "a claimant" + # (regs 27(1), 29(1)), and the model's own formulas give it only for a + # claimant's limited capability for work and work-related activity or + # caring responsibilities, so the element they give is included when + # positive; the check that a claimant has the circumstance guards + # against a formula that does not. A nil amount (a nil rate) is not + # included. # The Universal Credit rebalancing rescales the formula's elements # from 2026 (scenarios/uc_reform.py) and is read the same way. The # model does not have the reg 28 waiting period or regs 29(5)-(6), diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element_included_for_claimant.py b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element_included_for_claimant.py index 44b98612c..277734efa 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element_included_for_claimant.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/carer_element/uc_carer_element_included_for_claimant.py @@ -1,4 +1,7 @@ from policyengine_uk.model_api import * +from policyengine_uk.utils.uc_work_related_requirements import ( + other_member_of_single_claim, +) class uc_carer_element_included_for_claimant(Variable): @@ -7,7 +10,8 @@ class uc_carer_element_included_for_claimant(Variable): label = "Universal Credit carer element included in respect of this claimant" documentation = ( "Whether the Universal Credit award includes the carer element in " - "respect of this person. Only a claimant (`is_uc_single_or_joint_claimant`) " + "respect of this person. Only a claimant (`is_uc_assessed_claimant`, " + "less the other member of a single claim under reg. 3(3)) " "who has regular and substantial caring responsibilities for a " "severely disabled person (`is_carer_for_benefits`) qualifies, so a " "child or qualifying young person who cares does not. A claimant in " @@ -50,7 +54,9 @@ def formula(person, period, parameters): age = person("age", period) # Reg. 29(1): "where a claimant has regular and substantial caring # responsibilities for a severely disabled person" (reg. 30). - claimant = person("is_uc_single_or_joint_claimant", period) + claimant = person( + "is_uc_assessed_claimant", period + ) & ~other_member_of_single_claim(person, period) carer = claimant & person("is_carer_for_benefits", period) limited_capability = claimant & person("uc_limited_capability_for_WRA", period) # Reg. 29(4): a carer with limited capability "(and, in the case of diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_LCWRA_element.py b/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_LCWRA_element.py index dee075114..76122045e 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_LCWRA_element.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/disability_element/limited_work_ability/uc_LCWRA_element.py @@ -1,4 +1,7 @@ from policyengine_uk.model_api import * +from policyengine_uk.utils.uc_work_related_requirements import ( + other_member_of_single_claim, +) class uc_LCWRA_element(Variable): @@ -8,7 +11,8 @@ class uc_LCWRA_element(Variable): documentation = ( "The amount an award includes because a claimant has limited " "capability for work and work-related activity. Only a claimant's " - "limited capability counts (`is_uc_single_or_joint_claimant`): a " + "limited capability counts (`is_uc_assessed_claimant`, less the " + "other member of a single claim): a " "disabled child or qualifying young person gives the award the " "disabled child addition instead (`uc_individual_disabled_child_element`), " "and a partner who cannot be a joint claimant, so that the other " @@ -37,7 +41,9 @@ def formula(benunit, period, parameters): person = benunit.members # Reg. 27(1): the element is "in respect of the fact that a claimant # has limited capability for work and work-related activity". - claimant = person("is_uc_single_or_joint_claimant", period) + claimant = person( + "is_uc_assessed_claimant", period + ) & ~other_member_of_single_claim(person, period) limited_capability = claimant & person("uc_limited_capability_for_WRA", period) # Reg. 27(4)(a): "In the case of joint claimants, where each of them # has limited capability for work and work-related activity, the