Skip to content

Give Universal Credit and Housing Benefit their own benefit cap and LHA shared-accommodation rules - #2089

Merged
MaxGhenis merged 30 commits into
mainfrom
uc-hb-split-shared-cap-lha-rules
Oct 17, 2026
Merged

MaxGhenis merged 30 commits into
mainfrom
uc-hb-split-shared-cap-lha-rules

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Base is main. The head is e82c4acac, which merges main at e7610ac52 (#2242). #2084 (UC reg 3(3) single claims), which this PR was stacked on, merged as 4ed5b3ce4. Since Vahid Ahmadi's review at b7e0b2373:

Main has since moved to 14999dc03 (#2436, dated-reform backdating; #2440, pension relief in adjusted net income). Neither touches a cap, LHA, UC or HB file. #2440 edits test_trading_loss_properties.py, which this PR also edits (one renamed variable); the hunks are separate, and git merges them without conflict.

What was wrong

On main, one set of variables serves both Universal Credit and Housing Benefit, although the two schemes' rules differ:

  • Cap rate. is_benefit_cap_single_claimant_rate / benefit_cap use one "single claimant" test, with the family rate wherever either scheme's child or young-person test is met. UC Regs 2013 reg 80A(2) and HB Regs 2006 reg 75CA(2) each define it their own way.
  • Cap exceptions. is_benefit_cap_exempt_health_disability, _other and _earnings are the union of both schemes' grounds:
    • UC's earnings exception and the LCWRA and carer elements also lift the HB cap.
    • HB's working tax credit exception (reg 75E) also lifts the UC cap.
    • Each scheme's child and young-person tests count for the other.
    • Armed forces independence payment received by an HB young person lifts the UC cap too, although UC reg 2 makes it "attendance allowance", which reg 83(1)(c) reads for a claimant only.
    • The HB pension-age carve-out (housing_benefit_pension_age_regulations_apply, HB reg 5) also serves as the UC age test. The UC age exception is SI 2014/1230 reg 60C, which turns on the claimants alone.
  • Cap reduction. benefit_cap_reduction serves both schemes. It applies neither UC reg 81 (the childcare costs element comes off) nor HB reg 75D(2) (the 50p weekly minimum stays).
  • A family paid both. universal_credit and housing_benefit both subtract the one benefit_cap_reduction, whose total includes HB. A family given both awards has its HB counted in the UC total, against UC reg 80(2A), and has the reduction taken off both awards. The model's HB eligibility rules out a family that claims UC, so only supplied awards reach this today; Abolish working-age Housing Benefit from 1 July 2026 (NI 1 October 2026) #1910 would compute both.
  • UC shared accommodation rate. Since Give sharers, boarders and lodgers their own rent, tenure and LHA category #2006, HB has its own LHA category. UC's test, is_lha_shared_accommodation_rate_specified_renter, still applies a reg 3(3) single claim only when the family is not staying on legacy benefits, and leaves AFIP out of the para 29(5) list.

#2084 gated the reg 3(3) consequences in these shared variables on the family staying on legacy benefits, as a stopgap. That was wrong for any family whose legacy status did not match the scheme being calculated.

The law

legislation.gov.uk data.xml, current text, fetched 2026-10-02 (SI 2014/1230 regs 60A and 60C fetched 2026-10-09).

Who is a single claimant

  • UC reg 3(3): "A person who is a member of a couple may make a claim as a single person if the other member of the couple— (a) does not meet the basic condition in section 4(1)(a) (at least 18 years old) and is not a person in respect of whom the minimum age specified in regulation 8 applies; ... (e) is a person to whom section 115 of the Immigration and Asylum Act 1999 (exclusion from benefits) applies".
  • UC reg 78(2): "In this Part 'couple' means— (a) joint claimants; or (b) a single claimant who is a member of a couple within the meaning of section 39 of the Act and the other member of that couple".
  • UC reg 80A(2): "(a) £16,967 for a single claimant resident in Greater London who is not responsible for a child or qualifying young person; (b) £25,323 for— (i) joint claimants where either joint claimant is resident in Greater London; (ii) a single claimant resident in Greater London who is responsible for a child or qualifying young person; (c) £14,753 for a single claimant not resident in Greater London who is not responsible for a child or qualifying young person; (d) £22,020 for— (i) joint claimants not resident in Greater London; (ii) a single claimant not resident in Greater London who is responsible for a child or qualifying young person."
  • HB reg 75CA(2): "(a) £16,967 in the case of single claimants resident in Greater London; (b) £25,323 in the case of all other claimants resident in Greater London; (c) £14,753 in the case of single claimants not resident in Greater London; (d) £22,020 in the case of all other claimants not resident in Greater London."
  • HB reg 2(1): "'single claimant' means a claimant who neither has a partner nor is a lone parent"; "'lone parent' means a person who has no partner and who is responsible for and a member of the same household as a child or young person".

The welfare benefits counted

  • UC reg 80(2): "Where the welfare benefit is universal credit, the amount to be used is the amount to which the claimant is entitled before any reduction under regulation 81 ...". Reg 80(2A): "Where the welfare benefit is housing benefit under section 130 of the Contributions and Benefits Act, the amount to be used is nil."
  • HB reg 75C(2): "Where the welfare benefit is housing benefit, the relevant authority— (a) where the dwelling is accommodation specified in regulation 75H (specified accommodation), must use the amount of nil; and (b) in any other case, must not take account of any reduction under regulation 75D and must use the amount to which the claimant would be entitled by virtue of section 130 of the Act."

Exceptions

  • UC reg 79(1): "Unless regulation 82 or 83 applies, the benefit cap applies where the welfare benefits to which a single person or couple is entitled during the reference period exceed the relevant amount".
  • UC reg 82(1)(a): "the claimant's earned income or, if the claimant is a member of a couple, the couple's combined earned income, is equal to or exceeds [16 hours at the national minimum wage, monthly]".
  • UC reg 83(1): "(a) the LCWRA element is included in the award of universal credit or the claimant is receiving an employment and support allowance that includes the support component; (b) a claimant is receiving industrial injuries benefit; (c) a claimant is receiving attendance allowance; ... (g) a claimant, or a qualifying young person for whom a claimant is responsible, is receiving personal independence payment; ... (i) a claimant, or a qualifying young person for whom a claimant is responsible, is entitled to carer's allowance; ... (j) the carer element is included in the award of universal credit".
  • SI 2014/1230 reg 60C: "Where a qualifying claim is made by a single claimant who has, or joint claimants both of whom have, reached the qualifying age for state pension credit, regulation 79 (circumstances where the benefit cap applies) of the Universal Credit Regulations is not to apply for the purpose of calculating the amount of an award of universal credit (including the indicative UC amount)." Reg 60A(1) waives the condition in WRA 2012 s. 4(1)(b) (under the qualifying age) only for a qualifying claim by a single claimant, or joint claimants, who when the migration notice is issued have reached that age, are entitled to working tax credit and are not entitled to state pension credit.
  • HB reg 75A: "Unless regulation 75E or 75F applies, a benefit cap applies ...".
  • HB reg 75E(2): "This paragraph applies where the claimant is, or the claimant and the claimant's partner are jointly, entitled to working tax credit."
  • HB reg 75F(1): "(a) the claimant or the claimant's partner is receiving an employment and support allowance ... which includes a support component; (b) the claimant or the claimant's partner is receiving an industrial injuries benefit ...; (c) ... attendance allowance; ... (ea) the claimant, the claimant's partner or a young person for whom the claimant or the claimant's partner is responsible, is receiving a personal independence payment ...; ... (g) the claimant is receiving universal credit. (h) ... is entitled to a carer's allowance".
  • HB reg 2(3B): "For the purposes of these Regulations, a person ("P") is on universal credit on any day in respect of which P is entitled to universal credit (whether it is in payment or not)."
  • HB reg 5: "(1) These Regulations apply to a person who— (a) has not attained the qualifying age for state pension credit; or (b) has attained the qualifying age for state pension credit if he, or if he has a partner, his partner, is a person on universal credit, on income support, on an income-based jobseeker's allowance or on an income-related employment and support allowance. ... (3) Except as provided in paragraph (1), these Regulations shall not apply in relation to any person if he, or if he has a partner, his partner, has attained the qualifying age for state pension credit." The benefit cap is Part 8A of these Regulations. The pension-age regulations (SI 2006/214) have no cap.

Reductions

  • UC reg 81: "(1) ... the amount of the award for that period is to be reduced by— (a) the excess; minus (b) any amount included in the award for the childcare costs element in relation to that assessment period. (2) But no reduction is to be applied where the amount of the childcare costs element is greater than the excess."
  • HB reg 75D(2): "Where the reduction would reduce the claimant's housing benefit to less than the minimum amount of housing benefit provided for in regulation 75 (minimum housing benefit), the relevant authority must reduce the claimant's housing benefit by such amount as will leave the claimant entitled to that minimum amount." Reg 75: "... it shall not be payable where the amount to which a person would otherwise be entitled is less than 50 pence per benefit week."

Shared accommodation

  • UC Sch 4 para 28: "(2) The first condition is that the renter is a single person (or a member of a couple claiming as a single person) who— (a) is under 35 years old; and (b) is not an excepted person under paragraph 29. (3) The second condition is that the renter is not responsible for any children or qualifying young persons. (4) The third condition is that no person is a non-dependant in relation to the renter."
  • UC Sch 4 para 29(5): "E is under 35 years old and is in receipt of— (a) the care component of disability living allowance at the middle or highest rate; ... (b) attendance allowance; ... (c) the daily living component of personal independence payment". UC reg 2: "'attendance allowance' means— ... armed forces independence payment under the Armed Forces and Reserve Forces (Compensation Scheme) Order 2011".

What this PR does

Universal Credit Housing Benefit
Cap uc_benefit_cap (reg 80A) housing_benefit_benefit_cap (reg 75CA)
Single-claimant rate is_uc_benefit_cap_single_claimant_rate: not joint claimants (a reg 3(3) single claim is single), not responsible for a child or QYP under UC's own test is_housing_benefit_benefit_cap_single_claimant_rate: is_single_person (reg 2(1)); a reg 3(3) couple is a couple
Exceptions is_uc_benefit_cap_exempt = _earnings (reg 82: #2399's measure, the assessed claimants' uc_individual_earned_income_before_mif) or _specified_benefit (reg 83: the LCWRA or carer element in the award; a claimant's ESA with the support component; a claimant's benefits, AFIP included as attendance allowance (reg 2); UC's own child and QYP tests; the reg 3(3) other member is not a claimant) or _qualifying_age (SI 2014/1230 reg 60C: the single claimant, or both joint claimants, at the qualifying age for State Pension Credit). No WTC exception. is_housing_benefit_benefit_cap_exempt = _working_tax_credit (75E) or _specified_benefit (75F: the claimant's or partner's ESA with the support component and other benefits; AFIP, for a young person too; HB's own child and young-person tests; 75F(1)(g), a family with HB and UC before the cap) or housing_benefit_pension_age_regulations_apply (reg 5). No earnings, LCWRA or carer-element exception.
Reduction uc_benefit_cap_reduction: the excess of the welfare benefits, with HB counted as nil (reg 80(2A)), less the childcare costs element, on an award only (regs 78(1), 81) housing_benefit_benefit_cap_reduction: the excess, leaving 50p a week (regs 75, 75D)
Award reads universal_credit, uc_deductions, uc_has_deduction housing_benefit
LHA category LHA_category with is_lha_shared_accommodation_rate_specified_renter (Sch 4 paras 27-29): reg 3(3) applied whatever legacy benefits the family reports; AFIP added to the para 29(5) list (reg 2) main's housing_benefit_LHA_category (#2006), unchanged

Other changes:

LHA and #2006/#2030. #2006 merged with the HB LHA names this PR had converged on, so main's housing_benefit_LHA_category, housing_benefit_LHA_rate, is_housing_benefit_young_individual, housing_benefit_severe_disability_premium_applies, housing_benefit_LHA_allowed_bedrooms and LHA_cap stand unchanged. This PR changes only UC's shared-rate test. It keeps the household-composition proxy (lha_renter_has_non_dependant), and the test still reads responsibility under either scheme: a dependant under one scheme only is the other scheme's non-dependant, which the proxy does not identify. #2030 (open) replaces both with each scheme's own non-dependant test.

Not modelled (as before): the UC grace period (reg 82(1)(b)), the HB 39-week grace period (75E(3)-(5)), the weekly rounding of HB's relevant amount (75CA(1)), the reg 75 non-payment of HB below 50p, the para 29 exceptions other than disability benefits, and whether a claim is a reg 60C qualifying claim. Also HB reg 75C(2)(a), specified-accommodation HB counted as nil: main's input in_specified_or_temporary_accommodation (#2123) does not separate specified from temporary accommodation, so that HB counts at its amount before the cap.

Impact

Real Enhanced FRS runs (r10), one at a time. Base is main e7610ac52, the main this head merged; branch is the head e82c4acac. Private copy of enhanced_frs_2024_25.h5, sha256 e433e532…. Weighted £m a year; records are survey records.

Year UC HB Household net income UC benefit units changed: records (weighted) of which reg 81 childcare offset UC families capped, base → branch HB families capped benefit_cap_reduction, base → branch UC exemption lost / gained LHA category changed (UC/HB) AHC poverty (pp)
2025 +23.00 +0.00 +23.00 4 (2.5k) 4 157.8k → 157.8k 0 £605.3m → £582.3m 0 / 0 0/0 0.0000
2026 +23.75 +0.00 +23.75 6 (3.2k) 6 294.0k → 294.0k 0 £1,360.8m → £1,337.0m 0 / 0 0/0 0.0000
2027 +24.32 +0.00 +24.32 6 (3.2k) 6 338.0k → 338.0k 0 £1,557.0m → £1,532.6m 0 / 0 0/0 0.0000
2028 +24.90 +0.00 +24.90 6 (3.2k) 6 367.4k → 367.4k 0 £1,694.4m → £1,669.5m 0 / 0 0/0 0.0000
2029 +27.03 +0.00 +27.03 8 (7.7k) 8 440.6k → 436.1k 0 £1,907.8m → £1,880.8m 0 / 0 0/0 0.0000
2030 +29.18 +0.00 +29.18 10 (8.9k) 10 468.7k → 463.0k 0 £2,117.6m → £2,088.4m 0 / 0 0/0 0.0000
  • Every UC change is reg 81's childcare costs element. In each changed benefit unit the exemption and the cap rate are the same as on main, and the UC reduction is lower by the childcare costs element, or nil where that element exceeds the excess (reg 81(2)). The comparison leaves no change unattributed, and no benefit unit loses.
  • Why this is larger than the PR's first run. Against Model a Universal Credit claim by a member of a couple as a single person (UC Regs 2013 reg 3(3)) #2084's head (17b9b8a) the same split moved UC by £0 in 2025, +£0.14m to £0.15m a year in 2026-29 and +£1.39m in 2030. Most of the records that change now have the same childcare costs element as in that run, but a higher UC award before the cap on main, so main caps them and reg 81's offset now applies. The difference comes from main, not from this PR. Against the run at 4cfc373c4 (r9, base main a94c65df9), this PR's UC change is identical in 2025-28 and slightly higher in 2029-30 (+£26.44m and +£28.37m then). Main's UC standard allowance uplift (Apply the Universal Credit Act 2025 standard allowance uplifts to 2029-30 #2242), merged in e640e0a81, raises UC before the cap on both sides from 2027: in 2027-28 that changes main's capped totals but not this PR's effect.
  • Capped UC families are unchanged in 2025-28. In 2029 two records (about 4.5k benefit units) and in 2030 four records (about 5.6k) leave the cap: their childcare costs element exceeds the excess (reg 81(2)).
  • benefit_cap_reduction falls by the UC change. uk-data's DWP benefit-cap calibration targets read it. For 2025 the count of capped benefit units is unchanged and the total falls by £23.0m (3.8%).
  • No exemption is lost or gained under either scheme against main. Main already caps mixed-age couples on UC (Apply the benefit cap to mixed-age couples on Universal Credit, and keep them off pensioner Council Tax Reduction #1944), and no Enhanced FRS family reaches reg 60C.
  • HB: no HB family in the Enhanced FRS has a benefit cap reduction in either run, and no HB exemption is lost or gained, so the HB-side differences move £0. Those differences are the earnings, LCWRA and carer-element exceptions lost, reg 5, reg 75D(2), the couple rate for reg 3(3) couples, the reg 3(3) partner's benefits under 75F, and the 16-year-old above.
  • No family has both UC and HB before the cap, in any year, so reg 80(2A) and 75F(1)(g) move £0.
  • LHA categories are unchanged for both schemes. The Enhanced FRS has no AFIP and no reg 3(3) single claim.
  • Diagnostics (every year): no family has a positive benefit_cap_reduction without an award; LHA_cap and uc_LHA_cap are unchanged everywhere.
  • Under £1bn a year in every year, and it follows the regulations.

Scripts and outputs: impact/ in the handover folder (dataset_impact_r3.py, compare_r3.py, run_r10.sh, table_rows.py, r10/branch_vs_base.json).

Tests

  • New YAML, hand-computed from the law:
    • gov/dwp/benefit_cap_by_scheme.yaml (22 cases). Each scheme's exceptions:
      • UC reg 83(1)(a) LCWRA and (j) carer element, which are not HB exceptions; carer's allowance entitlement, which lifts both;
      • UC reg 82 earnings, which is not HB; HB reg 75E WTC, which is not UC;
      • HB reg 5 for a pension-age claimant, a mixed-age couple on UC (capped under both), a mixed-age couple off UC (HB pension-age regulations, no cap), and a partner on income-based JSA (HB Regs 2006 apply).
      • SI 2014/1230 reg 60C: joint claimants aged 72 and 68 (not capped); a single claimant aged 70 whose partner aged 50 cannot be a joint claimant (not capped); the same award to joint claimants aged 70 and 50 (London couple cap 25,323, excess 30,000 − 25,323 = 4,677).
      • Reductions: UC reg 81 (excess 7,980 less childcare 3,000 = 4,980; childcare above the excess gives 0; no award gives 0); HB reg 75D(1) (HB 10,000 − 7,980 = 2,020); 75D(2) (5,000 → 26); HB below 50p a week (unreduced).
      • A family paid both: total 40,000 less HB 10,000 = 30,000, excess over 22,020 = 7,980 off UC, HB exempt under 75F(1)(g) and paid in full; and a UC family whose HB alone would have taken it over the cap (30,000 − 10,000 = 20,000, below 22,020: no reduction).
      • A reg 3(3) couple in London: UC 16,967, HB 25,323.
      • AFIP lifts both caps (UC reg 83(1)(c) with reg 2; HB reg 75F(1)(ea)).
    • housing_benefit/housing_benefit_lha_category.yaml (9 cases): AFIP excepted under UC Sch 4 para 29(5)(b) with reg 2 and under HB Sch 3 para 14; HB young individual at 30 and at 35; para 14(2)(b) for a couple; an adult non-dependant defeats the premium unless they receive a qualifying benefit (para 14(4)); another family's claimant in the household; an HB-only young person blocks both shared rates.
  • Rewritten for both schemes:
  • New property tests (Hypothesis):
  • Updated: test_uc_single_claim_properties.py (Model a Universal Credit claim by a member of a couple as a single person (UC Regs 2013 reg 3(3)) #2084's structure kept; UC exceptions compared single vs stripped, HB variables single vs joint), housing_benefit/housing_benefit.yaml, universal_credit/uc_deductions.yaml, test_uc_deductions.py and test_abolish_benefit_cap_scenario.py (the scenario lifts both caps).
  • CI at e82c4acac: all 15 checks pass (gh pr checks exits 0), including Lint, the four smoke imports, the documentation builds, Fix evidence, and Test / YAML and the four Test / Python shards (run 38963800030), which run every pytest file above.
  • Local runs (targeted, one process at a time):
    • At e82c4acac: policyengine-core test policyengine_uk/tests/policy 3,512 passed; test_benefit_cap_lha_scheme_split_properties.py 7 passed; test_uc_single_claim_properties.py 8 passed; test_housing_benefit_lha_order_properties.py 3 passed; test_housing_benefit_pension_age_properties.py 4 passed; test_abolish_benefit_cap_scenario.py 1 passed; ruff format --check policyengine_uk clean (1,572 files).
    • At 4cfc373c4 (the same formulas; since then only documentation and the merge of Apply the Universal Credit Act 2025 standard allowance uplifts to 2029-30 #2242, which touches no file this PR changes): policyengine-core test policyengine_uk/tests/policy 3,512 passed; test_benefit_cap_lha_scheme_split_properties.py 7 passed; test_benefit_cap_uc_and_hb_properties.py 1 passed; test_benefit_cap_specified_benefits_properties.py 26 passed; ruff format --check policyengine_uk clean.
  • Mutation checks.
    • At c7bd4c0, against a detached copy: 10 of 10 killed by the YAML files above, the split property file, or both. The mutations: HB rate following the reg 3(3) single claim; UC reduction ignoring childcare; HB exception including LCWRA; LHA_cap reading the UC rate; HB reduction ignoring the 50p minimum; UC shared-rate test ignoring reg 3(3); UC exception including WTC; HB cap reaching pension-age claims; HB shared-rate test ignoring the severe disability premium; UC cap rate reading responsibility under either scheme.
    • The reg 60C claimant fix: its new case fails on the previous formula (1 failed, 19 passed) and passes now.
    • ea46f7c3e: reverting either of its two formula changes fails both of its YAML cases (2 failed, 20 passed each time).

Invariants

For every generated family and every example:

  1. Cap rate.
  2. Exceptions.
    • With nobody at State Pension age, no AFIP and no reg 3(3) single claim, the old exception is exactly the union of the two schemes' (UC ∪ HB). Wherever they agree, it is their value.
    • With nobody at State Pension age and no AFIP, for a non-legacy reg 3(3) single claim the old exception is UC's or HB's WTC exception.
    • The age and AFIP changes are intended (UC has only reg 60C; HB has reg 5; AFIP lifts both caps) and are pinned in YAML and the examples.
  3. LHA.
    • Without AFIP, the old category is the new category of the scheme the family claims under (for HB, where the two bedroom counts agree).
    • With no reg 3(3) single claim and equal bedroom counts, the UC and HB categories are equal.
  4. Reductions.
    • uc_benefit_cap_reduction = max(excess − childcare costs element, 0) on an award, else 0, where the excess counts HB as nil (reg 80(2A)).
    • 0 ≤ the HB reduction ≤ HB's excess, and a family receiving UC has no HB reduction (reg 75F(1)(g)).
    • HB after the cap ≥ min(HB before the cap, 50p × 52), and ≤ HB before the cap.
    • benefit_cap_reduction = UC + HB reductions.
  5. Awards. Where a scheme's cap equals the old cap, and neither reg 81's offset nor reg 75D(2)'s minimum applies, universal_credit / housing_benefit equal the award under the old shared cap.
  6. LHA rate. LHA_cap = min(rent, housing_benefit_LHA_rate). That rate equals the UC category's BRMA_LHA_rate wherever the categories agree.
  7. A family paid both (test_benefit_cap_uc_and_hb_properties.py, both awards supplied):
    • HB changes no UC reduction: a family's reduction equals that of the same family with no HB, and is reg 81's on the welfare benefits without HB.
    • HB enters benefit_cap_welfare_benefits at its amount before the cap (reg 75C(2)(b)).
    • A family receiving UC has no HB reduction, and its benefit_cap_reduction is its UC reduction; with HB, it is exempt from the HB cap.
    • A family without UC has no UC reduction, and its HB reduction is reg 75D's.

Reviews

Independent reviews through Subfleet, and reviews from Vahid Ahmadi and juaristi22:

Reviews of 85d9be9 and ea46f7c were cut short (a host shutdown and the six-hour limit); the 4cfc373 delta review covers both heads.

Coordination

axiom:

🤖 Generated with Claude Code

Reproducer: policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_claimant_composition.yaml, policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit.yaml::Partially reduced benefit, policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit.yaml::Capped at 0, policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/housing_benefit_lha_category.yaml, policyengine_uk/tests/policy/baseline/finance/benefit/family/legacy_award_readers/passports.yaml::The claimant's own income-related ESA with the support component exempts the family from the benefit cap, policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/claimant_child_element.yaml::Case 1, an explicitly identified young claimant is not their own child.

MaxGhenis and others added 4 commits October 2, 2026 13:28
…HA shared-accommodation rules

One benefit cap (rate, exceptions, reduction) and one LHA category served
both schemes although the law differs. Each scheme now has its own:

- UC: uc_benefit_cap (reg 80A), is_uc_benefit_cap_single_claimant_rate
  (a reg 3(3) single claim is single), is_uc_benefit_cap_exempt_earnings
  (reg 82), is_uc_benefit_cap_exempt_specified_benefit (reg 83, "a
  claimant"), uc_benefit_cap_reduction (reg 81, less the childcare costs
  element). LHA_category / is_lha_shared_accommodation_rate_specified_renter
  are UC's (Sch 4 paras 27-29).
- HB: housing_benefit_benefit_cap (reg 75CA), single claimant per reg 2(1),
  exceptions 75E (WTC) and 75F (claimant or partner), no cap under the
  pension-age regulations (reg 5), housing_benefit_benefit_cap_reduction
  (75D, keeping the reg 75 minimum). housing_benefit_LHA_category,
  is_housing_benefit_young_individual and housing_benefit_LHA_rate follow
  reg 13D(2)(a)(i), with names and helpers shared with #2006.
- universal_credit and housing_benefit read their own reduction;
  benefit_cap_reduction is their sum. The claims_legacy_benefits stopgap
  from #2084 is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ions and LHA category

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…B LHA rate; docs and changelog

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 2, 2026
MaxGhenis and others added 2 commits October 2, 2026 14:38
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…case, verbatim reference award

- programs.yaml: the Benefit Cap entry named the removed benefit_cap; it
  now reads benefit_cap_reduction and says each scheme has its own cap.
- Armed forces independence payment lifts both caps: UC reg 83(1)(c)
  (reg 2's "attendance allowance") and HB reg 75F(1)(ea).
- The HB-only young person LHA case supplies the two bedrooms both
  schemes' size criteria give (UC Sch 4 para 10(1); HB reg 13D(3)(b)): C.
- reference_universal_credit is the parent formula, protected floor
  included; the differential excludes AFIP families (an intended change).
- Document that uc_benefit_cap_reduction is the reg 81 amount, which can
  exceed the award; BRMA_LHA_rate is the UC category's weekly rate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Coordination from #2084, which this PR is stacked on. #2084's head moved to fa1e7448b after its round-2 review, and the shared-rules helpers this PR removes changed:

  • policyengine_uk/utils/uc_work_related_requirements.py gains stays_on_legacy_benefits(benunit, period), which is claims_legacy_benefits & ~would_claim_uc. That is the continuing-award route housing_benefit_eligible uses.
  • single_claim_in_rules_shared_with_legacy_benefits and other_member_of_single_claim_in_shared_rules now gate on it instead of on claims_legacy_benefits alone. Round 2 reproduced the bug this fixes: a family that reports Housing Benefit but is calculated on UC lost the UC rules in LHA_category, uc_housing_costs_element and the cap.
  • claims_as_single_person.yaml now has two cases where it had one legacy case:
    • "reports HB but claims UC": single cap of 14,753, no exception for the partner's PIP, shared rate (A);
    • "stays on legacy benefits" (would_claim_uc: false): couple rate, the partner's PIP lifts the cap under HB reg 75F(1)(ea), LHA B.
  • test_uc_single_claim_properties.py restructured its example families (EXAMPLE_INDEX, nine families, LCWRA and caring kept apart for reg 29(4)).

When #2084 is merged into this branch, keep this PR's deletion of the helpers. Then check that the per-scheme variables pass those two YAML cases, since they pin the UC-vs-HB routing the split has to keep. #2084's round-3 review (subfleet 20261003-101137-reg33-review-r3) is reviewing fa1e7448b.

@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Overlap with #1978 (Count only claimants' income in Universal Credit), for whichever of the two merges second.

#1978 limits two of the things this PR moves to the claimant and partner (is_uc_assessed_claimant):

  • The earnings exception. UC Regs reg 82(1)(a) tests "the claimant's earned income or, if the claimant is a member of a couple, the couple's combined earned income". Count only claimants' income in Universal Credit #1978 sums is_benefit_cap_exempt_earnings over is_uc_assessed_claimant. This PR moves that formula into is_uc_benefit_cap_exempt_earnings, which still sums every member.
  • The capped total. WRA 2012 s.96(1) and reg 80(1) count "the welfare benefits to which a single person or couple is entitled". Count only claimants' income in Universal Credit #1978 sums the person-level benefits in benefit_cap_reduction over is_uc_assessed_claimant. This PR moves the list into benefit_cap_welfare_benefits (add(benunit, period, CAPPED_BENEFITS)), which still sums every member.

The intended end state is this PR's split, with both new variables summing person-level amounts over is_uc_assessed_claimant (add_for_members(benunit, period, CAPPED_BENEFITS, claimants) for the total). #1978's YAML cases in universal_credit/dependants_income.yaml pin both. A qualifying young person's £12,000 must not meet the exception, and their contributory JSA must not be in the total. Rename the variables those cases read when the split lands.

#1978's head is 0e74b27d6, merged with main.

Base automatically changed from uc-reg3-3-single-claim to main October 8, 2026 17:47
MaxGhenis and others added 4 commits October 9, 2026 14:14
#2084 has merged, so this branch now targets main. Conflict resolutions:

- LHA (#2006, #2134, #2022): main's housing_benefit_LHA_category,
  housing_benefit_LHA_rate, housing_benefit_severe_disability_premium_applies,
  LHA_cap, BRMA_LHA_rate, LHA_category and uncapped_BRMA_LHA_rate. This
  branch's reg 3(3) lines stay in is_lha_shared_accommodation_rate_specified_renter
  (#2030 is still open, so lha_renter_has_non_dependant and the union
  responsibility term stay). Its expectations in LHA_claimant_composition.yaml
  and housing_benefit_lha_order.yaml; the two HB categories this branch added
  there are now C under main's HB size criteria (reg 13D(3)(b)).
- #1944: housing_benefit_pension_age_regulations_apply is main's. Its edit to
  the deleted is_benefit_cap_exempt_other is dropped. Its tests now read the
  per-scheme variables; the property file's "before" reform adds the age test
  to each scheme's exceptions.
- #1978: the UC earnings exception and the capped total
  (benefit_cap_welfare_benefits) sum over is_uc_assessed_claimant.
- Carer's Allowance overlap: both specified-benefit exceptions read
  is_entitled_to_carer_benefit (UC reg 83(1)(i)-(ia), HB reg 75F(1)(h)-(ha)).
- #2084 (final head): the stays_on_legacy_benefits helpers stay deleted; its
  two routing cases are rewritten on the per-scheme variables.
- SI 2014/1230 reg 60C: is_uc_benefit_cap_exempt_qualifying_age lifts the UC
  cap where every claimant has reached the qualifying age for State Pension
  Credit. That keeps the result #1944 pinned for such awards (inputs only;
  the model's UC eligibility needs a claimant under that age).
- Tests main added that read the removed shared variables now read the
  per-scheme ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion

- housing_benefit_lha_category.yaml: main's severe disability premium counts
  an adult outside the family only as the liable family's non-dependant
  (#2006). The case now makes the claimant's family the household head's,
  so the other adult is a non-dependant (HB reg 3) and defeats Sch 3 para
  14(2)(a)(ii), as the case intends.
- housing_benefit_on_passporting_benefit.yaml (main): universal_credit reads
  uc_benefit_cap_reduction, so the case sets that input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oom counts agree

The old shared category read the Universal Credit size criteria
(LHA_allowed_bedrooms). Since #2006, Housing Benefit has its own count
(HB Regs 2006 reg. 13D(3), housing_benefit_LHA_allowed_bedrooms), which
gives a bedroom to a young person the Universal Credit count leaves out.
The differential now compares Housing Benefit's category with the
reference only where the two counts agree, and the examples check that
the lone-parent case reaches that difference (HB category C).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 4 commits October 13, 2026 00:48
…p-lha-rules

Brings in origin/main 1d3abec: #2119 (UC claims at legacy closure;
would_claim_uc -> claims_universal_credit), #2177, #1905, #2376, #2379,
#2388 and rollup B's earlier landings.

Conflicts:
- utils/uc_work_related_requirements.py: keep this PR's deletion of
  stays_on_legacy_benefits and the two shared-rules helpers. #2119 only
  switched stays_on_legacy_benefits to claims_universal_credit; on main
  its sole readers are the shared cap and LHA files this PR replaces.
- docs universal-credit.ipynb: main's new claims_universal_credit
  paragraph, with this PR's per-scheme cap sentences.

Main's new tests that read the deleted shared variables now read UC's:
- legacy_closure_ineligible_partner.yaml and
  test_uc_legacy_closure_properties.py:
  is_benefit_cap_single_claimant_rate -> is_uc_benefit_cap_single_claimant_rate
- test_uc_statutory_pay_properties.py: is_benefit_cap_exempt(_earnings)
  -> is_uc_benefit_cap_exempt(_earnings) (the UC award is what it checks)
- legacy_closure_pension_route.yaml and test_data_built_imputations.py
  pin uc_benefit_cap_reduction, which universal_credit and
  uc_has_deduction read, instead of the HB+UC total.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UC Regs 2013 reg 83(1)(a) reads "the LCWRA element is included in the
award of universal credit", as (j) reads the carer element, which this
file already tests as uc_carer_element > 0. The exception now reads
uc_LCWRA_element > 0 instead of any non-excluded member's
limited-capability flag. uc_LCWRA_element uses the same member mask
today (everyone but the other member of a reg 3(3) single claim), so no
result changes on main; once #1989 limits the element to the claimants,
the exception follows it whichever of the two merges first (PR comment
from #1989's review, 2026-10-12).

Two YAML cases pin the reading: the flag without the element lifts no
cap; the element without the flag does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This reverts commit d7697ed.

Independent review (subfleet 20261013-010643, review-delta-d7697edee.md)
found that reading uc_LCWRA_element > 0 matches the old flag test at
baseline but not under a reform that sets the element's amount to nil
(gov.dwp.universal_credit.elements.disabled.amount = 0, or a nil
new-claimant health element): main keeps the exception, the element
reading drops it. Whether a nil element is "included in the award" is a
reading the law leaves open, so this PR keeps main's flag test and stays
a pure split. Restricting reg 83(1)(a) to the claimants is #1989's
change; the port it needs is noted on #1989 and #2089.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…97ed)

From the independent delta review (subfleet 20261013-010643,
review-delta-d7697edee.md):

- P1: utils/uc_work_related_requirements.py. The 10/9 merge (f4aa92b)
  kept this branch's whole copy of the file, which undid main's 7a71c86
  (#2081 review item 5): couple_members() came back and claimants()
  stopped reading is_uc_assessed_claimant. The file is now main's, less
  the three shared-rules helpers this PR removes. No output changes: the
  two formulas were identical.
- P2: is_uc_benefit_cap_exempt_qualifying_age said only a supplied award
  reaches reg 60C. Since #2119 a reg 60A claim by a Working Tax Credit
  recipient whose tax credits closed does too.
- P3: housing_benefit_pension_age_regulations_apply said its UC-award
  rule was "right for the cap"; UC's reg 60C exception no longer reads
  it, HB's cap does. benefit_cap_welfare_benefits now says the
  benefit-unit amounts are counted whole (#2298). The UC notebook's cap
  sentence names reg 60C. The legacy closure differential also compares
  uc_benefit_cap_reduction, which still reads the claim route through
  the UC award.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@vahid-ahmadi vahid-ahmadi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs changes before merge. The split itself reads the law correctly, but the branch predates #2076 and #2399, which main merged on 13 and 14 October. It now conflicts with main in 13 files and would undo both if resolved in its favour.

@MaxGhenis, findings at head b7e0b2373:

  1. must: integrate #2076 and #2399 before this can land. git merge-tree against main 714b1438c reports 13 conflicts. Five are modify/delete: this PR deletes is_benefit_cap_exempt.py, _earnings.py, _health_disability.py, _other.py and is_benefit_cap_single_claimant_rate.py, which #2076 rewrote. There is also an add/add conflict in utils/benefit_cap.py (#2076's benefit_cap_couple). The per-scheme files here carry the pre-#2076 logic, so taking them as they are would undo main's fixes:

    • universal_credit/benefit_cap/is_uc_benefit_cap_exempt_specified_benefit.py:64 and housing_benefit/benefit_cap/is_housing_benefit_benefit_cap_exempt_specified_benefit.py:51: any income-related ESA (esa_income > 0) lifts the cap. Main requires esa_includes_support_component and the claimant or partner as payee. UC reg 83(1)(a) and HB reg 75F(1)(a) both say "an employment and support allowance ... which includes a support component".
    • The UC LCWRA and carer exceptions read members' circumstances. Main reads whether the element is in the award (reg 83(1)(a), (j)), including entered elements.
    • benefit_cap_welfare_benefits.py:50 counts income-based JSA and income-related ESA whole and scopes people by is_uc_assessed_claimant alone. Main uses benefit_cap_couple and award_of_members, so only the couple's part counts.
    • universal_credit/benefit_cap/is_uc_benefit_cap_exempt_earnings.py:30-38 uses gross employment and self-employment income less tax and NI. #2399 moved main to uc_individual_earned_income_before_mif, which brings in statutory pay and pension contributions and leaves out MIF income (reg 82(4)).

    Fix: merge main, port each of #2076's per-person scopes and #2399's earnings base into the matching UC or HB file, and rerun the split property tests. The Coordination section still lists #2076 as open, so that needs updating too.

  2. must: uc_benefit_cap_reduction clashes with #1819. #1819 adds variables/gov/dwp/universal_credit/uc_benefit_cap_reduction.py: the cap reduction actually taken off the award, bounded by the award and the protected floor. This PR adds universal_credit/benefit_cap/uc_benefit_cap_reduction.py with the same name and a different meaning: the gross reg 81 amount, which the PR body notes can exceed the award. If both merge, the variable is defined twice. Both also change universal_credit.py, uc_deductions.py and test_uc_deductions.py. The two PRs need to agree which name is the gross reg 81 reduction and which is the amount applied.

  3. should: the "UC or HB, never both" assumption is about to stop holding. benefit_cap_welfare_benefits.py:3-7 adds both universal_credit_pre_benefit_cap and housing_benefit_pre_benefit_cap into one total, and the HB reduction does not check for UC. That is right only while no family gets both. #1910 opens joint HB and UC for specified or temporary accommodation, and main already has the in_specified_or_temporary_accommodation input (#2123). Once a family gets both, this PR would count HB in the UC cap, against UC reg 80(2A) ("the amount to be used is nil"), and apply the HB cap to someone receiving UC, against HB reg 75F(1)(g). It also leaves out HB reg 75C(2)(a), which counts specified-accommodation HB as nil. Now that the schemes are split, these are a few lines: leave HB out of the UC total, add universal_credit_pre_benefit_cap > 0 to the HB exception, and read the accommodation input for 75C(2)(a). That would also settle the double-subtraction problem flagged on #1910. If you would rather leave it, the property test that no family gets both needs to fail clearly once #1910 lands.

  4. should: the reg 82 threshold is a hardcoded £10,152 (is_uc_benefit_cap_exempt_earnings.py:39). Reg 82(1)(a) sets it at 16 hours a week at the national minimum wage hourly rate, × 52 / 12. From April 2026 the rate is £12.71 (already in gov.hmrc.minimum_wage), so the threshold is about £881.23 a month (£10,575 a year). The £846 figure is from 2025-26. This is pre-existing on main, and #1820 adds a parameter for it. Since this PR creates the new UC file, it is the place to read that parameter (or take #1820's) rather than copy the literal.

What is right: UC reg 80A(2) treats a reg 3(3) claimant as a single claimant, and HB reg 75CA(2) with reg 2(1) treats that couple as a couple. The reg 81 childcare offset and reg 81(2) "no reduction" rule are right, as is the reg 75D(2) 50p minimum. The scheme-specific exceptions are right: earnings and the LCWRA and carer elements for UC only, WTC (75E) for HB only. HB reg 5 for pension-age claims is right, and so is the AFIP treatment (UC reg 2 for reg 83(1)(c) and Sch 4 para 29(5); HB reg 75F(1)(ea)).

What I checked

  • Read the full PR body and diff. Read on legislation.gov.uk: UC Regs 2013 regs 80, 80A, 81, 82 and 83, and HB Regs 2006 regs 75C, 75D and 75F.
  • Hand-computed benefit_cap_by_scheme.yaml. Reg 81: 30,000 − 22,020 = 7,980, less 3,000 childcare = 4,980. Reg 75D(2): min(7,980, 5,000 − 0.50 × 52) = 4,974, leaving £26. Both match.
  • In a fresh environment at b7e0b2373, 11 changed YAML files and folders all passed (168 cases): benefit_cap_by_scheme, benefit_cap (file and folder), LHA_claimant_composition, housing_benefit_lha_category, housing_benefit, claims_as_single_person, uc_deductions, legacy_award_readers/passports, carers_allowance_overlapping_benefits and housing_benefit_on_passporting_benefit.
  • Mutation: with reg 81's childcare offset removed from uc_benefit_cap_reduction, benefit_cap_by_scheme.yaml fails 2 of 20 cases. I restored the line afterwards.
  • ruff check and ruff format --check on the 38 changed or added Python files: clean.
  • git merge-tree against main 714b1438c: 13 conflicts, listed above.
  • Compared files with Vahid's open PRs: #1820 changes three of the files this PR deletes and two of its test files, and #1819 is in item 2. #1855, #1864, #2184, #2187, #2189, #2193 and #2382 do not overlap.
  • CI at this head: all checks pass. That run predates #2076 and #2399 on main.
  • Not checked: I did not run the Hypothesis property files, the full YAML suite, or anything on the Enhanced FRS.

🤖 Generated with Claude Code

MaxGhenis and others added 5 commits October 14, 2026 20:26
…cap-lha-rules

Ports #2076's per-person scopes into the per-scheme files instead of
keeping the four shared exception files it rewrote (they stay deleted):
- UC reg 83 (is_uc_benefit_cap_exempt_specified_benefit): #2076's
  payee and support-component tests, scoped to "a claimant" (not the
  reg 3(3) other member), with UC's own child and young-person tests.
- HB reg 75F (is_housing_benefit_benefit_cap_exempt_specified_benefit):
  claimant or partner, ESA via the couple's award and payee, HB's own
  child and young-person tests.
- benefit_cap_welfare_benefits: #2076's benefit_cap_couple and
  award_of_members scoping of the capped total.
- utils/benefit_cap.py and utils/benefit_unit.py: both sides' helpers.
- #2076's converted tests (capped total, specified benefits, and its
  property file) read the per-scheme variables; the #2298 cap hold in
  the legacy-readers property file is dropped (fixed by #2076).

Resolved by the 2026-10-13 landing lane (job 20261013-115007), which
lost DNS before committing; snapshot pushed as
wip/uc-hb-split-merge-a98f8f084 (3e27c7f). Targeted cap YAML at this
tree: 143 passed (merge-2026-10-13b/logs/yaml_cap_targeted.log).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…shared-cap-lha-rules

#2399 changed the shared earnings exception this PR deletes. Its
measure, uc_individual_earned_income_before_mif summed over the
assessed claimants (reg. 82(1)(a), excluding MIF income under reg.
82(4)), is carried over unchanged into the UC-only
is_uc_benefit_cap_exempt_earnings; the shared file stays deleted.

Main's new tests that read the deleted shared names now read the UC
ones, with their expected values unchanged:
- test_uc_statutory_pay_properties.py (#2399; conflict): main's text
  and regression case, reading is_uc_benefit_cap_exempt(_earnings).
- benefit_cap_earnings_exception_earned_income.yaml (#2399) and
  uc_trading_loss.yaml (#2085).
- test_trading_loss_properties.py (#2085).

Targeted YAML (benefit_cap folder, benefit_cap_by_scheme,
uc_trading_loss): 121 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…2277) into uc-hb-split-shared-cap-lha-rules

No conflicts; no benefit cap, LHA, UC or HB file changed on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only Universal Credit's shared-rate test (is_lha_shared_accommodation_rate_specified_renter)
reads it; Housing Benefit's LHA category reads is_housing_benefit_young_individual and
housing_benefit_has_non_dependant. P3 from the delta review of 85d9be9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…reg 75F(1)(g))

UC Regs 2013 reg 80(2A) counts Housing Benefit as nil in the Universal Credit
total, so uc_benefit_cap_reduction takes it out of benefit_cap_welfare_benefits,
which keeps it for the Housing Benefit cap (HB Regs 2006 reg 75C(2)(b)). HB reg
75F(1)(g) lifts the Housing Benefit cap where the claimant is receiving
Universal Credit: is_housing_benefit_benefit_cap_exempt_specified_benefit now
includes a family with Housing Benefit and Universal Credit before the cap.

housing_benefit_eligible rules out a family that claims Universal Credit, so no
computed family gets both today and no baseline result changes; the rules
matter once a family is paid both (specified or temporary accommodation, #1910).
Reg 75C(2)(a) (specified-accommodation HB counted as nil) is not applied:
in_specified_or_temporary_accommodation does not separate specified from
temporary accommodation. Vahid Ahmadi's finding 3 on #2089.

Tests: two YAML cases (each fails on the previous formulas), a property file
(test_benefit_cap_uc_and_hb_properties.py), and the split property file's
reduction invariant now checks reg 80(2A) and 75F(1)(g) in place of
asserting that no family gets both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@juaristi22 juaristi22 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed ea46f7c3ec56bc757e1d1f9ac5ca07e26de50877 against merge base 7e10ccd98326b6c9acd244191bb4636985e710b0 with separate code/test and policy/source roles. The earlier integration and dual-award concerns are addressed; no new blocking formula defect was established. Two points remain to address before treating the review record as complete:

  • A1 — Refresh the description. Its opening still names 04ac713fa, says the diff is byte-identical to the old review, and states that UC/HB never coexist. This head deliberately handles supplied dual awards: HB is excluded from UC's cap total and HB is exempted when an established pre-cap UC award exists. Update the scope/invariant sections, distinguish the remaining formula-derived eligibility limitation, and label old-head impact/validation tables.
  • A2 — Carry forward the dated earnings-threshold correction. is_uc_benefit_cap_exempt_earnings.py still uses £10,152. UC reg82(1)(a) ties this to16hours/week at the minimum-wage rate; £12.71 from April2026 implies £10,574.72/year under that conversion. £10,400 of actual earnings therefore lies on opposite sides of the tests. This is Vahid's inherited nonblocking concern, not a newly introduced regression. Coordinate #1820's dated parameter into this renamed file or record the landing dependency.

The split now excludes HB from the UC cap under UC80(2A) and prevents a second HB cap under HB75F(1)(g). The DWP explanatory memorandum, paragraph7.2 supports using established pre-cap UC entitlement even where the cap later reduces UC to nil. That is distinct from unclaimed notional eligibility.

Validation: two independently executed final-income controls passed on Core3.33.0: managed migration preserves the same cap/final award as ordinary take-up; adding £10,000 supplied HB leaves the UC cap unchanged and raises final household income by £10,000. The local broad changed-file rerun hit its540s bound at25%, without a final pass count; it is not claimed as passing. Complete hosted YAML/four Python shards and required checks are green at this exact head. The source review validated28official originals, including the decisive PDF page. Private population impact was not rerun; the combined #1819/#1820 trees were not tested. The substantive Axiom status is present.

Before landing, coordinate #1819's competing uc_benefit_cap_reduction definition and #1820's threshold fix. Current GitHub mergeability was temporarily UNKNOWN after main advanced, so this is not a declaration that every merge gate has passed. Review only; no branch changes or merge performed.

…into uc-hb-split-shared-cap-lha-rules

No conflicts. Main's two edits to files this PR also changes
(test_benefit_cap_specified_benefits_properties.py: REBALANCED_YEARS from
REBALANCING_YEARS; test_legacy_award_readers_properties.py: comment) merged
automatically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@juaristi22 juaristi22 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review status: COMPLETE
Review severity: COMMENT
Reviewed head: 4cfc373c4228c756ab7878ef27cfff91dbd298ab (main a94c65df integrated).

The incremental review found no new critical findings or material evidence gaps. All 86 focused cases passed, including managed migration, supplied UC/HB awards with one cap, and protected/new LCWRA claimants through final UC and household income in 2026, 2029, 2030 and 2032.

Two prior follow-ups remain STILL OPEN at their existing SHOULD ADDRESS severity:

  • A1: update the description's old head/equivalence claims and its statement that UC/HB awards never overlap.
  • A2: the renamed UC earnings-exemption file still uses £10,152; coordinate the dated threshold from #1820. This remains inherited feedback, with no new critical escalation.

Local checks used Core 3.33.0 / Hypothesis 6.168.2, rather than locked 3.33.2 / 6.169. Hosted Python 1/4 and 2/4 and the required Result were still pending at the final guard; no CI failures were observed. The #1819/#1820 landing coordination remains, and combined dependency trees were not tested.

MaxGhenis and others added 3 commits October 16, 2026 16:23
- P3-1: document that the HB cap rate counts a 16-year-old for the year
  (has_sixteen_year_old_dependant) while the HB reg 75F exceptions read the
  model's Child Benefit qualifying young person, which leaves out one outside
  education; in law one status decides both (CB (General) Regs 2006 regs 5
  and 7), and the model has no school-leaver terminal date (#2287). No
  formula changes: main's #2076 case "A 16-year-old out of education with
  armed forces independence payment does not lift the cap" pins this
  reading of reg 75F(1)(ea).
- P3-2: the changelog said AFIP "now lifts both caps"; main already lifts
  them. Against main the UC exception narrows to a claimant's AFIP (UC reg
  83(1)(c) with reg 2); HB keeps the young person's (reg 75F(1)(ea)).
- P3-3: benefit_cap_welfare_benefits' documentation now says the total
  includes UC and HB before the cap, which uc_benefit_cap_reduction relies
  on for reg 80(2A); the capital-income property test's comment no longer
  names exemption routes the UC exemption stopped reading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No conflicts. #2242 uplifts the UC standard allowance (parameters, the
rebalancing uprating, its test); it touches no file this PR changes, and
nothing it adds reads a variable this PR removes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing counts

The e640e0a delta review's P3: the Child Benefit education or training
condition (meets_qualifying_young_person_education_or_training_condition_for_child_benefit)
accepts approved training as well as non-advanced education, so "outside
education" overstated who the HB exceptions leave out. Docs only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

@vahid-ahmadi, thanks for the review of b7e0b2373. Here is where each of your four findings stands at the current head e82c4acac: findings 1 and 3 with the commits that address them, 2 and 4 in writing.

1. Integrate #2076 and #2399 (must): done.

2. uc_benefit_cap_reduction clashes with #1819 (must): answered in writing; no code change here.

  • This PR keeps uc_benefit_cap_reduction as the gross UC reg 81 amount: the excess less the childcare costs element, which can exceed the award before the cap.
  • Reconcile UC deduction and benefit cap components under the protected floor #1819's amount is bounded by the award and the protected floor. It should take another name, such as uc_benefit_cap_reduction_after_floor (suggested on Reconcile UC deduction and benefit cap components under the protected floor #1819 on 10/02), and read the gross uc_benefit_cap_reduction, not benefit_cap_reduction. After this PR, benefit_cap_reduction is the sum of the UC and HB reductions.
  • The clash can't land silently. policyengine-core refuses a duplicate variable name at load, and both PRs edit the same line of universal_credit.py. So whichever lands second gets a conflict and a load error until it renames.

3. UC and HB both (should): done in ea46f7c3e, except reg 75C(2)(a), answered below.

  • UC reg 80(2A): uc_benefit_cap_reduction takes housing_benefit_pre_benefit_cap out of the total. benefit_cap_welfare_benefits keeps HB at its amount before the cap for the HB cap (reg 75C(2)(b)).
  • HB reg 75F(1)(g) ("the claimant is receiving universal credit"): is_housing_benefit_benefit_cap_exempt_specified_benefit lifts the HB cap for a family with HB and UC before the cap. So a UC award the cap reduces to nil still lifts the HB cap. That is the reading the variable documents. HB reg 2(3B) treats a person as on UC "whether it is in payment or not", and @juaristi22's review cites the DWP explanatory memorandum to SI 2014/771 (para 7.2) for the same reading.
  • HB reg 75C(2)(a) is documented but not applied. Main's in_specified_or_temporary_accommodation (Add the Housing Benefit earnings disregard for specified or temporary accommodation (Sch 4 para 18, from 5 October 2026) #2123) covers both specified and temporary accommodation, and 75C(2)(a) counts HB as nil only for specified accommodation (reg 75H). Applying it would need a specified-only input, which is Abolish working-age Housing Benefit from 1 July 2026 (NI 1 October 2026) #1910's territory.
  • Tests: two cases in benefit_cap_by_scheme.yaml each fail on the previous formulas. A new property file, test_benefit_cap_uc_and_hb_properties.py, checks four invariants for every generated family:
    • HB changes no UC reduction;
    • HB enters the total at its amount before the cap;
    • a family receiving UC has no HB reduction;
    • a family without UC gets the reg 75D reduction.
      The split property file's reduction invariant now checks reg 80(2A) and 75F(1)(g) instead of asserting that no family gets both.
  • Baseline: ea46f7c3e changes no baseline result. housing_benefit_eligible rules out a family that claims UC, so only supplied awards reach these rules until Abolish working-age Housing Benefit from 1 July 2026 (NI 1 October 2026) #1910; the Enhanced FRS run at this head has no family with both before the cap in any year.

4. The reg 82 threshold is a hardcoded £10,152 (should): answered in writing; the landing dependency is recorded in the description.

Since 4cfc373c4, ab514f5b8 and e82c4acac change documentation only, after the P3s of the 4cfc373c4 and e640e0a81 reviews, and e640e0a81 merges main e7610ac52 (#2242) without conflicts. Independent delta reviews approved each of 4cfc373c4, e640e0a81 and e82c4acac.

@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

@juaristi22, thanks for both rounds, and for the final-income controls. Where your two follow-ups stand at head e82c4acac:

A1, refresh the description: done.

  • The opening names the current head, the main it merged and today's main, and lists what changed since Vahid's review.
  • The "UC and HB never coexist" statement is gone. "What this PR does" now has a section on a family paid both: UC reg 80(2A) in uc_benefit_cap_reduction; HB reg 75F(1)(g) in the HB exception, with the reading it takes for a UC award the cap reduces to nil (your EM 7.2 citation is credited there). It also separates what still limits it: the model's HB eligibility rules out a family that claims UC, so only supplied awards reach these rules until Abolish working-age Housing Benefit from 1 July 2026 (NI 1 October 2026) #1910, and reg 75C(2)(a) is not applied.
  • The impact table is re-measured at this head against main e7610ac52 (run r10). The tests section says which head each local run was at.

A2, the reg 82 threshold: recorded as a landing dependency, not changed here.

The #1819 name clash is answered in the Coordination section and in my reply to Vahid: this PR keeps uc_benefit_cap_reduction as the gross reg 81 amount, and policyengine-core's duplicate-name check means whichever lands second must rename.

@MaxGhenis
MaxGhenis added this pull request to the merge queue Oct 16, 2026

@juaristi22 juaristi22 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the replies and revised description at e82c4acacafd516836ca4cc1797ea4536c3e9d63; both independent review roles are complete with no new findings. The previously reviewed code head is unchanged.

A1 is resolved: the description now identifies the current head, distinguishes supplied dual UC/HB awards from formula-derived eligibility, and corrects the reduction invariants and validation provenance.

A2 is resolved through the original request's record the integration dependency alternative. The £10,152 literal remains unchanged; this does not certify the dated threshold as fixed. Keep #1820's parameter/port and #1819's gross-versus-floor-bounded variable naming explicit in landing coordination.

No new model run or private impact validation was performed for this prose-only follow-up; earlier code/source checks and their limitations remain. All current checks are green and GitHub reports a clean merge, but combined dependency integration and Vahid's separate feedback still need coordination before calling this merge ready.

Merged via the queue into main with commit 0583e23 Oct 17, 2026
16 checks passed
@MaxGhenis
MaxGhenis deleted the uc-hb-split-shared-cap-lha-rules branch October 17, 2026 00:20
MaxGhenis added a commit that referenced this pull request Oct 17, 2026
Review r11 (Opus and GPT-6.1 Sol) on 871e46d: main's benefit cap
readers (#2076, #2089) called award_of_members on esa_income and
jsa_income. Under disable_simulated_benefits the held total matches
neither of its readings after the dataset year, so another member's held
award was read as the couple's: it raised benefit_cap_welfare_benefits
and could exempt a claimant with an ESA support component who receives
no ESA.

- utils/benefit_unit.py: award_of_couple reads claimant_or_partner_*
  wherever the rule's couple is the claimant and partner, and
  award_of_members for any other couple (a UC couple with is_uc_claimant
  entered). CLAIMANT_OR_PARTNER_AWARDS moves here; the reform imports it.
- benefit_cap_welfare_benefits and both specified-benefit exemptions read
  award_of_couple.
- The reform's docstring says it reads its inputs when it is applied, so
  a region a scenario's after-load modifier or set_input changes later is
  not followed; applying the reform after the change reads it (r11 Sol
  finding 2, Opus nit 4). New test for that route.
- Changelog: the first bullet no longer claims what #2047 shipped (r11
  Opus finding 2); new bullet for the benefit cap.
- New test_disable_simulated_benefits_benefit_cap.py: metamorphic (another
  member's report never changes the cap, reform on), differential against
  main's replaced reader (reform off), the fallback couple, and an entered
  award reaching the cap. Each reader's mutant fails it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants