From bee86d9780b6cb5d93ba6790ac6343fbf2c049e4 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 1 Oct 2026 14:30:07 -0400 Subject: [PATCH 01/14] Count other families' children and add the foster and overnight-care bedrooms in the LHA size criteria Universal Credit (UC Regs 2013 Sch 4 paras 9-12): the household head's non-dependants' children are in the extended benefit unit and share rooms with the renter's children; foster children are in no one's extended benefit unit; foster parents and adopters with a child placed (para 12(4)-(5)) and renters whose household includes someone with non-resident overnight care (para 12(A1), (3)) get one additional bedroom for each condition (12(9)). Foster parents are excepted from the shared accommodation rate (29(9A)), and armed forces independence payment counts in the para 29(5) exception. Housing Benefit (HB Regs 2006 and HB (SPC) Regs 2006 reg 13D): the children of non-dependants, boarders and lodgers are occupiers (13D(12)); a child placed with the claimant as a foster child or for adoption is not (reg 21(3)); a qualifying parent or carer and a person who requires overnight care give one additional bedroom each (13D(3A)-(3B)) and are not young individuals. New inputs: is_placed_for_adoption, is_approved_foster_parent_without_placement, has_non_resident_overnight_carer. Fixes #2011 Co-Authored-By: Claude Opus 5.5 --- changelog.d/2011.fixed.md | 1 + .../gov/dwp/LHA/overnight_care_benefits.yaml | 19 + ...ared_accommodation_exception_benefits.yaml | 3 + ...lies_children_and_additional_bedrooms.yaml | 598 ++++++++++++++++++ .../family/LHA_young_person_bedroom.yaml | 21 +- ...est_lha_bedroom_young_people_properties.py | 284 +++++++-- .../gov/dwp/LHA_additional_bedrooms.py | 53 ++ .../variables/gov/dwp/LHA_allowed_bedrooms.py | 146 +++-- ...housing_benefit_LHA_additional_bedrooms.py | 44 ++ .../housing_benefit_LHA_allowed_bedrooms.py | 49 +- ...ild_or_young_person_for_legacy_benefits.py | 10 +- ...hild_or_young_person_placed_with_family.py | 42 ++ ...sing_benefit_qualifying_parent_or_carer.py | 37 ++ .../is_housing_benefit_young_individual.py | 26 +- ...red_accommodation_rate_specified_renter.py | 23 +- .../dwp/is_non_dependant_of_household_head.py | 6 +- .../gov/dwp/lha_renter_has_non_dependant.py | 8 +- ...ha_renter_meets_foster_parent_condition.py | 40 ++ .../dwp/meets_lha_overnight_care_condition.py | 34 + .../has_non_resident_overnight_carer.py | 21 + ...pproved_foster_parent_without_placement.py | 22 + .../is_looked_after_by_local_authority.py | 20 +- .../demographic/is_placed_for_adoption.py | 25 + 23 files changed, 1388 insertions(+), 144 deletions(-) create mode 100644 changelog.d/2011.fixed.md create mode 100644 policyengine_uk/parameters/gov/dwp/LHA/overnight_care_benefits.yaml create mode 100644 policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml create mode 100644 policyengine_uk/variables/gov/dwp/LHA_additional_bedrooms.py create mode 100644 policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py create mode 100644 policyengine_uk/variables/gov/dwp/is_child_or_young_person_placed_with_family.py create mode 100644 policyengine_uk/variables/gov/dwp/is_housing_benefit_qualifying_parent_or_carer.py create mode 100644 policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py create mode 100644 policyengine_uk/variables/gov/dwp/meets_lha_overnight_care_condition.py create mode 100644 policyengine_uk/variables/household/demographic/has_non_resident_overnight_carer.py create mode 100644 policyengine_uk/variables/household/demographic/is_approved_foster_parent_without_placement.py create mode 100644 policyengine_uk/variables/household/demographic/is_placed_for_adoption.py diff --git a/changelog.d/2011.fixed.md b/changelog.d/2011.fixed.md new file mode 100644 index 0000000000..050a5351dc --- /dev/null +++ b/changelog.d/2011.fixed.md @@ -0,0 +1 @@ +- Count the children of the household head's non-dependants (Universal Credit, UC Regs 2013 Sch 4 paras 9-10) and of non-dependants, boarders and lodgers (Housing Benefit, HB Regs 2006 reg 13D(12)) in the household head's LHA size criteria, sharing rooms with the head's own children. Give foster children no bedroom of their own, and foster parents, adopters with a child placed, and households with a non-resident overnight carer an additional bedroom (UC Sch 4 para 12; HB reg 13D(3A)-(3B)), with new inputs `is_placed_for_adoption`, `is_approved_foster_parent_without_placement` and `has_non_resident_overnight_carer`. Except foster parents from the shared accommodation rate (UC para 29(9A); HB "young individual"), and count armed forces independence payment in the UC para 29(5) exception. diff --git a/policyengine_uk/parameters/gov/dwp/LHA/overnight_care_benefits.yaml b/policyengine_uk/parameters/gov/dwp/LHA/overnight_care_benefits.yaml new file mode 100644 index 0000000000..4e2353ce1d --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/LHA/overnight_care_benefits.yaml @@ -0,0 +1,19 @@ +description: Disability benefits whose recipients, if non-resident carers regularly stay overnight to care for them, give the household an additional bedroom in the LHA size criteria. +values: + 2011-04-01: + - attendance_allowance + - armed_forces_independence_payment + - dla_sc_middle_plus + - pip_dl +metadata: + unit: list + label: LHA overnight care qualifying benefits + reference: + - title: Universal Credit Regulations 2013 Sch 4 para 12(3)(a) + href: https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12 + - title: Universal Credit Regulations 2013 reg 2, "attendance allowance" (includes armed forces independence payment) + href: https://www.legislation.gov.uk/uksi/2013/376/regulation/2 + - title: Housing Benefit Regulations 2006 reg 2(1), "person who requires overnight care" + href: https://www.legislation.gov.uk/uksi/2006/213/regulation/2 + - title: Housing Benefit (State Pension Credit) Regulations 2006 reg 2(1), "person who requires overnight care" + href: https://www.legislation.gov.uk/uksi/2006/214/regulation/2 diff --git a/policyengine_uk/parameters/gov/dwp/LHA/shared_accommodation_exception_benefits.yaml b/policyengine_uk/parameters/gov/dwp/LHA/shared_accommodation_exception_benefits.yaml index 26f65c2490..96241d625d 100644 --- a/policyengine_uk/parameters/gov/dwp/LHA/shared_accommodation_exception_benefits.yaml +++ b/policyengine_uk/parameters/gov/dwp/LHA/shared_accommodation_exception_benefits.yaml @@ -2,6 +2,7 @@ description: Disability benefits whose recipients under 35 are excepted from the values: 2013-04-29: - attendance_allowance + - armed_forces_independence_payment - dla_sc_middle_plus - pip_dl metadata: @@ -10,3 +11,5 @@ metadata: reference: - title: Universal Credit Regulations 2013 Sch 4 para 29(5) href: https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/29 + - title: Universal Credit Regulations 2013 reg 2, "attendance allowance" (includes armed forces independence payment) + href: https://www.legislation.gov.uk/uksi/2013/376/regulation/2 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml new file mode 100644 index 0000000000..0164ea54e1 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml @@ -0,0 +1,598 @@ +# Who counts in the LHA size criteria beyond the claimant's own family, and +# the additional bedrooms for foster parents and overnight care. +# +# Universal Credit (UC Regs 2013 Sch 4): the renter's extended benefit unit is +# the renter, the children and qualifying young persons they are responsible +# for, and their non-dependants (para 9(1)). A non-dependant is anyone who +# normally lives with the renter except, among others, a foster child of the +# renter (9(2)(c), 9(3)), a person liable to pay for their occupation on a +# commercial basis such as a lodger (9(2)(d)), and a child or qualifying young +# person no one in the extended benefit unit is responsible for (9(2)(g)). A +# non-dependant's own child is their responsibility (reg 4(2)), so is a +# non-dependant too, and counts as a child in para 10(1)(d)-(f). No one is +# responsible for a child looked after by a local authority (reg 4(6)(a)), +# except a child placed for adoption (reg 4A(1)(c)). Para 12 gives one +# additional bedroom for each of the overnight care condition and the foster +# parent condition (12(9)). +# +# Housing Benefit (HB Regs 2006 and HB (SPC) Regs 2006 reg 13D): one bedroom +# for each category of "occupier", everyone who occupies the dwelling as +# their home except a joint tenant outside the claimant's household (13D(12)), +# so the children of a non-dependant, boarder or lodger count. A child or +# young person placed with the claimant as a foster child or for adoption +# does not occupy the dwelling (reg 21(3)); the claimant is then a +# "qualifying parent or carer" (reg 2(1)) with one additional bedroom +# (13D(3A)(b)), and one more for overnight care (13D(3A)(a), (3B)). +# +# DWP guidance: ADM F3016, F3034, F3111-F3112, F3131-F3138; LHA Guidance +# Manual (April 2014) paras 2.030-2.033, 2.090, 2.093; HB/CTB circular A4/2012 +# paras 36-38 and 44. +# +# Rents of £200 a week (£10,400 a year) sit between the published Maidstone +# one-bedroom (B) and two-bedroom (C) rates (VOA, LHA April 2024, held for +# 2025-26: B £172.60, C £208.27 a week), so where the category is C the rent +# is met in full. + +- name: A non-dependant's child of the opposite sex needs a bedroom of their own + period: 2026 + input: + people: + renter: + age: 48 + is_household_head: true + is_male: false + son: + age: 12 + is_male: true + daughter: + age: 24 + is_male: false + granddaughter: + age: 11 + is_male: false + benunits: + renter_family: + members: [renter, son] + daughter_family: + members: [daughter, granddaughter] + households: + home: + members: [renter, son, daughter, granddaughter] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_non_dependant_of_household_head: [false, false, true, true] + # UC: the renter (para 10(1)(a)), the daughter (10(1)(c)), and the son + # and the granddaughter, who are of opposite sexes and over 10 (10(1)(f)): + # 4 bedrooms. The daughter's own entitlement counts her and her child. + LHA_allowed_bedrooms: [4, 2] + LHA_category: [E, C] + # HB: the same four occupiers' categories (reg 13D(3)(a), (b), (e)). + housing_benefit_LHA_allowed_bedrooms: [4, 2] + housing_benefit_LHA_category: [E, C] + +- name: A non-dependant's child shares with the renter's child of the same sex + period: 2026 + input: + people: + renter: + age: 48 + is_household_head: true + is_male: false + son: + age: 12 + is_male: true + daughter: + age: 24 + is_male: false + grandson: + age: 8 + is_male: true + benunits: + renter_family: + members: [renter, son] + daughter_family: + members: [daughter, grandson] + households: + home: + members: [renter, son, daughter, grandson] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # The son and the grandson share (UC para 10(1)(e); HB reg 13D(3)(c)): + # 3 bedrooms, the same as without the grandson. + LHA_allowed_bedrooms: [3, 2] + housing_benefit_LHA_allowed_bedrooms: [3, 2] + +- name: A non-dependant's two children under 10 share one bedroom + period: 2026 + input: + people: + renter: + age: 55 + is_household_head: true + daughter: + age: 27 + is_male: false + grandson: + age: 6 + is_male: true + granddaughter: + age: 4 + is_male: false + benunits: + renter_family: + members: [renter] + daughter_family: + members: [daughter, grandson, granddaughter] + households: + home: + members: [renter, daughter, grandson, granddaughter] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: the renter, the daughter (para 10(1)(c)) and the two grandchildren + # under 10 (10(1)(d)): 3 bedrooms, category D. + LHA_allowed_bedrooms: [3, 2] + LHA_category: [D, C] + # HB: reg 13D(3)(a), (b), (d). + housing_benefit_LHA_allowed_bedrooms: [3, 2] + housing_benefit_LHA_category: [D, C] + +- name: A lodger's child counts for Housing Benefit but not Universal Credit + period: 2026 + input: + people: + householder: + age: 60 + is_household_head: true + lodger: + age: 30 + rent_paid_as_lodger: 5_000 + lodgers_child: + age: 5 + benunits: + householder_family: + members: [householder] + lodger_family: + members: [lodger, lodgers_child] + households: + home: + members: [householder, lodger, lodgers_child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_non_dependant_of_household_head: [false, false, false] + # UC: the lodger is liable to pay on a commercial basis (para 9(2)(d)), + # so is not in the householder's extended benefit unit, and the lodger's + # child is excluded by para 9(2)(g): the householder's bedroom only. + LHA_allowed_bedrooms: [1, 2] + # HB: the lodger and the lodger's child occupy the dwelling as their home + # (reg 13D(12); LHA Guidance Manual para 2.030; circular A4/2012 para + # 44): 3 bedrooms. The lodger's own claim counts their own family. + housing_benefit_LHA_allowed_bedrooms: [3, 2] + +- name: A joint tenant's child does not count for the other joint tenant + period: 2026 + input: + people: + tenant_1: + age: 40 + is_household_head: true + tenant_2: + age: 38 + tenant_2_child: + age: 7 + benunits: + family_1: + members: [tenant_1] + family_2: + members: [tenant_2, tenant_2_child] + liable_for_share_of_household_rent: true + households: + home: + members: [tenant_1, tenant_2, tenant_2_child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # A joint tenant outside the claimant's household is not an occupier + # (HB reg 13D(12)), nor their household (LHA Guidance Manual paras 2.031 + # and 2.093); under UC they are liable for their occupation (para + # 9(2)(d)). Each joint tenant counts their own family. + LHA_allowed_bedrooms: [1, 2] + housing_benefit_LHA_allowed_bedrooms: [1, 2] + +- name: Foster children have no bedrooms of their own; the foster parent has one additional bedroom + period: 2026 + input: + people: + foster_parent: + age: 45 + is_household_head: true + foster_son: + age: 12 + is_male: true + is_looked_after_by_local_authority: true + foster_daughter: + age: 11 + is_male: false + is_looked_after_by_local_authority: true + benunits: + family: + members: [foster_parent, foster_son, foster_daughter] + households: + home: + members: [foster_parent, foster_son, foster_daughter] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_child_or_qualifying_young_person_for_universal_credit: [false, false, false] + is_child_or_young_person_placed_with_family: [false, true, true] + # UC: foster children are in no one's extended benefit unit (paras 9(1), + # 9(2)(c), 9(3); reg 4(6)(a)); the renter is a foster parent with one + # additional bedroom however many children are placed (para 12(4)(a), + # (9)(b); ADM F3138 example 2): 2 bedrooms, category C. + lha_renter_meets_foster_parent_condition: true + LHA_additional_bedrooms: 1 + LHA_allowed_bedrooms: 2 + LHA_category: C + # HB: the foster children do not occupy the dwelling (reg 21(3); LHA + # Guidance Manual para 2.033); the claimant is a qualifying parent or + # carer (reg 2(1)) with one additional bedroom (reg 13D(3A)(b)). + is_housing_benefit_qualifying_parent_or_carer: true + housing_benefit_LHA_additional_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 2 + housing_benefit_LHA_category: C + uc_housing_costs_element: 10_400 + +- name: A couple fostering a child alongside their own child + period: 2026 + input: + people: + parent_1: + age: 38 + is_household_head: true + is_parent: true + parent_2: + age: 36 + is_parent: true + son: + age: 5 + is_male: true + foster_daughter: + age: 3 + is_male: false + is_looked_after_by_local_authority: true + benunits: + family: + members: [parent_1, parent_2, son, foster_daughter] + households: + home: + members: [parent_1, parent_2, son, foster_daughter] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_claimant_or_partner: [true, true, false, false] + # UC: the couple, their son (para 10(1)(f)), and the foster parent's + # additional bedroom (para 12(9)(b)): 3 bedrooms. The foster daughter + # does not share with the son as a child of the extended benefit unit. + LHA_allowed_bedrooms: 3 + LHA_category: D + # HB: the couple, the son (reg 13D(3)(e)) and the qualifying parent or + # carer's bedroom (13D(3A)(b)). + housing_benefit_LHA_allowed_bedrooms: 3 + housing_benefit_LHA_category: D + +- name: A single foster parent under 35 is not restricted to the shared accommodation rate + period: 2026 + input: + people: + foster_parent: + age: 28 + is_household_head: true + foster_child: + age: 7 + is_looked_after_by_local_authority: true + benunits: + family: + members: [foster_parent, foster_child] + households: + home: + members: [foster_parent, foster_child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: not responsible for the foster child (reg 4(6)(a)), who is not a + # non-dependant (para 9(2)(c)), but excepted as a renter who satisfies + # the foster parent condition (para 29(9A)). + is_responsible_for_child_or_young_person_for_uc_or_housing_benefit: false + lha_renter_has_non_dependant: false + is_lha_shared_accommodation_rate_specified_renter: false + LHA_allowed_bedrooms: 2 + LHA_category: C + # HB: a single claimant under 35, but a qualifying parent or carer is not + # a young individual (reg 2(1)). + is_housing_benefit_young_individual: false + housing_benefit_LHA_allowed_bedrooms: 2 + housing_benefit_LHA_category: C + +- name: An approved foster parent between placements keeps the additional bedroom + period: 2026 + input: + people: + foster_parent: + age: 30 + is_household_head: true + is_approved_foster_parent_without_placement: true + benunits: + family: + members: [foster_parent] + households: + home: + members: [foster_parent] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC Sch 4 para 12(5); HB reg 2(1), "qualifying parent or carer" (b). + lha_renter_meets_foster_parent_condition: true + is_lha_shared_accommodation_rate_specified_renter: false + LHA_allowed_bedrooms: 2 + LHA_category: C + is_housing_benefit_qualifying_parent_or_carer: true + is_housing_benefit_young_individual: false + housing_benefit_LHA_allowed_bedrooms: 2 + housing_benefit_LHA_category: C + uc_housing_costs_element: 10_400 + +- name: A child placed for adoption counts under Universal Credit but not Housing Benefit + period: 2026 + input: + people: + adopter: + age: 40 + is_household_head: true + child: + age: 4 + is_placed_for_adoption: true + benunits: + family: + members: [adopter, child] + households: + home: + members: [adopter, child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: the adopter is responsible for the child (reg 4A(1)(c), from 1 + # April 2024), who has a bedroom (para 10(1)(f)), and as an adopter with + # whom a child is placed meets the foster parent condition (para + # 12(4)(b); ADM F3133): 3 bedrooms. + is_child_or_qualifying_young_person_for_universal_credit: [false, true] + lha_renter_meets_foster_parent_condition: true + LHA_allowed_bedrooms: 3 + LHA_category: D + # HB: the child is not a member of the household nor an occupier (reg + # 21(3)(b)-(c)); the adopter is a qualifying parent or carer: 2 bedrooms. + is_child_or_young_person_for_legacy_benefits: [false, false] + is_child_or_young_person_placed_with_family: [false, true] + housing_benefit_LHA_allowed_bedrooms: 2 + housing_benefit_LHA_category: C + +- name: A disabled renter with a non-resident overnight carer has an additional bedroom + period: 2026 + input: + people: + renter: + age: 62 + is_household_head: true + attendance_allowance: 5_000 + has_non_resident_overnight_carer: true + benunits: + family: + members: [renter] + households: + home: + members: [renter] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC Sch 4 para 12(A1)(a), (3) (ADM F3132 example 1); HB reg + # 13D(3A)(a)(i). + meets_lha_overnight_care_condition: true + LHA_additional_bedrooms: 1 + LHA_allowed_bedrooms: 2 + LHA_category: C + housing_benefit_LHA_additional_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 2 + housing_benefit_LHA_category: C + uc_housing_costs_element: 10_400 + +- name: Overnight care from a partner who lives in the home gives no additional bedroom + period: 2026 + input: + people: + renter: + age: 50 + is_household_head: true + pip_dl: 5_000 + partner: + age: 49 + benunits: + couple: + members: [renter, partner] + households: + home: + members: [renter, partner] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # The carer must not live in the home (UC para 12(3)(b); ADM F3132 + # example 3; HB reg 2(1), "person who requires overnight care" (b)). + meets_lha_overnight_care_condition: [false, false] + LHA_additional_bedrooms: 0 + LHA_allowed_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 1 + +- name: Overnight care for a non-dependant gives the renter an additional bedroom + period: 2026 + input: + people: + renter: + age: 58 + is_household_head: true + son: + age: 30 + pip_dl: 5_000 + has_non_resident_overnight_carer: true + benunits: + renter_family: + members: [renter] + son_family: + members: [son] + households: + home: + members: [renter, son] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: the son is a non-dependant (para 9(2)), in the renter's extended + # benefit unit, and meets the overnight care condition (para + # 12(A1)(b)): the renter, the son and the carer's room, 3 bedrooms. + LHA_additional_bedrooms: [1, 1] + LHA_allowed_bedrooms: [3, 2] + # HB: an occupier other than the claimant or partner who requires + # overnight care (reg 13D(3A)(a)(iii)). + housing_benefit_LHA_additional_bedrooms: [1, 1] + housing_benefit_LHA_allowed_bedrooms: [3, 2] + +- name: Two people needing overnight care give one additional bedroom + period: 2026 + input: + people: + partner_1: + age: 55 + is_household_head: true + dla_sc_middle_plus: true + has_non_resident_overnight_carer: true + partner_2: + age: 54 + dla_sc_middle_plus: true + has_non_resident_overnight_carer: true + benunits: + couple: + members: [partner_1, partner_2] + households: + home: + members: [partner_1, partner_2] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC para 12(9)(a) (ADM F3138 example 1); HB reg 13D(3A)(a). + LHA_additional_bedrooms: 1 + LHA_allowed_bedrooms: 2 + housing_benefit_LHA_additional_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 2 + +- name: Foster parents one of whom needs overnight care have two additional bedrooms + period: 2026 + input: + people: + parent_1: + age: 44 + is_household_head: true + attendance_allowance: 5_000 + has_non_resident_overnight_carer: true + parent_2: + age: 42 + foster_child_1: + age: 9 + is_looked_after_by_local_authority: true + foster_child_2: + age: 6 + is_looked_after_by_local_authority: true + benunits: + family: + members: [parent_1, parent_2, foster_child_1, foster_child_2] + households: + home: + members: [parent_1, parent_2, foster_child_1, foster_child_2] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_claimant_or_partner: [true, true, false, false] + # UC para 12(9)(a)-(b) (ADM F3138 example 2): the couple's bedroom and + # one for each condition, 3 bedrooms. + LHA_additional_bedrooms: 2 + LHA_allowed_bedrooms: 3 + # HB reg 13D(3B): two additional bedrooms where 13D(3A)(a) and (b) both + # apply. + housing_benefit_LHA_additional_bedrooms: 2 + housing_benefit_LHA_allowed_bedrooms: 3 + +- name: A single Housing Benefit claimant under 35 who needs overnight care is not a young individual + period: 2026 + input: + people: + claimant: + age: 25 + is_household_head: true + pip_dl: 5_000 + has_non_resident_overnight_carer: true + benunits: + family: + members: [claimant] + households: + home: + members: [claimant] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # HB Regs 2006 reg 2(1), "young individual": not one who requires + # overnight care. One bedroom plus the carer's. + is_housing_benefit_young_individual: false + housing_benefit_LHA_allowed_bedrooms: 2 + housing_benefit_LHA_category: C + # UC: excepted from shared accommodation by the daily living component + # (para 29(5)); 2 bedrooms with the carer's. + is_lha_shared_accommodation_rate_specified_renter: false + LHA_allowed_bedrooms: 2 + LHA_category: C + +- name: An armed forces independence payment excepts a renter under 35 from the shared accommodation rate + period: 2026 + input: + people: + renter: + age: 30 + is_household_head: true + armed_forces_independence_payment: 8_000 + benunits: + family: + members: [renter] + households: + home: + members: [renter] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC Sch 4 para 29(5)(b): attendance allowance, which includes armed + # forces independence payment (reg 2). + is_lha_shared_accommodation_rate_specified_renter: false + LHA_category: B diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_young_person_bedroom.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_young_person_bedroom.yaml index d1a3e83f8e..16cd81fe79 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_young_person_bedroom.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_young_person_bedroom.yaml @@ -115,7 +115,7 @@ housing_benefit_LHA_allowed_bedrooms: 2 housing_benefit_LHA_category: C -- name: A qualifying young person no one is responsible for has no Universal Credit bedroom +- name: A looked-after qualifying young person has no Universal Credit bedroom of their own period: 2026 input: people: @@ -141,10 +141,21 @@ output: is_qualifying_young_person_for_universal_credit: [false, true] is_child_or_qualifying_young_person_for_universal_credit: [false, false] - # Not under para 10(1)(b), and not a non-dependant (para 9(2)(g)): the - # renter's bedroom only. - LHA_allowed_bedrooms: 1 - LHA_category: B + # Not under para 10(1)(b), and not a non-dependant (para 9(2)(g)), so no + # bedroom of their own. The carer they are placed with is a foster parent + # (reg 2), so has one additional bedroom (para 12(4)(a), (9)(b)): 2 + # bedrooms, category C. + LHA_additional_bedrooms: 1 + LHA_allowed_bedrooms: 2 + LHA_category: C + # HB: a young person placed with the claimant by a local authority does + # not occupy the dwelling (HB Regs 2006 reg 21(3)), but the claimant is a + # qualifying parent or carer (reg 2(1)) with one additional bedroom (reg + # 13D(3A)(b)): 2 bedrooms. + is_child_or_young_person_placed_with_family: [false, true] + housing_benefit_LHA_additional_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 2 + housing_benefit_LHA_category: C - name: A qualifying young person cannot share with a younger sibling of the same sex period: 2026 diff --git a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py index b23fa6a8cd..efbec9389f 100644 --- a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py +++ b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py @@ -1,29 +1,56 @@ -"""Property-based tests for the bedrooms of benefit-unit members aged 16 or -over who are not the claimant or partner (UC Regs 2013 Sch 4 paras 9-10; HB -Regs 2006 and HB (SPC) Regs 2006 reg 13D(3)). +"""Property-based tests for the LHA size criteria (UC Regs 2013 Sch 4 paras +9-12; HB Regs 2006 and HB (SPC) Regs 2006 reg 13D and reg 21(3)): the +bedrooms of benefit-unit members aged 16 or over who are not the claimant or +partner, the children of other families in the household, foster and +adoption placements, and the additional bedrooms for foster parents and +overnight care. Invariants, for any generated population of households: 1. Monotonicity: adding a person aged 16 to 19 to any family never lowers any family's Universal Credit or Housing Benefit bedrooms, whether the claimant and partner are supplied or inferred. -2. Exact effect: when the added person is not the claimant or partner, their - family gains one Housing Benefit bedroom, and one Universal Credit bedroom - unless they are a qualifying young person no one is responsible for (para - 9(2)(g)). The household head's family also gains one bedroom under both - schemes if the person joins a non-dependant's family, and one Housing - Benefit bedroom if they join a boarder's or lodger's family. No other - family's bedrooms change. -3. Reference: every family's bedrooms equal an independent count of the size - criteria: one for the claimant or couple; one for each other member aged - 16 or over (for Universal Credit, except a qualifying young person no one - is responsible for); for the household head's family, one for each person - aged 16 or over in a non-dependant's family (and, for Housing Benefit, in - a boarder's or lodger's family); and the fewest rooms that hold the - family's children under 16 two to a room, where only children of the same - sex or two children under 10 may share, found by brute force. Children of - other families are not counted for the household head, matching the model - (a separate, existing gap). +2. Exact effect of a person aged 16 to 19: when the added person is not the + claimant or partner, their family gains one Housing Benefit bedroom + unless the person is placed with it as a foster child (HB reg 21(3)), and + one Universal Credit bedroom unless they are a qualifying young person no + one is responsible for (para 9(2)(g)); a family they are placed with as a + foster child gains the foster parent's (UC, para 12) or qualifying parent + or carer's (HB, 13D(3A)(b)) bedroom if it did not already have it. The + household head's family also gains one bedroom under both schemes if the + person joins a non-dependant's family (UC: unless no one is responsible + for them), and one Housing Benefit bedroom if they join a boarder's or + lodger's family. No other family's bedrooms change. +3. Children: adding a child under 16 who is neither fostered nor placed for + adoption to any family never lowers any family's bedrooms under either + scheme and raises each by at most one. +4. Foster children: adding a foster child to a family gives that family one + more bedroom under each scheme if it did not already meet the foster + parent condition (UC) or have a qualifying parent or carer (HB), and none + otherwise. No other family's Universal Credit bedrooms change; the + household head's Housing Benefit bedrooms rise by at most one when the + child is placed with an occupier's family, whose children are occupiers. +5. Reference: every family's bedrooms, and its additional bedrooms, equal an + independent count of the size criteria: + - one for the claimant or couple; + - one for each other member aged 16 or over (UC: except a qualifying + young person no one is responsible for; HB: except one placed with the + family as a foster child); + - for the household head's family, one for each person aged 16 or over + in a non-dependant's family (and, for HB, in a boarder's or lodger's + family); + - the fewest rooms that hold the counted children under 16 two to a + room, where only children of the same sex or two children under 10 may + share, found by brute force. UC counts the family's own children and, + for the household head's family, the non-dependants' children, but no + foster child. HB counts the family's own children other than foster + children and children placed for adoption and, for the household + head's family, every child of a non-dependant's, boarder's or lodger's + family; + - one additional bedroom if anyone so counted, or a foster child of the + family, has overnight care, and one if the family fosters, has a child + placed for adoption, or has an approved foster parent between + placements. """ from itertools import combinations @@ -43,16 +70,22 @@ ) ROLES = ["sharer", "boarder", "lodger", "non_dependant"] EDUCATION = ["NOT_IN_EDUCATION", "UPPER_SECONDARY", "TERTIARY"] +KINDS = ["own", "own", "own", "foster", "adopted"] +OVERNIGHT = st.sampled_from([False, False, False, True]) -child = st.tuples(st.integers(0, 15), st.booleans()) # (age, is_male) +# (age, is_male, kind, has overnight care) +child = st.tuples(st.integers(0, 15), st.booleans(), st.sampled_from(KINDS), OVERNIGHT) @st.composite def family(draw, role): return dict( role=role, - adults=draw(st.lists(st.integers(20, 85), min_size=1, max_size=2)), + adults=draw( + st.lists(st.tuples(st.integers(20, 85), OVERNIGHT), min_size=1, max_size=2) + ), children=draw(st.lists(child, min_size=0, max_size=4)), + between_placements=draw(st.sampled_from([False, False, False, True])), ) @@ -87,9 +120,37 @@ def scenario(draw): return population, (h, f), draw(young_person()) -def build(population, added=None, supply_roles=True): - """One situation for the population, optionally with a person added to - family ``added[0]`` = (household, family) with attributes ``added[1]``. +@st.composite +def child_scenario(draw, kinds): + population = draw(st.lists(household(), min_size=1, max_size=4)) + h = draw(st.integers(0, len(population) - 1)) + f = draw(st.integers(0, len(population[h]) - 1)) + added = ( + draw(st.integers(0, 15)), + draw(st.booleans()), + draw(st.sampled_from(kinds)), + False, + ) + return population, (h, f), added + + +def child_inputs(age, male, kind, overnight): + inputs = {"age": age, "is_male": male, "is_household_head": False} + if kind == "foster": + inputs["is_looked_after_by_local_authority"] = True + if kind == "adopted": + inputs["is_placed_for_adoption"] = True + if overnight: + inputs["dla_sc_middle_plus"] = True + inputs["has_non_resident_overnight_carer"] = True + return inputs + + +def build(population, added=None, supply_roles=True, added_child=None): + """One situation for the population, optionally with a person aged 16 to + 19 added to family ``added[0]`` = (household, family) with attributes + ``added[1]``, or a child ``added_child[1]`` = (age, is_male, kind, + overnight) added to family ``added_child[0]``. Returns the situation and, per benefit unit, its (household, family).""" people, benunits, homes, keys = {}, {}, {}, [] @@ -98,17 +159,25 @@ def build(population, added=None, supply_roles=True): for f, fam in enumerate(families): name = f"h{h}_f{f}" ids = [] - for i, age in enumerate(fam["adults"]): + for i, (age, overnight) in enumerate(fam["adults"]): pid = f"{name}_adult_{i}" people[pid] = {"age": age, "is_household_head": False} + if overnight: + people[pid]["dla_sc_middle_plus"] = True + people[pid]["has_non_resident_overnight_carer"] = True if supply_roles: people[pid]["is_claimant_or_partner"] = True elif fam["children"]: people[pid]["is_parent"] = True ids.append(pid) - for i, (age, male) in enumerate(fam["children"]): + if fam["between_placements"]: + people[ids[0]]["is_approved_foster_parent_without_placement"] = True + children = list(fam["children"]) + if added_child is not None and added_child[0] == (h, f): + children.append(added_child[1]) + for i, spec in enumerate(children): pid = f"{name}_child_{i}" - people[pid] = {"age": age, "is_male": male, "is_household_head": False} + people[pid] = child_inputs(*spec) if supply_roles: people[pid]["is_claimant_or_partner"] = False ids.append(pid) @@ -165,6 +234,35 @@ def best(remaining): return best(list(children)) +def fosters(fam): + """Whether the family meets the foster parent condition (UC) or has a + qualifying parent or carer (HB) through its children or adults.""" + return fam["between_placements"] or any( + kind in ("foster", "adopted") for _, _, kind, _ in fam["children"] + ) + + +def flags(sim, situation, pid): + """The added person's attributes as the model determines them.""" + index = list(situation["people"]).index(pid) + + def value(variable): + return bool(sim.calculate(variable, YEAR)[index]) + + qualifying = value("is_qualifying_young_person_for_universal_credit") + responsible = value("is_child_or_qualifying_young_person_for_universal_credit") + return dict( + # A qualifying young person no one is responsible for (para 9(2)(g)). + unclaimed=qualifying and not responsible, + # Placed with the family by a local authority (HB reg 21(3)). + placed=value("is_child_or_young_person_placed_with_family"), + # A child under 18 looked after by a local authority is placed with + # a foster parent (UC Regs 2013 reg 2). + fostered=value("is_looked_after_by_local_authority") + and situation["people"][pid]["age"] < 18, + ) + + @PROPERTY_SETTINGS @given(scenario(), st.booleans()) def test_adding_a_person_aged_16_to_19_never_lowers_bedrooms(case, supply_roles): @@ -187,61 +285,125 @@ def test_exact_effect_of_adding_a_person_aged_16_to_19(case): after, _ = build(population, (target, person)) _, uc_before, hb_before = bedrooms(before) sim, uc_after, hb_after = bedrooms(after) - # Whether the added person is a qualifying young person no one is - # responsible for. (The qualifying young person tests themselves are - # covered elsewhere.) - pid = f"h{target[0]}_f{target[1]}_added" - index = list(after["people"]).index(pid) - qualifying = sim.calculate("is_qualifying_young_person_for_universal_credit", YEAR) - responsible = sim.calculate( - "is_child_or_qualifying_young_person_for_universal_credit", YEAR - ) - unclaimed = bool(qualifying[index]) and not bool(responsible[index]) + added = flags(sim, after, f"h{target[0]}_f{target[1]}_added") + fam = population[target[0]][target[1]] # 2. Exact effect. expected_uc = np.zeros(len(keys)) expected_hb = np.zeros(len(keys)) t = keys.index(target) - expected_uc[t] += 0 if unclaimed else 1 - expected_hb[t] += 1 - role = population[target[0]][target[1]]["role"] + expected_uc[t] += 0 if added["unclaimed"] else 1 + expected_uc[t] += int(added["fostered"] and not fosters(fam)) + expected_hb[t] += 0 if added["placed"] else 1 + expected_hb[t] += int(added["placed"] and not fosters(fam)) head = keys.index((target[0], 0)) - if role == "non_dependant": - expected_uc[head] += 1 + if fam["role"] == "non_dependant": + expected_uc[head] += 0 if added["unclaimed"] else 1 expected_hb[head] += 1 - elif role in ("boarder", "lodger"): + elif fam["role"] in ("boarder", "lodger"): expected_hb[head] += 1 assert np.array_equal(uc_after - uc_before, expected_uc) assert np.array_equal(hb_after - hb_before, expected_hb) +@PROPERTY_SETTINGS +@given(child_scenario(["own"])) +def test_adding_a_child_raises_each_family_by_at_most_one_bedroom(case): + population, target, added_child = case + before, keys = build(population) + after, _ = build(population, added_child=(target, added_child)) + _, uc_before, hb_before = bedrooms(before) + _, uc_after, hb_after = bedrooms(after) + # 3. Children. + for change in (uc_after - uc_before, hb_after - hb_before): + assert np.all((change == 0) | (change == 1)) + + +@PROPERTY_SETTINGS +@given(child_scenario(["foster"])) +def test_a_foster_child_adds_only_the_foster_parents_bedroom(case): + population, target, added_child = case + before, keys = build(population) + after, _ = build(population, added_child=(target, added_child)) + _, uc_before, hb_before = bedrooms(before) + _, uc_after, hb_after = bedrooms(after) + # 4. Foster children. + fam = population[target[0]][target[1]] + t = keys.index(target) + new_carer = int(not fosters(fam)) + expected_uc = np.zeros(len(keys)) + expected_uc[t] = new_carer + assert np.array_equal(uc_after - uc_before, expected_uc) + hb_change = hb_after - hb_before + assert hb_change[t] == new_carer + head = keys.index((target[0], 0)) + for b in range(len(keys)): + if b == t: + continue + if b == head and fam["role"] in ("non_dependant", "boarder", "lodger"): + assert hb_change[b] in (0, 1) + else: + assert hb_change[b] == 0 + + @PROPERTY_SETTINGS @given(scenario()) def test_bedrooms_match_an_independent_count_of_the_size_criteria(case): population, target, person = case situation, keys = build(population, (target, person)) sim, uc, hb = bedrooms(situation) - qualifying = sim.calculate("is_qualifying_young_person_for_universal_credit", YEAR) - responsible = sim.calculate( - "is_child_or_qualifying_young_person_for_universal_credit", YEAR - ) - unclaimed = dict( - zip(situation["people"], np.asarray(qualifying) & ~np.asarray(responsible)) + uc_additional = np.asarray(sim.calculate("LHA_additional_bedrooms", YEAR)) + hb_additional = np.asarray( + sim.calculate("housing_benefit_LHA_additional_bedrooms", YEAR) ) - # 3. Reference count. + added = flags(sim, situation, f"h{target[0]}_f{target[1]}_added") + # 5. Reference count. for b, (h, f) in enumerate(keys): families = population[h] fam = families[f] - added = (h, f) == target - own_children = fewest_rooms_for_children(fam["children"]) - own_uc = int(added and not unclaimed[f"h{h}_f{f}_added"]) - own_hb = int(added) - outside_uc = outside_hb = 0 + is_target = (h, f) == target + # Own members aged 16 or over who are not the claimant or partner. + uc_rooms = 1 + int(is_target and not added["unclaimed"]) + hb_rooms = 1 + int(is_target and not added["placed"]) + # Children: UC counts the children the family is responsible for + # (not foster children); HB counts occupiers (not children placed + # with the family as foster children or for adoption). + uc_children = [c for c in fam["children"] if c[2] != "foster"] + hb_children = [c for c in fam["children"] if c[2] == "own"] + # Overnight care: the family's own members, including a foster + # child (UC para 12(A1)(c); HB 13D(3A)(a)(iv)). + uc_overnight = hb_overnight = any(o for _, o in fam["adults"]) or any( + c[3] for c in fam["children"] + ) + uc_foster = fosters(fam) or (is_target and added["fostered"]) + hb_carer = fosters(fam) or (is_target and added["placed"]) if fam["role"] == "head": for g, other in enumerate(families[1:], start=1): - aged_16_or_over = len(other["adults"]) + int(target == (h, g)) + joins = target == (h, g) if other["role"] == "non_dependant": - outside_uc += aged_16_or_over + uc_rooms += len(other["adults"]) + int( + joins and not added["unclaimed"] + ) + uc_children += [c for c in other["children"] if c[2] != "foster"] + uc_overnight |= any(o for _, o in other["adults"]) or any( + c[3] for c in other["children"] if c[2] != "foster" + ) if other["role"] in ("non_dependant", "boarder", "lodger"): - outside_hb += aged_16_or_over - assert uc[b] == 1 + own_uc + outside_uc + own_children - assert hb[b] == 1 + own_hb + outside_hb + own_children + hb_rooms += len(other["adults"]) + int(joins) + hb_children += list(other["children"]) + hb_overnight |= any(o for _, o in other["adults"]) or any( + c[3] for c in other["children"] + ) + expected_uc_additional = int(uc_overnight) + int(uc_foster) + expected_hb_additional = int(hb_overnight) + int(hb_carer) + assert uc_additional[b] == expected_uc_additional + assert hb_additional[b] == expected_hb_additional + assert uc[b] == ( + uc_rooms + + fewest_rooms_for_children([c[:2] for c in uc_children]) + + expected_uc_additional + ) + assert hb[b] == ( + hb_rooms + + fewest_rooms_for_children([c[:2] for c in hb_children]) + + expected_hb_additional + ) diff --git a/policyengine_uk/variables/gov/dwp/LHA_additional_bedrooms.py b/policyengine_uk/variables/gov/dwp/LHA_additional_bedrooms.py new file mode 100644 index 0000000000..6a9dab2c14 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/LHA_additional_bedrooms.py @@ -0,0 +1,53 @@ +from policyengine_uk.model_api import * + + +class LHA_additional_bedrooms(Variable): + value_type = float + entity = BenUnit + label = "Additional bedrooms in the Universal Credit size criteria" + documentation = ( + "One additional bedroom if anyone in the renter's extended benefit " + "unit, or a foster child of the renter, meets the overnight care " + "condition (see meets_lha_overnight_care_condition), however many " + "people do; and one if the renter or a joint renter meets the foster " + "parent condition (see lha_renter_meets_foster_parent_condition), " + "however many children are placed. The extended benefit unit " + "includes the household head's non-dependants and their children. " + "The disabled child and disabled person conditions, for children or " + "couples who cannot share a bedroom, are not modelled." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/9", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/10", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", + ) + + def formula(benunit, period, parameters): + person = benunit.members + overnight_care = person("meets_lha_overnight_care_condition", period) + responsible = person( + "is_child_or_qualifying_young_person_for_universal_credit", period + ) + child = person("is_child_for_universal_credit", period) + no_one_responsible = ( + child | person("is_qualifying_young_person_for_universal_credit", period) + ) & ~responsible + # UC Regs 2013 Sch 4 para 12(A1)(a)-(b): the renter and the members of + # their extended benefit unit, which excludes a child or qualifying + # young person no one is responsible for (paras 9(1) and 9(2)(g)); + # (c): a child for whom the renter meets the foster parent condition. + own = overnight_care & ~(no_one_responsible & ~child) + # Para 9(1)(c): the household head's non-dependants from other + # families, and their children. + other = ( + overnight_care + & person("is_non_dependant_of_household_head", period) + & ~no_one_responsible + ) + head_family = benunit.any(person("is_household_head", period)) + others = head_family * benunit.max(person.household.sum(other)) + # Para 12(9)(a)-(b): one bedroom for each condition met. + overnight_room = benunit.any(own) | (others > 0) + foster_room = benunit("lha_renter_meets_foster_parent_condition", period) + return 1.0 * overnight_room + 1.0 * foster_room diff --git a/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py b/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py index 4f91b49750..a89c371159 100644 --- a/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py @@ -6,28 +6,18 @@ warnings.filterwarnings("ignore") -def bedrooms_for_children(benunit, period): - """Bedrooms for the family's children under 16. +def rooms_for_children(boys_under_10, older_boys, girls_under_10, older_girls): + """The fewest bedrooms that hold children two to a room, where two + children may share if they are of the same sex or both under 10. - Children must share rooms in pairs unless they are opposite-sex and one - is 10 or over. This is the minimum number of bedrooms that allocates them - under those rules. + The arguments count children under 16: boys and girls under 10, and boys + and girls aged 10 to 15. """ - person = benunit.members - age = person("age", period) - male = person("is_male", period) - under_16 = age < 16 - under_10 = age < 10 - child_over_10 = ~under_10 & under_16 - boys_under_10 = benunit.sum(under_10 & male) - boys_over_10 = benunit.sum(child_over_10 & male) - girls_under_10 = benunit.sum(under_10 & ~male) - girls_over_10 = benunit.sum(child_over_10 & ~male) # First, have over-10s share where possible - over_10_rooms = (boys_over_10 + 1) // 2 + (girls_over_10 + 1) // 2 + over_10_rooms = (older_boys + 1) // 2 + (older_girls + 1) // 2 # There may children over 10 still not sharing - space_for_boy_under_10 = boys_over_10 % 2 - space_for_girl_under_10 = girls_over_10 % 2 + space_for_boy_under_10 = older_boys % 2 + space_for_girl_under_10 = older_girls % 2 # Have those spaces filled where possible by children under 10 left_over_boys_under_10 = max_(boys_under_10 - space_for_boy_under_10, 0) left_over_girls_under_10 = max_(girls_under_10 - space_for_girl_under_10, 0) @@ -36,29 +26,76 @@ def bedrooms_for_children(benunit, period): return over_10_rooms + under_10_rooms +def bedrooms_for_children(benunit, period, own_child=None, other_child=None): + """Bedrooms for the children under 16 in the family's size criteria. + + Children must share rooms in pairs unless they are opposite-sex and one + is 10 or over. This is the minimum number of bedrooms that allocates them + under those rules. + + ``own_child`` marks the family's members counted as its children (by + default, every member under 16). ``other_child`` marks people under 16 in + other families of the household who count in the household head's + family's size criteria, such as a non-dependant's child; they share rooms + with the head family's own children. + """ + person = benunit.members + age = person("age", period) + male = person("is_male", period) + under_16 = age < 16 + under_10 = age < 10 + if own_child is None: + own_child = under_16 + own_child = own_child & under_16 + head_family = benunit.any(person("is_household_head", period)) + + def count(group): + own = benunit.sum(own_child & group) + if other_child is None: + return own + others = person.household.sum(other_child & under_16 & group) + return own + head_family * benunit.max(others) + + return rooms_for_children( + count(under_10 & male), + count(~under_10 & male), + count(under_10 & ~male), + count(~under_10 & ~male), + ) + + class LHA_allowed_bedrooms(Variable): value_type = float entity = BenUnit label = "The number of bedrooms covered by LHA for the benefit unit" documentation = ( - "Universal Credit size criteria: one bedroom for the renter or " - "couple, one for each member of the benefit unit aged 16 or over who " - "is not the claimant or partner, one for each non-dependant aged 16 or " - "over, and the children's bedrooms. A qualifying young person the " - "renter is responsible for has their own bedroom; any other member " - "aged 16 or over is a non-dependant and has one too, except a " - "qualifying young person no one is responsible for, such as one " - "looked after by a local authority. Joint tenants and other sharers " - "of the rent, boarders and lodgers are not non-dependants, so they " - "add no bedroom to anyone's entitlement; only the household head's " - "family has non-dependants from other families (see " + "Universal Credit size criteria for the renter's extended benefit " + "unit: one bedroom for the renter or couple, one for each member of " + "the benefit unit aged 16 or over who is not the claimant or partner, " + "one for each non-dependant aged 16 or over, the children's bedrooms, " + "and the additional bedrooms for overnight care and foster parents " + "(see LHA_additional_bedrooms). A qualifying young person the renter " + "is responsible for has their own bedroom; any other member aged 16 " + "or over is a non-dependant and has one too, except a qualifying " + "young person no one is responsible for, such as one looked after by " + "a local authority. The children are the renter's own children and " + "the children of the household head's non-dependants, such as a " + "grandchild whose parent lives with the renter; they share rooms " + "with each other. A child looked after by a local authority, such as " + "a foster child, is no one's responsibility and has no bedroom. Joint " + "tenants and other sharers of the rent, boarders and lodgers are not " + "non-dependants, so neither they nor their children add a bedroom to " + "anyone's entitlement; only the household head's family has " + "non-dependants from other families (see " "is_non_dependant_of_household_head)." ) definition_period = YEAR reference = ( "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/9", "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/10", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", "https://www.legislation.gov.uk/uksi/2013/376/regulation/4", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/4A", "https://www.legislation.gov.uk/uksi/2013/376/regulation/5", ) @@ -67,25 +104,50 @@ def formula(benunit, period, parameters): # A child is a person under 16 (WRA 2012 s.40). aged_16_or_over = person("age", period) >= 16 family_member = aged_16_or_over & ~person("is_claimant_or_partner", period) - # UC Regs 2013 Sch 4 para 10(1)(b): a qualifying young person for - # whom the renter is responsible (regs 4 and 5). - qualifying_young_person = family_member & person( + # UC Regs 2013 reg 4: a person is responsible for a child or + # qualifying young person who normally lives with them, but no one is + # responsible for one looked after by a local authority (reg 4(6)(a)), + # such as a foster child, unless reg 4A applies (for example a child + # placed for adoption). + responsible = person( "is_child_or_qualifying_young_person_for_universal_credit", period ) + qualifying = person("is_qualifying_young_person_for_universal_credit", period) + no_one_responsible = ( + person("is_child_for_universal_credit", period) | qualifying + ) & ~responsible + # UC Regs 2013 Sch 4 para 10(1)(b): a qualifying young person for + # whom the renter is responsible (regs 4 and 5). + qualifying_young_person = family_member & responsible # Para 10(1)(c) with para 9(2): any other member aged 16 or over is a # non-dependant, unless they are a qualifying young person for whom # no one is responsible (para 9(2)(g); reg 4(6)). - family_non_dependant = family_member & ~person( - "is_qualifying_young_person_for_universal_credit", period - ) + family_non_dependant = family_member & ~qualifying family_rooms = benunit.sum(qualifying_young_person | family_non_dependant) - # Para 10(1)(c): a non-dependant from another family who is not a - # child. - non_dependant = aged_16_or_over & person( - "is_non_dependant_of_household_head", period + # Para 9(1)(c) and 9(2): people of other families in the household + # who are non-dependants of the household head's family. Para 9(2)(g) + # excludes a child or qualifying young person no one in the extended + # benefit unit is responsible for, such as a non-dependant's foster + # child; a non-dependant's own child is their responsibility (reg + # 4(2)) and so is a non-dependant too. + non_dependant = ( + person("is_non_dependant_of_household_head", period) & ~no_one_responsible ) head_family = benunit.any(person("is_household_head", period)) - non_dependants = head_family * benunit.max(person.household.sum(non_dependant)) - return ( - 1 + family_rooms + non_dependants + bedrooms_for_children(benunit, period) + # Para 10(1)(c): a non-dependant who is not a child. + non_dependants = head_family * benunit.max( + person.household.sum(non_dependant & aged_16_or_over) + ) + # Para 10(1)(d)-(f): the children in the extended benefit unit, the + # renter's own and their non-dependants', allotted to the fewest + # bedrooms (para 10(2)). A foster child is in no one's extended + # benefit unit (paras 9(1)(b), 9(2)(c) and 9(3); reg 4(6)(a)). + children = bedrooms_for_children( + benunit, + period, + own_child=responsible, + other_child=non_dependant, ) + # Para 10(3)(b) and para 12: additional bedrooms. + additional = benunit("LHA_additional_bedrooms", period) + return 1 + family_rooms + non_dependants + children + additional diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py new file mode 100644 index 0000000000..41c9b637bf --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py @@ -0,0 +1,44 @@ +from policyengine_uk.model_api import * + + +class housing_benefit_LHA_additional_bedrooms(Variable): + value_type = float + entity = BenUnit + label = "Additional bedrooms in the Housing Benefit size criteria" + documentation = ( + "One additional bedroom if the claimant, partner, another occupier " + "or a child placed with the claimant needs overnight care (see " + "meets_lha_overnight_care_condition), and one if the claimant or " + "partner is a qualifying parent or carer (see " + "is_housing_benefit_qualifying_parent_or_carer): two if both. " + "Occupiers outside the family count for the household head's family: " + "non-dependants, boarders and lodgers and their children, but not a " + "sharer of the rent. Children and couples who cannot share a bedroom " + "because of disability are not modelled." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2006/213/regulation/13D", + "https://www.legislation.gov.uk/uksi/2006/214/regulation/13D", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/2", + ) + + def formula(benunit, period, parameters): + person = benunit.members + overnight_care = person("meets_lha_overnight_care_condition", period) + # HB Regs 2006 reg 13D(3A)(a)(iii) and 13D(12): occupiers of other + # families, other than a joint tenant outside the claimant's + # household. + head_family = person.benunit.any(person("is_household_head", period)) + sharer = person.benunit("liable_for_share_of_household_rent", period) + other = overnight_care & ~head_family & ~sharer + is_head_family = benunit.any(person("is_household_head", period)) + others = is_head_family * benunit.max(person.household.sum(other)) + # Reg 13D(3A)(a)(i)-(iv): the claimant, partner, other members of the + # family, and a child or young person for whom the claimant or + # partner is a qualifying parent or carer. + overnight_room = benunit.any(overnight_care) | (others > 0) + # Reg 13D(3A)(b). + carer_room = benunit("is_housing_benefit_qualifying_parent_or_carer", period) + # Reg 13D(3B): two additional bedrooms where both apply. + return 1.0 * overnight_room + 1.0 * carer_room diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py index 12ff521ea5..36929674aa 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py @@ -10,13 +10,19 @@ class housing_benefit_LHA_allowed_bedrooms(Variable): label = "Bedrooms in the Housing Benefit size criteria" documentation = ( "Housing Benefit size criteria: one bedroom for the claimant or " - "couple, one for each other occupier aged 16 or over, and the " - "children's bedrooms. Occupiers are everyone who lives in the " - "dwelling as their home except a joint tenant outside the claimant's " - "household. So every member of the benefit unit aged 16 or over who " - "is not the claimant or partner adds a bedroom, such as a young " - "person in full-time education; so do a householder's boarder or " - "lodger and a non-dependant, but a sharer of the rent does not. A " + "couple, one for each other occupier aged 16 or over, the bedrooms " + "for the occupiers' children, and the additional bedrooms for " + "overnight care and qualifying parents or carers (see " + "housing_benefit_LHA_additional_bedrooms). Occupiers are everyone " + "who lives in the dwelling as their home except a joint tenant " + "outside the claimant's household. So every member of the benefit " + "unit aged 16 or over who is not the claimant or partner adds a " + "bedroom, such as a young person in full-time education; so do a " + "householder's boarder or lodger and a non-dependant, but a sharer " + "of the rent does not. The children of a non-dependant, boarder or " + "lodger are occupiers too, and share rooms with the claimant's " + "children. A child or young person placed with the claimant or " + "partner as a foster child or for adoption is not an occupier. A " "sharer's, boarder's or lodger's own claim counts only their own " "family." ) @@ -26,26 +32,41 @@ class housing_benefit_LHA_allowed_bedrooms(Variable): "https://www.legislation.gov.uk/uksi/2006/214/regulation/13D", "https://www.legislation.gov.uk/uksi/2006/213/regulation/2", "https://www.legislation.gov.uk/uksi/2006/214/regulation/2", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/21", + "https://www.legislation.gov.uk/uksi/2006/214/regulation/21", ) def formula(benunit, period, parameters): person = benunit.members # A child is a person under 16 (HB Regs 2006 reg 2(1)). aged_16_or_over = person("age", period) >= 16 + # HB Regs 2006 reg 21(3): a child or young person placed with the + # claimant or partner as a foster child or for adoption does not + # occupy the claimant's dwelling. + occupier = ~person("is_child_or_young_person_placed_with_family", period) # HB Regs 2006 reg 13D(3)(b): a person who is not a child, here a # member of the benefit unit other than the claimant or partner. family_rooms = benunit.sum( - aged_16_or_over & ~person("is_claimant_or_partner", period) + aged_16_or_over & ~person("is_claimant_or_partner", period) & occupier ) head_family = person.benunit.any(person("is_household_head", period)) sharer = person.benunit("liable_for_share_of_household_rent", period) - # Reg 13D(3)(b) for occupiers outside the family, as defined in - # 13D(12). - other_occupier = aged_16_or_over & ~head_family & ~sharer + # Reg 13D(3) for occupiers outside the family, as defined in 13D(12): + # everyone in a non-dependant's, boarder's or lodger's family, + # including their children. + other_occupier = ~head_family & ~sharer is_head_family = benunit.any(person("is_household_head", period)) other_occupiers = is_head_family * benunit.max( - person.household.sum(other_occupier) + person.household.sum(other_occupier & aged_16_or_over) ) - return ( - 1 + family_rooms + other_occupiers + bedrooms_for_children(benunit, period) + # Reg 13D(3)(c)-(e): the occupiers' children, the claimant's own and + # other families', share rooms with each other. + children = bedrooms_for_children( + benunit, + period, + own_child=occupier, + other_child=other_occupier, ) + # Reg 13D(3A) and (3B): additional bedrooms. + additional = benunit("housing_benefit_LHA_additional_bedrooms", period) + return 1 + family_rooms + other_occupiers + children + additional diff --git a/policyengine_uk/variables/gov/dwp/is_child_or_young_person_for_legacy_benefits.py b/policyengine_uk/variables/gov/dwp/is_child_or_young_person_for_legacy_benefits.py index 7091a57918..b456a6ba9c 100644 --- a/policyengine_uk/variables/gov/dwp/is_child_or_young_person_for_legacy_benefits.py +++ b/policyengine_uk/variables/gov/dwp/is_child_or_young_person_for_legacy_benefits.py @@ -11,8 +11,10 @@ class is_child_or_young_person_for_legacy_benefits(Variable): (SSCBA 1992 s.142), excluding young persons entitled to a benefit in their own right. That is the Child Benefit child or qualifying young person test, restricted to members who are not the claimant or partner. - A child or young person placed with the claimant by a local authority is - not a member of the claimant's household (IS reg 16(4); HB reg 21(3)). + A child or young person placed with the claimant by a local authority, + such as a foster child, or placed for adoption is not a member of the + claimant's household (IS reg 16(4); JSA reg 78(4); ESA reg 156(5); HB reg + 21(3)). """ value_type = bool @@ -28,11 +30,13 @@ class is_child_or_young_person_for_legacy_benefits(Variable): "https://www.legislation.gov.uk/uksi/2012/2885/regulation/2", "https://www.legislation.gov.uk/uksi/1987/1967/regulation/16", "https://www.legislation.gov.uk/uksi/2006/213/regulation/21", + "https://www.legislation.gov.uk/uksi/1996/207/regulation/78", + "https://www.legislation.gov.uk/uksi/2008/794/regulation/156", ) def formula(person, period, parameters): return ( person("is_child_or_qualifying_young_person_for_child_benefit", period) & ~person("is_claimant_or_partner", period) - & ~person("is_looked_after_by_local_authority", period) + & ~person("is_child_or_young_person_placed_with_family", period) ) diff --git a/policyengine_uk/variables/gov/dwp/is_child_or_young_person_placed_with_family.py b/policyengine_uk/variables/gov/dwp/is_child_or_young_person_placed_with_family.py new file mode 100644 index 0000000000..592130460f --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_child_or_young_person_placed_with_family.py @@ -0,0 +1,42 @@ +from policyengine_uk.model_api import * + + +class is_child_or_young_person_placed_with_family(Variable): + value_type = bool + entity = Person + label = "Child or young person placed with the family by a local authority or for adoption" + documentation = ( + "A child or young person (the Child Benefit definition) who is not " + "the claimant or partner and is placed with their benefit unit by a " + "local authority, such as a foster child (see " + "is_looked_after_by_local_authority), or placed for adoption (see " + "is_placed_for_adoption). Under Income Support, income-based " + "Jobseeker's Allowance, income-related Employment and Support " + "Allowance and Housing Benefit they are not a member of the " + "claimant's household, and under Housing Benefit they do not occupy " + "the claimant's dwelling. HB reg 21(3)(a) refers to a placement " + "under section 22C(2) of the Children Act 1989; the model follows " + "DWP's guidance that foster children are within reg 21(3) (LHA " + "Guidance Manual para 2.033)." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2006/213/regulation/21", + "https://www.legislation.gov.uk/uksi/2006/214/regulation/21", + "https://www.legislation.gov.uk/uksi/1987/1967/regulation/16", + "https://www.legislation.gov.uk/uksi/1996/207/regulation/78", + "https://www.legislation.gov.uk/uksi/2008/794/regulation/156", + "https://assets.publishing.service.gov.uk/media/5a758238ed915d6faf2b38a2/lha-guidance-manual.pdf", + ) + + def formula(person, period, parameters): + # HB Regs 2006 reg 21(3)(a)-(c); IS Regs 1987 reg 16(4); JSA Regs + # 1996 reg 78(4); ESA Regs 2008 reg 156(5). + placed = person("is_looked_after_by_local_authority", period) | person( + "is_placed_for_adoption", period + ) + return ( + placed + & person("is_child_or_qualifying_young_person_for_child_benefit", period) + & ~person("is_claimant_or_partner", period) + ) diff --git a/policyengine_uk/variables/gov/dwp/is_housing_benefit_qualifying_parent_or_carer.py b/policyengine_uk/variables/gov/dwp/is_housing_benefit_qualifying_parent_or_carer.py new file mode 100644 index 0000000000..d958787359 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_housing_benefit_qualifying_parent_or_carer.py @@ -0,0 +1,37 @@ +from policyengine_uk.model_api import * + + +class is_housing_benefit_qualifying_parent_or_carer(Variable): + value_type = bool + entity = BenUnit + label = "Housing Benefit claimant or partner is a qualifying parent or carer" + documentation = ( + "Whether the claimant or partner has a child or young person placed " + "with them as a foster child or for adoption (see " + "is_child_or_young_person_placed_with_family), or is an approved " + "foster parent who has had no child placed with them for at most 52 " + "weeks (is_approved_foster_parent_without_placement). The claimant " + "gets one additional bedroom, and is not a young individual for the " + "shared accommodation rate. The definition also requires a bedroom " + "in the dwelling additional to those the occupiers use; the model " + "does not observe the dwelling's bedrooms and assumes there is one." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2006/213/regulation/2", + "https://www.legislation.gov.uk/uksi/2006/214/regulation/2", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/13D", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/21", + ) + + def formula(benunit, period, parameters): + person = benunit.members + # HB Regs 2006 reg 2(1), "qualifying parent or carer", (a): a child + # or young person placed with them as mentioned in reg 21(3). + placed = person("is_child_or_young_person_placed_with_family", period) + # (b): an approved foster parent with no placement for at most 52 + # weeks. + between_placements = person("is_claimant_or_partner", period) & person( + "is_approved_foster_parent_without_placement", period + ) + return benunit.any(placed | between_placements) diff --git a/policyengine_uk/variables/gov/dwp/is_housing_benefit_young_individual.py b/policyengine_uk/variables/gov/dwp/is_housing_benefit_young_individual.py index f66e7af8ee..d54721850a 100644 --- a/policyengine_uk/variables/gov/dwp/is_housing_benefit_young_individual.py +++ b/policyengine_uk/variables/gov/dwp/is_housing_benefit_young_individual.py @@ -7,17 +7,29 @@ class is_housing_benefit_young_individual(Variable): label = "Young individual (Housing Benefit)" documentation = ( "A single claimant (no partner and not a lone parent) under the " - "shared accommodation age threshold. The exceptions for a housing " - "association landlord, care leavers, former hostel residents, " - "offenders under multi-agency management, people who need overnight " - "care, qualifying parents or carers, victims of domestic violence " - "and of modern slavery are not identified." + "shared accommodation age threshold, other than one who requires " + "overnight care (see meets_lha_overnight_care_condition) or is a " + "qualifying parent or carer (see " + "is_housing_benefit_qualifying_parent_or_carer). The exceptions for " + "a housing association landlord, care leavers, former hostel " + "residents, offenders under multi-agency management, and victims of " + "domestic violence and of modern slavery are not identified." ) definition_period = YEAR reference = "https://www.legislation.gov.uk/uksi/2006/213/regulation/2" def formula(benunit, period, parameters): threshold = parameters(period).gov.dwp.LHA.shared_accommodation_age_threshold - return benunit("is_single_person", period) & ( - benunit("eldest_claimant_or_partner_age", period) < threshold + person = benunit.members + # HB Regs 2006 reg 2(1), "young individual": not a claimant who is a + # person who requires overnight care or a qualifying parent or carer. + requires_overnight_care = benunit.any( + person("is_claimant_or_partner", period) + & person("meets_lha_overnight_care_condition", period) + ) + return ( + benunit("is_single_person", period) + & (benunit("eldest_claimant_or_partner_age", period) < threshold) + & ~requires_overnight_care + & ~benunit("is_housing_benefit_qualifying_parent_or_carer", period) ) diff --git a/policyengine_uk/variables/gov/dwp/is_lha_shared_accommodation_rate_specified_renter.py b/policyengine_uk/variables/gov/dwp/is_lha_shared_accommodation_rate_specified_renter.py index 2e43025fa3..b288b574dc 100644 --- a/policyengine_uk/variables/gov/dwp/is_lha_shared_accommodation_rate_specified_renter.py +++ b/policyengine_uk/variables/gov/dwp/is_lha_shared_accommodation_rate_specified_renter.py @@ -10,10 +10,11 @@ class is_lha_shared_accommodation_rate_specified_renter(Variable): "accommodation age threshold, not responsible for a child or young " "person under the UC or Housing Benefit rules, no non-dependant under " "the household composition proxy, and not excepted by a disability " - "benefit (UC Schedule 4 paragraph 29(5)). Other paragraph 29 " - "exceptions and couples claiming as single people are not modelled. " - "The same category serves Housing Benefit, whose young-individual " - "definition (HB regulation 2(1)) has no disability exception." + "benefit (UC Schedule 4 paragraph 29(5)) or as a foster parent or " + "adopter (paragraph 29(9A)). Other paragraph 29 exceptions and " + "couples claiming as single people are not modelled. Housing Benefit " + "has its own young-individual test: see " + "is_housing_benefit_young_individual." ) definition_period = YEAR reference = ( @@ -29,14 +30,19 @@ def formula(benunit, period, parameters): p = parameters(period).gov.dwp.LHA person = benunit.members # UC Sch 4 para 29(5): a renter under 35 receiving - # attendance allowance, DLA care at the middle or highest rate, or the - # PIP daily living component is excepted. Other para 29 exceptions - # (care leavers, hostel residents, MAPPA, domestic abuse, modern - # slavery, foster parents) are not observed. + # attendance allowance (which includes armed forces independence + # payment, reg 2), DLA care at the middle or highest rate, or the PIP + # daily living component is excepted. Other para 29 exceptions (care + # leavers, hostel residents, MAPPA, domestic abuse, modern slavery) + # are not observed. excepted_disabled_renter = benunit.any( person("is_claimant_or_partner", period) & (add(person, period, p.shared_accommodation_exception_benefits) > 0) ) + # Para 29(9A): a renter who satisfies the foster parent condition. + excepted_foster_parent = benunit( + "lha_renter_meets_foster_parent_condition", period + ) return ( ~benunit("is_couple", period) & ( @@ -49,4 +55,5 @@ def formula(benunit, period, parameters): ) & ~benunit("lha_renter_has_non_dependant", period) & ~excepted_disabled_renter + & ~excepted_foster_parent ) diff --git a/policyengine_uk/variables/gov/dwp/is_non_dependant_of_household_head.py b/policyengine_uk/variables/gov/dwp/is_non_dependant_of_household_head.py index 2c80db6274..5042743e15 100644 --- a/policyengine_uk/variables/gov/dwp/is_non_dependant_of_household_head.py +++ b/policyengine_uk/variables/gov/dwp/is_non_dependant_of_household_head.py @@ -15,8 +15,10 @@ class is_non_dependant_of_household_head(Variable): "Credit counts a non-dependant in one claim only, which the model " "gives to the household head's family; Housing Benefit and Council " "Tax Reduction apportion a non-dependant of several joint occupiers " - "between them. Foster children and carers engaged through a charity " - "are not identified." + "between them. A non-dependant's own children are non-dependants " + "too; a child looked after by a local authority is not (see " + "LHA_allowed_bedrooms). Carers engaged through a charity are not " + "identified." ) definition_period = YEAR reference = ( diff --git a/policyengine_uk/variables/gov/dwp/lha_renter_has_non_dependant.py b/policyengine_uk/variables/gov/dwp/lha_renter_has_non_dependant.py index 31f652b563..5d0f6f3ce0 100644 --- a/policyengine_uk/variables/gov/dwp/lha_renter_has_non_dependant.py +++ b/policyengine_uk/variables/gov/dwp/lha_renter_has_non_dependant.py @@ -12,12 +12,14 @@ class lha_renter_has_non_dependant(Variable): "household head (see is_non_dependant_of_household_head): joint " "tenants and other sharers of the rent, boarders and lodgers are not " "non-dependants, and a sharer, boarder or lodger has none from the " - "household head's family. Foster children are not identified." + "household head's family. A child or young person placed with the " + "family as a foster child or for adoption is not a non-dependant." ) definition_period = YEAR reference = ( "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/9", "https://www.legislation.gov.uk/uksi/2006/213/regulation/3", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/21", ) def formula(benunit, period, parameters): @@ -26,9 +28,13 @@ def formula(benunit, period, parameters): # A child or young person under either scheme (UC reg 5; HB reg 19) # is a dependant, not a non-dependant. age = person("age", period) + # A foster child or a child placed for adoption is not a + # non-dependant either (UC Sch 4 para 9(2)(c) and (g); HB reg 3(2)(c) + # and reg 21(3)). child_or_qyp = ( person("is_child_or_qualifying_young_person_for_universal_credit", period) | person("is_child_or_young_person_for_legacy_benefits", period) + | person("is_child_or_young_person_placed_with_family", period) | ((age >= 16) & (age < 17)) ) within_benefit_unit = benunit.any(~claimant_or_partner & ~child_or_qyp) diff --git a/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py b/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py new file mode 100644 index 0000000000..f4f26f299b --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py @@ -0,0 +1,40 @@ +from policyengine_uk.model_api import * + + +class lha_renter_meets_foster_parent_condition(Variable): + value_type = bool + entity = BenUnit + label = "Universal Credit renter meets the foster parent condition" + documentation = ( + "Whether the renter or a joint renter is a foster parent or an " + "adopter with whom a child has been placed for adoption. A foster " + "parent has a child under 18 looked after by a local authority " + "placed with them (a member of the benefit unit flagged " + "is_looked_after_by_local_authority), or is an approved foster " + "parent whose last placement ended, or who was approved, within 12 " + "months (is_approved_foster_parent_without_placement). The renter " + "gets one additional bedroom however many children are placed, and " + "is excepted from the shared accommodation rate." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/29", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/2", + ) + + def formula(benunit, period, parameters): + person = benunit.members + claimant_or_partner = person("is_claimant_or_partner", period) + # UC Regs 2013 reg 2, "foster parent": a person with whom a child is + # placed under the care planning regulations; a child here is a + # person under 18 looked after by a local authority. + placed = ( + person("is_looked_after_by_local_authority", period) + | person("is_placed_for_adoption", period) + ) & (person("age", period) < 18) + # UC Regs 2013 Sch 4 para 12(4)(a)-(b) and (5). + between_placements = claimant_or_partner & person( + "is_approved_foster_parent_without_placement", period + ) + return benunit.any((placed & ~claimant_or_partner) | between_placements) diff --git a/policyengine_uk/variables/gov/dwp/meets_lha_overnight_care_condition.py b/policyengine_uk/variables/gov/dwp/meets_lha_overnight_care_condition.py new file mode 100644 index 0000000000..ad86b14b6d --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/meets_lha_overnight_care_condition.py @@ -0,0 +1,34 @@ +from policyengine_uk.model_api import * + + +class meets_lha_overnight_care_condition(Variable): + value_type = bool + entity = Person + label = "Meets the LHA overnight care condition" + documentation = ( + "Whether this person receives a qualifying disability benefit " + "(attendance allowance or armed forces independence payment, the " + "care component of Disability Living Allowance at the middle or " + "highest rate, or the daily living component of Personal " + "Independence Payment) and has non-resident carers who regularly " + "stay overnight to care for them. This is the Universal Credit " + "overnight care condition and the Housing Benefit 'person who " + "requires overnight care'. Two Housing Benefit routes are not " + "modelled: a person without a qualifying benefit whose need the " + "local authority accepts on other evidence, and the requirement that " + "the carers have the use of a bedroom additional to the occupiers'." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/2", + "https://www.legislation.gov.uk/uksi/2006/214/regulation/2", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.dwp.LHA + # UC Regs 2013 Sch 4 para 12(3)(a); HB Regs 2006 reg 2(1), "person + # who requires overnight care", (a)(i)-(iib). + qualifying_benefit = add(person, period, p.overnight_care_benefits) > 0 + # UC para 12(3)(b)-(c); HB reg 2(1), (b). + return qualifying_benefit & person("has_non_resident_overnight_carer", period) diff --git a/policyengine_uk/variables/household/demographic/has_non_resident_overnight_carer.py b/policyengine_uk/variables/household/demographic/has_non_resident_overnight_carer.py new file mode 100644 index 0000000000..e608119809 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/has_non_resident_overnight_carer.py @@ -0,0 +1,21 @@ +from policyengine_uk.model_api import * + + +class has_non_resident_overnight_carer(Variable): + value_type = bool + entity = Person + label = "has a non-resident overnight carer" + documentation = ( + "Whether one or more people who do not live in the home are engaged, " + "under arrangements made for that purpose, to provide this person " + "with overnight care and to stay overnight in the home on a regular " + "basis. With a qualifying disability benefit, this gives the " + "household an additional bedroom in the Universal Credit and Housing " + "Benefit size criteria (see meets_lha_overnight_care_condition)." + ) + definition_period = YEAR + default_value = False + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/2", + ) diff --git a/policyengine_uk/variables/household/demographic/is_approved_foster_parent_without_placement.py b/policyengine_uk/variables/household/demographic/is_approved_foster_parent_without_placement.py new file mode 100644 index 0000000000..402cfae680 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_approved_foster_parent_without_placement.py @@ -0,0 +1,22 @@ +from policyengine_uk.model_api import * + + +class is_approved_foster_parent_without_placement(Variable): + value_type = bool + entity = Person + label = "approved foster parent between placements" + documentation = ( + "Whether this person is an approved foster parent (in Scotland, a " + "foster carer or kinship carer) with no child currently placed with " + "them, whose last placement ended, or who was approved and has had " + "no placement since, no more than 12 months ago. Housing Benefit's " + "limit is 52 weeks. Such a person keeps the foster parent's " + "additional bedroom (UC Regs 2013 Sch 4 para 12(5); HB Regs 2006 " + "reg 2(1), 'qualifying parent or carer')." + ) + definition_period = YEAR + default_value = False + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/2", + ) diff --git a/policyengine_uk/variables/household/demographic/is_looked_after_by_local_authority.py b/policyengine_uk/variables/household/demographic/is_looked_after_by_local_authority.py index d2a18247a4..96b158ba78 100644 --- a/policyengine_uk/variables/household/demographic/is_looked_after_by_local_authority.py +++ b/policyengine_uk/variables/household/demographic/is_looked_after_by_local_authority.py @@ -5,6 +5,24 @@ class is_looked_after_by_local_authority(Variable): value_type = bool entity = Person label = "looked after by a local authority" + documentation = ( + "Whether this child or young person is looked after by a local " + "authority. A looked-after child who lives in the household, such as " + "a foster child, is placed with the family whose benefit unit they " + "are in: no one in it is responsible for them under Universal Credit " + "(UC Regs 2013 reg 4(6)(a)), they are not a member of the claimant's " + "household under Housing Benefit and the other legacy schemes (HB " + "Regs 2006 reg 21(3)), they have no bedroom of their own in the size " + "criteria, and their carer meets the foster parent condition for an " + "additional bedroom. Leave it false for a child placed for adoption " + "(see is_placed_for_adoption) or placed with their own parent, for " + "whom Universal Credit treats the carer as responsible (reg 4A)." + ) definition_period = YEAR default_value = False - reference = "https://www.legislation.gov.uk/uksi/2015/448/regulation/4" + reference = ( + "https://www.legislation.gov.uk/uksi/2015/448/regulation/4", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/4", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/4A", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/21", + ) diff --git a/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py b/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py new file mode 100644 index 0000000000..533c791196 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py @@ -0,0 +1,25 @@ +from policyengine_uk.model_api import * + + +class is_placed_for_adoption(Variable): + value_type = bool + entity = Person + label = "placed for adoption" + documentation = ( + "Whether this child is placed for adoption with the claimant or " + "partner of their benefit unit (an adopter), or placed with them " + "prior to adoption. Put the child in the adopter's benefit unit and " + "leave is_looked_after_by_local_authority false: under Universal " + "Credit the adopter is responsible for the child (UC Regs 2013 reg " + "4A(1)(c)) and satisfies the foster parent condition for an " + "additional bedroom (Sch 4 para 12(4)(b)); under Housing Benefit and " + "the other legacy schemes the child is not a member of the " + "claimant's household (HB Regs 2006 reg 21(3)(b)-(c))." + ) + definition_period = YEAR + default_value = False + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/regulation/4A", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/21", + ) From 8e42058d202e9d090d6981674e86f1022eac4c0b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 1 Oct 2026 14:42:35 -0400 Subject: [PATCH 02/14] Treat a child placed with any family as no Housing Benefit occupier HB reg 21(3) names only children placed with the claimant or partner, but DWP treats foster children as not occupying the dwelling (LHA Guidance Manual para 2.033; HB circular A21/2013 para 22), as Universal Credit does (UC Sch 4 para 9(2)(g)). The household head's Housing Benefit size criteria and overnight-care bedroom now leave out a child placed with a non-dependant, boarder or lodger as a foster child or for adoption. Adds a YAML case and tightens the foster-child Hypothesis property to an exact effect on every family. Co-Authored-By: Claude Opus 5.5 --- ...lies_children_and_additional_bedrooms.yaml | 38 ++++++++++++++++ ...est_lha_bedroom_young_people_properties.py | 44 ++++++++----------- ...housing_benefit_LHA_additional_bedrooms.py | 13 ++++-- .../housing_benefit_LHA_allowed_bedrooms.py | 12 +++-- 4 files changed, 74 insertions(+), 33 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml index 0164ea54e1..c499c5bae1 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml @@ -207,6 +207,44 @@ LHA_allowed_bedrooms: [1, 2] housing_benefit_LHA_allowed_bedrooms: [1, 2] +- name: A non-dependant's foster child does not count for the renter + period: 2026 + input: + people: + renter: + age: 60 + is_household_head: true + daughter: + age: 32 + is_male: false + foster_child: + age: 6 + is_looked_after_by_local_authority: true + benunits: + renter_family: + members: [renter] + daughter_family: + members: [daughter, foster_child] + households: + home: + members: [renter, daughter, foster_child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: no one in the renter's extended benefit unit is responsible for a + # looked-after child (reg 4(6)(a)), so the child is not a non-dependant + # (para 9(2)(g)): the renter and the daughter, 2 bedrooms. + LHA_allowed_bedrooms: [2, 2] + # HB: reg 21(3) names only a child placed with the claimant or partner, + # but DWP treats foster children as not occupying the dwelling (LHA + # Guidance Manual para 2.033; circular A21/2013 para 22), and the model + # follows it: 2 bedrooms. The daughter's own size criteria include the + # foster parent's or qualifying parent or carer's additional bedroom. + housing_benefit_LHA_allowed_bedrooms: [2, 2] + LHA_additional_bedrooms: [0, 1] + housing_benefit_LHA_additional_bedrooms: [0, 1] + - name: Foster children have no bedrooms of their own; the foster parent has one additional bedroom period: 2026 input: diff --git a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py index efbec9389f..db44dad3e4 100644 --- a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py +++ b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py @@ -20,16 +20,18 @@ household head's family also gains one bedroom under both schemes if the person joins a non-dependant's family (UC: unless no one is responsible for them), and one Housing Benefit bedroom if they join a boarder's or - lodger's family. No other family's bedrooms change. + lodger's family (HB: unless placed with it as a foster child). No other + family's bedrooms change. 3. Children: adding a child under 16 who is neither fostered nor placed for adoption to any family never lowers any family's bedrooms under either scheme and raises each by at most one. 4. Foster children: adding a foster child to a family gives that family one more bedroom under each scheme if it did not already meet the foster parent condition (UC) or have a qualifying parent or carer (HB), and none - otherwise. No other family's Universal Credit bedrooms change; the - household head's Housing Benefit bedrooms rise by at most one when the - child is placed with an occupier's family, whose children are occupiers. + otherwise. No other family's bedrooms change under either scheme: a + child placed with another family is not in the household head's + extended benefit unit (UC para 9(2)(g)) and, following DWP, not an + occupier (HB; LHA Guidance Manual para 2.033). 5. Reference: every family's bedrooms, and its additional bedrooms, equal an independent count of the size criteria: - one for the claimant or couple; @@ -38,15 +40,15 @@ family as a foster child); - for the household head's family, one for each person aged 16 or over in a non-dependant's family (and, for HB, in a boarder's or lodger's - family); + family), except one no one is responsible for (UC) or placed with that + family as a foster child (HB); - the fewest rooms that hold the counted children under 16 two to a room, where only children of the same sex or two children under 10 may share, found by brute force. UC counts the family's own children and, for the household head's family, the non-dependants' children, but no - foster child. HB counts the family's own children other than foster - children and children placed for adoption and, for the household - head's family, every child of a non-dependant's, boarder's or lodger's - family; + foster child. HB counts the children of the family and, for the + household head's family, of a non-dependant's, boarder's or lodger's + family, other than foster children and children placed for adoption; - one additional bedroom if anyone so counted, or a foster child of the family, has overnight care, and one if the family fosters, has a child placed for adoption, or has an approved foster parent between @@ -298,9 +300,8 @@ def test_exact_effect_of_adding_a_person_aged_16_to_19(case): head = keys.index((target[0], 0)) if fam["role"] == "non_dependant": expected_uc[head] += 0 if added["unclaimed"] else 1 - expected_hb[head] += 1 - elif fam["role"] in ("boarder", "lodger"): - expected_hb[head] += 1 + if fam["role"] in ("non_dependant", "boarder", "lodger"): + expected_hb[head] += 0 if added["placed"] else 1 assert np.array_equal(uc_after - uc_before, expected_uc) assert np.array_equal(hb_after - hb_before, expected_hb) @@ -333,16 +334,7 @@ def test_a_foster_child_adds_only_the_foster_parents_bedroom(case): expected_uc = np.zeros(len(keys)) expected_uc[t] = new_carer assert np.array_equal(uc_after - uc_before, expected_uc) - hb_change = hb_after - hb_before - assert hb_change[t] == new_carer - head = keys.index((target[0], 0)) - for b in range(len(keys)): - if b == t: - continue - if b == head and fam["role"] in ("non_dependant", "boarder", "lodger"): - assert hb_change[b] in (0, 1) - else: - assert hb_change[b] == 0 + assert np.array_equal(hb_after - hb_before, expected_uc) @PROPERTY_SETTINGS @@ -388,10 +380,12 @@ def test_bedrooms_match_an_independent_count_of_the_size_criteria(case): c[3] for c in other["children"] if c[2] != "foster" ) if other["role"] in ("non_dependant", "boarder", "lodger"): - hb_rooms += len(other["adults"]) + int(joins) - hb_children += list(other["children"]) + hb_rooms += len(other["adults"]) + int( + joins and not added["placed"] + ) + hb_children += [c for c in other["children"] if c[2] == "own"] hb_overnight |= any(o for _, o in other["adults"]) or any( - c[3] for c in other["children"] + c[3] for c in other["children"] if c[2] == "own" ) expected_uc_additional = int(uc_overnight) + int(uc_foster) expected_hb_additional = int(hb_overnight) + int(hb_carer) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py index 41c9b637bf..5c9d55d1ee 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py @@ -13,8 +13,11 @@ class housing_benefit_LHA_additional_bedrooms(Variable): "is_housing_benefit_qualifying_parent_or_carer): two if both. " "Occupiers outside the family count for the household head's family: " "non-dependants, boarders and lodgers and their children, but not a " - "sharer of the rent. Children and couples who cannot share a bedroom " - "because of disability are not modelled." + "sharer of the rent or a child placed with them as a foster child or " + "for adoption. The definitions require a bedroom in the dwelling for " + "the carer or the foster child; the model does not observe the " + "dwelling's bedrooms and assumes there is one. Children and couples " + "who cannot share a bedroom because of disability are not modelled." ) definition_period = YEAR reference = ( @@ -28,10 +31,12 @@ def formula(benunit, period, parameters): overnight_care = person("meets_lha_overnight_care_condition", period) # HB Regs 2006 reg 13D(3A)(a)(iii) and 13D(12): occupiers of other # families, other than a joint tenant outside the claimant's - # household. + # household and a child placed with another family as a foster child + # or for adoption (see housing_benefit_LHA_allowed_bedrooms). head_family = person.benunit.any(person("is_household_head", period)) sharer = person.benunit("liable_for_share_of_household_rent", period) - other = overnight_care & ~head_family & ~sharer + placed = person("is_child_or_young_person_placed_with_family", period) + other = overnight_care & ~head_family & ~sharer & ~placed is_head_family = benunit.any(person("is_household_head", period)) others = is_head_family * benunit.max(person.household.sum(other)) # Reg 13D(3A)(a)(i)-(iv): the claimant, partner, other members of the diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py index 36929674aa..414d2d577c 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py @@ -21,8 +21,8 @@ class housing_benefit_LHA_allowed_bedrooms(Variable): "householder's boarder or lodger and a non-dependant, but a sharer " "of the rent does not. The children of a non-dependant, boarder or " "lodger are occupiers too, and share rooms with the claimant's " - "children. A child or young person placed with the claimant or " - "partner as a foster child or for adoption is not an occupier. A " + "children. A child or young person placed with a family in the " + "household as a foster child or for adoption is not an occupier. A " "sharer's, boarder's or lodger's own claim counts only their own " "family." ) @@ -42,7 +42,11 @@ def formula(benunit, period, parameters): aged_16_or_over = person("age", period) >= 16 # HB Regs 2006 reg 21(3): a child or young person placed with the # claimant or partner as a foster child or for adoption does not - # occupy the claimant's dwelling. + # occupy the claimant's dwelling. Reg 21(3) names only placements + # with the claimant or partner; the model follows DWP in treating a + # child placed with anyone in the household as no occupier (LHA + # Guidance Manual para 2.033; HB circular A21/2013 para 22), as + # Universal Credit does (UC Sch 4 para 9(2)(g)). occupier = ~person("is_child_or_young_person_placed_with_family", period) # HB Regs 2006 reg 13D(3)(b): a person who is not a child, here a # member of the benefit unit other than the claimant or partner. @@ -54,7 +58,7 @@ def formula(benunit, period, parameters): # Reg 13D(3) for occupiers outside the family, as defined in 13D(12): # everyone in a non-dependant's, boarder's or lodger's family, # including their children. - other_occupier = ~head_family & ~sharer + other_occupier = ~head_family & ~sharer & occupier is_head_family = benunit.any(person("is_household_head", period)) other_occupiers = is_head_family * benunit.max( person.household.sum(other_occupier & aged_16_or_over) From 8e8b0876bcd93b6f31ec6434a2da1c451daeac8a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 1 Oct 2026 16:01:02 -0400 Subject: [PATCH 03/14] Give an occupier couple one Housing Benefit bedroom HB Regs 2006 reg 13D(3)(a) puts any occupier couple in "a couple" (one bedroom; LHA Guidance Manual para 2.012), so a non-dependant's, boarder's or lodger's couple adds one bedroom to the household head's HB size criteria, not two. Universal Credit is unchanged: each non-dependant who is not a child has a bedroom (UC Sch 4 para 10(1)(c); ADM F3112 note 1). Reported by the disabled-rooms session (chip task_cc724597). Co-Authored-By: Claude Opus 5.5 --- ...lies_children_and_additional_bedrooms.yaml | 30 +++++++++++++++++++ ...est_lha_bedroom_young_people_properties.py | 6 ++-- .../housing_benefit_LHA_allowed_bedrooms.py | 14 +++++++-- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml index c499c5bae1..fb3d46a037 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml @@ -142,6 +142,36 @@ housing_benefit_LHA_allowed_bedrooms: [3, 2] housing_benefit_LHA_category: [D, C] +- name: A non-dependant couple has a bedroom each under Universal Credit but one between them under Housing Benefit + period: 2026 + input: + people: + renter: + age: 60 + is_household_head: true + son: + age: 30 + sons_partner: + age: 29 + benunits: + renter_family: + members: [renter] + son_family: + members: [son, sons_partner] + households: + home: + members: [renter, son, sons_partner] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: each is "a non-dependant who is not a child" (para 10(1)(c); ADM + # F3112 note 1): 3 bedrooms. + LHA_allowed_bedrooms: [3, 1] + # HB: the occupier couple is one category, "a couple" (reg 13D(3)(a); + # LHA Guidance Manual para 2.012): 2 bedrooms. + housing_benefit_LHA_allowed_bedrooms: [2, 1] + - name: A lodger's child counts for Housing Benefit but not Universal Credit period: 2026 input: diff --git a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py index db44dad3e4..3bd022adcb 100644 --- a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py +++ b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py @@ -380,9 +380,9 @@ def test_bedrooms_match_an_independent_count_of_the_size_criteria(case): c[3] for c in other["children"] if c[2] != "foster" ) if other["role"] in ("non_dependant", "boarder", "lodger"): - hb_rooms += len(other["adults"]) + int( - joins and not added["placed"] - ) + # HB reg 13D(3)(a): the family's claimant or couple + # has one bedroom. + hb_rooms += 1 + int(joins and not added["placed"]) hb_children += [c for c in other["children"] if c[2] == "own"] hb_overnight |= any(o for _, o in other["adults"]) or any( c[3] for c in other["children"] if c[2] == "own" diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py index 414d2d577c..2891316d14 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py @@ -19,7 +19,8 @@ class housing_benefit_LHA_allowed_bedrooms(Variable): "unit aged 16 or over who is not the claimant or partner adds a " "bedroom, such as a young person in full-time education; so do a " "householder's boarder or lodger and a non-dependant, but a sharer " - "of the rent does not. The children of a non-dependant, boarder or " + "of the rent does not. A couple, such as a non-dependant and their " + "partner, has one bedroom between them. The children of a non-dependant, boarder or " "lodger are occupiers too, and share rooms with the claimant's " "children. A child or young person placed with a family in the " "household as a foster child or for adoption is not an occupier. A " @@ -59,9 +60,18 @@ def formula(benunit, period, parameters): # everyone in a non-dependant's, boarder's or lodger's family, # including their children. other_occupier = ~head_family & ~sharer & occupier + # Reg 13D(3)(a): a couple, here the claimant and partner of an + # occupier's family, has one bedroom; (b): every other occupier aged + # 16 or over has their own. Each member of a couple counts as half. + couple_member = person("is_claimant_or_partner", period) & person.benunit( + "is_couple", period + ) + occupier_rooms = (other_occupier & aged_16_or_over) * where( + couple_member, 0.5, 1 + ) is_head_family = benunit.any(person("is_household_head", period)) other_occupiers = is_head_family * benunit.max( - person.household.sum(other_occupier & aged_16_or_over) + person.household.sum(occupier_rooms) ) # Reg 13D(3)(c)-(e): the occupiers' children, the claimant's own and # other families', share rooms with each other. From 084664c5cdeaaaa2293ffc162fbf464fe858a6ad Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 2 Oct 2026 01:07:27 -0400 Subject: [PATCH 04/14] Exclude a child placed for adoption from Tax-Free Childcare Childcare Payments (Eligibility) Regs 2015 reg 4(2)(d): no one is responsible for a child placed for adoption by an adoption agency in the home of a person proposing to adopt them. The new is_placed_for_adoption input is documented for each scheme it touches: Universal Credit and Pension Credit treat the adopter as responsible (UC reg 4A(1)(c); SPC Sch IIA para 4(3)(c)); HB, the legacy schemes and English pensioner CTR do not count the child in the household; CTC's rule 4 case B depends on local authority payments and is not modelled. Changelog covers the HB occupier couple rule too. Found by the first review attempt's probes. Co-Authored-By: Claude Opus 5.5 --- changelog.d/2011.fixed.md | 2 +- .../tax_free_childcare_qualifying_child.yaml | 11 +++++++++++ .../tax_free_childcare_qualifying_child.py | 11 ++++++++++- .../demographic/is_placed_for_adoption.py | 18 +++++++++++++++--- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/changelog.d/2011.fixed.md b/changelog.d/2011.fixed.md index 050a5351dc..2d7859acda 100644 --- a/changelog.d/2011.fixed.md +++ b/changelog.d/2011.fixed.md @@ -1 +1 @@ -- Count the children of the household head's non-dependants (Universal Credit, UC Regs 2013 Sch 4 paras 9-10) and of non-dependants, boarders and lodgers (Housing Benefit, HB Regs 2006 reg 13D(12)) in the household head's LHA size criteria, sharing rooms with the head's own children. Give foster children no bedroom of their own, and foster parents, adopters with a child placed, and households with a non-resident overnight carer an additional bedroom (UC Sch 4 para 12; HB reg 13D(3A)-(3B)), with new inputs `is_placed_for_adoption`, `is_approved_foster_parent_without_placement` and `has_non_resident_overnight_carer`. Except foster parents from the shared accommodation rate (UC para 29(9A); HB "young individual"), and count armed forces independence payment in the UC para 29(5) exception. +- Count the children of the household head's non-dependants (Universal Credit, UC Regs 2013 Sch 4 paras 9-10) and of non-dependants, boarders and lodgers (Housing Benefit, HB Regs 2006 reg 13D(12)) in the household head's LHA size criteria, sharing rooms with the head's own children. Give foster children no bedroom of their own, and foster parents, adopters with a child placed, and households with a non-resident overnight carer an additional bedroom (UC Sch 4 para 12; HB reg 13D(3A)-(3B)), with new inputs `is_placed_for_adoption`, `is_approved_foster_parent_without_placement` and `has_non_resident_overnight_carer`. Except foster parents from the shared accommodation rate (UC para 29(9A); HB "young individual"), and count armed forces independence payment in the UC para 29(5) exception. Give an occupier couple from another family one Housing Benefit bedroom (HB reg 13D(3)(a)). A child placed for adoption leaves the legacy household (HB reg 21(3), IS reg 16(4), JSA reg 78(4), ESA reg 156(5)) and is not a Tax-Free Childcare qualifying child (Childcare Payments (Eligibility) Regs 2015 reg 4(2)(d)). diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/tax_free_childcare/tax_free_childcare_qualifying_child.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/tax_free_childcare/tax_free_childcare_qualifying_child.yaml index 7c98bbea98..30daa36fe6 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/tax_free_childcare/tax_free_childcare_qualifying_child.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/tax_free_childcare/tax_free_childcare_qualifying_child.yaml @@ -25,6 +25,17 @@ output: tax_free_childcare_qualifying_child: false +- name: A child placed for adoption is not a qualifying child + period: 2025 + input: + tax_free_childcare_child_age_eligible: true + is_parent: false + is_looked_after_by_local_authority: false + is_placed_for_adoption: true + output: + # Childcare Payments (Eligibility) Regs 2015 reg 4(2)(d). + tax_free_childcare_qualifying_child: false + - name: Age-ineligible child is not qualifying period: 2025 input: diff --git a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/tax_free_childcare_qualifying_child.py b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/tax_free_childcare_qualifying_child.py index 0e7ecdb297..da49637338 100644 --- a/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/tax_free_childcare_qualifying_child.py +++ b/policyengine_uk/variables/gov/hmrc/tax_free_childcare/conditions/tax_free_childcare_qualifying_child.py @@ -21,4 +21,13 @@ def formula(person, period, parameters): "is_looked_after_by_local_authority", period, ) - return meets_age_condition & ~is_parent & ~looked_after_by_local_authority + # Childcare Payments (Eligibility) Regs 2015 reg 4(2)(c)-(d): no one is + # responsible for a child looked after by a local authority, or placed + # for adoption in the home of a person proposing to adopt them. + placed_for_adoption = person("is_placed_for_adoption", period) + return ( + meets_age_condition + & ~is_parent + & ~looked_after_by_local_authority + & ~placed_for_adoption + ) diff --git a/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py b/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py index 533c791196..eebe11ef2b 100644 --- a/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py +++ b/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py @@ -12,9 +12,17 @@ class is_placed_for_adoption(Variable): "leave is_looked_after_by_local_authority false: under Universal " "Credit the adopter is responsible for the child (UC Regs 2013 reg " "4A(1)(c)) and satisfies the foster parent condition for an " - "additional bedroom (Sch 4 para 12(4)(b)); under Housing Benefit and " - "the other legacy schemes the child is not a member of the " - "claimant's household (HB Regs 2006 reg 21(3)(b)-(c))." + "additional bedroom (Sch 4 para 12(4)(b)), and Pension Credit treats " + "them as the adopter's responsibility too (SPC Regs 2002 Sch IIA " + "para 4(3)(c)). Under Housing Benefit, the other legacy schemes and " + "council tax reduction the child is not a member of the claimant's " + "household (HB Regs 2006 reg 21(3)(b)-(c); CTR (England) prescribed " + "requirements reg 8(2)(b)-(c)), and no one is responsible for them " + "for Tax-Free Childcare (Childcare Payments (Eligibility) Regs 2015 " + "reg 4(2)(d)). Child Tax Credit excludes a child placed for adoption " + "only while the local authority pays for their accommodation or " + "maintenance (CTC Regs 2002 reg 3, rule 4 case B), which is not " + "modelled." ) definition_period = YEAR default_value = False @@ -22,4 +30,8 @@ class is_placed_for_adoption(Variable): "https://www.legislation.gov.uk/uksi/2013/376/regulation/4A", "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", "https://www.legislation.gov.uk/uksi/2006/213/regulation/21", + "https://www.legislation.gov.uk/uksi/2012/2885/regulation/8", + "https://www.legislation.gov.uk/uksi/2015/448/regulation/4", + "https://www.legislation.gov.uk/uksi/2002/1792/schedule/IIA", + "https://www.legislation.gov.uk/uksi/2002/2007/regulation/3", ) From 9311e4e998679cfc2cb3e153e27971e30571989b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 2 Oct 2026 02:44:40 -0400 Subject: [PATCH 05/14] Address review r1: foster-child age, half rooms, date gate, independent test predicates - HB occupier couple: count the half rooms only when both members are counted, so a benefit unit flagged as a couple without two adults no longer produces a fractional bedroom (and an enum error downstream). - UC: read "child" as under 18 for foster children throughout (is_lha_foster_child; parameter gov.dwp.LHA.foster_child_age_limit): a looked-after 16- or 17-year-old placed with the renter has no room of their own, and their overnight care counts under para 12(A1)(c). - Overnight care beyond the renter or HB claimant and partner applies from 1 April 2017 (SI 2017/213; parameter overnight_care_beyond_claimant). - youngest_child_age_for_legacy_benefits leaves out a child placed with the family (IS reg 16(4)). - Property tests derive the added person's status from the inputs (UC reg 5, Child Benefit QYP, looked after under 18) instead of the model, bias the generator towards looked-after young people, and give the added person overnight care in the reference property. - YAML: 13 new cases pinning the reviewer's surviving mutants and probes, a case with an inconsistent couple flag, and a 2016 date-gate case; the fostering-couple case now uses ages that detect counting the foster child; corrected citations. - Documentation: Scottish working-age CTR adoption gap, Scottish devolved disability benefits, HB 13D(3A)(a)(iii) reading, reg 21(3)(a) and s.22C, and the overnight-carer and adoption inputs. Co-Authored-By: Claude Opus 5.5 --- changelog.d/2011.fixed.md | 2 +- .../gov/dwp/LHA/foster_child_age_limit.yaml | 13 + .../gov/dwp/LHA/overnight_care_benefits.yaml | 2 +- .../LHA/overnight_care_beyond_claimant.yaml | 14 + ...ared_accommodation_exception_benefits.yaml | 2 +- ...lies_children_and_additional_bedrooms.yaml | 451 +++++++++++++++++- .../family/LHA_young_person_bedroom.yaml | 8 +- ...est_lha_bedroom_young_people_properties.py | 128 +++-- .../gov/dwp/LHA_additional_bedrooms.py | 15 +- .../variables/gov/dwp/LHA_allowed_bedrooms.py | 7 +- ...housing_benefit_LHA_additional_bedrooms.py | 28 +- .../housing_benefit_LHA_allowed_bedrooms.py | 16 +- ...hild_or_young_person_placed_with_family.py | 16 +- .../variables/gov/dwp/is_lha_foster_child.py | 36 ++ .../dwp/is_non_dependant_of_household_head.py | 10 +- ...ha_renter_meets_foster_parent_condition.py | 20 +- .../dwp/meets_lha_overnight_care_condition.py | 7 +- .../youngest_child_age_for_legacy_benefits.py | 9 +- .../has_non_resident_overnight_carer.py | 10 +- .../is_looked_after_by_local_authority.py | 7 +- .../demographic/is_placed_for_adoption.py | 41 +- 21 files changed, 731 insertions(+), 111 deletions(-) create mode 100644 policyengine_uk/parameters/gov/dwp/LHA/foster_child_age_limit.yaml create mode 100644 policyengine_uk/parameters/gov/dwp/LHA/overnight_care_beyond_claimant.yaml create mode 100644 policyengine_uk/variables/gov/dwp/is_lha_foster_child.py diff --git a/changelog.d/2011.fixed.md b/changelog.d/2011.fixed.md index 2d7859acda..9c8e9ebca4 100644 --- a/changelog.d/2011.fixed.md +++ b/changelog.d/2011.fixed.md @@ -1 +1 @@ -- Count the children of the household head's non-dependants (Universal Credit, UC Regs 2013 Sch 4 paras 9-10) and of non-dependants, boarders and lodgers (Housing Benefit, HB Regs 2006 reg 13D(12)) in the household head's LHA size criteria, sharing rooms with the head's own children. Give foster children no bedroom of their own, and foster parents, adopters with a child placed, and households with a non-resident overnight carer an additional bedroom (UC Sch 4 para 12; HB reg 13D(3A)-(3B)), with new inputs `is_placed_for_adoption`, `is_approved_foster_parent_without_placement` and `has_non_resident_overnight_carer`. Except foster parents from the shared accommodation rate (UC para 29(9A); HB "young individual"), and count armed forces independence payment in the UC para 29(5) exception. Give an occupier couple from another family one Housing Benefit bedroom (HB reg 13D(3)(a)). A child placed for adoption leaves the legacy household (HB reg 21(3), IS reg 16(4), JSA reg 78(4), ESA reg 156(5)) and is not a Tax-Free Childcare qualifying child (Childcare Payments (Eligibility) Regs 2015 reg 4(2)(d)). +- Count the children of the household head's non-dependants (Universal Credit, UC Regs 2013 Sch 4 paras 9-10) and of non-dependants, boarders and lodgers (Housing Benefit, HB Regs 2006 reg 13D(12)) in the household head's LHA size criteria, sharing rooms with the head's own children. Give foster children no bedroom of their own, and foster parents, adopters with a child placed, and households with a non-resident overnight carer an additional bedroom (UC Sch 4 para 12; HB reg 13D(3A)-(3B)), with new inputs `is_placed_for_adoption`, `is_approved_foster_parent_without_placement` and `has_non_resident_overnight_carer`. Except foster parents from the shared accommodation rate (UC para 29(9A); HB "young individual"), and count armed forces independence payment in the UC para 29(5) exception. Give an occupier couple from another family one Housing Benefit bedroom (HB reg 13D(3)(a)). A child placed for adoption leaves the legacy household (HB reg 21(3), IS reg 16(4), JSA reg 78(4), ESA reg 156(5)) and is not a Tax-Free Childcare qualifying child (Childcare Payments (Eligibility) Regs 2015 reg 4(2)(d)). Treat a person under 18 looked after by a local authority and placed with the renter as the renter's foster child throughout the Universal Credit size criteria (`is_lha_foster_child`). Apply the 2017 extension of the overnight care bedroom beyond the renter or claimant from April 2017. Leave a child placed with the family out of the Income Support youngest-child test (IS reg 16(4)). diff --git a/policyengine_uk/parameters/gov/dwp/LHA/foster_child_age_limit.yaml b/policyengine_uk/parameters/gov/dwp/LHA/foster_child_age_limit.yaml new file mode 100644 index 0000000000..f7c5c478a5 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/LHA/foster_child_age_limit.yaml @@ -0,0 +1,13 @@ +description: Age below which a child looked after by a local authority and placed with a renter is a foster child, making the renter a foster parent, in the Universal Credit size criteria. +values: + 2013-04-29: 18 +metadata: + unit: year + label: LHA foster child age limit + reference: + - title: Universal Credit Regulations 2013 reg 2, "foster parent" (a child placed under the Care Planning, Placement and Case Review (England) Regulations 2010) + href: https://www.legislation.gov.uk/uksi/2013/376/regulation/2 + - title: Universal Credit Regulations 2013 Sch 4 para 9(3), "foster child" + href: https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/9 + - title: Children Act 1989 s.105(1), "child" means a person under the age of eighteen + href: https://www.legislation.gov.uk/ukpga/1989/41/section/105 diff --git a/policyengine_uk/parameters/gov/dwp/LHA/overnight_care_benefits.yaml b/policyengine_uk/parameters/gov/dwp/LHA/overnight_care_benefits.yaml index 4e2353ce1d..a3b58e7ebd 100644 --- a/policyengine_uk/parameters/gov/dwp/LHA/overnight_care_benefits.yaml +++ b/policyengine_uk/parameters/gov/dwp/LHA/overnight_care_benefits.yaml @@ -1,4 +1,4 @@ -description: Disability benefits whose recipients, if non-resident carers regularly stay overnight to care for them, give the household an additional bedroom in the LHA size criteria. +description: Disability benefits whose recipients, if non-resident carers regularly stay overnight to care for them, give an additional bedroom in the LHA size criteria. The law also lists the Scottish devolved equivalents (child disability payment, adult disability payment, Scottish adult disability living allowance, pension age disability payment), which the model has no variables for. values: 2011-04-01: - attendance_allowance diff --git a/policyengine_uk/parameters/gov/dwp/LHA/overnight_care_beyond_claimant.yaml b/policyengine_uk/parameters/gov/dwp/LHA/overnight_care_beyond_claimant.yaml new file mode 100644 index 0000000000..d8628e728f --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/LHA/overnight_care_beyond_claimant.yaml @@ -0,0 +1,14 @@ +description: Whether the overnight care additional bedroom in the LHA size criteria covers people other than the renter or claimant and partner, such as non-dependants, other occupiers and foster children. +values: + 2011-04-01: false + 2017-04-01: true +metadata: + unit: bool + label: LHA overnight care covers the household + reference: + - title: Housing Benefit and Universal Credit (Size Criteria) (Miscellaneous Amendments) Regulations 2017 (SI 2017/213) regs 4 and 6 + href: https://www.legislation.gov.uk/uksi/2017/213/made + - title: Universal Credit Regulations 2013 Sch 4 para 12(A1) + href: https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12 + - title: Housing Benefit Regulations 2006 reg 13D(3A)(a) + href: https://www.legislation.gov.uk/uksi/2006/213/regulation/13D diff --git a/policyengine_uk/parameters/gov/dwp/LHA/shared_accommodation_exception_benefits.yaml b/policyengine_uk/parameters/gov/dwp/LHA/shared_accommodation_exception_benefits.yaml index 96241d625d..3de4aab0b4 100644 --- a/policyengine_uk/parameters/gov/dwp/LHA/shared_accommodation_exception_benefits.yaml +++ b/policyengine_uk/parameters/gov/dwp/LHA/shared_accommodation_exception_benefits.yaml @@ -1,4 +1,4 @@ -description: Disability benefits whose recipients under 35 are excepted from the shared accommodation rate. +description: Disability benefits whose recipients under 35 are excepted from the shared accommodation rate. The law also lists the Scottish devolved equivalents (child disability payment, adult disability payment, Scottish adult disability living allowance), which the model has no variables for. values: 2013-04-29: - attendance_allowance diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml index fb3d46a037..4281a53228 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml @@ -68,7 +68,8 @@ # 4 bedrooms. The daughter's own entitlement counts her and her child. LHA_allowed_bedrooms: [4, 2] LHA_category: [E, C] - # HB: the same four occupiers' categories (reg 13D(3)(a), (b), (e)). + # HB: the claimant, the daughter and the two children: the same four + # occupiers' categories (reg 13D(3)(b), (e)). housing_benefit_LHA_allowed_bedrooms: [4, 2] housing_benefit_LHA_category: [E, C] @@ -138,7 +139,8 @@ # under 10 (10(1)(d)): 3 bedrooms, category D. LHA_allowed_bedrooms: [3, 2] LHA_category: [D, C] - # HB: reg 13D(3)(a), (b), (d). + # HB: the claimant, the daughter (reg 13D(3)(b)) and the two + # grandchildren ((d)). housing_benefit_LHA_allowed_bedrooms: [3, 2] housing_benefit_LHA_category: [D, C] @@ -230,10 +232,12 @@ tenure_type: RENT_PRIVATELY brma: MAIDSTONE output: - # A joint tenant outside the claimant's household is not an occupier - # (HB reg 13D(12)), nor their household (LHA Guidance Manual paras 2.031 - # and 2.093); under UC they are liable for their occupation (para - # 9(2)(d)). Each joint tenant counts their own family. + # A joint tenant outside the claimant's household is not an occupier (HB + # reg 13D(12)), and in the private sector each joint tenant's size + # criteria count only their own household (LHA Guidance Manual para + # 2.093; HB circular A21/2013 para 25). Under UC they are liable for + # their occupation (para 9(2)(d)), so neither they nor their children are + # in the other tenant's extended benefit unit. LHA_allowed_bedrooms: [1, 2] housing_benefit_LHA_allowed_bedrooms: [1, 2] @@ -331,10 +335,10 @@ age: 36 is_parent: true son: - age: 5 + age: 12 is_male: true foster_daughter: - age: 3 + age: 11 is_male: false is_looked_after_by_local_authority: true benunits: @@ -349,8 +353,9 @@ output: is_claimant_or_partner: [true, true, false, false] # UC: the couple, their son (para 10(1)(f)), and the foster parent's - # additional bedroom (para 12(9)(b)): 3 bedrooms. The foster daughter - # does not share with the son as a child of the extended benefit unit. + # additional bedroom (para 12(9)(b)): 3 bedrooms. Counted as a child of + # the extended benefit unit, the foster daughter (11) could not share + # with the son (12) and would add a fourth. LHA_allowed_bedrooms: 3 LHA_category: D # HB: the couple, the son (reg 13D(3)(e)) and the qualifying parent or @@ -486,7 +491,7 @@ housing_benefit_LHA_category: C uc_housing_costs_element: 10_400 -- name: Overnight care from a partner who lives in the home gives no additional bedroom +- name: A disability benefit without a non-resident overnight carer gives no additional bedroom period: 2026 input: people: @@ -664,3 +669,427 @@ # forces independence payment (reg 2). is_lha_shared_accommodation_rate_specified_renter: false LHA_category: B + +- name: A lodger flagged as a couple without a partner still counts as one occupier + period: 2026 + input: + people: + householder: + age: 55 + is_household_head: true + lodger: + age: 40 + rent_paid_as_lodger: 3_000 + benunits: + householder_family: + members: [householder] + lodger_family: + members: [lodger] + relation_type: COUPLE + households: + home: + members: [householder, lodger] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # HB: the lodger is "a person who is not a child" (reg 13D(3)(b)); the + # couple rule (a) needs two members, so no half room: 2 bedrooms. The + # lodger, entitled to one bedroom without exclusive use of the + # accommodation, has the shared rate (reg 13D(2)(a)(ii)). + housing_benefit_LHA_allowed_bedrooms: [2, 1] + housing_benefit_LHA_category: [C, A] + +- name: A looked-after 17-year-old not in education is the renter's foster child under Universal Credit + period: 2026 + input: + people: + renter: + age: 40 + is_household_head: true + young_person: + age: 17 + current_education: NOT_IN_EDUCATION + is_looked_after_by_local_authority: true + benunits: + family: + members: [renter, young_person] + households: + home: + members: [renter, young_person] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: a person under 18 placed with the renter by a local authority + # makes the renter a foster parent (reg 2) and is their foster child + # (para 9(2)(c), 9(3); see is_lha_foster_child): no bedroom of their own, + # one foster room: 2. (Read as under 16, they would be a non-dependant + # with a bedroom and there would be no foster room: also 2.) + is_lha_foster_child: [false, true] + LHA_additional_bedrooms: 1 + LHA_allowed_bedrooms: 2 + # HB: not a "young person" (reg 19), so reg 21(3) does not apply: an + # occupier aged 16 or over with a bedroom, and no qualifying parent or + # carer room: 2. + is_child_or_young_person_placed_with_family: [false, false] + housing_benefit_LHA_additional_bedrooms: 0 + housing_benefit_LHA_allowed_bedrooms: 2 + +- name: A looked-after 17-year-old placed with a non-dependant + period: 2026 + input: + people: + renter: + age: 50 + is_household_head: true + daughter: + age: 35 + is_male: false + young_person: + age: 17 + current_education: NOT_IN_EDUCATION + is_looked_after_by_local_authority: true + benunits: + renter_family: + members: [renter] + daughter_family: + members: [daughter, young_person] + households: + home: + members: [renter, daughter, young_person] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: for the renter, the 17-year-old is neither a child nor a qualifying + # young person, so para 9(2)(g) does not exclude them, and they are not + # the renter's foster child: renter, daughter and young person, 3. The + # daughter's own size criteria: herself and the foster room, 2. + LHA_allowed_bedrooms: [3, 2] + # HB: not a "young person" (reg 19), so an occupier: 3. The daughter's + # own: 1 + the 17-year-old (13D(3)(b)) = 2; no carer room, since reg + # 21(3) does not apply. + housing_benefit_LHA_allowed_bedrooms: [3, 2] + +- name: A foster child who needs overnight care gives the overnight and the foster bedrooms + period: 2026 + input: + people: + foster_parent: + age: 40 + is_household_head: true + foster_child: + age: 8 + is_looked_after_by_local_authority: true + dla_sc_middle_plus: true + has_non_resident_overnight_carer: true + benunits: + family: + members: [foster_parent, foster_child] + households: + home: + members: [foster_parent, foster_child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC para 12(A1)(c), (9)(a)-(b); HB reg 13D(3A)(a)(iv), (3B). + LHA_additional_bedrooms: 2 + LHA_allowed_bedrooms: 3 + housing_benefit_LHA_additional_bedrooms: 2 + housing_benefit_LHA_allowed_bedrooms: 3 + +- name: A looked-after qualifying young person who needs overnight care + period: 2026 + input: + people: + carer: + age: 40 + is_household_head: true + is_parent: true + young_person: + age: 17 + current_education: UPPER_SECONDARY + is_looked_after_by_local_authority: true + dla_sc_middle_plus: true + has_non_resident_overnight_carer: true + benunits: + family: + members: [carer, young_person] + households: + home: + members: [carer, young_person] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_qualifying_young_person_for_universal_credit: [false, true] + is_child_or_qualifying_young_person_for_universal_credit: [false, false] + # UC: the young person is the renter's foster child (under 18), so their + # overnight care counts under para 12(A1)(c): foster and overnight rooms, + # 3. + LHA_additional_bedrooms: 2 + LHA_allowed_bedrooms: 3 + # HB: placed under reg 21(3), so not an occupier, but within + # 13D(3A)(a)(iv); with the qualifying parent or carer room, (3B): 3. + housing_benefit_LHA_additional_bedrooms: 2 + housing_benefit_LHA_allowed_bedrooms: 3 + +- name: A lodger who needs overnight care counts for Housing Benefit only + period: 2026 + input: + people: + householder: + age: 60 + is_household_head: true + lodger: + age: 45 + rent_paid_as_lodger: 5_000 + pip_dl: 5_000 + has_non_resident_overnight_carer: true + benunits: + householder_family: + members: [householder] + lodger_family: + members: [lodger] + households: + home: + members: [householder, lodger] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: the lodger is outside the extended benefit unit (para 9(2)(d)): 1. + # HB: the lodger is an occupier (13D(3)(b)) who requires overnight care + # (13D(3A)(a)(iii)): 3. The lodger's own claim: 1 + carer room = 2. + LHA_allowed_bedrooms: [1, 2] + housing_benefit_LHA_allowed_bedrooms: [3, 2] + +- name: A joint tenant's overnight care counts only for that tenant + period: 2026 + input: + people: + tenant_1: + age: 40 + is_household_head: true + tenant_2: + age: 38 + pip_dl: 5_000 + has_non_resident_overnight_carer: true + benunits: + family_1: + members: [tenant_1] + family_2: + members: [tenant_2] + liable_for_share_of_household_rent: true + households: + home: + members: [tenant_1, tenant_2] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: the joint tenant is not in the other's extended benefit unit (para + # 9(2)(d)). HB: 13D(3A)(a)(iii) says "a person ... who occupies the + # claimant's dwelling as their home", without the defined term + # "occupiers"; the model applies 13D(12)'s joint-tenant exclusion, as + # private-sector joint tenants' size criteria rest on each tenant's own + # household (HB circular A21/2013 para 25). + LHA_allowed_bedrooms: [1, 2] + housing_benefit_LHA_allowed_bedrooms: [1, 2] + +- name: A boarder couple with a child counts as two Housing Benefit bedrooms + period: 2026 + input: + people: + householder: + age: 60 + is_household_head: true + boarder: + age: 35 + rent_paid_as_boarder: 5_000 + boarders_partner: + age: 33 + boarders_child: + age: 4 + benunits: + householder_family: + members: [householder] + boarder_family: + members: [boarder, boarders_partner, boarders_child] + households: + home: + members: [householder, boarder, boarders_partner, boarders_child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # HB: the householder, the boarder couple (13D(3)(a)) and the child: 3. + housing_benefit_LHA_allowed_bedrooms: [3, 2] + LHA_allowed_bedrooms: [1, 2] + +- name: A non-dependant's child placed for adoption + period: 2026 + input: + people: + renter: + age: 60 + is_household_head: true + daughter: + age: 30 + is_male: false + child: + age: 5 + is_placed_for_adoption: true + benunits: + renter_family: + members: [renter] + daughter_family: + members: [daughter, child] + households: + home: + members: [renter, daughter, child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: reg 4A(1)(c) makes the daughter responsible, so the child is in + # the renter's extended benefit unit: 3. The daughter's own: herself, the + # child and the adopter's room, 3. + LHA_allowed_bedrooms: [3, 3] + # HB: following DWP, a child placed with anyone in the household is not + # an occupier: 2. The daughter's own: herself and the qualifying parent + # or carer room, 2. + housing_benefit_LHA_allowed_bedrooms: [2, 2] + +- name: Five or more bedrooms map to the four-bedroom category + period: 2026 + input: + people: + renter: + age: 50 + is_household_head: true + is_approved_foster_parent_without_placement: true + non_dependant_1: + age: 30 + non_dependant_2: + age: 29 + non_dependant_3: + age: 40 + benunits: + renter_family: + members: [renter] + non_dependant_family_1: + members: [non_dependant_1, non_dependant_2] + non_dependant_family_2: + members: [non_dependant_3] + households: + home: + members: [renter, non_dependant_1, non_dependant_2, non_dependant_3] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: the renter, three non-dependants and the foster room: 5, capped at + # category E (para 26). HB: the claimant, the couple (13D(3)(a)), the + # other non-dependant and the carer room: 4, E. + LHA_allowed_bedrooms: [5, 1, 1] + LHA_category: [E, B, B] + housing_benefit_LHA_allowed_bedrooms: [4, 1, 1] + housing_benefit_LHA_category: [E, B, B] + +- name: Before April 2017 a non-dependant's overnight care gave no additional bedroom + period: 2016 + input: + people: + renter: + age: 58 + is_household_head: true + son: + age: 30 + pip_dl: 5_000 + has_non_resident_overnight_carer: true + benunits: + renter_family: + members: [renter] + son_family: + members: [son] + households: + home: + members: [renter, son] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC Sch 4 para 12(A1)(b) and HB reg 13D(3A)(a)(iii) came in on 1 April + # 2017 (SI 2017/213); before then only the renter or claimant and + # partner counted. The son's own claim keeps his room. + LHA_additional_bedrooms: [0, 1] + LHA_allowed_bedrooms: [2, 2] + housing_benefit_LHA_additional_bedrooms: [0, 1] + housing_benefit_LHA_allowed_bedrooms: [2, 2] + +- name: A non-dependant's foster child who needs overnight care counts only for the non-dependant + period: 2026 + input: + people: + renter: + age: 60 + is_household_head: true + daughter: + age: 32 + is_male: false + foster_child: + age: 6 + is_looked_after_by_local_authority: true + dla_sc_middle_plus: true + has_non_resident_overnight_carer: true + benunits: + renter_family: + members: [renter] + daughter_family: + members: [daughter, foster_child] + households: + home: + members: [renter, daughter, foster_child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: the foster child is in no one's extended benefit unit but the + # foster parent's (para 9(2)(g); 12(A1)(c)), so the renter gets no + # overnight room: 2. The daughter: herself, the foster room and the + # overnight room, 3. HB: not an occupier of the renter's dwelling (model + # follows DWP); the daughter's own: herself, the carer room and the + # overnight room ((3A)(a)(iv)), 3. + LHA_additional_bedrooms: [0, 2] + LHA_allowed_bedrooms: [2, 3] + housing_benefit_LHA_additional_bedrooms: [0, 2] + housing_benefit_LHA_allowed_bedrooms: [2, 3] + +- name: A claimant who is looked after gives no foster room + period: 2026 + input: + people: + claimant: + age: 19 + is_household_head: true + is_looked_after_by_local_authority: true + benunits: + family: + members: [claimant] + households: + home: + members: [claimant] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # The foster parent condition needs a child placed with the renter, not + # the renter's own status (UC reg 2; HB reg 2(1)). + is_lha_foster_child: false + lha_renter_meets_foster_parent_condition: false + LHA_additional_bedrooms: 0 + is_housing_benefit_qualifying_parent_or_carer: false + housing_benefit_LHA_additional_bedrooms: 0 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_young_person_bedroom.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_young_person_bedroom.yaml index 16cd81fe79..315a51eade 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_young_person_bedroom.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_young_person_bedroom.yaml @@ -143,8 +143,12 @@ is_child_or_qualifying_young_person_for_universal_credit: [false, false] # Not under para 10(1)(b), and not a non-dependant (para 9(2)(g)), so no # bedroom of their own. The carer they are placed with is a foster parent - # (reg 2), so has one additional bedroom (para 12(4)(a), (9)(b)): 2 - # bedrooms, category C. + # (reg 2: a person with whom a child is placed under the care planning + # regulations, which place children under 18), so has one additional + # bedroom (para 12(4)(a), (9)(b)): 2 bedrooms, category C. The model reads + # "child" there as under 18 (see is_lha_foster_child; reg 91(2)(b)-(d) + # also speak of foster parents of qualifying young persons). Read as under + # 16 (WRA 2012 s.40), there would be no foster room and 1 bedroom. LHA_additional_bedrooms: 1 LHA_allowed_bedrooms: 2 LHA_category: C diff --git a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py index 3bd022adcb..65d3b70cc4 100644 --- a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py +++ b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py @@ -14,14 +14,15 @@ claimant or partner, their family gains one Housing Benefit bedroom unless the person is placed with it as a foster child (HB reg 21(3)), and one Universal Credit bedroom unless they are a qualifying young person no - one is responsible for (para 9(2)(g)); a family they are placed with as a - foster child gains the foster parent's (UC, para 12) or qualifying parent - or carer's (HB, 13D(3A)(b)) bedroom if it did not already have it. The - household head's family also gains one bedroom under both schemes if the - person joins a non-dependant's family (UC: unless no one is responsible - for them), and one Housing Benefit bedroom if they join a boarder's or - lodger's family (HB: unless placed with it as a foster child). No other - family's bedrooms change. + one is responsible for (para 9(2)(g)) or the family's foster child (under + 18, para 9(2)(c)); a family they are placed with as a foster child gains + the foster parent's (UC, para 12) or qualifying parent or carer's (HB, + 13D(3A)(b)) bedroom if it did not already have it. The household head's + family also gains one bedroom under both schemes if the person joins a + non-dependant's family (UC: unless no one is responsible for them; HB: + unless placed with it), and one Housing Benefit bedroom if they join a + boarder's or lodger's family (unless placed with it). No other family's + bedrooms change. 3. Children: adding a child under 16 who is neither fostered nor placed for adoption to any family never lowers any family's bedrooms under either scheme and raises each by at most one. @@ -32,12 +33,19 @@ child placed with another family is not in the household head's extended benefit unit (UC para 9(2)(g)) and, following DWP, not an occupier (HB; LHA Guidance Manual para 2.033). -5. Reference: every family's bedrooms, and its additional bedrooms, equal an - independent count of the size criteria: +5. Reference: every family's bedrooms, and its additional bedrooms, equal a + count of the size criteria made by the test itself. The added person's + status comes from the test's own predicates on the inputs (UC reg 5 + qualifying young person, the Child Benefit qualifying young person for + HB reg 19, looked after and under 18 for a foster child), not from the + model, and children's rooms come from a brute-force pairing. The count + applies the same legal readings as the model, including its judgment + calls (see the PR), so it checks the implementation of those readings, + not the readings themselves: - one for the claimant or couple; - one for each other member aged 16 or over (UC: except a qualifying - young person no one is responsible for; HB: except one placed with the - family as a foster child); + young person no one is responsible for and a foster child under 18; + HB: except one placed with the family as a foster child); - for the household head's family, one for each person aged 16 or over in a non-dependant's family (and, for HB, in a boarder's or lodger's family), except one no one is responsible for (UC) or placed with that @@ -71,7 +79,7 @@ suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], ) ROLES = ["sharer", "boarder", "lodger", "non_dependant"] -EDUCATION = ["NOT_IN_EDUCATION", "UPPER_SECONDARY", "TERTIARY"] +EDUCATION = ["NOT_IN_EDUCATION", "UPPER_SECONDARY", "UPPER_SECONDARY", "TERTIARY"] KINDS = ["own", "own", "own", "foster", "adopted"] OVERNIGHT = st.sampled_from([False, False, False, True]) @@ -101,7 +109,7 @@ def household(draw): @st.composite def young_person(draw): - return dict( + person = dict( age=draw(st.integers(16, 19)), is_male=draw(st.booleans()), current_education=draw(st.sampled_from(EDUCATION)), @@ -109,9 +117,21 @@ def young_person(draw): is_before_universal_credit_qualifying_young_person_terminal_date=draw( st.booleans() ), - is_looked_after_by_local_authority=draw(st.booleans()), - receives_benefits_in_own_right=draw(st.booleans()), + is_looked_after_by_local_authority=draw(st.sampled_from([False, True, True])), + receives_benefits_in_own_right=draw(st.sampled_from([False, False, True])), ) + if draw(OVERNIGHT): + person["dla_sc_middle_plus"] = True + person["has_non_resident_overnight_carer"] = True + return person + + +def without_overnight_care(person): + return { + k: v + for k, v in person.items() + if k not in ("dla_sc_middle_plus", "has_non_resident_overnight_carer") + } @st.composite @@ -244,24 +264,54 @@ def fosters(fam): ) -def flags(sim, situation, pid): - """The added person's attributes as the model determines them.""" - index = list(situation["people"]).index(pid) +def in_qualifying_education(p, age_limit_for_entry=19): + """Full-time non-advanced education, entered before 19.""" + return p["current_education"] == "UPPER_SECONDARY" and ( + p["age"] < age_limit_for_entry + or p["age_started_or_accepted_current_education_or_training"] + < age_limit_for_entry + ) - def value(variable): - return bool(sim.calculate(variable, YEAR)[index]) - qualifying = value("is_qualifying_young_person_for_universal_credit") - responsible = value("is_child_or_qualifying_young_person_for_universal_credit") +def uc_qualifying_young_person(p): + """UC Regs 2013 reg 5: aged 16 to 19, in non-advanced education entered + before 19, before the terminal date if 19, and not receiving benefits in + their own right.""" + return ( + 16 <= p["age"] < 20 + and in_qualifying_education(p) + and ( + p["age"] < 19 + or p["is_before_universal_credit_qualifying_young_person_terminal_date"] + ) + and not p["receives_benefits_in_own_right"] + ) + + +def child_benefit_qualifying_young_person(p): + """SSCBA 1992 s.142 and the Child Benefit (General) Regulations 2006, as + HB reg 19's "young person": aged 16 to 19 in non-advanced education + entered before 19, and not receiving benefits in their own right.""" + return ( + 16 <= p["age"] < 20 + and in_qualifying_education(p) + and not p["receives_benefits_in_own_right"] + ) + + +def flags(p): + """The added person's status, from the inputs alone.""" + looked_after = p.get("is_looked_after_by_local_authority", False) return dict( - # A qualifying young person no one is responsible for (para 9(2)(g)). - unclaimed=qualifying and not responsible, + # A qualifying young person no one is responsible for (UC para + # 9(2)(g); reg 4(6)(a)). + unclaimed=looked_after and uc_qualifying_young_person(p), + # The family's foster child: looked after and under 18 (UC reg 2, + # "foster parent"; para 9(3)). + fostered=looked_after and p["age"] < 18, # Placed with the family by a local authority (HB reg 21(3)). - placed=value("is_child_or_young_person_placed_with_family"), - # A child under 18 looked after by a local authority is placed with - # a foster parent (UC Regs 2013 reg 2). - fostered=value("is_looked_after_by_local_authority") - and situation["people"][pid]["age"] < 18, + placed=looked_after and child_benefit_qualifying_young_person(p), + overnight=p.get("has_non_resident_overnight_carer", False), ) @@ -283,17 +333,18 @@ def test_adding_a_person_aged_16_to_19_never_lowers_bedrooms(case, supply_roles) @given(scenario()) def test_exact_effect_of_adding_a_person_aged_16_to_19(case): population, target, person = case + person = without_overnight_care(person) before, keys = build(population) after, _ = build(population, (target, person)) _, uc_before, hb_before = bedrooms(before) - sim, uc_after, hb_after = bedrooms(after) - added = flags(sim, after, f"h{target[0]}_f{target[1]}_added") + _, uc_after, hb_after = bedrooms(after) + added = flags(person) fam = population[target[0]][target[1]] # 2. Exact effect. expected_uc = np.zeros(len(keys)) expected_hb = np.zeros(len(keys)) t = keys.index(target) - expected_uc[t] += 0 if added["unclaimed"] else 1 + expected_uc[t] += 0 if added["unclaimed"] or added["fostered"] else 1 expected_uc[t] += int(added["fostered"] and not fosters(fam)) expected_hb[t] += 0 if added["placed"] else 1 expected_hb[t] += int(added["placed"] and not fosters(fam)) @@ -347,14 +398,14 @@ def test_bedrooms_match_an_independent_count_of_the_size_criteria(case): hb_additional = np.asarray( sim.calculate("housing_benefit_LHA_additional_bedrooms", YEAR) ) - added = flags(sim, situation, f"h{target[0]}_f{target[1]}_added") + added = flags(person) # 5. Reference count. for b, (h, f) in enumerate(keys): families = population[h] fam = families[f] is_target = (h, f) == target # Own members aged 16 or over who are not the claimant or partner. - uc_rooms = 1 + int(is_target and not added["unclaimed"]) + uc_rooms = 1 + int(is_target and not (added["unclaimed"] or added["fostered"])) hb_rooms = 1 + int(is_target and not added["placed"]) # Children: UC counts the children the family is responsible for # (not foster children); HB counts occupiers (not children placed @@ -366,6 +417,9 @@ def test_bedrooms_match_an_independent_count_of_the_size_criteria(case): uc_overnight = hb_overnight = any(o for _, o in fam["adults"]) or any( c[3] for c in fam["children"] ) + if is_target and added["overnight"]: + uc_overnight |= added["fostered"] or not added["unclaimed"] + hb_overnight = True uc_foster = fosters(fam) or (is_target and added["fostered"]) hb_carer = fosters(fam) or (is_target and added["placed"]) if fam["role"] == "head": @@ -379,6 +433,9 @@ def test_bedrooms_match_an_independent_count_of_the_size_criteria(case): uc_overnight |= any(o for _, o in other["adults"]) or any( c[3] for c in other["children"] if c[2] != "foster" ) + uc_overnight |= ( + joins and added["overnight"] and not added["unclaimed"] + ) if other["role"] in ("non_dependant", "boarder", "lodger"): # HB reg 13D(3)(a): the family's claimant or couple # has one bedroom. @@ -387,6 +444,7 @@ def test_bedrooms_match_an_independent_count_of_the_size_criteria(case): hb_overnight |= any(o for _, o in other["adults"]) or any( c[3] for c in other["children"] if c[2] == "own" ) + hb_overnight |= joins and added["overnight"] and not added["placed"] expected_uc_additional = int(uc_overnight) + int(uc_foster) expected_hb_additional = int(hb_overnight) + int(hb_carer) assert uc_additional[b] == expected_uc_additional diff --git a/policyengine_uk/variables/gov/dwp/LHA_additional_bedrooms.py b/policyengine_uk/variables/gov/dwp/LHA_additional_bedrooms.py index 6a9dab2c14..3d580ea582 100644 --- a/policyengine_uk/variables/gov/dwp/LHA_additional_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/LHA_additional_bedrooms.py @@ -13,6 +13,8 @@ class LHA_additional_bedrooms(Variable): "parent condition (see lha_renter_meets_foster_parent_condition), " "however many children are placed. The extended benefit unit " "includes the household head's non-dependants and their children. " + "Before 1 April 2017 only the renter could meet the overnight care " + "condition. " "The disabled child and disabled person conditions, for children or " "couples who cannot share a bedroom, are not modelled." ) @@ -29,15 +31,16 @@ def formula(benunit, period, parameters): responsible = person( "is_child_or_qualifying_young_person_for_universal_credit", period ) - child = person("is_child_for_universal_credit", period) no_one_responsible = ( - child | person("is_qualifying_young_person_for_universal_credit", period) + person("is_child_for_universal_credit", period) + | person("is_qualifying_young_person_for_universal_credit", period) ) & ~responsible # UC Regs 2013 Sch 4 para 12(A1)(a)-(b): the renter and the members of # their extended benefit unit, which excludes a child or qualifying # young person no one is responsible for (paras 9(1) and 9(2)(g)); # (c): a child for whom the renter meets the foster parent condition. - own = overnight_care & ~(no_one_responsible & ~child) + foster_child = person("is_lha_foster_child", period) + own = overnight_care & (~no_one_responsible | foster_child) # Para 9(1)(c): the household head's non-dependants from other # families, and their children. other = ( @@ -47,6 +50,12 @@ def formula(benunit, period, parameters): ) head_family = benunit.any(person("is_household_head", period)) others = head_family * benunit.max(person.household.sum(other)) + # Before 1 April 2017 (SI 2017/213 reg 6) only the renter could meet + # the overnight care condition. + p = parameters(period).gov.dwp.LHA + renter = person("is_claimant_or_partner", period) + own = where(p.overnight_care_beyond_claimant, own, overnight_care & renter) + others = others * p.overnight_care_beyond_claimant # Para 12(9)(a)-(b): one bedroom for each condition met. overnight_room = benunit.any(own) | (others > 0) foster_room = benunit("lha_renter_meets_foster_parent_condition", period) diff --git a/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py b/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py index a89c371159..c210ae2f12 100644 --- a/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py @@ -121,8 +121,11 @@ def formula(benunit, period, parameters): qualifying_young_person = family_member & responsible # Para 10(1)(c) with para 9(2): any other member aged 16 or over is a # non-dependant, unless they are a qualifying young person for whom - # no one is responsible (para 9(2)(g); reg 4(6)). - family_non_dependant = family_member & ~qualifying + # no one is responsible (para 9(2)(g); reg 4(6)) or the renter's + # foster child (para 9(2)(c), 9(3); see is_lha_foster_child). + family_non_dependant = ( + family_member & ~qualifying & ~person("is_lha_foster_child", period) + ) family_rooms = benunit.sum(qualifying_young_person | family_non_dependant) # Para 9(1)(c) and 9(2): people of other families in the household # who are non-dependants of the household head's family. Para 9(2)(g) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py index 5c9d55d1ee..d43048fd17 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py @@ -14,7 +14,9 @@ class housing_benefit_LHA_additional_bedrooms(Variable): "Occupiers outside the family count for the household head's family: " "non-dependants, boarders and lodgers and their children, but not a " "sharer of the rent or a child placed with them as a foster child or " - "for adoption. The definitions require a bedroom in the dwelling for " + "for adoption. Before 1 April 2017 only the claimant and partner " + "counted for overnight care. The definitions require a bedroom in " + "the dwelling for " "the carer or the foster child; the model does not observe the " "dwelling's bedrooms and assumes there is one. Children and couples " "who cannot share a bedroom because of disability are not modelled." @@ -29,10 +31,15 @@ class housing_benefit_LHA_additional_bedrooms(Variable): def formula(benunit, period, parameters): person = benunit.members overnight_care = person("meets_lha_overnight_care_condition", period) - # HB Regs 2006 reg 13D(3A)(a)(iii) and 13D(12): occupiers of other - # families, other than a joint tenant outside the claimant's - # household and a child placed with another family as a foster child - # or for adoption (see housing_benefit_LHA_allowed_bedrooms). + # HB Regs 2006 reg 13D(3A)(a)(iii): "a person ... who occupies the + # claimant's dwelling as their home". (iii) does not use the defined + # term "occupiers", but the model applies 13D(12)'s exclusion of a + # joint tenant outside the claimant's household and, following DWP, + # of a child placed with another family as a foster child or for + # adoption (see housing_benefit_LHA_allowed_bedrooms). A3/2017 frames + # the 2017 extension as a room for carers of a disabled child or + # non-dependant, and A21/2013 para 25 bases private-sector joint + # tenants' size criteria on each tenant's own household. head_family = person.benunit.any(person("is_household_head", period)) sharer = person.benunit("liable_for_share_of_household_rent", period) placed = person("is_child_or_young_person_placed_with_family", period) @@ -41,8 +48,15 @@ def formula(benunit, period, parameters): others = is_head_family * benunit.max(person.household.sum(other)) # Reg 13D(3A)(a)(i)-(iv): the claimant, partner, other members of the # family, and a child or young person for whom the claimant or - # partner is a qualifying parent or carer. - overnight_room = benunit.any(overnight_care) | (others > 0) + # partner is a qualifying parent or carer. Before 1 April 2017 (SI + # 2017/213 reg 4) only the claimant and partner. + p = parameters(period).gov.dwp.LHA + claimant = person("is_claimant_or_partner", period) + own = where( + p.overnight_care_beyond_claimant, overnight_care, overnight_care & claimant + ) + others = others * p.overnight_care_beyond_claimant + overnight_room = benunit.any(own) | (others > 0) # Reg 13D(3A)(b). carer_room = benunit("is_housing_benefit_qualifying_parent_or_carer", period) # Reg 13D(3B): two additional bedrooms where both apply. diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py index 2891316d14..e11f0cec7e 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py @@ -20,7 +20,8 @@ class housing_benefit_LHA_allowed_bedrooms(Variable): "bedroom, such as a young person in full-time education; so do a " "householder's boarder or lodger and a non-dependant, but a sharer " "of the rent does not. A couple, such as a non-dependant and their " - "partner, has one bedroom between them. The children of a non-dependant, boarder or " + "partner, has one bedroom between them. The children of a " + "non-dependant, boarder or " "lodger are occupiers too, and share rooms with the claimant's " "children. A child or young person placed with a family in the " "household as a foster child or for adoption is not an occupier. A " @@ -62,13 +63,12 @@ def formula(benunit, period, parameters): other_occupier = ~head_family & ~sharer & occupier # Reg 13D(3)(a): a couple, here the claimant and partner of an # occupier's family, has one bedroom; (b): every other occupier aged - # 16 or over has their own. Each member of a couple counts as half. - couple_member = person("is_claimant_or_partner", period) & person.benunit( - "is_couple", period - ) - occupier_rooms = (other_occupier & aged_16_or_over) * where( - couple_member, 0.5, 1 - ) + # 16 or over has their own. Each member of a couple counts as half, + # and only where both members are counted, so the halves always pair. + counted = other_occupier & aged_16_or_over + claimant_or_partner = counted & person("is_claimant_or_partner", period) + couple = person.benunit.sum(claimant_or_partner) == 2 + occupier_rooms = counted * where(claimant_or_partner & couple, 0.5, 1) is_head_family = benunit.any(person("is_household_head", period)) other_occupiers = is_head_family * benunit.max( person.household.sum(occupier_rooms) diff --git a/policyengine_uk/variables/gov/dwp/is_child_or_young_person_placed_with_family.py b/policyengine_uk/variables/gov/dwp/is_child_or_young_person_placed_with_family.py index 592130460f..8ab5cb8ce6 100644 --- a/policyengine_uk/variables/gov/dwp/is_child_or_young_person_placed_with_family.py +++ b/policyengine_uk/variables/gov/dwp/is_child_or_young_person_placed_with_family.py @@ -4,7 +4,7 @@ class is_child_or_young_person_placed_with_family(Variable): value_type = bool entity = Person - label = "Child or young person placed with the family by a local authority or for adoption" + label = "Child or young person placed with the family (foster or adoption)" documentation = ( "A child or young person (the Child Benefit definition) who is not " "the claimant or partner and is placed with their benefit unit by a " @@ -14,10 +14,14 @@ class is_child_or_young_person_placed_with_family(Variable): "Jobseeker's Allowance, income-related Employment and Support " "Allowance and Housing Benefit they are not a member of the " "claimant's household, and under Housing Benefit they do not occupy " - "the claimant's dwelling. HB reg 21(3)(a) refers to a placement " - "under section 22C(2) of the Children Act 1989; the model follows " - "DWP's guidance that foster children are within reg 21(3) (LHA " - "Guidance Manual para 2.033)." + "the claimant's dwelling. Since SI 2017/901, HB reg 21(3)(a) refers " + "to a placement under section 22C(2) of the Children Act 1989, " + "which is placement with a parent; foster placements are under " + "s.22C(5)-(6), and the earlier text referred to s.23(2)(a). The " + "model treats foster placements as within reg 21(3), as the " + "'qualifying parent or carer' definition presupposes and as the " + "English pensioner and Welsh council tax reduction rules say " + "expressly ('section 22C'; 'section 22C or 23(2)(a)')." ) definition_period = YEAR reference = ( @@ -26,6 +30,8 @@ class is_child_or_young_person_placed_with_family(Variable): "https://www.legislation.gov.uk/uksi/1987/1967/regulation/16", "https://www.legislation.gov.uk/uksi/1996/207/regulation/78", "https://www.legislation.gov.uk/uksi/2008/794/regulation/156", + "https://www.legislation.gov.uk/uksi/2012/2885/regulation/8", + "https://www.legislation.gov.uk/wsi/2013/3029/regulation/8", "https://assets.publishing.service.gov.uk/media/5a758238ed915d6faf2b38a2/lha-guidance-manual.pdf", ) diff --git a/policyengine_uk/variables/gov/dwp/is_lha_foster_child.py b/policyengine_uk/variables/gov/dwp/is_lha_foster_child.py new file mode 100644 index 0000000000..374f381ac7 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_lha_foster_child.py @@ -0,0 +1,36 @@ +from policyengine_uk.model_api import * + + +class is_lha_foster_child(Variable): + value_type = bool + entity = Person + label = "Foster child in the Universal Credit size criteria" + documentation = ( + "A person under 18 looked after by a local authority and placed with " + "the claimant or partner of their benefit unit. The renter is their " + "foster parent (UC Regs 2013 reg 2, a person with whom a child is " + "placed under the care planning regulations, where a child is a " + 'person under 18). The model reads "foster child" in Sch 4 para ' + '9(3) and "a child" in para 12(A1)(c) the same way, so a foster ' + "child aged 16 or 17 is not a non-dependant and has no bedroom of " + "their own, and their overnight care counts for the renter. The " + "narrower reading, under 16 as in the Welfare Reform Act 2012 s.40, " + "would instead make such a person a non-dependant with a bedroom " + "and give the renter no foster room. The guidance does not settle " + "which applies." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/regulation/2", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/9", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", + "https://www.legislation.gov.uk/ukpga/1989/41/section/105", + ) + + def formula(person, period, parameters): + age_limit = parameters(period).gov.dwp.LHA.foster_child_age_limit + return ( + person("is_looked_after_by_local_authority", period) + & (person("age", period) < age_limit) + & ~person("is_claimant_or_partner", period) + ) diff --git a/policyengine_uk/variables/gov/dwp/is_non_dependant_of_household_head.py b/policyengine_uk/variables/gov/dwp/is_non_dependant_of_household_head.py index 5042743e15..c5228ab41c 100644 --- a/policyengine_uk/variables/gov/dwp/is_non_dependant_of_household_head.py +++ b/policyengine_uk/variables/gov/dwp/is_non_dependant_of_household_head.py @@ -15,10 +15,12 @@ class is_non_dependant_of_household_head(Variable): "Credit counts a non-dependant in one claim only, which the model " "gives to the household head's family; Housing Benefit and Council " "Tax Reduction apportion a non-dependant of several joint occupiers " - "between them. A non-dependant's own children are non-dependants " - "too; a child looked after by a local authority is not (see " - "LHA_allowed_bedrooms). Carers engaged through a charity are not " - "identified." + "between them. The flag covers every member of such a family, " + "including children. A non-dependant's own children are " + "non-dependants too, but a child or qualifying young person looked " + "after by a local authority is not (UC Sch 4 para 9(2)(g)); the " + "size criteria apply that exclusion (see LHA_allowed_bedrooms). " + "Carers engaged through a charity are not identified." ) definition_period = YEAR reference = ( diff --git a/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py b/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py index f4f26f299b..fab56739ba 100644 --- a/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py +++ b/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py @@ -26,15 +26,19 @@ class lha_renter_meets_foster_parent_condition(Variable): def formula(benunit, period, parameters): person = benunit.members claimant_or_partner = person("is_claimant_or_partner", period) - # UC Regs 2013 reg 2, "foster parent": a person with whom a child is - # placed under the care planning regulations; a child here is a - # person under 18 looked after by a local authority. - placed = ( - person("is_looked_after_by_local_authority", period) - | person("is_placed_for_adoption", period) - ) & (person("age", period) < 18) + # UC Regs 2013 reg 2, "foster parent": a person with whom a child + # (under 18) is placed under the care planning regulations; reg 89(3) + # and Sch 4 para 12(4)(b): an adopter with whom a child is placed for + # adoption. + age_limit = parameters(period).gov.dwp.LHA.foster_child_age_limit + placed_for_adoption = ( + person("is_placed_for_adoption", period) + & (person("age", period) < age_limit) + & ~claimant_or_partner + ) + placed = person("is_lha_foster_child", period) | placed_for_adoption # UC Regs 2013 Sch 4 para 12(4)(a)-(b) and (5). between_placements = claimant_or_partner & person( "is_approved_foster_parent_without_placement", period ) - return benunit.any((placed & ~claimant_or_partner) | between_placements) + return benunit.any(placed | between_placements) diff --git a/policyengine_uk/variables/gov/dwp/meets_lha_overnight_care_condition.py b/policyengine_uk/variables/gov/dwp/meets_lha_overnight_care_condition.py index ad86b14b6d..f9da22caf9 100644 --- a/policyengine_uk/variables/gov/dwp/meets_lha_overnight_care_condition.py +++ b/policyengine_uk/variables/gov/dwp/meets_lha_overnight_care_condition.py @@ -13,7 +13,12 @@ class meets_lha_overnight_care_condition(Variable): "Independence Payment) and has non-resident carers who regularly " "stay overnight to care for them. This is the Universal Credit " "overnight care condition and the Housing Benefit 'person who " - "requires overnight care'. Two Housing Benefit routes are not " + "requires overnight care'. The law also lists the Scottish child " + "disability payment, adult disability payment, Scottish adult " + "disability living allowance and pension age disability payment; " + "the model has no variables for those, so they count only where the " + "data records them as the DWP equivalents. Two Housing Benefit " + "routes are not " "modelled: a person without a qualifying benefit whose need the " "local authority accepts on other evidence, and the requirement that " "the carers have the use of a bedroom additional to the occupiers'." diff --git a/policyengine_uk/variables/gov/dwp/youngest_child_age_for_legacy_benefits.py b/policyengine_uk/variables/gov/dwp/youngest_child_age_for_legacy_benefits.py index de2c1449f3..179e7d5648 100644 --- a/policyengine_uk/variables/gov/dwp/youngest_child_age_for_legacy_benefits.py +++ b/policyengine_uk/variables/gov/dwp/youngest_child_age_for_legacy_benefits.py @@ -16,11 +16,16 @@ class youngest_child_age_for_legacy_benefits(Variable): "https://www.legislation.gov.uk/ukpga/1992/4/section/137", "https://www.legislation.gov.uk/ukpga/1992/4/section/142", "https://www.legislation.gov.uk/uksi/1987/1967/schedule/1B", + "https://www.legislation.gov.uk/uksi/1987/1967/regulation/16", ) def formula(benunit, period, parameters): person = benunit.members - child = person("is_child_for_child_benefit", period) & ~person( - "is_claimant_or_partner", period + # A child placed with the family by a local authority or for adoption + # is not a member of the household (IS Regs 1987 reg 16(4)). + child = ( + person("is_child_for_child_benefit", period) + & ~person("is_claimant_or_partner", period) + & ~person("is_child_or_young_person_placed_with_family", period) ) return benunit.min(where(child, person("age", period), np.inf)) diff --git a/policyengine_uk/variables/household/demographic/has_non_resident_overnight_carer.py b/policyengine_uk/variables/household/demographic/has_non_resident_overnight_carer.py index e608119809..4ce8137c67 100644 --- a/policyengine_uk/variables/household/demographic/has_non_resident_overnight_carer.py +++ b/policyengine_uk/variables/household/demographic/has_non_resident_overnight_carer.py @@ -9,9 +9,13 @@ class has_non_resident_overnight_carer(Variable): "Whether one or more people who do not live in the home are engaged, " "under arrangements made for that purpose, to provide this person " "with overnight care and to stay overnight in the home on a regular " - "basis. With a qualifying disability benefit, this gives the " - "household an additional bedroom in the Universal Credit and Housing " - "Benefit size criteria (see meets_lha_overnight_care_condition)." + "basis. With a qualifying disability benefit, this can give an " + "additional bedroom in the Universal Credit and Housing Benefit size " + "criteria (see meets_lha_overnight_care_condition): to the renter " + "whose extended benefit unit the person is in, or who fosters them " + "(UC), or to the claimant whose dwelling they occupy (HB). A " + "lodger's or joint tenant's carer gives the household head's family " + "nothing under Universal Credit." ) definition_period = YEAR default_value = False diff --git a/policyengine_uk/variables/household/demographic/is_looked_after_by_local_authority.py b/policyengine_uk/variables/household/demographic/is_looked_after_by_local_authority.py index 96b158ba78..93c3fef0b7 100644 --- a/policyengine_uk/variables/household/demographic/is_looked_after_by_local_authority.py +++ b/policyengine_uk/variables/household/demographic/is_looked_after_by_local_authority.py @@ -16,7 +16,12 @@ class is_looked_after_by_local_authority(Variable): "criteria, and their carer meets the foster parent condition for an " "additional bedroom. Leave it false for a child placed for adoption " "(see is_placed_for_adoption) or placed with their own parent, for " - "whom Universal Credit treats the carer as responsible (reg 4A)." + "whom Universal Credit treats the carer as responsible (reg 4A). " + "Under Housing Benefit and the other legacy schemes, a child placed " + "with a parent by a local authority under Children Act 1989 s.22C(2) " + "is outside the household (HB Regs 2006 reg 21(3)(a)); one flag " + "cannot represent both, and the model treats such a child as a " + "household member." ) definition_period = YEAR default_value = False diff --git a/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py b/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py index eebe11ef2b..56c659822a 100644 --- a/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py +++ b/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py @@ -7,22 +7,30 @@ class is_placed_for_adoption(Variable): label = "placed for adoption" documentation = ( "Whether this child is placed for adoption with the claimant or " - "partner of their benefit unit (an adopter), or placed with them " - "prior to adoption. Put the child in the adopter's benefit unit and " - "leave is_looked_after_by_local_authority false: under Universal " - "Credit the adopter is responsible for the child (UC Regs 2013 reg " - "4A(1)(c)) and satisfies the foster parent condition for an " - "additional bedroom (Sch 4 para 12(4)(b)), and Pension Credit treats " - "them as the adopter's responsibility too (SPC Regs 2002 Sch IIA " - "para 4(3)(c)). Under Housing Benefit, the other legacy schemes and " - "council tax reduction the child is not a member of the claimant's " - "household (HB Regs 2006 reg 21(3)(b)-(c); CTR (England) prescribed " - "requirements reg 8(2)(b)-(c)), and no one is responsible for them " - "for Tax-Free Childcare (Childcare Payments (Eligibility) Regs 2015 " - "reg 4(2)(d)). Child Tax Credit excludes a child placed for adoption " - "only while the local authority pays for their accommodation or " - "maintenance (CTC Regs 2002 reg 3, rule 4 case B), which is not " - "modelled." + "partner of their benefit unit under the Adoption and Children Act " + "2002 or the Adoption and Children (Scotland) Act 2007. Put the child " + "in the adopter's benefit unit and leave " + "is_looked_after_by_local_authority false. A fostering-for-adoption " + "placement (Children Act 1989 s.22C(9A)-(9B)) is a foster placement: " + "flag that child as looked after instead. Universal Credit treats the " + "adopter as responsible for the child (UC Regs 2013 reg 4A(1)(c)), " + "and the adopter satisfies the foster parent condition for an " + "additional bedroom (Sch 4 para 12(4)(b)). That condition's " + "definition of adopter (reg 89(3)(a)) excludes a foster parent or " + "close relative of the child, which the model does not check. " + "Pension Credit also treats the adopter as responsible (SPC Regs 2002 " + "Sch IIA para 4(3)(c)). Under Housing Benefit, Income Support, " + "income-based Jobseeker's Allowance, income-related Employment and " + "Support Allowance and the English pensioner, Welsh and Scottish " + "pension-age council tax reduction schemes, the child is not a " + "member of the claimant's household. Tax-Free Childcare does not " + "count them (Childcare Payments (Eligibility) Regs 2015 reg " + "4(2)(d)). Child Tax Credit excludes them only while the local " + "authority pays for their keep (CTC Regs 2002 reg 3, rule 4 case B), " + "which is not modelled. Scottish working-age council tax reduction " + "excludes only foster placements (SSI 2021/249 reg 7(8)), so the " + "child should count there; the model's shared legacy household test " + "leaves them out." ) definition_period = YEAR default_value = False @@ -34,4 +42,5 @@ class is_placed_for_adoption(Variable): "https://www.legislation.gov.uk/uksi/2015/448/regulation/4", "https://www.legislation.gov.uk/uksi/2002/1792/schedule/IIA", "https://www.legislation.gov.uk/uksi/2002/2007/regulation/3", + "https://www.legislation.gov.uk/ssi/2021/249/regulation/7", ) From 500424e0e22e28bc55b7db0341a1fe2ccd67d81d Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 2 Oct 2026 04:44:47 -0400 Subject: [PATCH 06/14] Address review r2: looked-after only under 18, single couples, IS fostering - is_looked_after_child: the looked-after flag applies only under 18 (Children Act 1989 ss.22(1), 105(1)). It replaces the raw flag in the UC, CTC and Pension Credit child tests and in the legacy placement test, so a flagged 18- or 19-year-old qualifying young person keeps their UC room and gives no HB carer room. The looked-after age limit moves to household.demographic.looked_after_child_age_limit. - HB occupier couples need is_couple as well as two counted claimant or partner members, so relation_type SINGLE is respected again. - Income Support Sch 1B para 2: a single claimant or lone parent with whom a child is placed by a local authority is a prescribed category. - YAML: a looked-after 17-year-old claimant (pins the claimant-guard mutant), a looked-after 18-year-old with a single renter of 30, a non-dependant pair entered as single, two IS para 2 cases. - Property predicates apply the under-18 limit and accept any non-tertiary education; P5 renamed to a reference count. The child-definition property reference applies the same under-18 limit. - Documentation: HB reg 19(2)(c) eligible children; looked-after flag under 18 only. Co-Authored-By: Claude Opus 5.5 --- changelog.d/2011.fixed.md | 2 +- .../gov/dwp/LHA/foster_child_age_limit.yaml | 13 ---- .../looked_after_child_age_limit.yaml | 11 +++ ...lies_children_and_additional_bedrooms.yaml | 77 ++++++++++++++++++- .../family/LHA_young_person_bedroom.yaml | 4 +- .../benefit/family/income_support.yaml | 45 ++++++++++- ..._child_and_adult_definitions_properties.py | 10 ++- ...est_lha_bedroom_young_people_properties.py | 12 +-- .../housing_benefit_LHA_allowed_bedrooms.py | 4 +- .../gov/dwp/income_support_eligible.py | 17 +++- ...fying_young_person_for_child_tax_credit.py | 2 +- ...hild_or_young_person_placed_with_family.py | 10 ++- .../variables/gov/dwp/is_lha_foster_child.py | 7 +- ...ha_renter_meets_foster_parent_condition.py | 4 +- ...lifying_young_person_for_pension_credit.py | 2 +- ...fying_young_person_for_universal_credit.py | 5 +- .../is_looked_after_by_local_authority.py | 3 +- .../demographic/is_looked_after_child.py | 30 ++++++++ 18 files changed, 216 insertions(+), 42 deletions(-) delete mode 100644 policyengine_uk/parameters/gov/dwp/LHA/foster_child_age_limit.yaml create mode 100644 policyengine_uk/parameters/household/demographic/looked_after_child_age_limit.yaml create mode 100644 policyengine_uk/variables/household/demographic/is_looked_after_child.py diff --git a/changelog.d/2011.fixed.md b/changelog.d/2011.fixed.md index 9c8e9ebca4..f77ad1bd60 100644 --- a/changelog.d/2011.fixed.md +++ b/changelog.d/2011.fixed.md @@ -1 +1 @@ -- Count the children of the household head's non-dependants (Universal Credit, UC Regs 2013 Sch 4 paras 9-10) and of non-dependants, boarders and lodgers (Housing Benefit, HB Regs 2006 reg 13D(12)) in the household head's LHA size criteria, sharing rooms with the head's own children. Give foster children no bedroom of their own, and foster parents, adopters with a child placed, and households with a non-resident overnight carer an additional bedroom (UC Sch 4 para 12; HB reg 13D(3A)-(3B)), with new inputs `is_placed_for_adoption`, `is_approved_foster_parent_without_placement` and `has_non_resident_overnight_carer`. Except foster parents from the shared accommodation rate (UC para 29(9A); HB "young individual"), and count armed forces independence payment in the UC para 29(5) exception. Give an occupier couple from another family one Housing Benefit bedroom (HB reg 13D(3)(a)). A child placed for adoption leaves the legacy household (HB reg 21(3), IS reg 16(4), JSA reg 78(4), ESA reg 156(5)) and is not a Tax-Free Childcare qualifying child (Childcare Payments (Eligibility) Regs 2015 reg 4(2)(d)). Treat a person under 18 looked after by a local authority and placed with the renter as the renter's foster child throughout the Universal Credit size criteria (`is_lha_foster_child`). Apply the 2017 extension of the overnight care bedroom beyond the renter or claimant from April 2017. Leave a child placed with the family out of the Income Support youngest-child test (IS reg 16(4)). +- Count the children of the household head's non-dependants (Universal Credit, UC Regs 2013 Sch 4 paras 9-10) and of non-dependants, boarders and lodgers (Housing Benefit, HB Regs 2006 reg 13D(12)) in the household head's LHA size criteria, sharing rooms with the head's own children. Give foster children no bedroom of their own, and foster parents, adopters with a child placed, and households with a non-resident overnight carer an additional bedroom (UC Sch 4 para 12; HB reg 13D(3A)-(3B)), with new inputs `is_placed_for_adoption`, `is_approved_foster_parent_without_placement` and `has_non_resident_overnight_carer`. Except foster parents from the shared accommodation rate (UC para 29(9A); HB "young individual"), and count armed forces independence payment in the UC para 29(5) exception. Give an occupier couple from another family one Housing Benefit bedroom (HB reg 13D(3)(a)). A child placed for adoption leaves the legacy household (HB reg 21(3), IS reg 16(4), JSA reg 78(4), ESA reg 156(5)) and is not a Tax-Free Childcare qualifying child (Childcare Payments (Eligibility) Regs 2015 reg 4(2)(d)). Treat a person under 18 looked after by a local authority and placed with the renter as the renter's foster child throughout the Universal Credit size criteria (`is_lha_foster_child`). Apply the 2017 extension of the overnight care bedroom beyond the renter or claimant from April 2017. Leave a child placed with the family out of the Income Support youngest-child test (IS reg 16(4)). Treat the looked-after flag as applying only under 18 (`is_looked_after_child`) in the UC, CTC, Pension Credit and legacy placement tests. Add Income Support Sch 1B para 2 (a single claimant or lone parent with whom a child is placed by a local authority). diff --git a/policyengine_uk/parameters/gov/dwp/LHA/foster_child_age_limit.yaml b/policyengine_uk/parameters/gov/dwp/LHA/foster_child_age_limit.yaml deleted file mode 100644 index f7c5c478a5..0000000000 --- a/policyengine_uk/parameters/gov/dwp/LHA/foster_child_age_limit.yaml +++ /dev/null @@ -1,13 +0,0 @@ -description: Age below which a child looked after by a local authority and placed with a renter is a foster child, making the renter a foster parent, in the Universal Credit size criteria. -values: - 2013-04-29: 18 -metadata: - unit: year - label: LHA foster child age limit - reference: - - title: Universal Credit Regulations 2013 reg 2, "foster parent" (a child placed under the Care Planning, Placement and Case Review (England) Regulations 2010) - href: https://www.legislation.gov.uk/uksi/2013/376/regulation/2 - - title: Universal Credit Regulations 2013 Sch 4 para 9(3), "foster child" - href: https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/9 - - title: Children Act 1989 s.105(1), "child" means a person under the age of eighteen - href: https://www.legislation.gov.uk/ukpga/1989/41/section/105 diff --git a/policyengine_uk/parameters/household/demographic/looked_after_child_age_limit.yaml b/policyengine_uk/parameters/household/demographic/looked_after_child_age_limit.yaml new file mode 100644 index 0000000000..20223de340 --- /dev/null +++ b/policyengine_uk/parameters/household/demographic/looked_after_child_age_limit.yaml @@ -0,0 +1,11 @@ +description: Age below which a person can be a child looked after by a local authority, and so a foster child. +values: + 2010-01-01: 18 +metadata: + unit: year + label: looked-after child age limit + reference: + - title: Children Act 1989 s.22(1) (a child who is looked after by a local authority) and s.105(1) ("child" means a person under the age of eighteen) + href: https://www.legislation.gov.uk/ukpga/1989/41/section/105 + - title: Universal Credit Regulations 2013 reg 2, "foster parent" and "looked after by a local authority" + href: https://www.legislation.gov.uk/uksi/2013/376/regulation/2 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml index 4281a53228..9d722cc08f 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml @@ -1068,13 +1068,15 @@ housing_benefit_LHA_additional_bedrooms: [0, 2] housing_benefit_LHA_allowed_bedrooms: [2, 3] -- name: A claimant who is looked after gives no foster room +- name: A looked-after claimant aged 17 gives no foster room period: 2026 input: people: claimant: - age: 19 + age: 17 is_household_head: true + is_claimant_or_partner: true + current_education: UPPER_SECONDARY is_looked_after_by_local_authority: true benunits: family: @@ -1087,9 +1089,78 @@ brma: MAIDSTONE output: # The foster parent condition needs a child placed with the renter, not - # the renter's own status (UC reg 2; HB reg 2(1)). + # the renter's own status (UC reg 2; HB reg 2(1), reg 21(3)). is_lha_foster_child: false lha_renter_meets_foster_parent_condition: false LHA_additional_bedrooms: 0 + is_child_or_young_person_placed_with_family: false is_housing_benefit_qualifying_parent_or_carer: false housing_benefit_LHA_additional_bedrooms: 0 + +- name: A person aged 18 cannot be looked after, so a single renter of 30 has a qualifying young person's bedroom + period: 2026 + input: + people: + renter: + age: 30 + is_household_head: true + is_parent: true + young_person: + age: 18 + current_education: UPPER_SECONDARY + is_looked_after_by_local_authority: true + benunits: + family: + members: [renter, young_person] + households: + home: + members: [renter, young_person] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # Children Act 1989 ss.22(1) and 105(1): only a person under 18 is looked + # after. UC: the renter is responsible for the qualifying young person + # (reg 4(2)), who has a bedroom (para 10(1)(b)); not a specified renter + # (para 28(3)): 2, C. HB: a member of the household aged 16 or over + # (13D(3)(b)): 2, C. No foster or carer room. + is_looked_after_child: [false, false] + is_child_or_qualifying_young_person_for_universal_credit: [false, true] + is_lha_shared_accommodation_rate_specified_renter: false + LHA_additional_bedrooms: 0 + LHA_allowed_bedrooms: 2 + LHA_category: C + is_child_or_young_person_placed_with_family: [false, false] + housing_benefit_LHA_additional_bedrooms: 0 + housing_benefit_LHA_allowed_bedrooms: 2 + housing_benefit_LHA_category: C + +- name: A non-dependant parent and adult son entered as single count one Housing Benefit bedroom each + period: 2026 + input: + people: + renter: + age: 60 + is_household_head: true + parent: + age: 50 + son: + age: 22 + benunits: + renter_family: + members: [renter] + parent_family: + members: [parent, son] + relation_type: SINGLE + households: + home: + members: [renter, parent, son] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # Not a couple, so each is "a person who is not a child" (HB reg + # 13D(3)(b)): 3. The parent family's own figure follows the model's + # inferred claimant and partner (the eldest two adults); it has no rent, + # so it is unused. + housing_benefit_LHA_allowed_bedrooms: [3, 1] diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_young_person_bedroom.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_young_person_bedroom.yaml index 315a51eade..34eafbb281 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_young_person_bedroom.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_young_person_bedroom.yaml @@ -141,8 +141,8 @@ output: is_qualifying_young_person_for_universal_credit: [false, true] is_child_or_qualifying_young_person_for_universal_credit: [false, false] - # Not under para 10(1)(b), and not a non-dependant (para 9(2)(g)), so no - # bedroom of their own. The carer they are placed with is a foster parent + # Not under para 10(1)(b), and not a non-dependant (para 9(2)(c) and (g)), + # so no bedroom of their own. The carer they are placed with is a foster parent # (reg 2: a person with whom a child is placed under the care planning # regulations, which place children under 18), so has one additional # bedroom (para 12(4)(a), (9)(b)): 2 bedrooms, category C. The model reads diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml index 091b8e9041..6e2ac52ccf 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml @@ -23,4 +23,47 @@ child_tax_credit: 0 child_benefit: 1825 income_support_eligible: true - income_support: 0 \ No newline at end of file + income_support: 0 + +- name: A single foster carer is in an Income Support prescribed category + period: 2025 + input: + people: + carer: + age: 30 + income_support_reported: true + foster_child: + age: 3 + is_looked_after_by_local_authority: true + benunits: + family: + members: [carer, foster_child] + output: + # IS Regs 1987 Sch 1B para 2: a single claimant or lone parent with whom + # a child is placed by a local authority. The foster child is not a + # member of the household (reg 16(4)), so the carer is a single claimant. + is_single_person: true + income_support_eligible: true + +- name: A lone parent with an older child and a young foster child is in an Income Support prescribed category + period: 2025 + input: + people: + parent: + age: 30 + income_support_reported: true + child: + age: 10 + foster_child: + age: 2 + is_looked_after_by_local_authority: true + benunits: + family: + members: [parent, child, foster_child] + output: + # Sch 1B para 1 needs a child under 5 in the household, which the foster + # child is not (reg 16(4)); para 2 covers a lone parent with whom a child + # is placed. + youngest_child_age_for_legacy_benefits: 10 + income_support_eligible: true + diff --git a/policyengine_uk/tests/test_child_and_adult_definitions_properties.py b/policyengine_uk/tests/test_child_and_adult_definitions_properties.py index 2bb2f7413f..255500c71f 100644 --- a/policyengine_uk/tests/test_child_and_adult_definitions_properties.py +++ b/policyengine_uk/tests/test_child_and_adult_definitions_properties.py @@ -187,13 +187,19 @@ def qualifying_young_person(p, programme): return True +def looked_after_child(p): + # Only a person under 18 can be looked after by a local authority: + # https://www.legislation.gov.uk/ukpga/1989/41/section/105 + return p["is_looked_after_by_local_authority"] and p["age"] < 18 + + def child_or_young_person(p, programme): # Encoded responsibility filters, without their statutory exceptions: # https://www.legislation.gov.uk/uksi/2013/376/regulation/4 # https://www.legislation.gov.uk/uksi/2002/2007/regulation/3 # https://www.legislation.gov.uk/uksi/2002/1792/schedule/IIA eligible = statutory_child(p["age"]) or qualifying_young_person(p, programme) - if programme != "child_benefit" and p["is_looked_after_by_local_authority"]: + if programme != "child_benefit" and looked_after_child(p): return False return eligible @@ -252,7 +258,7 @@ def legacy_child_or_young_person(p, claimant): # https://www.legislation.gov.uk/uksi/2006/213/regulation/21 return ( not claimant - and not p["is_looked_after_by_local_authority"] + and not looked_after_child(p) and child_or_young_person(p, "child_benefit") ) diff --git a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py index 65d3b70cc4..e80ae2d80e 100644 --- a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py +++ b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py @@ -265,8 +265,9 @@ def fosters(fam): def in_qualifying_education(p, age_limit_for_entry=19): - """Full-time non-advanced education, entered before 19.""" - return p["current_education"] == "UPPER_SECONDARY" and ( + """Full-time non-advanced education (any education short of tertiary), + entered before 19.""" + return p["current_education"] not in ("NOT_IN_EDUCATION", "TERTIARY") and ( p["age"] < age_limit_for_entry or p["age_started_or_accepted_current_education_or_training"] < age_limit_for_entry @@ -301,14 +302,15 @@ def child_benefit_qualifying_young_person(p): def flags(p): """The added person's status, from the inputs alone.""" - looked_after = p.get("is_looked_after_by_local_authority", False) + # Only a person under 18 can be looked after (Children Act 1989 s.105). + looked_after = p.get("is_looked_after_by_local_authority", False) and p["age"] < 18 return dict( # A qualifying young person no one is responsible for (UC para # 9(2)(g); reg 4(6)(a)). unclaimed=looked_after and uc_qualifying_young_person(p), # The family's foster child: looked after and under 18 (UC reg 2, # "foster parent"; para 9(3)). - fostered=looked_after and p["age"] < 18, + fostered=looked_after, # Placed with the family by a local authority (HB reg 21(3)). placed=looked_after and child_benefit_qualifying_young_person(p), overnight=p.get("has_non_resident_overnight_carer", False), @@ -390,7 +392,7 @@ def test_a_foster_child_adds_only_the_foster_parents_bedroom(case): @PROPERTY_SETTINGS @given(scenario()) -def test_bedrooms_match_an_independent_count_of_the_size_criteria(case): +def test_bedrooms_match_a_reference_count_of_the_size_criteria(case): population, target, person = case situation, keys = build(population, (target, person)) sim, uc, hb = bedrooms(situation) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py index e11f0cec7e..bbf65f77cb 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py @@ -67,7 +67,9 @@ def formula(benunit, period, parameters): # and only where both members are counted, so the halves always pair. counted = other_occupier & aged_16_or_over claimant_or_partner = counted & person("is_claimant_or_partner", period) - couple = person.benunit.sum(claimant_or_partner) == 2 + couple = (person.benunit.sum(claimant_or_partner) == 2) & person.benunit( + "is_couple", period + ) occupier_rooms = counted * where(claimant_or_partner & couple, 0.5, 1) is_head_family = benunit.any(person("is_household_head", period)) other_occupiers = is_head_family * benunit.max( diff --git a/policyengine_uk/variables/gov/dwp/income_support_eligible.py b/policyengine_uk/variables/gov/dwp/income_support_eligible.py index c0f6715ffa..d504a3b22e 100644 --- a/policyengine_uk/variables/gov/dwp/income_support_eligible.py +++ b/policyengine_uk/variables/gov/dwp/income_support_eligible.py @@ -6,7 +6,10 @@ class income_support_eligible(Variable): entity = BenUnit label = "Whether eligible for Income Support" definition_period = YEAR - reference = "https://www.legislation.gov.uk/uksi/1987/1967/schedule/1B" + reference = ( + "https://www.legislation.gov.uk/uksi/1987/1967/schedule/1B", + "https://www.legislation.gov.uk/uksi/1987/1967/schedule/1B/paragraph/2", + ) def formula(benunit, period, parameters): IS = parameters(period).gov.dwp.income_support @@ -18,6 +21,16 @@ def formula(benunit, period, parameters): ) lone_parent = benunit("is_lone_parent", period) lone_parent_with_young_child = lone_parent & youngest_child_5_or_under + # Schedule 1B para 2: a single claimant or lone parent with whom a + # child is placed by a local authority, such as a foster child. + person = benunit.members + foster_child = person("is_looked_after_child", period) & person( + "is_child_for_child_benefit", period + ) + single_or_lone_parent = benunit("is_single_person", period) | lone_parent + fosters_child = single_or_lone_parent & benunit.any( + foster_child & ~person("is_claimant_or_partner", period) + ) has_carers = add(benunit, period, ["is_carer_for_benefits"]) > 0 none_SP_age = ~benunit.any(benunit.members("is_SP_age", period)) has_esa_income = benunit("esa_income", period) > 0 @@ -25,7 +38,7 @@ def formula(benunit, period, parameters): capital = benunit("income_support_assessable_capital", period) limit = IS.means_test.capital.limit return ( - (has_carers | lone_parent_with_young_child) + (has_carers | lone_parent_with_young_child | fosters_child) & none_SP_age & ~has_esa_income & already_claiming diff --git a/policyengine_uk/variables/gov/dwp/is_child_or_qualifying_young_person_for_child_tax_credit.py b/policyengine_uk/variables/gov/dwp/is_child_or_qualifying_young_person_for_child_tax_credit.py index d765404b1a..13a810d594 100644 --- a/policyengine_uk/variables/gov/dwp/is_child_or_qualifying_young_person_for_child_tax_credit.py +++ b/policyengine_uk/variables/gov/dwp/is_child_or_qualifying_young_person_for_child_tax_credit.py @@ -17,5 +17,5 @@ def formula(person, period, parameters): "is_child_for_child_tax_credit", period ) | person("is_qualifying_young_person_for_child_tax_credit", period) return child_or_qualifying_young_person & ~person( - "is_looked_after_by_local_authority", period + "is_looked_after_child", period ) diff --git a/policyengine_uk/variables/gov/dwp/is_child_or_young_person_placed_with_family.py b/policyengine_uk/variables/gov/dwp/is_child_or_young_person_placed_with_family.py index 8ab5cb8ce6..4512a69058 100644 --- a/policyengine_uk/variables/gov/dwp/is_child_or_young_person_placed_with_family.py +++ b/policyengine_uk/variables/gov/dwp/is_child_or_young_person_placed_with_family.py @@ -21,7 +21,13 @@ class is_child_or_young_person_placed_with_family(Variable): "model treats foster placements as within reg 21(3), as the " "'qualifying parent or carer' definition presupposes and as the " "English pensioner and Welsh council tax reduction rules say " - "expressly ('section 22C'; 'section 22C or 23(2)(a)')." + "expressly ('section 22C'; 'section 22C or 23(2)(a)'). HB reg " + "19(2)(c) excludes from 'young person' anyone to whom the Children " + "(Leaving Care) Act 2000 s.6 applies, such as an eligible child aged " + "16 or 17 who has been looked after for 13 weeks since age 14; such " + "a person is an occupier, not placed. The model has no input for " + "that and treats every looked-after qualifying young person as " + "placed." ) definition_period = YEAR reference = ( @@ -38,7 +44,7 @@ class is_child_or_young_person_placed_with_family(Variable): def formula(person, period, parameters): # HB Regs 2006 reg 21(3)(a)-(c); IS Regs 1987 reg 16(4); JSA Regs # 1996 reg 78(4); ESA Regs 2008 reg 156(5). - placed = person("is_looked_after_by_local_authority", period) | person( + placed = person("is_looked_after_child", period) | person( "is_placed_for_adoption", period ) return ( diff --git a/policyengine_uk/variables/gov/dwp/is_lha_foster_child.py b/policyengine_uk/variables/gov/dwp/is_lha_foster_child.py index 374f381ac7..aeb6da3f1b 100644 --- a/policyengine_uk/variables/gov/dwp/is_lha_foster_child.py +++ b/policyengine_uk/variables/gov/dwp/is_lha_foster_child.py @@ -28,9 +28,6 @@ class is_lha_foster_child(Variable): ) def formula(person, period, parameters): - age_limit = parameters(period).gov.dwp.LHA.foster_child_age_limit - return ( - person("is_looked_after_by_local_authority", period) - & (person("age", period) < age_limit) - & ~person("is_claimant_or_partner", period) + return person("is_looked_after_child", period) & ~person( + "is_claimant_or_partner", period ) diff --git a/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py b/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py index fab56739ba..7c4b1e31eb 100644 --- a/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py +++ b/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py @@ -30,7 +30,9 @@ def formula(benunit, period, parameters): # (under 18) is placed under the care planning regulations; reg 89(3) # and Sch 4 para 12(4)(b): an adopter with whom a child is placed for # adoption. - age_limit = parameters(period).gov.dwp.LHA.foster_child_age_limit + age_limit = parameters( + period + ).household.demographic.looked_after_child_age_limit placed_for_adoption = ( person("is_placed_for_adoption", period) & (person("age", period) < age_limit) diff --git a/policyengine_uk/variables/gov/dwp/pension_credit/guarantee_credit/minimum_guarantee/additional/is_child_or_qualifying_young_person_for_pension_credit.py b/policyengine_uk/variables/gov/dwp/pension_credit/guarantee_credit/minimum_guarantee/additional/is_child_or_qualifying_young_person_for_pension_credit.py index fed5985857..4a03ef4953 100644 --- a/policyengine_uk/variables/gov/dwp/pension_credit/guarantee_credit/minimum_guarantee/additional/is_child_or_qualifying_young_person_for_pension_credit.py +++ b/policyengine_uk/variables/gov/dwp/pension_credit/guarantee_credit/minimum_guarantee/additional/is_child_or_qualifying_young_person_for_pension_credit.py @@ -13,5 +13,5 @@ def formula(person, period, parameters): "is_child_for_pension_credit", period ) | person("is_qualifying_young_person_for_pension_credit", period) return child_or_qualifying_young_person & ~person( - "is_looked_after_by_local_authority", period + "is_looked_after_child", period ) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/child_element/is_child_or_qualifying_young_person_for_universal_credit.py b/policyengine_uk/variables/gov/dwp/universal_credit/child_element/is_child_or_qualifying_young_person_for_universal_credit.py index b75cb06d25..6e89ee6f7a 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/child_element/is_child_or_qualifying_young_person_for_universal_credit.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/child_element/is_child_or_qualifying_young_person_for_universal_credit.py @@ -15,6 +15,9 @@ def formula(person, period, parameters): child_or_qualifying_young_person = person( "is_child_for_universal_credit", period ) | person("is_qualifying_young_person_for_universal_credit", period) + # UC Regs 2013 reg 4(6)(a): no one is responsible for a child or + # qualifying young person looked after by a local authority, which + # only a person under 18 can be. return child_or_qualifying_young_person & ~person( - "is_looked_after_by_local_authority", period + "is_looked_after_child", period ) diff --git a/policyengine_uk/variables/household/demographic/is_looked_after_by_local_authority.py b/policyengine_uk/variables/household/demographic/is_looked_after_by_local_authority.py index 93c3fef0b7..f4a29dc9c4 100644 --- a/policyengine_uk/variables/household/demographic/is_looked_after_by_local_authority.py +++ b/policyengine_uk/variables/household/demographic/is_looked_after_by_local_authority.py @@ -7,7 +7,8 @@ class is_looked_after_by_local_authority(Variable): label = "looked after by a local authority" documentation = ( "Whether this child or young person is looked after by a local " - "authority. A looked-after child who lives in the household, such as " + "authority. Only a person under 18 can be (see " + "is_looked_after_child), so the flag has no effect at 18 or over. A looked-after child who lives in the household, such as " "a foster child, is placed with the family whose benefit unit they " "are in: no one in it is responsible for them under Universal Credit " "(UC Regs 2013 reg 4(6)(a)), they are not a member of the claimant's " diff --git a/policyengine_uk/variables/household/demographic/is_looked_after_child.py b/policyengine_uk/variables/household/demographic/is_looked_after_child.py new file mode 100644 index 0000000000..8cef423468 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_looked_after_child.py @@ -0,0 +1,30 @@ +from policyengine_uk.model_api import * + + +class is_looked_after_child(Variable): + value_type = bool + entity = Person + label = "Child looked after by a local authority" + documentation = ( + "Whether this person is flagged is_looked_after_by_local_authority " + "and is under 18. Only a child, a person under 18, can be looked " + "after by a local authority (Children Act 1989 ss.22(1) and 105(1); " + "the Scottish and Welsh definitions are also limited to children), " + "so the flag has no effect at 18 or over: Universal Credit treats " + "the carer as responsible for a qualifying young person aged 18 or " + "19, and Housing Benefit counts them as a household member." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/ukpga/1989/41/section/22", + "https://www.legislation.gov.uk/ukpga/1989/41/section/105", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/2", + ) + + def formula(person, period, parameters): + age_limit = parameters( + period + ).household.demographic.looked_after_child_age_limit + return person("is_looked_after_by_local_authority", period) & ( + person("age", period) < age_limit + ) From e5c36dc45da69271c8e9105c44460477687b4806 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 2 Oct 2026 05:24:37 -0400 Subject: [PATCH 07/14] Address review r3: Income Support for adopters, explicit roles in the single case - Income Support Sch 1B para 2A: a single claimant or lone parent with whom a child is placed for adoption by an adoption agency is a prescribed category, so leaving the placed child out of the para 1 youngest-child test no longer removes eligibility. Two YAML cases. - The relation_type SINGLE YAML case now marks the parent and son's roles explicitly, so it asserts the legally correct [3, 2]; a separate case pins the is_couple guard on the renter's count. Co-Authored-By: Claude Opus 5.5 --- changelog.d/2011.fixed.md | 2 +- ...lies_children_and_additional_bedrooms.yaml | 45 +++++++++++++++++-- .../benefit/family/income_support.yaml | 41 +++++++++++++++++ .../gov/dwp/income_support_eligible.py | 13 +++--- 4 files changed, 91 insertions(+), 10 deletions(-) diff --git a/changelog.d/2011.fixed.md b/changelog.d/2011.fixed.md index f77ad1bd60..14da1ea118 100644 --- a/changelog.d/2011.fixed.md +++ b/changelog.d/2011.fixed.md @@ -1 +1 @@ -- Count the children of the household head's non-dependants (Universal Credit, UC Regs 2013 Sch 4 paras 9-10) and of non-dependants, boarders and lodgers (Housing Benefit, HB Regs 2006 reg 13D(12)) in the household head's LHA size criteria, sharing rooms with the head's own children. Give foster children no bedroom of their own, and foster parents, adopters with a child placed, and households with a non-resident overnight carer an additional bedroom (UC Sch 4 para 12; HB reg 13D(3A)-(3B)), with new inputs `is_placed_for_adoption`, `is_approved_foster_parent_without_placement` and `has_non_resident_overnight_carer`. Except foster parents from the shared accommodation rate (UC para 29(9A); HB "young individual"), and count armed forces independence payment in the UC para 29(5) exception. Give an occupier couple from another family one Housing Benefit bedroom (HB reg 13D(3)(a)). A child placed for adoption leaves the legacy household (HB reg 21(3), IS reg 16(4), JSA reg 78(4), ESA reg 156(5)) and is not a Tax-Free Childcare qualifying child (Childcare Payments (Eligibility) Regs 2015 reg 4(2)(d)). Treat a person under 18 looked after by a local authority and placed with the renter as the renter's foster child throughout the Universal Credit size criteria (`is_lha_foster_child`). Apply the 2017 extension of the overnight care bedroom beyond the renter or claimant from April 2017. Leave a child placed with the family out of the Income Support youngest-child test (IS reg 16(4)). Treat the looked-after flag as applying only under 18 (`is_looked_after_child`) in the UC, CTC, Pension Credit and legacy placement tests. Add Income Support Sch 1B para 2 (a single claimant or lone parent with whom a child is placed by a local authority). +- Count the children of the household head's non-dependants (Universal Credit, UC Regs 2013 Sch 4 paras 9-10) and of non-dependants, boarders and lodgers (Housing Benefit, HB Regs 2006 reg 13D(12)) in the household head's LHA size criteria, sharing rooms with the head's own children. Give foster children no bedroom of their own, and foster parents, adopters with a child placed, and households with a non-resident overnight carer an additional bedroom (UC Sch 4 para 12; HB reg 13D(3A)-(3B)), with new inputs `is_placed_for_adoption`, `is_approved_foster_parent_without_placement` and `has_non_resident_overnight_carer`. Except foster parents from the shared accommodation rate (UC para 29(9A); HB "young individual"), and count armed forces independence payment in the UC para 29(5) exception. Give an occupier couple from another family one Housing Benefit bedroom (HB reg 13D(3)(a)). A child placed for adoption leaves the legacy household (HB reg 21(3), IS reg 16(4), JSA reg 78(4), ESA reg 156(5)) and is not a Tax-Free Childcare qualifying child (Childcare Payments (Eligibility) Regs 2015 reg 4(2)(d)). Treat a person under 18 looked after by a local authority and placed with the renter as the renter's foster child throughout the Universal Credit size criteria (`is_lha_foster_child`). Apply the 2017 extension of the overnight care bedroom beyond the renter or claimant from April 2017. Leave a child placed with the family out of the Income Support youngest-child test (IS reg 16(4)). Treat the looked-after flag as applying only under 18 (`is_looked_after_child`) in the UC, CTC, Pension Credit and legacy placement tests. Add Income Support Sch 1B paras 2 and 2A (a single claimant or lone parent with whom a child is placed by a local authority, or placed for adoption). diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml index 9d722cc08f..3ad37a3c8a 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml @@ -1135,17 +1135,20 @@ housing_benefit_LHA_allowed_bedrooms: 2 housing_benefit_LHA_category: C -- name: A non-dependant parent and adult son entered as single count one Housing Benefit bedroom each +- name: A non-dependant parent and adult son count one Housing Benefit bedroom each period: 2026 input: people: renter: age: 60 is_household_head: true + is_claimant_or_partner: true parent: age: 50 + is_claimant_or_partner: true son: age: 22 + is_claimant_or_partner: false benunits: renter_family: members: [renter] @@ -1160,7 +1163,41 @@ brma: MAIDSTONE output: # Not a couple, so each is "a person who is not a child" (HB reg - # 13D(3)(b)): 3. The parent family's own figure follows the model's - # inferred claimant and partner (the eldest two adults); it has no rent, - # so it is unused. + # 13D(3)(b)): the renter's 3. The parent's own size criteria: themself + # and the son, 2. + housing_benefit_LHA_allowed_bedrooms: [3, 2] + +- name: An explicit single relationship stops the Housing Benefit couple rule + period: 2026 + input: + people: + renter: + age: 60 + is_household_head: true + is_claimant_or_partner: true + adult_1: + age: 40 + is_claimant_or_partner: true + adult_2: + age: 38 + is_claimant_or_partner: true + benunits: + renter_family: + members: [renter] + other_family: + members: [adult_1, adult_2] + relation_type: SINGLE + households: + home: + members: [renter, adult_1, adult_2] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # With relation_type SINGLE the family is not a couple (is_couple), so + # reg 13D(3)(a) does not merge the two adults into one bedroom for the + # renter: 3. (The inputs flag both as claimant or partner, which a + # single family cannot have; only the renter's figure is the point, and + # the other family's 1 just reflects those inputs.) + is_couple: [false, false] housing_benefit_LHA_allowed_bedrooms: [3, 1] diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml index 6e2ac52ccf..ad09d44cd9 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml @@ -67,3 +67,44 @@ youngest_child_age_for_legacy_benefits: 10 income_support_eligible: true +- name: A single adopter with a child placed for adoption is in an Income Support prescribed category + period: 2025 + input: + people: + adopter: + age: 30 + income_support_reported: true + child: + age: 3 + is_placed_for_adoption: true + benunits: + family: + members: [adopter, child] + output: + # IS Regs 1987 Sch 1B para 2A. The child is not a member of the household + # (reg 16(4)(b)-(c)), so the adopter is a single claimant. + is_single_person: true + income_support_eligible: true + +- name: A lone parent with an older child and a young child placed for adoption is in an Income Support prescribed category + period: 2025 + input: + people: + parent: + age: 30 + income_support_reported: true + child: + age: 10 + placed_child: + age: 2 + is_placed_for_adoption: true + benunits: + family: + members: [parent, child, placed_child] + output: + # Para 1 needs a child under 5 in the household, which the placed child + # is not (reg 16(4)); para 2A covers a lone parent with whom a child is + # placed for adoption. + youngest_child_age_for_legacy_benefits: 10 + income_support_eligible: true + diff --git a/policyengine_uk/variables/gov/dwp/income_support_eligible.py b/policyengine_uk/variables/gov/dwp/income_support_eligible.py index d504a3b22e..25342fbfb1 100644 --- a/policyengine_uk/variables/gov/dwp/income_support_eligible.py +++ b/policyengine_uk/variables/gov/dwp/income_support_eligible.py @@ -9,6 +9,7 @@ class income_support_eligible(Variable): reference = ( "https://www.legislation.gov.uk/uksi/1987/1967/schedule/1B", "https://www.legislation.gov.uk/uksi/1987/1967/schedule/1B/paragraph/2", + "https://www.legislation.gov.uk/uksi/1987/1967/schedule/1B/paragraph/2A", ) def formula(benunit, period, parameters): @@ -22,14 +23,16 @@ def formula(benunit, period, parameters): lone_parent = benunit("is_lone_parent", period) lone_parent_with_young_child = lone_parent & youngest_child_5_or_under # Schedule 1B para 2: a single claimant or lone parent with whom a - # child is placed by a local authority, such as a foster child. + # child is placed by a local authority, such as a foster child; para + # 2A: or placed for adoption by an adoption agency. person = benunit.members - foster_child = person("is_looked_after_child", period) & person( - "is_child_for_child_benefit", period - ) + placed_child = ( + person("is_looked_after_child", period) + | person("is_placed_for_adoption", period) + ) & person("is_child_for_child_benefit", period) single_or_lone_parent = benunit("is_single_person", period) | lone_parent fosters_child = single_or_lone_parent & benunit.any( - foster_child & ~person("is_claimant_or_partner", period) + placed_child & ~person("is_claimant_or_partner", period) ) has_carers = add(benunit, period, ["is_carer_for_benefits"]) > 0 none_SP_age = ~benunit.any(benunit.members("is_SP_age", period)) From 1f3ce21a08e8341b25af524bc04d3c940ad844bd Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 2 Oct 2026 07:06:59 -0400 Subject: [PATCH 08/14] Address review r4 nits: ineligible IS cases, renter-only assertion, reuse placement test - Two Income Support cases that must be ineligible: a couple with a child placed for adoption, and a placement aged 16 (para 2A needs a single claimant or lone parent, and a child under 16). - The is_couple guard case asserts only the renter's bedrooms. - income_support_eligible reuses is_child_or_young_person_placed_with_family (under 16) instead of re-deriving it; renamed has_placed_child. - is_placed_for_adoption documentation mentions IS Sch 1B para 2A. Co-Authored-By: Claude Opus 5.5 --- ...lies_children_and_additional_bedrooms.yaml | 7 ++-- .../benefit/family/income_support.yaml | 37 +++++++++++++++++++ .../gov/dwp/income_support_eligible.py | 11 ++---- .../demographic/is_placed_for_adoption.py | 4 +- 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml index 3ad37a3c8a..9b6d982431 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml @@ -1197,7 +1197,8 @@ # With relation_type SINGLE the family is not a couple (is_couple), so # reg 13D(3)(a) does not merge the two adults into one bedroom for the # renter: 3. (The inputs flag both as claimant or partner, which a - # single family cannot have; only the renter's figure is the point, and - # the other family's 1 just reflects those inputs.) + # single family cannot have, so only the renter's figure is asserted.) is_couple: [false, false] - housing_benefit_LHA_allowed_bedrooms: [3, 1] + benunits: + renter_family: + housing_benefit_LHA_allowed_bedrooms: 3 diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml index ad09d44cd9..60d02430ee 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml @@ -108,3 +108,40 @@ youngest_child_age_for_legacy_benefits: 10 income_support_eligible: true +- name: A couple with a child placed for adoption is not in the Income Support adoption category + period: 2025 + input: + people: + adopter_1: + age: 30 + income_support_reported: true + adopter_2: + age: 31 + child: + age: 2 + is_placed_for_adoption: true + benunits: + family: + members: [adopter_1, adopter_2, child] + output: + # Sch 1B para 2A covers only a single claimant or lone parent. + income_support_eligible: false + +- name: A placement aged 16 is not a child for the Income Support adoption category + period: 2025 + input: + people: + adopter: + age: 40 + income_support_reported: true + young_person: + age: 16 + current_education: UPPER_SECONDARY + is_placed_for_adoption: true + benunits: + family: + members: [adopter, young_person] + output: + # Para 2A needs "a child", a person under 16 (SSCBA 1992 s.137(1)). + income_support_eligible: false + diff --git a/policyengine_uk/variables/gov/dwp/income_support_eligible.py b/policyengine_uk/variables/gov/dwp/income_support_eligible.py index 25342fbfb1..8e4c247280 100644 --- a/policyengine_uk/variables/gov/dwp/income_support_eligible.py +++ b/policyengine_uk/variables/gov/dwp/income_support_eligible.py @@ -26,14 +26,11 @@ def formula(benunit, period, parameters): # child is placed by a local authority, such as a foster child; para # 2A: or placed for adoption by an adoption agency. person = benunit.members - placed_child = ( - person("is_looked_after_child", period) - | person("is_placed_for_adoption", period) + placed_child = person( + "is_child_or_young_person_placed_with_family", period ) & person("is_child_for_child_benefit", period) single_or_lone_parent = benunit("is_single_person", period) | lone_parent - fosters_child = single_or_lone_parent & benunit.any( - placed_child & ~person("is_claimant_or_partner", period) - ) + has_placed_child = single_or_lone_parent & benunit.any(placed_child) has_carers = add(benunit, period, ["is_carer_for_benefits"]) > 0 none_SP_age = ~benunit.any(benunit.members("is_SP_age", period)) has_esa_income = benunit("esa_income", period) > 0 @@ -41,7 +38,7 @@ def formula(benunit, period, parameters): capital = benunit("income_support_assessable_capital", period) limit = IS.means_test.capital.limit return ( - (has_carers | lone_parent_with_young_child | fosters_child) + (has_carers | lone_parent_with_young_child | has_placed_child) & none_SP_age & ~has_esa_income & already_claiming diff --git a/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py b/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py index 56c659822a..cf236868a6 100644 --- a/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py +++ b/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py @@ -23,7 +23,9 @@ class is_placed_for_adoption(Variable): "income-based Jobseeker's Allowance, income-related Employment and " "Support Allowance and the English pensioner, Welsh and Scottish " "pension-age council tax reduction schemes, the child is not a " - "member of the claimant's household. Tax-Free Childcare does not " + "member of the claimant's household; a single claimant or lone " + "parent with such a child is in an Income Support prescribed " + "category (IS Regs 1987 Sch 1B para 2A). Tax-Free Childcare does not " "count them (Childcare Payments (Eligibility) Regs 2015 reg " "4(2)(d)). Child Tax Credit excludes them only while the local " "authority pays for their keep (CTC Regs 2002 reg 3, rule 4 case B), " From 5445e798592ef7a8da5f57c087fc9aa6f14aacce Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 00:06:14 +0000 Subject: [PATCH 09/14] Keep placed children out of Housing Benefit non-dependants The legacy child flag now leaves out every child placed with the family, so housing_benefit_has_non_dependant and the severe disability premium's non-dependant test read such a child as a non-dependant. HB reg 3(2)(c) says otherwise; both now apply the placement test. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LwQq6FYBy8zKExK1r9z9fW --- ...lies_children_and_additional_bedrooms.yaml | 30 +++++++++++++++++++ .../housing_benefit_has_non_dependant.py | 16 +++++++--- ...nefit_severe_disability_premium_applies.py | 7 ++++- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml index 9b6d982431..f644122e7b 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml @@ -1202,3 +1202,33 @@ benunits: renter_family: housing_benefit_LHA_allowed_bedrooms: 3 + +- name: A child placed with the claimant is not a Housing Benefit non-dependant + period: 2026 + input: + people: + carer: + age: 40 + is_household_head: true + foster_child: + age: 10 + is_looked_after_by_local_authority: true + adopted_child: + age: 4 + is_placed_for_adoption: true + benunits: + family: + members: [carer, foster_child, adopted_child] + households: + home: + members: [carer, foster_child, adopted_child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # Neither child is a member of the carer's household (HB reg 21(3)(a) + # and (c)), so neither is a child or young person of the family; reg + # 3(2)(c) still takes each out of "non-dependant": the carer has none. + is_child_or_young_person_for_legacy_benefits: [false, false, false] + is_child_or_young_person_placed_with_family: [false, true, true] + housing_benefit_has_non_dependant: false diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_has_non_dependant.py b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_has_non_dependant.py index a5904137be..9880fa79f2 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_has_non_dependant.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_has_non_dependant.py @@ -7,7 +7,8 @@ class housing_benefit_has_non_dependant(Variable): label = "Has a non-dependant residing with them (Housing Benefit)" documentation = ( "Someone in this benefit unit who is neither a claimant or partner " - "nor a child or young person, or, for a family liable for the " + "nor a child or young person (including one placed with the family " + "as a foster child or for adoption), or, for a family liable for the " "household's rent (the household head's or a sharer's), anyone in " "the household who is a non-dependant (see " "is_non_dependant_of_household_head). A non-dependant normally " @@ -18,6 +19,7 @@ class housing_benefit_has_non_dependant(Variable): definition_period = YEAR reference = ( "https://www.legislation.gov.uk/uksi/2006/213/regulation/3", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/21", "https://www.legislation.gov.uk/uksi/2006/213/regulation/74", ) @@ -26,9 +28,15 @@ def formula(benunit, period, parameters): person = benunit.members claimant_or_partner = person("is_claimant_or_partner", period) age = person("age", period) - child_or_young_person = person( - "is_child_or_young_person_for_legacy_benefits", period - ) | ((age >= 16) & (age < 17)) + # Reg 3(2)(c): a child or young person living with the claimant who + # is not a member of the household by virtue of reg 21, such as one + # placed with the claimant or partner as a foster child or for + # adoption (reg 21(3)), is not a non-dependant. + child_or_young_person = ( + person("is_child_or_young_person_for_legacy_benefits", period) + | person("is_child_or_young_person_placed_with_family", period) + | ((age >= 16) & (age < 17)) + ) within_benefit_unit = benunit.any(~claimant_or_partner & ~child_or_young_person) liable_family = benunit.any(person("is_liable_for_household_rent", period)) non_dependants = benunit.max( diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_severe_disability_premium_applies.py b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_severe_disability_premium_applies.py index 0e0ac10e22..aa247a5dcb 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_severe_disability_premium_applies.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_severe_disability_premium_applies.py @@ -45,7 +45,12 @@ def formula(benunit, period, parameters): # housing_benefit_has_non_dependant. age = person("age", period) adult_non_dependant = (age >= 18) & ~qualifying & ~blind - young_person = person("is_child_or_young_person_for_legacy_benefits", period) + # A child or young person placed with the family as a foster child or + # for adoption is not a non-dependant either (reg 3(2)(c) with reg + # 21(3)). + young_person = person( + "is_child_or_young_person_for_legacy_benefits", period + ) | person("is_child_or_young_person_placed_with_family", period) within = benunit.any(adult_non_dependant & ~claimant_or_partner & ~young_person) liable_family = benunit.any(person("is_liable_for_household_rent", period)) outside = benunit.max( From c7f9dfa078b4f229b28cc7ba444e2a3f29c21698 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 00:14:32 +0000 Subject: [PATCH 10/14] Show Scottish working-age CTR keeps a child placed for adoption SSI 2021/249 reg 7(8) takes out only a child placed with a foster carer. On that branch the CTR flag reads the Universal Credit flag, which keeps a child placed for adoption, so the adopter stays a lone parent. Adds the review's family through final CTR, the pension-age contrast and a property over Scottish working-age families from 2022-23. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LwQq6FYBy8zKExK1r9z9fW --- .../council_tax_reduction_young_person.yaml | 118 ++++++++++++++++++ ...l_tax_reduction_young_person_properties.py | 45 +++++++ ..._young_person_for_council_tax_reduction.py | 10 +- 3 files changed, 172 insertions(+), 1 deletion(-) diff --git a/policyengine_uk/tests/policy/baseline/gov/local_authorities/council_tax_reduction/council_tax_reduction_young_person.yaml b/policyengine_uk/tests/policy/baseline/gov/local_authorities/council_tax_reduction/council_tax_reduction_young_person.yaml index 1abdbeba87..264792dfbc 100644 --- a/policyengine_uk/tests/policy/baseline/gov/local_authorities/council_tax_reduction/council_tax_reduction_young_person.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/local_authorities/council_tax_reduction/council_tax_reduction_young_person.yaml @@ -351,3 +351,121 @@ is_council_tax_reduction_applicant_or_partner: [true, false, false] is_child_or_young_person_for_council_tax_reduction: [true, false, false] council_tax_reduction_applicable_income: 10_000 + +- name: Case 11, Scotland working age, an adopter of 23 with a child of 3 who lives with them. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + adopter: + age: 23 + employment_income: 11_000 + is_household_head: true + child: + age: 3 + is_placed_for_adoption: false + benunits: + family: + members: [adopter, child] + would_claim_uc: false + households: + home: + members: [adopter, child] + country: SCOTLAND + local_authority: CITY_OF_EDINBURGH + council_tax: 1_800 + output: + # SSI 2021/249 reg 7(2): the adopter is responsible for a child who + # normally lives with them; reg 7(8) takes out only a child placed with + # a foster carer, so an adoption placement leaves the child in the + # family and the adopter a lone parent. + is_child_or_young_person_for_council_tax_reduction: [false, true] + council_tax_reduction_pensioner: false + is_council_tax_reduction_lone_parent: true + # Lone parent aged 18 or over, £92.05 a week in 2025-26: 92.05 * 52. + council_tax_reduction_applicable_amount: 4_786.60 + # Earnings of £11,000 fall below the £12,570 personal allowance and + # National Insurance primary threshold, so no tax or contributions. The + # model counts the earnings with no earnings disregard on this route, + # plus Child Benefit for an eldest child, £26.05 a week: 11,000 + + # 26.05 * 52 = 12,354.60. + council_tax_reduction_applicable_income: 12_354.60 + # Scotland's withdrawal rate is 20%: 1,800 - 0.2 * (12,354.60 - + # 4,786.60) = 1,800 - 1,513.60 = 286.40. + council_tax_reduction: 286.40 + +- name: Case 12, Scotland working age, the same family with the child placed for adoption. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + adopter: + age: 23 + employment_income: 11_000 + is_household_head: true + child: + age: 3 + is_placed_for_adoption: true + benunits: + family: + members: [adopter, child] + would_claim_uc: false + households: + home: + members: [adopter, child] + country: SCOTLAND + local_authority: CITY_OF_EDINBURGH + council_tax: 1_800 + output: + # SSI 2021/249 reg 7(2): the adopter is responsible for a child who + # normally lives with them; reg 7(8) takes out only a child placed with + # a foster carer, so an adoption placement leaves the child in the + # family and the adopter a lone parent. + is_child_or_young_person_for_council_tax_reduction: [false, true] + council_tax_reduction_pensioner: false + is_council_tax_reduction_lone_parent: true + # Lone parent aged 18 or over, £92.05 a week in 2025-26: 92.05 * 52. + council_tax_reduction_applicable_amount: 4_786.60 + # Earnings of £11,000 fall below the £12,570 personal allowance and + # National Insurance primary threshold, so no tax or contributions. The + # model counts the earnings with no earnings disregard on this route, + # plus Child Benefit for an eldest child, £26.05 a week: 11,000 + + # 26.05 * 52 = 12,354.60. + council_tax_reduction_applicable_income: 12_354.60 + # Scotland's withdrawal rate is 20%: 1,800 - 0.2 * (12,354.60 - + # 4,786.60) = 1,800 - 1,513.60 = 286.40. + council_tax_reduction: 286.40 + # Housing Benefit and Income Support still leave the child out of the + # household (HB Regs 2006 reg 21(3)(c); IS Regs 1987 reg 16(4)). + is_child_or_young_person_placed_with_family: [false, true] + is_child_or_young_person_for_legacy_benefits: [false, false] + is_responsible_for_child_or_young_person_for_legacy_benefits: false + is_lone_parent: false + +- name: Case 13, Scotland's pension-age scheme leaves out a child placed for adoption. + period: 2025 + input: + people: + adopter: + age: 70 + is_household_head: true + child: + age: 3 + is_placed_for_adoption: true + benunits: + family: + members: [adopter, child] + would_claim_uc: false + households: + home: + members: [adopter, child] + country: SCOTLAND + local_authority: CITY_OF_EDINBURGH + council_tax: 1_800 + output: + # SSI 2012/319 reg 11(2)(c): a child placed for adoption with the + # applicant is not a member of the household, so the adopter is not a + # lone parent. + council_tax_reduction_pensioner: true + is_child_or_young_person_for_council_tax_reduction: [false, false] + is_council_tax_reduction_lone_parent: false diff --git a/policyengine_uk/tests/test_council_tax_reduction_young_person_properties.py b/policyengine_uk/tests/test_council_tax_reduction_young_person_properties.py index 1bf2efbf8f..ea766b4a46 100644 --- a/policyengine_uk/tests/test_council_tax_reduction_young_person_properties.py +++ b/policyengine_uk/tests/test_council_tax_reduction_young_person_properties.py @@ -34,6 +34,11 @@ means test where Scotland's working-age scheme does not read them as a young person. A member who is not the applicant never counts: a young person's income is not the applicant's. +7. Adoption placements. From 2022-23, placing any member of a Scottish + working-age family for adoption leaves its lone-parent status and its + applicable amount unchanged: SSI 2021/249 reg. 7(8) takes out only a + child or young person placed with a foster carer, and the applicant is + responsible for one who normally lives with them (reg. 7(2)). The pensioner test and the claimant and partner are supplied, so these tests exercise the young person rule alone; the YAML cases use the derived tests. @@ -459,3 +464,43 @@ def test_applicants_own_earnings_count_under_every_scheme(): # Not vacuous: a non-applicant who is the scheme's young person does not # count, nor one who is not. assert (~heads & ~young_person).any() and (~heads & young_person).any() + + +@SETTINGS +@given( + st.lists(families(), min_size=1, max_size=4), + st.sampled_from((2022, 2026)), + st.data(), +) +def test_scottish_working_age_adoption_placement_keeps_the_family(units, year, data): + # Invariant 7. + units = [dict(unit, country="SCOTLAND", pensioner=False) for unit in units] + people = [p for unit in units for p in unit["members"]] + placed = data.draw( + st.lists(st.booleans(), min_size=len(people), max_size=len(people)) + ) + + def with_placement(flags): + result, k = [], 0 + for unit in units: + members = [] + for p in unit["members"]: + members.append(dict(p, is_placed_for_adoption=flags[k])) + k += 1 + result.append(dict(unit, members=members)) + return result + + outputs = [] + for flags in ([False] * len(people), placed): + sim = simulate(with_placement(flags), year) + outputs.append( + ( + sim.calculate( + "is_child_or_young_person_for_council_tax_reduction", year + ), + sim.calculate("is_council_tax_reduction_lone_parent", year), + sim.calculate("council_tax_reduction_applicable_amount", year), + ) + ) + for before, after in zip(*outputs): + np.testing.assert_array_equal(before, after) diff --git a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/is_child_or_young_person_for_council_tax_reduction.py b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/is_child_or_young_person_for_council_tax_reduction.py index 476e395c44..e8eaf00d26 100644 --- a/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/is_child_or_young_person_for_council_tax_reduction.py +++ b/policyengine_uk/variables/gov/local_authorities/council_tax_reduction/is_child_or_young_person_for_council_tax_reduction.py @@ -25,7 +25,15 @@ class is_child_or_young_person_for_council_tax_reduction(Variable): local authority, as the Universal Credit and legacy flags do. That is broader than Scotland's working-age rule, which names only a child or young person placed with a foster carer under the Looked After Children - (Scotland) Regulations 2009 (reg. 7(8)). + (Scotland) Regulations 2009 (reg. 7(8)). The Child Benefit branch also + leaves out a child placed for adoption with the applicant or partner + (SI 2012/2885 reg. 8(2); SI 2012/2886 Sch para. 8(2); WSI 2013/3029 + reg. 8(2); SSI 2012/319 reg. 11(2)). The Universal Credit branch keeps them, since the Universal + Credit flag leaves out only looked-after children, so on Scotland's + working-age scheme from 1 April 2022 the applicant is responsible for a + child placed for adoption who normally lives with them (reg. 7(2)). + Before 1 April 2022 Scotland's working-age scheme follows the Child + Benefit branch. """ value_type = bool From 02125f5d6f85a66dcdc8b7a3262e15eda13ecf8b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 00:14:32 +0000 Subject: [PATCH 11/14] Count a child placed with another family in the host's HB rooms; apply the UC adopter definition HB reg 21(3) names only a placement with the claimant or partner, so a child placed with a non-dependant, boarder or lodger occupies the claimant's dwelling (13D(12)) and counts in the host's size criteria and under 13D(3A)(a)(iii). The carer's own claim still leaves the child out. UC Sch 4 para 12(4)(b) applies to an adopter as reg 89(3)(a) defines it, which excludes a foster parent or close relative of the child. The new child-level input adopter_is_foster_parent_or_close_relative feeds only the foster parent condition, and so para 29(9A). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LwQq6FYBy8zKExK1r9z9fW --- changelog.d/2011.fixed.md | 2 +- ...lies_children_and_additional_bedrooms.yaml | 364 ++++++++++++++++-- ...est_lha_bedroom_young_people_properties.py | 143 +++++-- ...housing_benefit_LHA_additional_bedrooms.py | 12 +- .../housing_benefit_LHA_allowed_bedrooms.py | 18 +- ...ha_renter_meets_foster_parent_condition.py | 16 +- ...pter_is_foster_parent_or_close_relative.py | 32 ++ .../demographic/is_placed_for_adoption.py | 14 +- 8 files changed, 516 insertions(+), 85 deletions(-) create mode 100644 policyengine_uk/variables/household/demographic/adopter_is_foster_parent_or_close_relative.py diff --git a/changelog.d/2011.fixed.md b/changelog.d/2011.fixed.md index 14da1ea118..de593718fe 100644 --- a/changelog.d/2011.fixed.md +++ b/changelog.d/2011.fixed.md @@ -1 +1 @@ -- Count the children of the household head's non-dependants (Universal Credit, UC Regs 2013 Sch 4 paras 9-10) and of non-dependants, boarders and lodgers (Housing Benefit, HB Regs 2006 reg 13D(12)) in the household head's LHA size criteria, sharing rooms with the head's own children. Give foster children no bedroom of their own, and foster parents, adopters with a child placed, and households with a non-resident overnight carer an additional bedroom (UC Sch 4 para 12; HB reg 13D(3A)-(3B)), with new inputs `is_placed_for_adoption`, `is_approved_foster_parent_without_placement` and `has_non_resident_overnight_carer`. Except foster parents from the shared accommodation rate (UC para 29(9A); HB "young individual"), and count armed forces independence payment in the UC para 29(5) exception. Give an occupier couple from another family one Housing Benefit bedroom (HB reg 13D(3)(a)). A child placed for adoption leaves the legacy household (HB reg 21(3), IS reg 16(4), JSA reg 78(4), ESA reg 156(5)) and is not a Tax-Free Childcare qualifying child (Childcare Payments (Eligibility) Regs 2015 reg 4(2)(d)). Treat a person under 18 looked after by a local authority and placed with the renter as the renter's foster child throughout the Universal Credit size criteria (`is_lha_foster_child`). Apply the 2017 extension of the overnight care bedroom beyond the renter or claimant from April 2017. Leave a child placed with the family out of the Income Support youngest-child test (IS reg 16(4)). Treat the looked-after flag as applying only under 18 (`is_looked_after_child`) in the UC, CTC, Pension Credit and legacy placement tests. Add Income Support Sch 1B paras 2 and 2A (a single claimant or lone parent with whom a child is placed by a local authority, or placed for adoption). +The LHA size criteria now count other families' children, give foster children no bedroom of their own and add the foster-parent, adopter and overnight-care bedrooms (UC Regs 2013 Sch 4 paras 9-12 and 29(9A); HB Regs 2006 reg 13D(3)-(3B)), with new inputs for adoption placements, foster parents between placements, non-resident overnight carers and adopters who are a foster parent or close relative of the child (UC reg 89(3)(a)). Housing Benefit counts a child placed with a non-dependant, boarder or lodger in the claimant's size criteria (reg 21(3) names only the claimant or partner), and the legacy household, Income Support (Sch 1B paras 2 and 2A) and Tax-Free Childcare leave out a child placed for adoption. diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml index f644122e7b..c52261f487 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_other_families_children_and_additional_bedrooms.yaml @@ -19,13 +19,19 @@ # for each category of "occupier", everyone who occupies the dwelling as # their home except a joint tenant outside the claimant's household (13D(12)), # so the children of a non-dependant, boarder or lodger count. A child or -# young person placed with the claimant as a foster child or for adoption -# does not occupy the dwelling (reg 21(3)); the claimant is then a -# "qualifying parent or carer" (reg 2(1)) with one additional bedroom -# (13D(3A)(b)), and one more for overnight care (13D(3A)(a), (3B)). +# young person placed with the claimant or partner as a foster child or for +# adoption does not occupy the claimant's dwelling (reg 21(3)); the claimant +# is then a "qualifying parent or carer" (reg 2(1)) with one additional +# bedroom (13D(3A)(b)), and one more for overnight care (13D(3A)(a), (3B)). +# Every limb of reg 21(3) names a placement "with the claimant or his +# partner", so a child placed with a non-dependant, boarder or lodger +# occupies the claimant's dwelling (13D(12)): they count in the claimant's +# size criteria and, if they require overnight care, under 13D(3A)(a)(iii). +# The carer's own claim leaves them out and has the qualifying parent or +# carer's room. # # DWP guidance: ADM F3016, F3034, F3111-F3112, F3131-F3138; LHA Guidance -# Manual (April 2014) paras 2.030-2.033, 2.090, 2.093; HB/CTB circular A4/2012 +# Manual (April 2014) paras 2.030-2.031, 2.090, 2.093; HB/CTB circular A4/2012 # paras 36-38 and 44. # # Rents of £200 a week (£10,400 a year) sit between the published Maidstone @@ -241,7 +247,7 @@ LHA_allowed_bedrooms: [1, 2] housing_benefit_LHA_allowed_bedrooms: [1, 2] -- name: A non-dependant's foster child does not count for the renter +- name: A non-dependant's foster child counts for the renter under Housing Benefit only period: 2026 input: people: @@ -270,12 +276,14 @@ # looked-after child (reg 4(6)(a)), so the child is not a non-dependant # (para 9(2)(g)): the renter and the daughter, 2 bedrooms. LHA_allowed_bedrooms: [2, 2] - # HB: reg 21(3) names only a child placed with the claimant or partner, - # but DWP treats foster children as not occupying the dwelling (LHA - # Guidance Manual para 2.033; circular A21/2013 para 22), and the model - # follows it: 2 bedrooms. The daughter's own size criteria include the - # foster parent's or qualifying parent or carer's additional bedroom. - housing_benefit_LHA_allowed_bedrooms: [2, 2] + # HB: reg 21(3) takes out only a child placed with the claimant or + # partner, so the child placed with the daughter occupies the renter's + # dwelling as their home (13D(12)): the renter (13D(3)(b)), the daughter + # ((b)) and the child ((e)), 3 bedrooms. The daughter's own claim leaves + # the child out (reg 21(3)(a)) and has the qualifying parent or carer's + # additional bedroom (13D(3A)(b)): 1 + 1 = 2. The UC daughter's room is + # the foster parent condition's (para 12(4)(a)). + housing_benefit_LHA_allowed_bedrooms: [3, 2] LHA_additional_bedrooms: [0, 1] housing_benefit_LHA_additional_bedrooms: [0, 1] @@ -314,9 +322,9 @@ LHA_additional_bedrooms: 1 LHA_allowed_bedrooms: 2 LHA_category: C - # HB: the foster children do not occupy the dwelling (reg 21(3); LHA - # Guidance Manual para 2.033); the claimant is a qualifying parent or - # carer (reg 2(1)) with one additional bedroom (reg 13D(3A)(b)). + # HB: the foster children, placed with the claimant, do not occupy the + # claimant's dwelling (reg 21(3)(a)); the claimant is a qualifying parent + # or carer (reg 2(1)) with one additional bedroom (reg 13D(3A)(b)). is_housing_benefit_qualifying_parent_or_carer: true housing_benefit_LHA_additional_bedrooms: 1 housing_benefit_LHA_allowed_bedrooms: 2 @@ -958,10 +966,12 @@ # the renter's extended benefit unit: 3. The daughter's own: herself, the # child and the adopter's room, 3. LHA_allowed_bedrooms: [3, 3] - # HB: following DWP, a child placed with anyone in the household is not - # an occupier: 2. The daughter's own: herself and the qualifying parent - # or carer room, 2. - housing_benefit_LHA_allowed_bedrooms: [2, 2] + # HB: reg 21(3)(c) takes out only a child placed for adoption with the + # claimant or partner, so the child placed with the daughter occupies the + # renter's dwelling (13D(12)): the renter, the daughter and the child + # (13D(3)(b), (b), (e)), 3. The daughter's own: herself and the + # qualifying parent or carer room (13D(3A)(b)), 2. + housing_benefit_LHA_allowed_bedrooms: [3, 2] - name: Five or more bedrooms map to the four-bedroom category period: 2026 @@ -1030,7 +1040,7 @@ housing_benefit_LHA_additional_bedrooms: [0, 1] housing_benefit_LHA_allowed_bedrooms: [2, 2] -- name: A non-dependant's foster child who needs overnight care counts only for the non-dependant +- name: A non-dependant's foster child who needs overnight care gives the renter a Housing Benefit overnight room period: 2026 input: people: @@ -1060,13 +1070,19 @@ # UC: the foster child is in no one's extended benefit unit but the # foster parent's (para 9(2)(g); 12(A1)(c)), so the renter gets no # overnight room: 2. The daughter: herself, the foster room and the - # overnight room, 3. HB: not an occupier of the renter's dwelling (model - # follows DWP); the daughter's own: herself, the carer room and the - # overnight room ((3A)(a)(iv)), 3. + # overnight room, 3. LHA_additional_bedrooms: [0, 2] LHA_allowed_bedrooms: [2, 3] - housing_benefit_LHA_additional_bedrooms: [0, 2] - housing_benefit_LHA_allowed_bedrooms: [2, 3] + # HB: the child, placed with the daughter and not with the renter (reg + # 21(3)), occupies the renter's dwelling, so is "a person (other than + # the claimant or the claimant's partner) who occupies the claimant's + # dwelling as their home" (13D(3A)(a)(iii)): the renter has the + # overnight room. Renter: 1 + daughter 1 + child 1 + overnight 1 = 4. + # The daughter's own claim: herself 1, the qualifying parent or carer + # room ((3A)(b)) and the overnight room for a child for whom she is a + # qualifying parent or carer ((3A)(a)(iv)), two together ((3B)): 3. + housing_benefit_LHA_additional_bedrooms: [1, 2] + housing_benefit_LHA_allowed_bedrooms: [4, 3] - name: A looked-after claimant aged 17 gives no foster room period: 2026 @@ -1232,3 +1248,301 @@ is_child_or_young_person_for_legacy_benefits: [false, false, false] is_child_or_young_person_placed_with_family: [false, true, true] housing_benefit_has_non_dependant: false + +- name: A non-dependant's foster child raises the renter's final Housing Benefit + period: 2026 + absolute_error_margin: 0.01 + input: + people: + renter: + age: 60 + is_household_head: true + housing_benefit_reported: 1 + daughter: + age: 32 + is_male: false + employment_income: 20_000 + weekly_hours: 35 + foster_child: + age: 6 + is_looked_after_by_local_authority: true + benunits: + renter_family: + members: [renter] + claims_universal_credit: false + daughter_family: + members: [daughter, foster_child] + households: + home: + members: [renter, daughter, foster_child] + rent: 15_600 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # Size criteria: the renter, the daughter and the child placed with her + # (reg 21(3) takes out only a child placed with the claimant or + # partner; 13D(12)), 3 bedrooms, category D. + housing_benefit_LHA_allowed_bedrooms: [3, 2] + housing_benefit_LHA_category: [D, C] + # Maidstone D, £276.16 a week (the VOA April 2024 rates, frozen for 2026), + # below the £300 rent: 276.16 * 52 = 14,360.32. At two bedrooms the C + # rate, £208.27, would give 10,830.04. + LHA_cap: [14_360.32, 0] + # Non-dependant deduction: the daughter works 35 hours (16 or more, so + # in remunerative work) on £20,000, 20,000 / 52 = £384.62 a week, in the + # £365 to £485 band: £105.20 a week, 105.20 * 52 = 5,470.40. + housing_benefit_non_dep_deductions: [5_470.40, 0] + # The renter has no income, below the single person's applicable amount + # (aged 25 or over, £95.55 a week), so no taper: 14,360.32 - 5,470.40 = + # 8,889.92. At two bedrooms: 10,830.04 - 5,470.40 = 5,359.64, so the + # placed child adds 3,530.28 (67.89 a week * 52). + housing_benefit: [8_889.92, 0] + +- name: A non-dependant's child placed for adoption raises the renter's final Housing Benefit + period: 2026 + absolute_error_margin: 0.01 + input: + people: + renter: + age: 60 + is_household_head: true + housing_benefit_reported: 1 + daughter: + age: 32 + is_male: false + employment_income: 20_000 + weekly_hours: 35 + child: + age: 6 + is_placed_for_adoption: true + benunits: + renter_family: + members: [renter] + claims_universal_credit: false + daughter_family: + members: [daughter, child] + households: + home: + members: [renter, daughter, child] + rent: 15_600 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # As the foster case: reg 21(3)(c) takes out only a child placed for + # adoption with the claimant or partner, so the renter has 3 bedrooms + # and the daughter's own claim 2 (herself and the qualifying parent or + # carer room). HB: 276.16 * 52 - 105.20 * 52 = 14,360.32 - 5,470.40 = + # 8,889.92. + housing_benefit_LHA_allowed_bedrooms: [3, 2] + housing_benefit: [8_889.92, 0] + # The renter does not claim Universal Credit; "A non-dependant's child + # placed for adoption" has the Universal Credit rooms, [3, 3]. + +- name: A looked-after young person of 17 placed with a non-dependant + period: 2026 + input: + people: + renter: + age: 50 + is_household_head: true + daughter: + age: 35 + is_male: false + young_person: + age: 17 + current_education: UPPER_SECONDARY + is_looked_after_by_local_authority: true + benunits: + renter_family: + members: [renter] + daughter_family: + members: [daughter, young_person] + households: + home: + members: [renter, daughter, young_person] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_child_or_young_person_placed_with_family: [false, false, true] + # HB: a young person (reg 19) placed with the daughter, not with the + # renter (reg 21(3)(a)), so an occupier of the renter's dwelling + # (13D(12)) and a person who is not a child (13D(3)(b)): renter 1 + + # daughter 1 + young person 1 = 3. The daughter's own: herself and the + # qualifying parent or carer room, 2. + housing_benefit_LHA_allowed_bedrooms: [3, 2] + # UC: a qualifying young person no one is responsible for (reg 4(6)(a)) + # is not a non-dependant (para 9(2)(g)): renter 1 + daughter 1 = 2. The + # daughter's own: herself and the foster room, 2. + LHA_allowed_bedrooms: [2, 2] + +- name: A child placed with a lodger counts for the householder under Housing Benefit only + period: 2026 + input: + people: + householder: + age: 60 + is_household_head: true + lodger: + age: 30 + rent_paid_as_lodger: 5_000 + foster_child: + age: 5 + is_looked_after_by_local_authority: true + benunits: + householder_family: + members: [householder] + lodger_family: + members: [lodger, foster_child] + households: + home: + members: [householder, lodger, foster_child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # HB: the lodger and the child placed with the lodger occupy the + # householder's dwelling (13D(12); reg 21(3) takes out only a child + # placed with the claimant or partner): householder 1 + lodger 1 + + # child 1 = 3. The lodger's own claim: the lodger and the qualifying + # parent or carer room, 2. + housing_benefit_LHA_allowed_bedrooms: [3, 2] + # UC: the lodger is outside the householder's extended benefit unit + # (para 9(2)(d)) and so is the child (9(2)(c) for the lodger's claim, + # 9(2)(g) for the householder's): 1. The lodger's own: the lodger and + # the foster room, 2. + LHA_allowed_bedrooms: [1, 2] + +- name: A child placed with a joint tenant does not count for the other tenant + period: 2026 + input: + people: + tenant_1: + age: 40 + is_household_head: true + tenant_2: + age: 38 + foster_child: + age: 7 + is_looked_after_by_local_authority: true + benunits: + family_1: + members: [tenant_1] + family_2: + members: [tenant_2, foster_child] + liable_for_share_of_household_rent: true + households: + home: + members: [tenant_1, tenant_2, foster_child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # HB: a joint tenant outside the claimant's household is not an + # occupier (13D(12)), and private-sector size criteria rest on each + # tenant's own household (circular A21/2013 para 25): 1. The second + # tenant's own: the tenant and the qualifying parent or carer room, 2. + housing_benefit_LHA_allowed_bedrooms: [1, 2] + housing_benefit_LHA_additional_bedrooms: [0, 1] + # UC: the joint tenant is liable for the rent (para 9(2)(d)): 1 and 2. + LHA_allowed_bedrooms: [1, 2] + +- name: An adopter who is a close relative of the child has no Universal Credit foster room + period: 2026 + input: + people: + adopter: + age: 40 + is_household_head: true + child: + age: 4 + is_placed_for_adoption: true + adopter_is_foster_parent_or_close_relative: true + benunits: + family: + members: [adopter, child] + households: + home: + members: [adopter, child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: "adopter" (reg 2) takes the meaning in reg 89(3)(a), which + # excludes "a person who is a foster parent or close relative of the + # child", so the renter does not meet para 12(4)(b). The child is still + # placed for adoption, so the renter is responsible (reg 4A(1)(c)) and + # the child has a bedroom: 1 + 1 = 2, one fewer than an adopter who is + # not a close relative. + is_child_or_qualifying_young_person_for_universal_credit: [false, true] + lha_renter_meets_foster_parent_condition: false + LHA_additional_bedrooms: 0 + LHA_allowed_bedrooms: 2 + # HB: reg 21(3)(c) and the reg 2(1) "qualifying parent or carer" carry + # no adopter definition: the child does not occupy the dwelling and the + # claimant has the qualifying parent or carer room, 1 + 1 = 2. + is_housing_benefit_qualifying_parent_or_carer: true + housing_benefit_LHA_allowed_bedrooms: 2 + +- name: A close-relative adopter under 35 has no foster parent exception from the shared accommodation rate + period: 2026 + input: + people: + adopter: + age: 30 + is_household_head: true + child: + age: 4 + is_placed_for_adoption: true + adopter_is_foster_parent_or_close_relative: true + benunits: + family: + members: [adopter, child] + households: + home: + members: [adopter, child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # Para 29(9A) excepts a renter under 35 who "satisfies the foster parent + # condition (within the meaning of paragraph 12(4))"; a close relative + # is not an "adopter" (reg 89(3)(a)), so the renter does not. The + # renter is still responsible for the child (reg 4A(1)(c)), so not a + # specified renter. UC: 1 + 1 = 2. HB: 1 + the qualifying parent or + # carer room = 2. + lha_renter_meets_foster_parent_condition: false + is_lha_shared_accommodation_rate_specified_renter: false + LHA_allowed_bedrooms: 2 + housing_benefit_LHA_allowed_bedrooms: 2 + +- name: A close-relative adopter between foster placements keeps the foster room + period: 2026 + input: + people: + adopter: + age: 30 + is_household_head: true + is_approved_foster_parent_without_placement: true + child: + age: 4 + is_placed_for_adoption: true + adopter_is_foster_parent_or_close_relative: true + benunits: + family: + members: [adopter, child] + households: + home: + members: [adopter, child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # Para 12(5): an approved foster parent within 12 months of a placement + # or of approval is a foster parent for para 12(4)(a), whatever the + # adoption placement. UC: renter, child (reg 4A(1)(c)) and the foster + # room, 3. HB: 1 + the qualifying parent or carer room (reg 2(1)(a) or + # (b)) = 2. + lha_renter_meets_foster_parent_condition: true + LHA_allowed_bedrooms: 3 + housing_benefit_LHA_allowed_bedrooms: 2 diff --git a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py index 3eaf829a1e..16c1e4e19e 100644 --- a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py +++ b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py @@ -24,27 +24,30 @@ 13D(3A)(b)) bedroom if it did not already have it; - if they join a non-dependant's family, the claim that counts the household's non-dependants (uc_non_dependants_counted) gains one - Universal Credit bedroom unless no one is responsible for them, and - every family liable for the rent gains one Housing Benefit bedroom - unless they are placed with the non-dependant's family; - - if they join a boarder's or lodger's family, every family liable for - the rent gains one Housing Benefit bedroom unless they are placed with - it; + Universal Credit bedroom unless no one is responsible for them; + - if they join a non-dependant's, boarder's or lodger's family, every + family liable for the rent gains one Housing Benefit bedroom, even if + they are placed with that family: reg 21(3) takes out only a child or + young person placed with the claimant or partner, so they occupy the + claimant's dwelling (reg 13D(12)); - no other family's bedrooms change. 3. Children: adding a child under 16 who is neither fostered nor placed for adoption to any family never lowers any family's bedrooms under either scheme and raises each by at most one. 4. Placed children: adding a child under 16 placed with a family as a - foster child gives that family one more bedroom under each scheme if it - did not already meet the foster parent condition (UC) or have a - qualifying parent or carer (HB), and none otherwise. No other family's - bedrooms change under either scheme: a child placed with another family - is not in the counting claim's extended benefit unit (UC para 9(2)(g)) - and, following DWP, not an occupier (HB; LHA Guidance Manual para - 2.033). A child placed for adoption gives the adopter's family the same - room under both schemes and, under Universal Credit, counts as a child - (reg 4A(1)(c)): that family and the claim counting a non-dependant - adopter's household each gain 0 or 1 more. + foster child or for adoption gives that family one more bedroom under + each scheme if it did not already meet the foster parent condition (UC) + or have a qualifying parent or carer (HB), and none otherwise. If the + family is a non-dependant's, boarder's or lodger's, each family liable + for the rent gains 0 or 1 Housing Benefit bedrooms: the child occupies + its dwelling (reg 21(3) names only a placement with the claimant or + partner; reg 13D(12)) and shares a room with the other children if it + can. A foster child changes no other family's Universal Credit bedrooms: + no one is responsible for them (reg 4(6)(a); para 9(2)(g)). A child + placed for adoption counts as a child under Universal Credit (reg + 4A(1)(c)): the adopter's family and the claim counting a non-dependant + adopter's household each gain 0 or 1 more. No joint tenant's bedrooms + change, and no other family's. 5. Reference: every family's bedrooms, and its additional bedrooms, equal a count of the size criteria made by the test itself. The added person's status comes from the test's own predicates on the inputs (UC reg 5 @@ -64,19 +67,19 @@ aged 16 or over in a non-dependant's family, except one no one is responsible for; for a family liable for the rent (HB), one for the couple or single adult of each non-dependant's, boarder's or lodger's - family and one for each other member aged 16 or over, except one placed - with that family; + family and one for each other member aged 16 or over, including one + placed with that family; - the fewest rooms that hold the counted children under 16 two to a room, where only children of the same sex or two children under 10 may share, found by brute force. UC counts the family's own children other than foster children and, for the counting claim, the non-dependants' children other than foster children. HB counts the family's children - and, for a family liable for the rent, the children of each - non-dependant's, boarder's or lodger's family, other than foster - children and children placed for adoption; - - one additional bedroom if anyone so counted, or a foster child of the - family, has overnight care, and one if the family fosters, has a child - placed for adoption, or has an approved foster parent between + other than foster children and children placed for adoption and, for + a family liable for the rent, every child of each non-dependant's, + boarder's or lodger's family, placed or not; + - one additional bedroom if anyone so counted, or a child placed with + the family, has overnight care, and one if the family fosters, has a + child placed for adoption, or has an approved foster parent between placements. 6. In-unit non-dependants: every member of a benefit unit aged 16 or over who is neither the claimant nor partner nor a Universal Credit @@ -86,6 +89,14 @@ without them, and making them qualifying young persons the renter is responsible for (para 10(1)(b)) leaves every family's Universal Credit bedrooms unchanged. +7. Close-relative adopters: marking every child's adopter as a foster + parent or close relative of the child (UC reg 89(3)(a)) takes away the + foster parent condition exactly where an adoption placement was the + family's only route to it (para 12(4)(b)); it lowers that family's + Universal Credit bedrooms by one and changes nothing else: Universal + Credit responsibility (reg 4A(1)(c)), the Housing Benefit placement test + (reg 21(3)), the qualifying parent or carer and every family's Housing + Benefit bedrooms stay the same. """ import numpy as np @@ -434,7 +445,7 @@ def test_exact_effect_of_adding_a_person_aged_16_to_19(case): same_household = np.array([h == target[0] for h, _ in keys]) if fam["role"] == "non_dependant" and not added["unclaimed"]: expected_uc[same_household & counted] += 1 - if fam["role"] in OCCUPIER_ROLES and not added["placed"]: + if fam["role"] in OCCUPIER_ROLES: expected_hb[same_household & liable] += 1 assert np.array_equal(uc_after - uc_before, expected_uc) assert np.array_equal(hb_after - hb_before, expected_hb) @@ -468,7 +479,14 @@ def test_a_placed_child_adds_the_carers_bedroom(case): new_carer = int(not fosters(fam)) expected = np.zeros(len(keys)) expected[t] = new_carer - assert np.array_equal(hb_after - hb_before, expected) + same_household = np.array([h == target[0] for h, _ in keys]) + # Under Housing Benefit the child occupies the dwelling of each family + # liable for the rent if placed with a non-dependant, boarder or lodger. + hb_child_room = np.zeros(len(keys), dtype=bool) + if fam["role"] in OCCUPIER_ROLES: + hb_child_room = same_household & liable + hb_extra = hb_after - hb_before - expected + assert np.all((hb_extra == 0) | ((hb_extra == 1) & hb_child_room)) uc_change = uc_after - uc_before if added_child[2] == "foster": assert np.array_equal(uc_change, expected) @@ -479,7 +497,6 @@ def test_a_placed_child_adds_the_carers_bedroom(case): child_room = np.zeros(len(keys), dtype=bool) child_room[t] = True if fam["role"] == "non_dependant": - same_household = np.array([h == target[0] for h, _ in keys]) child_room |= same_household & counted extra = uc_change - expected assert np.all((extra == 0) | ((extra == 1) & child_room)) @@ -525,9 +542,7 @@ def test_bedrooms_match_a_reference_count_of_the_size_criteria(case): continue joins = target == (h, g) if counted[b] and other["role"] == "non_dependant": - uc_rooms += len(other["adults"]) + int( - joins and not added["unclaimed"] - ) + uc_rooms += len(other["adults"]) + int(joins and not added["unclaimed"]) uc_children += [c for c in other["children"] if c[2] != "foster"] uc_overnight |= any(o for _, o in other["adults"]) or any( c[3] for c in other["children"] if c[2] != "foster" @@ -536,12 +551,14 @@ def test_bedrooms_match_a_reference_count_of_the_size_criteria(case): if liable[b] and other["role"] in OCCUPIER_ROLES: # HB reg 13D(3)(a), (b): the occupier family's couple or # single adult has one bedroom, and the added person one. - hb_rooms += 1 + int(joins and not added["placed"]) - hb_children += [c for c in other["children"] if c[2] == "own"] + # Reg 21(3) takes out only a child placed with the claimant + # or partner, so every child of the family counts. + hb_rooms += 1 + int(joins) + hb_children += other["children"] hb_overnight |= any(o for _, o in other["adults"]) or any( - c[3] for c in other["children"] if c[2] == "own" + c[3] for c in other["children"] ) - hb_overnight |= joins and added["overnight"] and not added["placed"] + hb_overnight |= joins and added["overnight"] expected_uc_additional = int(uc_overnight) + int(uc_foster) expected_hb_additional = int(hb_overnight) + int(hb_carer) assert uc_additional[b] == expected_uc_additional @@ -624,3 +641,61 @@ def test_every_in_unit_non_dependant_has_a_bedroom(case): sim_as_dependants, uc_as_dependants, _ = bedrooms(as_dependants) assert not in_unit_non_dependants(sim_as_dependants).any() assert np.array_equal(uc, uc_as_dependants) + + +def as_close_relative_adopters(situation): + return { + **situation, + "people": { + name: {**attributes, "adopter_is_foster_parent_or_close_relative": True} + for name, attributes in situation["people"].items() + }, + } + + +@PROPERTY_SETTINGS +@given(scenario()) +def test_a_close_relative_adopter_loses_only_the_foster_room(case): + population, target, person = case + situation, keys = build(population, (target, person)) + added = flags(person) + outputs = [] + for variant in (situation, as_close_relative_adopters(situation)): + sim, uc, hb = bedrooms(variant) + outputs.append( + dict( + uc=uc, + hb=hb, + foster=np.asarray( + sim.calculate("lha_renter_meets_foster_parent_condition", YEAR) + ), + **{ + name: np.asarray(sim.calculate(name, YEAR)) + for name in ( + "is_child_or_qualifying_young_person_for_universal_credit", + "is_child_or_young_person_placed_with_family", + "is_housing_benefit_qualifying_parent_or_carer", + "housing_benefit_LHA_additional_bedrooms", + ) + }, + ) + ) + before, after = outputs + # 7. The foster parent condition without the adoption route: a foster + # child, an approved foster parent between placements, or the added + # person placed as a foster child. + expected_foster = np.array( + [ + population[h][f]["between_placements"] + or any(c[2] == "foster" for c in population[h][f]["children"]) + or ((h, f) == target and added["fostered"]) + for h, f in keys + ] + ) + assert np.array_equal(after["foster"], expected_foster) + lost = before["foster"] & ~after["foster"] + assert np.array_equal(before["uc"] - after["uc"], lost.astype(float)) + assert np.array_equal(before["hb"], after["hb"]) + for name in before: + if name not in ("uc", "hb", "foster"): + assert np.array_equal(before[name], after[name]) diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py index 06fa051ce5..6d0b7bce5e 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py @@ -13,8 +13,8 @@ class housing_benefit_LHA_additional_bedrooms(Variable): "is_housing_benefit_qualifying_parent_or_carer): two if both. " "Occupiers outside the family count for a family liable for the " "rent: non-dependants, boarders and lodgers and their children, " - "but not a sharer of the rent or a child placed with them as a " - "foster child or for adoption. A child placed with the " + "including a child placed with one of them as a foster child or for " + "adoption, but not a sharer of the rent. A child placed with the " "claimant or partner counts as one for whom they are a qualifying " "parent or carer. Before 1 April 2017 only the claimant and partner " "counted for overnight care. The definitions require a bedroom in " @@ -39,8 +39,9 @@ def formula(benunit, period, parameters): # size criteria (13D(12); see housing_benefit_LHA_allowed_bedrooms): # for a claim by a family liable for the rent, everyone in the # families not liable for it (non-dependants, boarders and lodgers - # and their children), except, following DWP, a child placed with - # one of them as a foster child or for adoption. A joint tenant outside + # and their children), including a child placed with one of them as + # a foster child or for adoption, since reg 21(3) takes out only a + # child placed with the claimant or partner. A joint tenant outside # the claimant's household is not counted: A21/2013 para 25 bases # private-sector joint tenants' size criteria on each tenant's own # household. A boarder's or lodger's own claim counts only their own @@ -48,8 +49,7 @@ def formula(benunit, period, parameters): liable_family = person.benunit.any( person("is_liable_for_household_rent", period) ) - placed = person("is_child_or_young_person_placed_with_family", period) - other = overnight_care & ~liable_family & ~placed + other = overnight_care & ~liable_family is_liable_claim = benunit.any(person("is_liable_for_household_rent", period)) others = where(is_liable_claim, benunit.max(person.household.sum(other)), 0) # Reg 13D(3A)(a)(i)-(iv): the claimant, partner, other members of the diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py index 59e6d50911..baa3b8e976 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py @@ -23,8 +23,9 @@ class housing_benefit_LHA_allowed_bedrooms(Variable): "boarder's or lodger's own claim counts only their own family. A " "child or young person placed with the claimant or partner as a " "foster child or for adoption does not occupy the claimant's " - "dwelling; the model treats a child placed with anyone in the " - "household the same way. " + "dwelling, so the claimant's own claim leaves them out; a child " + "placed with a non-dependant, boarder or lodger occupies the " + "claimant's dwelling and counts in the claimant's size criteria. " "Couples and children who cannot share a bedroom are not modelled." ) definition_period = YEAR @@ -51,16 +52,17 @@ def formula(benunit, period, parameters): is_liable_claim = benunit.any(person("is_liable_for_household_rent", period)) # HB Regs 2006 reg 21(3): a child or young person placed with the # claimant or partner as a foster child or for adoption does not - # occupy the claimant's dwelling. Reg 21(3) names only placements - # with the claimant or partner; the model follows DWP in treating a - # child placed with anyone in the household as no occupier (LHA - # Guidance Manual para 2.033; HB circular A21/2013 para 22), as - # Universal Credit does (UC Sch 4 para 9(2)(g)). + # occupy the claimant's dwelling. Every limb names a placement "with + # the claimant or his partner", so a child placed with a + # non-dependant, boarder or lodger is one of the persons who occupy + # the claimant's dwelling as their home (13D(12)) and counts in the + # claimant's size criteria, while the carer's own claim leaves them + # out. placed = person("is_child_or_young_person_placed_with_family", period) def occupiers_total(values): own = benunit.sum(values & ~placed) - others = benunit.max(person.household.sum(values & other & ~placed)) + others = benunit.max(person.household.sum(values & other)) return own + where(is_liable_claim, others, 0) # 13D(3)(zb), (a): one bedroom for each couple or single claimant. A diff --git a/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py b/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py index 7c4b1e31eb..b352144bfe 100644 --- a/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py +++ b/policyengine_uk/variables/gov/dwp/lha_renter_meets_foster_parent_condition.py @@ -7,8 +7,11 @@ class lha_renter_meets_foster_parent_condition(Variable): label = "Universal Credit renter meets the foster parent condition" documentation = ( "Whether the renter or a joint renter is a foster parent or an " - "adopter with whom a child has been placed for adoption. A foster " - "parent has a child under 18 looked after by a local authority " + "adopter with whom a child has been placed for adoption. An adopter " + "(UC Regs 2013 reg 89(3)(a)) is not a foster parent or close " + "relative of the child (see " + "adopter_is_foster_parent_or_close_relative). A foster parent has " + "a child under 18 looked after by a local authority " "placed with them (a member of the benefit unit flagged " "is_looked_after_by_local_authority), or is an approved foster " "parent whose last placement ended, or who was approved, within 12 " @@ -21,20 +24,23 @@ class lha_renter_meets_foster_parent_condition(Variable): "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/29", "https://www.legislation.gov.uk/uksi/2013/376/regulation/2", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/89", ) def formula(benunit, period, parameters): person = benunit.members claimant_or_partner = person("is_claimant_or_partner", period) # UC Regs 2013 reg 2, "foster parent": a person with whom a child - # (under 18) is placed under the care planning regulations; reg 89(3) - # and Sch 4 para 12(4)(b): an adopter with whom a child is placed for - # adoption. + # (under 18) is placed under the care planning regulations. Sch 4 + # para 12(4)(b): an adopter with whom a child is placed for adoption, + # where "adopter" (reg 2, reg 89(3)(a)) excludes a foster parent or + # close relative of the child. age_limit = parameters( period ).household.demographic.looked_after_child_age_limit placed_for_adoption = ( person("is_placed_for_adoption", period) + & ~person("adopter_is_foster_parent_or_close_relative", period) & (person("age", period) < age_limit) & ~claimant_or_partner ) diff --git a/policyengine_uk/variables/household/demographic/adopter_is_foster_parent_or_close_relative.py b/policyengine_uk/variables/household/demographic/adopter_is_foster_parent_or_close_relative.py new file mode 100644 index 0000000000..e92263584b --- /dev/null +++ b/policyengine_uk/variables/household/demographic/adopter_is_foster_parent_or_close_relative.py @@ -0,0 +1,32 @@ +from policyengine_uk.model_api import * + + +class adopter_is_foster_parent_or_close_relative(Variable): + value_type = bool + entity = Person + label = "adopter is a foster parent or close relative of the child" + documentation = ( + "Whether the person with whom this child is placed for adoption is " + "a foster parent or close relative of the child. Universal Credit's " + "'adopter' (UC Regs 2013 reg 2, taking the meaning in reg 89(3)(a)) " + "excludes such a person, so they do not meet the foster parent " + "condition through the adoption placement (Sch 4 para 12(4)(b)), " + "nor the shared accommodation rate exception that follows it (para " + "29(9A)). A close relative (reg 2) is a parent, parent-in-law, son, " + "son-in-law, daughter, daughter-in-law, step-parent, step-son, " + "step-daughter, brother or sister, or the other member of a couple " + "with any of them. The adopter remains responsible for the child " + "(reg 4A(1)(c)), and Housing Benefit's placement rule (HB Regs 2006 " + "reg 21(3)) and qualifying parent or carer carry no such words, so " + "neither changes. A foster parent keeps the foster parent condition " + "as a foster parent (para 12(4)(a), (5)): flag their placements or " + "set is_approved_foster_parent_without_placement. Set this on the " + "child; it has no effect unless is_placed_for_adoption is true." + ) + definition_period = YEAR + default_value = False + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/regulation/2", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/89", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", + ) diff --git a/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py b/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py index cf236868a6..73e9361808 100644 --- a/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py +++ b/policyengine_uk/variables/household/demographic/is_placed_for_adoption.py @@ -15,9 +15,10 @@ class is_placed_for_adoption(Variable): "flag that child as looked after instead. Universal Credit treats the " "adopter as responsible for the child (UC Regs 2013 reg 4A(1)(c)), " "and the adopter satisfies the foster parent condition for an " - "additional bedroom (Sch 4 para 12(4)(b)). That condition's " - "definition of adopter (reg 89(3)(a)) excludes a foster parent or " - "close relative of the child, which the model does not check. " + "additional bedroom (Sch 4 para 12(4)(b)) and the shared " + "accommodation rate exception (para 29(9A)), unless the adopter is " + "a foster parent or close relative of the child (reg 89(3)(a); see " + "adopter_is_foster_parent_or_close_relative). " "Pension Credit also treats the adopter as responsible (SPC Regs 2002 " "Sch IIA para 4(3)(c)). Under Housing Benefit, Income Support, " "income-based Jobseeker's Allowance, income-related Employment and " @@ -30,9 +31,9 @@ class is_placed_for_adoption(Variable): "4(2)(d)). Child Tax Credit excludes them only while the local " "authority pays for their keep (CTC Regs 2002 reg 3, rule 4 case B), " "which is not modelled. Scottish working-age council tax reduction " - "excludes only foster placements (SSI 2021/249 reg 7(8)), so the " - "child should count there; the model's shared legacy household test " - "leaves them out." + "excludes only foster placements (SSI 2021/249 reg 7(8)), so from 1 " + "April 2022 the child counts in the adopter's family there (see " + "is_child_or_young_person_for_council_tax_reduction)." ) definition_period = YEAR default_value = False @@ -45,4 +46,5 @@ class is_placed_for_adoption(Variable): "https://www.legislation.gov.uk/uksi/2002/1792/schedule/IIA", "https://www.legislation.gov.uk/uksi/2002/2007/regulation/3", "https://www.legislation.gov.uk/ssi/2021/249/regulation/7", + "https://www.legislation.gov.uk/uksi/2013/376/regulation/89", ) From 9931ec1f2e9d8585654ce59333da45bbba63bb69 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 00:14:50 +0000 Subject: [PATCH 12/14] Format LHA_allowed_bedrooms and trim income_support.yaml Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LwQq6FYBy8zKExK1r9z9fW --- .../baseline/finance/benefit/family/income_support.yaml | 1 - policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml index 60d02430ee..97a69ea985 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml @@ -144,4 +144,3 @@ output: # Para 2A needs "a child", a person under 16 (SSCBA 1992 s.137(1)). income_support_eligible: false - diff --git a/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py b/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py index 378681f2c0..7d60763c28 100644 --- a/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py @@ -145,9 +145,9 @@ def formula(benunit, period, parameters): # (reg 4(2)) and so a non-dependant too; para 9(2)(g) excludes a # child or qualifying young person no one is responsible for, such as # a non-dependant's foster child. - non_dependant = person( - "is_non_dependant_of_household_head", period - ) & ~no_one_responsible + non_dependant = ( + person("is_non_dependant_of_household_head", period) & ~no_one_responsible + ) counted = benunit("uc_non_dependants_counted", period) # Para 10(1)(c): a non-dependant from another family who is not a # child. From a04bc25e5c64346e23e183b0a5e2ae3d6398f98a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 00:43:46 +0000 Subject: [PATCH 13/14] Update two older properties for the merged child rules The CTR scheme-selection reference now stops looked-after status at 18, as is_looked_after_child does. The sharers' no-op bedroom check covers households with no child outside the head's family, since non-dependants' children now share the head's children's rooms. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LwQq6FYBy8zKExK1r9z9fW --- ...council_tax_reduction_young_person_properties.py | 5 ++++- ...nt_of_sharers_boarders_and_lodgers_properties.py | 13 ++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/policyengine_uk/tests/test_council_tax_reduction_young_person_properties.py b/policyengine_uk/tests/test_council_tax_reduction_young_person_properties.py index ea766b4a46..7613a2ef3a 100644 --- a/policyengine_uk/tests/test_council_tax_reduction_young_person_properties.py +++ b/policyengine_uk/tests/test_council_tax_reduction_young_person_properties.py @@ -156,7 +156,10 @@ def assert_scheme_selection(units, year): sim = simulate(units, year) people = [p for unit in units for p in unit["members"]] claimant_or_partner = np.array([p["is_claimant_or_partner"] for p in people]) - looked_after = np.array([p["is_looked_after_by_local_authority"] for p in people]) + # Only a person under 18 can be looked after (is_looked_after_child). + looked_after = np.array( + [p["is_looked_after_by_local_authority"] and p["age"] < 18 for p in people] + ) under_16 = np.array([p["age"] < 16 for p in people]) flag = sim.calculate("is_child_or_young_person_for_council_tax_reduction", year) legacy = sim.calculate("is_child_or_young_person_for_legacy_benefits", year) diff --git a/policyengine_uk/tests/test_rent_of_sharers_boarders_and_lodgers_properties.py b/policyengine_uk/tests/test_rent_of_sharers_boarders_and_lodgers_properties.py index 0b68d8b3b1..37374fc9c4 100644 --- a/policyengine_uk/tests/test_rent_of_sharers_boarders_and_lodgers_properties.py +++ b/policyengine_uk/tests/test_rent_of_sharers_boarders_and_lodgers_properties.py @@ -404,7 +404,10 @@ def test_no_op_without_sharers_boarders_or_lodgers(population): head_claims = per_household(sim, claims_uc) > 0 assert np.allclose(head_deductions[head_claims], outside[head_claims], atol=0.01) # Bedrooms: what the head's family would have alone, plus one for each - # person aged 16 or over outside it. + # person aged 16 or over outside it. Non-dependants' children under 16 + # share rooms with the head's own children (UC Sch 4 para 10(1)(d)-(f)); + # test_lha_bedroom_young_people_properties.py counts those, so this check + # covers households with no child outside the head's family. alone = [dict(house, others=[]) for house in population] alone_situation, _ = build(alone) alone_sim = Simulation(situation=alone_situation) @@ -413,10 +416,14 @@ def test_no_op_without_sharers_boarders_or_lodgers(population): adults_outside = sim.map_result( aged_16_or_over & ~person_head_family, "person", "household" ) + children_outside = sim.map_result( + ~aged_16_or_over & ~person_head_family, "person", "household" + ) + checked = head_claims & (children_outside == 0) head_bedrooms = per_household(sim, calc(sim, "LHA_allowed_bedrooms") * head) assert np.allclose( - head_bedrooms[head_claims], - (bedrooms_alone + adults_outside)[head_claims], + head_bedrooms[checked], + (bedrooms_alone + adults_outside)[checked], ) # Council Tax Reduction: one claim, the household head's family's, on its # own scheme; every family's reduction is simulated or reported as that From 983488065231c5eaa10850196c9df25cfb17d2e2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 01:50:49 +0000 Subject: [PATCH 14/14] Pin a non-dependant's placed child's overnight care in the reference count Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LwQq6FYBy8zKExK1r9z9fW --- ...est_lha_bedroom_young_people_properties.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py index 16c1e4e19e..7d8e61425d 100644 --- a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py +++ b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py @@ -504,6 +504,25 @@ def test_a_placed_child_adds_the_carers_bedroom(case): @PROPERTY_SETTINGS @given(scenario()) +# A non-dependant's foster child who requires overnight care: the renter's +# Housing Benefit overnight room (reg 13D(3A)(a)(iii); reg 21(3)). +@example( + ( + [ + [ + lone("head", 60), + dict( + role="non_dependant", + adults=[(32, False)], + children=[(6, False, "foster", True)], + between_placements=False, + ), + ] + ], + (0, 0), + dict(LOOKED_AFTER_QYP, is_looked_after_by_local_authority=False), + ) +) def test_bedrooms_match_a_reference_count_of_the_size_criteria(case): population, target, person = case situation, keys = build(population, (target, person))