Skip to content

Fix HB and national CTR non-dependant deductions - #2007

Open
MaxGhenis wants to merge 55 commits into
mainfrom
hb-ctr-non-dep-exemptions
Open

MaxGhenis wants to merge 55 commits into
mainfrom
hb-ctr-non-dep-exemptions

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #2004
Fixes #1934. Main already keeps people on IS, JSA(IB), ESA(IR) or Universal Credit out of the pensioner scheme (#1944). This PR adds the issue's non-dependant deductions. The model lacks inputs for assessment-phase ESA(IR) and a working student's summer vacation.

Summary

The model now calculates Housing Benefit and national Council Tax Reduction non-dependant deductions under each scheme's own rules. We use legislation.gov.uk's 7 April 2025 text and each year's CTR amending instruments.

Housing Benefit (SI 2006/213 reg 74). SI 2006/214 reg 55 sets pension-age HB deductions.

Rule Main This PR
Banding, reg 74(2) right=True puts £192.00 a week in the band below Each band runs from "not less than" its lower edge
Remunerative work, reg 74(1)-(2), reg 6(1), (6) For a non-worker the council deducts the lower of the flat amount and their band, so nothing for one with no banding income For a non-worker, or anyone on IS, JSA(IB) or ESA(IR), the council deducts the reg 74(1)(b) amount whatever their income (£20.40 a week in 2026-27)
Couples, reg 74(3)-(4) The council deducts for each member on their own income The council makes one deduction for a couple, the higher, on the couple's joint income, and a separate one for any other adult in the family
Non-dependant exemptions, reg 74(7), (8), (10) None Full-time students; State Pension Credit recipients; under-25s on IS or income-based JSA, or entitled to Universal Credit that DWP calculates on no earned income
Claimant exemption, reg 74(6) None The council deducts nothing for anyone if the claimant or partner is blind or receives AA, the DLA care component, the PIP daily living component or AFIP

Council Tax Reduction. We model three national schemes:

  • England, pension age: SI 2012/2885 Sch 1 para 8 and reg 10.
  • Wales: WSI 2013/3029 Sch 1 para 3, Sch 6 para 5 and reg 10.
  • Scotland: SSI 2012/319 reg 48 and reg 6; SSI 2012/303 reg 67, then SSI 2021/249 reg 90, reg 10 and reg 4.

Changes

  • Scales. Main's national CTR used the Housing Benefit scale. We give each scheme its own weekly scale for 2013-14 to 2026-27 and cite the amending instrument for every year. In 2026-27:
    • England: £5.20 to £15.95, on £279/£485/£605.
    • Wales: £6.70 to £20.05, on the same bands.
    • Scotland: £5.55 to £16.55, on £286/£497/£614.
    • Pension-age and working-age scales match in Wales and in Scotland in every year. From 2027-28 the model uprates each scale's thresholds and amounts by gov.benefit_uprating_cpi.
  • Banding, remunerative work and couples. As for HB. Councils treat recipients of IS, JSA(IB) or ESA(IR) as outside remunerative work under each scheme's reg 10(6) (SSI 2012/319 reg 6(6)).
    • Under Wales's working-age scheme, the council deducts for both members of a non-dependant couple whose own Universal Credit award assesses either of them (Sch 6 para 5(3): "other than where there is an award of universal credit"). Another member's award as a single claimant leaves the couple one deduction. Sch 1 para 3(3), for pension-age applicants, has no such words, so the council deducts both amounts only for a working-age applicant.
  • Non-dependant exemptions, at any age (para 8(7)-(8) and equivalents): full-time students; non-dependants on IS, income-based JSA, income-related ESA or State Pension Credit; an adult for whom someone else has claimed child benefit (LGFA 1992 Sch 1 para 3 needs another person's entitlement, and SSAA 1992 s.1(1) makes entitlement depend on a claim); and entitlement to Universal Credit that DWP calculates on no earned income, from the year each scheme added it (England and Wales 2015-16, Scotland 2016-17).
    • From 1 April 2022, Scottish councils also exempt, under the working-age scheme, a non-dependant whose partner receives IS, income-based JSA or income-related ESA and who doesn't receive Universal Credit. SSI 2021/249 reg 90(8)(a) exempts a "qualifying income-related benefit claimant", which reg 4(1) defines as "an applicant who is, or who has a partner who is," on one of those benefits. Read literally, "applicant" would leave reg 90(8)(a) no case to apply to, so we read it for the non-dependant.
  • Applicant exemption (para 8(6) and equivalents). We now apply council_tax_reduction_applicant_has_non_dep_exemption, which only the English local schemes read on main, to national CTR calculations.
  • English working-age local schemes (Merton, Kingston, Newham, Westminster, Oxford) keep their own variables, unchanged.

New variables: housing_benefit_non_dep_deduction_exempt (Person), housing_benefit_non_dep_deductions_claimant_exempt (BenUnit), council_tax_reduction_non_dep_deduction_exempt (Person), uc_benefit_cap_welfare_benefits (BenUnit).

New parameters: HB income_related_benefit_age_limit (25); per CTR scheme, non_dep_deduction/{amount, exempt_universal_credit_without_earned_income}; Scotland working_age_exempt_partner_of_income_related_benefit_claimant (from 2022-04-01). HB age_threshold (18) now starts in 2006, when the 2006 Regulations took effect.

Universal Credit's cap. The new uc_benefit_cap_welfare_benefits holds the welfare benefits the Universal Credit cap counts: those in benefit_cap_welfare_benefits, for the same single person or couple, with Housing Benefit at nil (UC reg 80(2A)). uc_benefit_cap_reduction reads it where main read the combined total less housing_benefit_pre_benefit_cap. The Housing Benefit cap (HB reg 75C) keeps the combined total. A caller who supplies benefit_cap_welfare_benefits, a total that includes Housing Benefit, must also supply housing_benefit_pre_benefit_cap, which the new variable subtracts, or uc_benefit_cap_welfare_benefits itself, with a value for every benefit unit. Otherwise the new variable raises ValueError and names both inputs, whether or not any household has a non-dependant. A value a scenario stores with set_model_value supplies neither. No baseline formula on universal_credit's route then calculates Housing Benefit, so the deductions can read the award itself. uc_benefit_cap_reduction_after_floor keeps main's code. The baseline cap and award arrays match main (invariant 14).

Gross income

DWP instructs councils to count "earnings, and any other income" as a non-dependant's gross weekly income (HB guidance A5.542), to add savings interest to earnings (A5.570), and to take no business expenses off a self-employed person's total income (A5.551). Reg 74(9) and its equivalents disregard specified disability, compensation and trust payments.

  • A person's own income comes from the programme's gross-income variable that Split total income into programme-specific concepts #2620 added (housing_benefit_non_dep_gross_income, council_tax_reduction_non_dep_gross_income): earnings, statutory sick, maternity and paternity pay, pensions, the taxable social security benefits, savings and dividend income, miscellaneous income, Maternity Allowance, IIDB, SDA, war widow's pension, and child and spousal maintenance. We add other investment income, ISA interest and ISA dividends, as main's local CTR schemes do. tax_free_savings_income counts ISA interest unless the inputs supply a total. The model holds self-employment and property profits rather than the receipts A5.551 describes, so we count each profit and treat a loss as nil.
  • A claimant or partner of the non-dependant's family: the council bands them on the couple's two incomes plus the family's awards: child and working tax credits, child benefit, Income Support, the couple's income-based JSA and income-related ESA (claimant_or_partner_jsa_income, claimant_or_partner_esa_income), Pension Credit, and Universal Credit where the award assesses either of them. It bands anyone else in the family on their own income, plus the Universal Credit where is_uc_claimant makes them its claimant instead.
  • Universal Credit. The council counts the family's award after the cap and before debt deductions, so the model reads universal_credit, plus uc_deductions where the award is positive. DWP awards the maximum amount less the amounts for income (Welfare Reform Act 2012 s.8(1)). DWP then takes advances (SI 2013/383 reg 10), overpayments (SI 2013/384 regs 10-11) and third-party payments (SI 2013/380 Sch 6 para 2(1)) from the sum payable, so those deductions stay the family's income.
    • A stored award, a stored or replaced cap reduction and a reform of universal_credit's formula reach the deduction through universal_credit itself.
    • Parliament lists Maternity Allowance as a welfare benefit (Welfare Reform Act 2012 s.96(10)(i)). uc_benefit_cap_welfare_benefits reads it through CLAIMANT_OR_PARTNER_BENEFITS, so DWP's cut reaches the award the council counts. For a non-dependant family with £20,000 of Universal Credit before a £14,753 cap and £5,000 of Maternity Allowance, DWP pays £9,753 of Universal Credit, and the council counts that award.
    • No baseline formula on Universal Credit's route calculates Housing Benefit, so the deduction reads universal_credit without a cycle in any mix of households, and housing_benefit_non_dep_deductions needs no early return. A supplied combined total comes with a supplied housing_benefit_pre_benefit_cap or uc_benefit_cap_welfare_benefits for every benefit unit (invariant 15). A reform can add a read of calculated Housing Benefit to that route; the engine then raises CycleError in the deduction, as it does in universal_credit.
    • A reform that replaces or neutralizes benefit_cap_welfare_benefits changes the Housing Benefit cap alone. To change the benefits the Universal Credit cap counts, a reform replaces uc_benefit_cap_welfare_benefits. A reform that neutralizes housing_benefit_pre_benefit_cap drops a supplied value, so a caller who supplies the total under it supplies uc_benefit_cap_welfare_benefits; the ValueError message says so.

Inputs and choices

  • On a benefit. The exemptions and the remunerative-work test read Count only the claimant's and partner's legacy awards in means tests and passports #2027's is_on_income_support, is_on_income_based_jsa, is_on_income_related_esa and is_on_pension_credit, which ask whether DWP pays the award to that person (HB reg 2(1), (3), (3A)). Universal Credit counts for each claimant its award assesses (is_uc_assessed_claimant, the claimant and partner unless is_uc_claimant names others). For entitlement we read the award before the cap (universal_credit_pre_benefit_cap > 0): UC reg 80(2) uses "the amount to which the claimant is entitled before any reduction under regulation 81".
  • Full-time student: current_education != NOT_IN_EDUCATION or in_HE, the helper the local schemes use. With no input, the model gives an 18- or 19-year-old tertiary education, so it counts them as students.
  • Claimant or partner: is_claimant_or_partner (Replace generic child and adult flags with each programme's legal definitions #1896), the benefit unit's claimant and at most one partner (SSCBA s.137).
  • "Earned income" in the Universal Credit limb. HB reg 74(11) and the CTR provisions adopt UC reg 52's definition of earned income. We read uc_individual_earned_income, so UC's deductions, company attribution, trading losses and conditional minimum income floor reach the exemption. DWP takes allowable employment expenses, tax, National Insurance and relievable pension contributions off earnings (UC reg 55(3), (5)). It adds trades' profits and losses together and attributes a company's income to its owner (reg 57(2), reg 77(3)(b)), and it applies the minimum income floor only where reg 62 does, a couple's floor included. The limb asks whether "the award is calculated on the basis that the non-dependant does not have any earned income", and DWP calculates a joint award on combined earnings under reg 22(1)(b). We test the non-dependant's own earned income; we found no DWP guidance on joint claims.

Not modelled

  • Pension-age HB's 26-week deferral of an increase in a deduction (SI 2006/214 reg 59(10)-(13)), and the CTR applicant exemption's protection while DWP suspends AA, DLA or PIP during a hospital stay.
  • HB: a working full-time student's summer vacation (reg 74(7)(d)). No input separates assessment-phase income-related ESA from the work-related activity group, so we withhold the reg 74(8)(a) exemption from every under-25 on ESA(IR).
  • Wales: Welsh councils deduct for ESA(IR) recipients in the work-related activity group; we exempt every recipient. esa_includes_support_component marks the support group only.
  • CTR: people whom the council tax discount rules disregard (LGFA 1992 Sch 1), other than students and the para 3 adults. is_apprentice has no £195 earnings cap, so we don't read it.
  • All schemes: youth training allowances, a normal home elsewhere, long hospital stays, prison and armed forces operations, and the benefit-week and leave rules of reg 6 (annual hours stand in for them).
  • Gross income: student support; statutory adoption, shared parental, parental bereavement and neonatal care pay (no inputs).
  • Joint-claim JSA couples (HB reg 2(3)(c)): with no joint-claim input, the model counts only the payee as receiving it.
  • Scottish disability assistance for the claimant exemption has no separate input.

How this composes with main

  • Housing Benefit (housing_benefit_non_dep_deductions). For each family we sum each other family's amount (one per couple, the higher, plus one for each other member) over the non-dependants of the household head (Give sharers, boarders and lodgers their own rent, tenure and LHA category #2006). We treat a family that bears any share of the rent as a joint occupier, not a non-dependant (reg 3(2)(d)), even at nil rent. We then multiply the total by the family's share_of_household_rent (reg 74(5)) and zero it under the family's own claimant exemption.
  • Council Tax Reduction (council_tax_reduction_non_dep_deductions). Each applicant family takes its deductions under its own scheme (council_tax_reduction_pensioner, Assess each Council Tax Reduction claim under its own family's scheme and exemption #2015). The person-level variables report the scheme of the household head's family, which Give Council Tax Reduction to the household head's family, not the oldest adult's #2009 makes the applicant; for a jointly liable applicant on the other scheme, _national_non_dependants.py recomputes the deductions under that scheme. We multiply the total by council_tax_reduction_joint_liability_share (para 8(5)) and zero it under the family's own council_tax_reduction_applicant_has_non_dep_exemption.
  • Split total income into programme-specific concepts #2620's work gate becomes the full rule: inclusive band edges, the flat amount for every non-worker who isn't exempt, and the exemptions. Its remunerative-work-hours parameters, including the Northern Ireland reference, stay as main has them.

Invariants

The property tests check these over generated populations (10 examples each, with explicit cases for rare combinations), the exhaustive edge test checks invariant 7, the fixture comparison invariant 8, test_rent_of_sharers_boarders_and_lodgers_properties.py invariant 10, differential runs invariant 14, and test_non_dependant_cycle_contracts.py and test_non_dependant_population_consistent_total.py invariant 15:

  1. The council deducts nothing for an exempt non-dependant, for HB and CTR.
  2. For an eligible, non-exempt non-dependant the council deducts between the scale's lowest and highest amounts. The council makes no national CTR deduction in English working-age households.
  3. For a non-dependant working under 16 hours, or (for HB) on IS, JSA(IB) or ESA(IR), the council deducts the lowest amount whatever their income.
  4. The council deducts nothing when the claimant or applicant has an exemption.
  5. A family's deductions equal the sum, over the other families in the household, of each family's higher couple amount (both for a Welsh working-age applicant's non-dependant couple whose own Universal Credit award assesses either member) plus each other member's, times the family's share of the rent (HB) or council tax liability (CTR). The generated populations vary is_uc_claimant, so another member's award as a single claimant appears.
  6. Raising a non-dependant's income never lowers any deduction.
  7. In every year 2015-2030 (2019-2030 for HB), the council deducts a band's amount for an income at its lower edge, and the band below's amount for the next lower representable income.
  8. Every CTR parameter value equals the enacted sums in tests/fixtures/council_tax_reduction_non_dependant_deductions.yaml.
  9. The exemption flags equal a reading of the provisions that the test computes from the inputs, with Universal Credit's earned income from invariant 13.
  10. HB deductions over the families liable for a household's rent equal the pooled amount (one per couple, the higher, with sharers never in the pool) times the share that families without an exempt claimant bear, and never exceed the sum of individual deductions.
  11. For a non-dependant in remunerative work who is neither exempt nor on IS, JSA(IB) or ESA(IR), the council deducts the amount of the band containing the gross income that the test computes from the inputs.
  12. Universal Credit's cap counts the Housing Benefit cap's welfare benefits other than Housing Benefit. No baseline formula on Universal Credit's route calculates Housing Benefit: its traced calculation reads no Housing Benefit variable but the reported award, and, where the inputs supply the combined total, the supplied housing_benefit_pre_benefit_cap. A reform can add a read of calculated Housing Benefit, and its cycle raises CycleError. The Universal Credit in a non-dependant family's gross income equals universal_credit plus uc_deductions. That sum equals the award before the cap less the cap reduction applied, under current law and under an 85% protected minimum floor, and the floor never lowers an award. Under a reform that scales universal_credit, each household's HB and CTR deductions equal those of the same household with the reformed award supplied. The HB deduction equals the band of wages plus that award, so it moves when the award crosses a band.
  13. For each claimant the award assesses, the Universal Credit exemption agrees with uc_individual_earned_income across employment expenses, trading losses, company income, pension contributions and single and couple floors, and with the inputs wherever they settle it without the tax and National Insurance calculation.
  14. At 02cc0896f, under current law and under the 85% floor, main and that head gave bit-identical uc_benefit_cap_reduction, uc_benefit_cap_reduction_after_floor, uc_deductions and universal_credit arrays for 60 families on Universal Credit, and main's cap formulas and that head's gave identical arrays over 4,096 rows of inputs, with and without a supplied cap reduction and a protected floor. At 95fbf3014 the same 60 families give arrays bit-identical to 02cc0896f's. Where a family supplies Housing Benefit alongside Universal Credit, which the model's Housing Benefit eligibility rules out when it calculates both, the basket differs from main's combined total less Housing Benefit by at most £0.004, one float32 step.
  15. Inputs that supply benefit_cap_welfare_benefits also supply housing_benefit_pre_benefit_cap or uc_benefit_cap_welfare_benefits; otherwise the model raises ValueError when it calculates Universal Credit's cap, with or without a non-dependant in the population. A value stored with set_model_value supplies neither, and a reform that gives the combined total a formula leaves Universal Credit's basket on its own sum. When the inputs give each benefit unit its own values, each household's Universal Credit, Housing Benefit and HB non-dependant deductions equal its results alone, for any mix of independent households and whichever result the first request asks for. A situation that sets either input for some benefit units stores nil for the rest.

Tests

  • YAML (hand-worked expectations from the law):

    • HB band edges in and out of remunerative work (2026-27), 2025-26 cases in four of the six bands, couples, a dependent child's income, each exemption and its age boundary, each claimant exemption (a partner's PIP included), and aggregation, where a family with a 0.5% share of the rent is a joint occupier.
    • CTR band edges for each scheme (2026-27, with 2025-26 checks), every exemption at age 40, the UC-limb start dates, couples including the Welsh UC rule, the applicant exemption, and jointly liable applicants under different schemes.
    • The Universal Credit exemption follows Universal Credit's earned income. Wages cancelled by allowable expenses, or a profit cancelled by a trading loss, leave none, so the council deducts nothing. A company's income attributed to its owner counts, so the councils deduct £1,060.80 (HB) and £348.40 (Welsh CTR) a year. A partner whose earnings meet the couple threshold keeps the minimum income floor off the other member, so the Welsh deduction for the couple comes to £348.40, not £696.80.
    • In Wales, a third adult's own Universal Credit leaves the couple one deduction: £694.20 a year, and £521.30 for each of two jointly liable applicants on different schemes.
    • The gross-income variables count other investment income, ISA interest and ISA dividends (£9,000 of wages, £20,000 of exempt interest and £30,000 of ISA dividends give £59,000), and treat a self-employment or property loss as nil.
    • A family's Universal Credit counts in the income of the claimant it assesses, and another adult's own income-related ESA stays out of the couple's income: £2,121.60 in each case, where counting the award in the parents' income would give £4,407.00.
    • A non-dependant family's Universal Credit counts after the cap: DWP counts the partner's £6,000 of Maternity Allowance and cuts the award from £14,000 to £10,890.20, so the couple's £600.38 a week falls in the £485 bands, £6,232.20 HB and £691.60 CTR.
    • Universal Credit's cap leaves out Housing Benefit the family receives. With £15,000 of Universal Credit, £5,000 of child benefit and £10,000 of Housing Benefit, the Housing Benefit cap counts £30,000 and Universal Credit's £20,000, so the award comes to £9,753. A supplied £26,000 total with £6,000 of Housing Benefit gives a £14,753 award.
    • End to end: a 40-year-old claimant paying £6,000 rent with a zero-income full-time student gets £6,000 of HB; a Guarantee Credit pensioner whose son earns £30,000 at 37.5 hours has a £691.60 deduction and £1,308.40 of CTR (National council tax reduction: non-dependant deductions use Housing Benefit amounts and skip exemptions; pensioner gate ignores benefit exclusions #1934's example).
  • Main's tests that change. Each move follows from the law:

    • household_benefits_individual_non_dep_deduction.yaml:
      • "Other investment income does not band a non-worker": £0 becomes reg 74(1)(b)'s £19.65 × 52 = £1,021.80.
      • "Other investment income does not move a worker's band" becomes "A worker's band counts their investment income": £739.02 a week falls in the top band, £126.65 × 52, where main banded the earnings alone at £45.15.
      • The 2018 case keeps HB at £793. Its CTR half moves to Wales's own scale, £4.35 a week from 1 April 2018 (WSI 2018/14), £226.20; England has no national scheme for a working-age household.
      • Every single-person case now holds the family's awards at zero. The council bands a claimant or partner of the non-dependant's family on the couple's income plus those awards (reg 74(4)), so without them the case's stated income wouldn't be its gross income.
    • housing_benefit_non_dep_remunerative_work.yaml (Split total income into programme-specific concepts #2620): for the nil-income non-worker the council deducts £20.40 × 52 = £1,060.80 (reg 74(1)(b)), not £0; the under-25 on Income Support now reaches £0 through the reg 74(8)(a) exemption, and the case computes eligibility instead of taking it as an input.
    • council_tax_reduction_non_dep_remunerative_work.yaml (Split total income into programme-specific concepts #2620), 37 cases: each scheme's 2026-27 scale replaces the HB scale. For non-workers the councils deduct £270.40 (England), £288.60 (Scotland) or £348.40 (Wales) a year, and for £403.85 a week in work £551.20, £569.40 or £694.20. For a recipient of IS, JSA(IB) or ESA(IR) they deduct nothing (para 8(8)(a)), and for the nil-income non-worker England's £270.40. Each English case with a 30-year-old non-dependant now gives the household a separate 70-year-old applicant, and the model works out each family's scheme.
    • council_tax_reduction_non_dep_gross_income.yaml (Split total income into programme-specific concepts #2620), 15 cases: £183.08 a week falls below every scheme's first band edge, so each scheme deducts its 2025-26 lowest amount (England £260, Scotland £278.20, Wales £325 a year) and the reduction comes to £4,000 less that.
    • test_non_dependant_spousal_maintenance.py: the national CTR comparison moves to Wales, whose scheme covers working-age applicants: [£348.40, £348.40, £694.20, £348.40].
    • housing_benefit_non_dep_deductions.yaml: for the non-worker with investment income the council deducts £19.65 × 52 = £1,021.80, where main's right=True gave £0.
    • council_tax_reduction_individual_non_dep_deduction.yaml, on England's 2025-26 pensioner scale (SI 2025/39): for a non-worker with £556 a week of investment income the council deducts £5.00 × 52 = £260, and a worker's band counts it, £15.35 × 52 = £798.20.
    • council_tax_reduction_pension_credit.yaml: for the son with no hours the council deducts England's £5.20 a week, so CTR is £1,729.60. claimant_family_scheme.yaml: for the 40-year-old adult child it deducts £5.20 × 52 = £270.40, so CTR is £1,529.60.
    • housing_benefit_individual_non_dep_deduction_eligible.yaml: the 18-year-old "Eligible" case sets current_education and would_claim_uc: false; otherwise the age default makes them a student, and an income-free 18-year-old has Universal Credit without earnings.
    • test_rent_of_sharers_boarders_and_lodgers_properties.py: the HB pool counts one deduction per couple, the higher, and only families without an exempt claimant bear it (invariant 10).
    • benefit_cap_by_scheme.yaml, "UC reg 81(2) - no reduction where the childcare costs element exceeds the excess": the case supplies the combined total, so it now supplies the £0 of Housing Benefit the model calculates for it, and its outputs don't change. The other eight YAML cases that supply the total already supply housing_benefit_pre_benefit_cap. Before this change only one Python test supplied the total, the one in test_non_dependant_deduction_properties.py that this PR rewrites.
    • pension_age_severe_disability_conditions.yaml, claimant_household_head.yaml, housing_benefit_non_dep_gross_income.yaml and test_housing_benefit_non_dep_history.py change only in comments, apart from the new ISA case in the gross-income file.
  • test_non_dependant_deduction_properties.py: Hypothesis populations for invariants 1-6, 9 and 11, which vary is_uc_claimant, with explicit examples for the rare Welsh-couple and under-25 cases; the exhaustive edge check (7); the fixture comparison (8); and the earned-income comparison (13). For invariant 12, one property compares the basket and the award under current law and an 85% floor with debt deduction rates; its explicit example takes £720.25 of debt and no cap reduction, so the award counts £20,000. Another scales the award by a reform, and its explicit example moves a non-dependant from the 348 band to the 266 band. A third traces Universal Credit, which reads no calculated Housing Benefit, and checks that a supplied total without Housing Benefit raises ValueError. The invariant 12 tests build core simulations on one tax-benefit system for the module.

  • test_non_dependant_cycle_contracts.py (invariant 15) uses two independent households: a single Universal Credit claimant with £20,000 before a £14,753 cap and no rent, and a 70-year-old paying £6,000 rent with a non-dependant who earns £9,000 at 20 hours and claims no Universal Credit. For the second's non-dependant, the council deducts £19.65 × 52 = £1,021.80 (HB regs 70 and 74(2)(a), 2025-26), leaving £4,978.20 of Housing Benefit.

    • With the combined total and Universal Credit's basket supplied, the population gives Universal Credit of [£14,753, £0, £0] and Housing Benefit of [£0, £4,978.20, £0], each household's results alone, whether the first request asks for Universal Credit, Housing Benefit or the family deduction.
    • With Housing Benefit supplied instead, the basket counts the total less that Housing Benefit: £26,000 less £6,000 leaves £20,000, and the award comes to £14,753.
    • A supplied total with neither raises ValueError, with or without the non-dependant. set_model_value supplies neither input, and a reform that gives the total a formula leaves Universal Credit's basket on its own sum.
    • For any mix of up to three independent households, some with a non-dependant on Universal Credit, and Universal Credit's basket supplied, a Hypothesis property checks that each household's Universal Credit, Housing Benefit and deductions equal its results alone.
    • Reforms that add calculated Housing Benefit to Universal Credit's basket or to its award before the cap raise CycleError. With nothing supplied, a traced run shows Universal Credit reading only reported Housing Benefit, and the national and five local CTR schemes then read Universal Credit.
  • test_non_dependant_population_consistent_total.py takes each household's combined total and Housing Benefit from its own simulation with nothing supplied (£20,000 and £0; £4,978.20 and £4,978.20). Supplied together for the mixed population, they give each household its results alone for each first request, and the totals without the Housing Benefit raise ValueError.

  • code_health/test_benefit_cap_lists_unique.py checks that each name occurs once in the lists the two caps and a non-dependant family's income add up.

  • Mutation testing. At 97f792542 the tests killed the mutants of the earned-income limb, the Welsh couple rule, either exemption's earned-income test, the ISA income, the joint-occupier share and the partner's PIP. At 86e86d3db they killed every other mutant of the rules except one that removes CTR's reg 10(6) work exclusion, which changes no result, because each scheme's para (8)(a) exemption already gives such a non-dependant no deduction. At 02cc0896f, the tests killed nine of ten mutants of the award and the cap's basket:

    • the award dropping its debt deductions, rebuilding itself from the cap variables in place of universal_credit, or counting Universal Credit before the cap;
    • uc_benefit_cap_reduction reading the combined total with Housing Benefit; a supplied combined total never reaching the cap, or keeping its Housing Benefit; the basket leaving out income-based JSA and income-related ESA;
    • housing_benefit_non_dep_deductions reading income with no non-dependant, or returning nil for every household.

    The survivor adds debt deductions to a nil award. It changes no result under current law, where a positive deduction always leaves at least a penny of award. The basket leaving out child benefit and the cap ignoring the childcare costs element also failed their tests at 02cc0896f. This head deletes the early return that the no-non-dependant mutant changed.

    We also ran two mutants of the supplied-total contract. Restoring the calculated read, so that the basket subtracts housing_benefit_pre_benefit_cap from a supplied total whether or not the inputs supply it, makes test_a_supplied_total_alone_raises[mixed] and [single] raise CycleError instead of ValueError, and test_model_values_supply_neither_input stops raising. Dropping the ValueError, so that a supplied total without Housing Benefit counts in full, makes all three stop raising.

  • Differential against main. In 20 households with 40 people in five English councils (Merton, Kingston, Newham, Westminster and Oxford), each non-dependant earns £9,000 and has nothing else, £20,000 of other investment income, a £5,000 self-employment loss or a £5,000 property loss. At 86e86d3db, main 2da212abc and the head gave identical arrays for all five local schemes' reductions, individual and family non-dependant deductions, and the shared eligibility, applicant-exemption, pensioner and joint-liability flags: 19 of 21 arrays. The two national gross-income arrays differed in the 15 investment and loss cases: £9,000 becomes £29,000 with investment income, and £4,000 becomes £9,000 with a loss.

  • Runs, locally with no survey data, each file alone under a 4 GB memory cap (2 GB for mutants), at 95fbf3014 unless stated. 2667976b8 then adds only a module-scoped fixture that clears scaled_award_system's cache.

    • YAML: gov/dwp/benefit_cap/ 118 passed, the HB non-dependant directory 139, gov/local_authorities/council_tax_reduction/ 376, and benefit_cap_by_scheme.yaml 22 at 2667976b8.
    • test_non_dependant_cycle_contracts.py 14 passed, test_non_dependant_population_consistent_total.py 5, test_non_dependant_deduction_properties.py 10 (also at 2667976b8), test_uc_deductions.py 24, test_benefit_cap_uc_and_hb_properties.py 1, the sharers properties 6 and the LHA scheme-split properties 7.
    • code_health/ without test_changelog_fragments.py: 2,999 passed and 2 failed. test_pytest_default_collection_confirms_function_and_class_names fails only when PYTEST_ADDOPTS carries -n 0 into its nested pytest, and passes without it. test_every_test_module_cache_is_released found scaled_award_system's module-level cache, which 2667976b8 releases; at 2667976b8 that file reports 5 passed. test_changelog_fragments.py, run on its own with towncrier installed, reports 103 passed.
    • The 60-family cap differential gives bit-identical uc_benefit_cap_reduction, uc_benefit_cap_reduction_after_floor, uc_deductions and universal_credit arrays at 95fbf3014 and 02cc0896f, under current law and under the 85% floor.
    • ruff format --check, ruff check, ruff check --select F811,F821 and git diff --check against main report nothing.
    • At 4ec0693f7, whose two commits after 2667976b8 change only documentation strings, comments, the changelog and the ValueError message's text (no test asserts the changed wording; with every string literal blanked, each changed Python file parses to the same tree): test_non_dependant_cycle_contracts.py 14 passed, test_non_dependant_population_consistent_total.py 5 and gov/dwp/benefit_cap/ YAML 118 at c2c55086e; test_non_dependant_deduction_properties.py 10, code_health/test_test_module_caches_released.py 5 and test_parameter_descriptions.py 24 at 4ec0693f7; code_health/ with towncrier installed 3,104 passed at c2c55086e; ruff format --check, ruff check, ruff check --select F811,F821 and git diff --check report nothing at 4ec0693f7.

Impact (Enhanced FRS 2024-25, certified uk-data 1.58.1)

CI's aggregate impact job (run 42049540193) compared main e3765cb61 with a00efbd699, its merge of this head into that main. It ran each fiscal year as a full microsimulation on both sides, on the certified dataset (sha256 056e80fd8e91), broke no check and flagged no move for review.

Change 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Housing Benefit (£m) +16.1 +11.2 +11.6 +12.0 +12.3 +13.0 +12.5
Council Tax Reduction (£m) +6.1 +6.5 +6.8 +7.2 +8.5 +8.9 +9.4
Household net income (£m) +22.2 +17.7 +18.4 +19.3 +20.8 +22.0 +21.9
Housing Benefit caseload +238 +30 +30 +30 +31 +31 +31
CTR caseload (k) +6.0 +6.2 +6.2 +6.3 +5.9 +5.9 +6.0

Tax receipts and population don't change in any year. We estimate that absolute poverty after housing costs falls by 2.2k to 7.2k people a year, and absolute poverty before housing costs rises by 47 to 1.3k. Relative poverty after housing costs falls in six years, by up to 7.1k (2026-27), and rises by 322 in 2031-32. The flat charge for non-workers and the wider gross-income rules leave some households worse off.

Merge with main

0198ce4c98b3ec37f8bf6d11a5c125c8cc8b7865 merges main at 5c7744144 into fde435bd3 with a merge commit. Git stopped on two files, and the merge keeps both sides in each:

  • docs/book/programs/gov/dwp/housing-benefit.ipynb, step 3 ("Means test"). This PR adds the sentences on reg 74's bands, couples and exemptions after the reg 70 sentence. main appends sentences on applicable income: income tax and National Insurance come off earnings, pensions and benefits, regulation 46(4) and the pension-age Schedule 5 paragraph 22 treat income from capital as capital, and the rent for letting part of the home counts less £20 a week per occupier. The merged sentence runs reg 70, this PR's sentences, the taper and Northern Ireland sentences, then main's.
  • policyengine_uk/tests/shard_durations.toml: this PR's four test_band_edges_are_inclusive timings and main's seven test_person_isa_capital.py timings with their comment, at the same place.

Git merged every other file, uc_benefit_cap_reduction.py, supplied_inputs.py, programs.yaml and council_tax_reduction_pension_credit.yaml among them, without a conflict. At the merge, this PR's 20 YAML test files report 339 passed, and its seven Python test files (test_benefit_cap_lists_unique.py, test_housing_benefit_non_dep_history.py, test_non_dependant_cycle_contracts.py, test_non_dependant_deduction_properties.py, test_non_dependant_population_consistent_total.py, test_non_dependant_spousal_maintenance.py and test_rent_of_sharers_boarders_and_lodgers_properties.py) report 48 passed, so main moved none of this PR's results. main did move one test of its own: benefit_cap_reduction_applied.yaml, new on main, supplies benefit_cap_welfare_benefits alone in seven cases, so at the merge each raised this PR's ValueError (invariant 15) and gov/dwp YAML reported 7 failed and 961 passed. 6900b88b7 supplies the nil Housing Benefit the model calculates for each case, which has no rent, as this PR did for benefit_cap_by_scheme.yaml; the outputs don't change, and the file reports 8 passed. main's #2815 adds uc_receives_attendance_allowance, which this PR doesn't read: the HB and CTR claimant exemptions follow their own regulations and count armed forces independence payment separately.

At 6900b88b7006fbc96690839ff297f16003459c23, locally with no survey data, each run under -n 4:

  • policyengine-core test on finance/benefit/family 1,808 passed, gov/dwp 968 passed and gov/local_authorities/council_tax_reduction 391 passed, none failing;
  • test_non_dependant_deduction_properties.py 10, test_council_tax_reduction_claimant_properties.py 3, test_household_head_agreement_properties.py 3 and test_uc_afip_housing_contribution_properties.py 3 passed; the other files that read uc_non_dep_deductions, uc_receives_attendance_allowance or uc_is_ineligible_partner: test_benefit_cap_lha_scheme_split_properties.py 7, test_rent_of_sharers_boarders_and_lodgers_properties.py 6, test_uc_legacy_closure_properties.py 4, test_uc_non_dep_pension_credit.py 3, test_uc_receiving_education_properties.py 5, test_uc_single_claim_properties.py 8 and test_uc_work_related_groups_properties.py 14 passed; policyengine_uk/tests/code_health 3,168 passed;
  • uvx ruff@0.17.2 format --check ., ruff check . and ruff check --select F811,F821 policyengine_uk report nothing; git diff --check over this branch's new commits reports nothing; git merge-tree --write-tree --name-only HEAD origin/main against main at 5c7744144 reports no conflict; .github/check_fix_pr.py passes on this title and body.

7e239f148048fb4735154b2b167185659d8aaa35 merges main at 47a2817e2 into 6900b88b7, which brings in #2811. Git stopped on one file, the documentation of benefit_cap_welfare_benefits. The merge keeps this PR's sentences on the two baskets and the supplied total, and #2811's sentences on Maternity Allowance and the other welfare benefits. 4f3962360f4e9f042ea705859ffda507ae9c4221 moves the case "A non-dependant family's Universal Credit counts after the benefit cap" to the figures its comment worked out: universal_credit [£0, £10,890.20], housing_benefit_non_dep_deductions [£6,232.20, £0] and council_tax_reduction_non_dep_deductions [£691.60, £0]. The local runs above read 6900b88b7; CI tests this head.

ce2aef8c6f393e87d973cfa069ba09cb5d4764a9 merges main at fed9bcabe into 4f3962360, which brings in #2813, #2819 and #2826. Git stopped on one file, the comment above CLAIMANT_OR_PARTNER_BENEFITS in benefit_cap_welfare_benefits.py. The merge keeps this PR's sentence on uc_benefit_cap_welfare_benefits and #2813's sentences on a family receiving both awards. #2813's case in esa_income_abolition.yaml reads benefit_cap_welfare_benefits and uc_benefit_cap_reduction as outputs, and this PR's basket gives its £2,442.20 reduction: £24,462.20 of benefits against the £22,020 cap.

ae4721f2457622593bd1b0e64ad2980f6e83dfe1 sets would_claim_uc_at_legacy_closure: false on two cases in non_dependant_deduction_review_cases.yaml. #2813's esa_income_abolition_reached moves a couple reporting income-related ESA to Universal Credit in 2026, and the award joins the band income: in the case "Income-related ESA is payable to the recipient, not the working partner", the partner moves from the £192 band to the £279 band, and the deductions rise from £2,436.20 to £3,346.20 for Housing Benefit and from £270.40 to £551.20 for Council Tax Reduction. These two cases test how income-related ESA counts in the band, so the couples stay on it. Their figures don't change.

Merge order and overlaps

Axiom

axiom: TheAxiomFoundation/rulespec-uk#407 queued

rulespec-uk encodes none of HB reg 74, HB (SPC) reg 55, England's para 8 or Scotland's regs 48 and 90. Its Welsh Sch 1 para 3 and Sch 6 para 5 modules band a supplied gross income at 2026-27 amounts and defer the exemptions, the couple rule and apportionment. #407 holds the encoding brief, which binds the non-dependant's Universal Credit income to the award after the cap and before debt (uc_pilot_award_amount); no separately approved billed signed-encoder run exists for it.

Reproducer: policyengine_uk/tests/test_non_dependant_deduction_properties.py::test_band_edges_are_inclusive, policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_non_dep_deduction_chain.yaml

🤖 Generated with Claude Code

@MaxGhenis

MaxGhenis commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator Author

Composition note: #2015 (stacked on #2009 → #2006) deletes the two household flags this PR reads. council_tax_reduction_household_has_pensioner becomes council_tax_reduction_pensioner and council_tax_reduction_household_has_non_dep_exemption becomes council_tax_reduction_applicant_has_non_dep_exemption; both are BenUnit, so each jointly liable claimant uses its own family. #2015 does not add the national applicant exemption; that stays here. Whichever lands second should do three things:

  1. Read the applicant's own exemption flag for applicant_exempt.
  2. Do not substitute the family flag into each_member_deducted: that is evaluated on the non-dependant family, and Sch 6 para 5(3) turns on the applicant's scheme. Instead build two household pools of non-dependant families' deductions, the higher member's and the sum for a non-dependant couple on UC. Each claimant takes the sum pool when it is in Wales and its own council_tax_reduction_pensioner is false, and the higher-member pool otherwise.
  3. Pick England's pensioner scale in the person-level variables by country only. Each claim's own scheme already decides whether its award uses it. The two tests that pin "no national deduction in English working-age households" then check that the working-age award is unaffected instead.

See the "National schemes and the exemption" section of #2015. (Corrected per this PR's author.)

MaxGhenis added a commit that referenced this pull request Oct 1, 2026
Cherry-picks #2007 (59c7082: HB reg 74 / SPC
reg 55 bands, remunerative-work test, couple rule and exemptions; national
CTR own scales and exemptions) onto the in-unit non-dependant change (#2014)
and the #2006 rent shares, as agreed with #2007's author:

- HB and national CTR aggregation: one deduction for a non-dependant couple
  (the claimant and partner of a non-dependant family, the higher amount;
  both for a Welsh working-age couple on UC), any other adult in that family
  counted separately, in-unit non-dependants pooled and apportioned by rent
  share (HB) or joint-liability share (CTR), and the claimant's exemption.
- Joint income for the bands is the couple's only (reg 74(4), para 8(4));
  another adult is banded on their own income.
- The IS, income-based JSA, income-related ESA, Pension Credit and UC
  exemptions apply only to the award's claimant and partner.
- The HB claimant exemption reads is_claimant_or_partner, not is_adult.

Drops council_tax_reduction_pension_credit.yaml and the
in_receipt_of_guarantee_credit output, which depend on #1909 (main only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 1, 2026
Cherry-picks #2007 (59c7082: HB reg 74 / SPC
reg 55 bands, remunerative-work test, couple rule and exemptions; national
CTR own scales and exemptions) onto the in-unit non-dependant change (#2014)
and the #2006 rent shares, as agreed with #2007's author:

- HB and national CTR aggregation: one deduction for a non-dependant couple
  (the claimant and partner of a non-dependant family, the higher amount;
  both for a Welsh working-age couple on UC), any other adult in that family
  counted separately, in-unit non-dependants pooled and apportioned by rent
  share (HB) or joint-liability share (CTR), and the claimant's exemption.
- Joint income for the bands is the couple's only (reg 74(4), para 8(4));
  another adult is banded on their own income.
- The IS, income-based JSA, income-related ESA, Pension Credit and UC
  exemptions apply only to the award's claimant and partner.
- The HB claimant exemption reads is_claimant_or_partner, not is_adult.

Drops council_tax_reduction_pension_credit.yaml and the
in_receipt_of_guarantee_credit output, which depend on #1909 (main only).

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

Copy link
Copy Markdown
Collaborator Author

Response to review r1 of #2007 (59c7082)

The review asked for changes and was right on all five P1s. Each is fixed below with the law, the change, and a test that fails on 59c7082 and passes now.

# Finding Disposition Change Test
1 is_adult is not "claimant or partner" (an 18-year-old dependant's PIP exempted the claimant; a 17-year-old claimant's PIP did not) Fixed New helper is_claimant_or_partner: the benefit unit's head, or any member who is not an SSCBA s.142 child or qualifying young person (is_child_or_qualifying_young_person_for_child_benefit, already on main). That is how HB Regs 2006 regs 2(1) and 19 and the English and Welsh CTR regs (reg 2(1)) define a family's members other than claimant and partner. It is used in the HB claimant exemption and in council_tax_reduction_household_has_non_dep_exemption (which the local schemes also read) non_dependant_deduction_review_cases.yaml: "A dependent 18-year-old's PIP does not exempt the claimant" (HB £6,232.20, CTR £691.60); "A 17-year-old claimant's PIP exempts their family"
2 A dependent child's income was added to the couple's Fixed Joint income (reg 74(4), para 8(4)) sums only the claimant and partner of the non-dependant's family. Other members are banded on their own income. One deduction per couple (reg 74(3)) covers only the couple; each other member is deducted separately "A parent non-dependant is banded on their own income…" (HB £1,060.80, CTR £270.40); household_benefits_individual_non_dep_deduction.yaml "A dependent young person in the family is banded on their own income"
3 HB reg 6(6): a person on IS, JSA(IB) or ESA(IR) is not in remunerative work Fixed The HB individual deduction treats the claimant or partner of an IS, JSA(IB) or ESA(IR) award as not in remunerative work. CTR exempts those recipients outright, so it needs no equivalent "On income-related ESA, 16 hours is not remunerative work" (£1,060.80)
4 A self-employment loss cancelled wages in the UC no-earned-income test Fixed has_earned_income: employment income plus self-employed earnings floored at nil (UC reg 57(2) step 3) "A self-employed loss does not cancel wages…" (both exemptions false; HB £1,060.80, CTR £270.40)
5 Taxable total_income omits UC, tax credits and child benefit Fixed Gross income for a claimant or partner adds the family's UC (before the benefit cap, which would be circular through HB), CTC, WTC and child benefit to the couple's taxable income. The disability benefits disregarded by reg 74(9) are still excluded, since they are untaxed and not added "Universal Credit counts towards gross income" (£290 a week, £3,346.20)

Also from the review:

  • Benefit receipt (IS, JSA(IB), ESA(IR), SPC, UC) now exempts only the claimant or partner of the award, in both exemption variables.
  • Two omissions are now listed as not modelled, because no input exists:
    • pension-age HB's 26-week deferral of a deduction increase (SI 2006/214 reg 59(10)-(13));
    • the CTR applicant exemption's protection while AA, DLA or PIP is suspended in hospital (para 8(6)).
  • The property tests' generator now includes dependent young people with their own income (in full-time education, or on approved training and so not exempt) and self-employed losses. The oracles use the claimant-or-partner rule, reg 6(6) and the floored earnings.
  • The Welsh "award of universal credit" reading stays as stated in the PR description, an interpretation.

Not changed: the review's UNVERIFIED items on impact (exact zeros, decomposition, poverty causation). The impact is rerun on the new head after the host is free (the Axiom build has priority until about 10:00Z). The PR description's impact section is replaced with the rerun.

MaxGhenis added a commit that referenced this pull request Oct 2, 2026
Applies #2007 at 6257277 (59c7082 plus the
r1 review commit) onto #2017 (in-unit non-dependants, #2006 rent shares):

- One definition of claimant or partner: #1896's is_claimant_or_partner
  variable. #2007's proxy helper (benefit-unit head or any member who is not
  a Child Benefit child or QYP) is removed and its call sites read the
  variable.
- HB and national CTR aggregation: another family's non-dependants per
  deduction_per_family (the couple's higher amount, both for a Welsh
  working-age UC couple, and each other member's own), counted once per
  family and apportioned by rent share (HB) or joint-liability share (CTR);
  a family's own in-unit non-dependants in full (LHA Guidance Manual 2.093;
  HBGM A5 5.622); the claimant exemptions. charged_to_other_families
  ("household minus own family") is no longer used and is removed.
- #2007's property oracle reads is_claimant_or_partner; the in-unit
  property oracle counts a couple once and applies the claimant exemptions.
- Drops council_tax_reduction_pension_credit.yaml and the
  in_receipt_of_guarantee_credit output (#1909, main only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 2, 2026
Carries 2d54628 (review r2 F3-F5) onto the composition with #2007:
- HB pools every non-dependant family amount, in-unit ones included, through
  #2007's deduction_per_family and apportions it by rent share; a boarder or
  lodger also bears its own in full; the claimant exemption still zeroes the
  claim.
- National CTR pools every family's amount (one per couple, the higher, or
  both for a Welsh working-age UC couple), replaces the applicant's own
  family amount with its in-unit non-dependants only, and apportions by
  joint-liability share; the per-applicant exemption still zeroes the claim.
- CTR eligibility includes a lodger's adult son; the local schemes stay
  neutral.

The blind-sharer case now expects the non-exempt head to bear half of the
sharer's son (para 8(5)).

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

Copy link
Copy Markdown
Collaborator Author

Response to review r2 of #2007 (6257277)

The review asked for changes again and was right on all five P1s. Findings 2-5 are fixed in the next commit, each with a YAML test that fails on 6257277 (non_dependant_deduction_review_cases.yaml, "Round 2" section). Finding 1 is fixed by adopting #1896's is_claimant_or_partner when #1896 lands; it is about to merge, and its owner asked #2007 to use it.

# Finding Disposition Change Test
1 Claimant or partner via "head or not a child benefit QYP" misclassifies explicit young partners (HB reg 2(1) "partner"), and in the reverse direction a third adult; this reaches the English local schemes through the shared exemption flag Fixed with #1896 The helper is replaced by #1896's is_claimant_or_partner, which caps the set at the head and one partner (SSCBA s.137 couple) and can be supplied as an input for explicit couples. The r1 original student case also resolves, since #1896 presumes a 19-year-old 26 years younger than the parent is their child The r2 explicit-couple cases are added after the rebase, along with the r1 cases
2 "No earned income" missed SMP, relievable pension contributions and the minimum income floor (UC regs 55 and 62) Fixed has_earned_income: employment income plus SSP and SMP, less relievable pension contributions (reg 55(4)-(5)); self-employed earnings, a loss nil (reg 57(2)), with the minimum income floor where the UC model applies it (reg 62); and other paid work SMP £8,000 → not exempt (£1,060.80/£270.40); wages all paid into a pension → exempt (£0/£0); MIF applies → not exempt (£1,060.80/£270.40)
3 ESA(IR) receipt was extended to the working partner (HB reg 2(3A): "payable to him") Fixed is_award_payee: IS, JSA(IB), ESA(IR) and SPC count only for the member who reports the award, or the head where none does; UC still counts for both joint claimants (HB reg 2(3B): entitlement). Used for the exemptions and for HB reg 6(6) Recipient and partner earning £210 a week at 16 hours → HB £2,436.20, CTR £270.40
4 Pre-cap UC overstated gross income (UC reg 81) Fixed Gross income now uses UC after the benefit cap. The cap is applied to the capped benefits other than Housing Benefit, which a non-rent-liable family does not receive; this avoids the HB → UC → cap → HB cycle Pre-cap £30,000, cap £20,000 → HB £5,470.40 (365 band), CTR £551.20
5 CTR's LGFA 1992 Sch 1 para 3 disregard (an adult for whom another person is entitled to child benefit) was omitted although inputs exist Fixed The CTR exemption adds an adult qualifying young person (child benefit, s.142) who is not the claimant or partner of their family 19-year-old on approved training with child benefit → CTR exempt, parent alone £270.40; HB still charges both (£3,497.00)

On the property tests, the review said the oracles repeat the implementation's classification. They now take the payee as the head (the generator reports no awards) and read the child benefit QYP flag from the model. After the #1896 rebase they read is_claimant_or_partner from the model, which is not code under test. The generator now draws dependent young people and losses. SMP, pension and MIF cases are pinned in YAML rather than generated.

@juaristi22

Copy link
Copy Markdown
Collaborator

Codex review — changes required

Reviewed commit e92b5d107d48fe6d6cd62927059e092a75d585c2. PARTIAL; 1 confirmed blocking finding(s). Independent code/test and policy/source reviews were consolidated for the changed behavior and affected dependencies.

code-1 — [P1] Replace the QYP-complement helper with actual claimant/partner roles

The current helper _non_dependants.py:28–36 still defines the claimant/partner set as the head or anybody who is not a Child Benefit child/qualifying young person. That is neither a relationship test nor a two-person limit. It can exclude an actual young partner in education and include a third adult. The helper controls the new disability exemptions, joint gross income and one-deduction-per-couple aggregation, so this is a material remaining integration blocker. The author's latest response also explicitly defers this finding to adoption of #1896; the captured head has not adopted it.

Executed illustration: a reported continuing HB award for an intended couple aged70/19, both parent-flagged, with the19-year-old in qualifying education and receiving standard PIP daily living, plus a separate40-year-old working non-dependant earning£30,000. The QYP complement excludes the young partner and sets the claimant exemption false. With £10,000 council rent and HB assessable income isolated at zero, the deduction is£6,232.20 and final HB£3,767.80. A control supplying the legally appropriate claimant/partner disability exemption gives deduction£0 and final HB£10,000. This snapshot lacks the canonical explicit claimant/partner input; the diagnostic's relationship is stated as the intended premise, while the code defect is the inability of this helper to honor that premise.

Adopt the canonical relationship/role variable after the prerequisite lands, then add explicit young-partner and third-adult regression cases through final awards. Do not repeat the other four r2 findings: the current tree now includes statutory sick/maternity pay, employed pension deductions and MIF; the benefit-payee test; UC after the benefit cap; and CTR's adult Child Benefit QYP exemption. Confidence: high for the current unresolved helper/integration defect.

Independent source review corroborates the actual-couple and claimant/partner conditions in HB regulation 74(3)–(6), HB interpretation and England CTR paragraph 8. The overlapping code and policy finding is counted once.

The current tree addresses round-two findings 2–5; this review does not repeat those stale blockers. Current 2026 scale values were corroborated. The remaining confirmed blocker is adoption of the canonical claimant/partner roles and coverage of their downstream HB/CTR behavior.

Outstanding evidence/decision:

  • The prerequisite claimant/partner implementation is absent, so the eventual composed behavior cannot yet be tested.
  • Historical CTR rates/dates for 2013–2025 were not all corroborated. Complete UC net-earnings treatment, including self-employed pension contributions, remains source-unresolved and is not asserted as a second confirmed defect.

Validation: 123 focused tests passed, including round-two regression tests. An executed controlled diagnostic isolates the missing partner disability exemption's final-HB effect. The intended relationship is an explicit scenario premise, and supplied benefits/HB assessable income isolate this path; the run does not establish any actual data record's demographics. Six official source originals were inspected and their cached bytes verified.

Focused tests used an existing cached Python 3.13 environment (Core 3.32.9, NumPy 2.1.3, pandas 2.3.1, microdf-python 1.2.1, Hypothesis 6.168.2, pytest 8.4.2) with this PR’s isolated source snapshot. No dependencies were installed; this was not a freshly synced lock environment or a full-suite/population run. This is a review comment, not a formal GitHub review vote.

Live check before posting (2026-10-02T10:41:08.415704+00:00): same commit, UNSTABLE; 0 failed checks; 1 pending checks.

@vahid-ahmadi

Copy link
Copy Markdown
Collaborator

Review pass (Claude Code, on Vahid's behalf) at e92b5d10

Short version: the rules match the law I read, and the tests pass locally. Two things before merge: the claimant-or-partner helper still needs #1896's role variable (the Codex review's P1, which I agree with and won't repeat), and one new should-fix on the student exemption's age default in the household calculator.

1. Should-fix: with no education input, every 18- or 19-year-old non-dependant counts as a full-time student and is exempt. housing_benefit_non_dep_deduction_exempt and the CTR equivalent use is_full_time_student_non_dep, which reads current_education. That variable's formula defaults ages 18–19 to TERTIARY when nothing is supplied (variables/household/demographic/current_education.py:36). The PR body notes this, but the effect in the household calculator is large. Probe at this head, 2026, a 45-year-old HB claimant (council rent £10,000) with a 19-year-old non-dependant earning £30,000 at 37.5 hours a week:

Non-dependant current_education Exempt HB deduction
age 18, no input TERTIARY (default) yes £0
age 19, no input TERTIARY (default) yes £0
age 20, no input NOT_IN_EDUCATION no £6,232.20
age 19, NOT_IN_EDUCATION supplied NOT_IN_EDUCATION no £6,232.20

On main this non-dependant is charged, so the PR introduces the gap for HB and the national CTR schemes (the English local schemes already had it through the same helper). Datasets are unaffected, since the Enhanced FRS stores current_education. Suggested fix: base the student exemption on a stored or supplied education value only, not the age default. Alternatively, don't treat the default as full-time study for someone in remunerative work (16+ hours). Either way, add the 19-year-old case above as a test.

2. The claimant-or-partner helper (Codex P1). _non_dependants.py:28-36 is still "head or not a child-benefit QYP". The author has said it will switch to #1896's is_claimant_or_partner, and #1896 is still open, so this PR should wait for that rebase and add the explicit young-partner and third-adult cases the r2 response lists.

Checked and correct:

  • Law: I read HB Regs 2006 reg 74 and HB (SPC) Regs reg 55 on legislation.gov.uk.
    • The bands are "not less than" at the lower edge, and the 2026-27 amounts are £20.40 to £131.45 on £192/£279/£365/£485/£605.
    • Couples get one deduction, the higher, on joint income (reg 74(3)-(4)).
    • The claimant exemption covers blindness, AA, the DLA care component, the PIP daily living component and AFIP (reg 74(6)).
    • The student exemption is reg 74(7)(c)-(e); the under-25 IS/JSA(IB)/ESA(IR)/UC-no-earned-income exemption is reg 74(8); SPC is reg 74(10) (reg 55(9) for pension age).
    • England CTR Sch 1 para 8 has £5.20/£10.60/£13.30/£15.95 on £279/£485/£605, with its own para 8(7)-(8) exemptions, including the LGFA Sch 1 disregard and the UC limb.
  • Remunerative work: banding applies only at 16+ hours and not on an income-related benefit (reg 6(6)); anyone else pays the (1)(b) amount.
  • has_earned_income counts miscellaneous_income, the same as the UC model's own uc_mif_capped_earned_income, so it is consistent within the model.
  • Tests at this head: the HB and local-authority YAML suites pass 205/205, and test_non_dependant_deduction_properties.py passes 6/6. CI's Test job was still running when I checked; the other six checks pass.

MaxGhenis added a commit that referenced this pull request Oct 2, 2026
A person is on income-based JSA or income-related ESA on a day it "is
payable to him" (reg 2(3), (3A)), and a person on income support is one "in
receipt of" it (reg 2(1)). A couple's award is paid to the claimant, not the
partner. is_on_income_related_esa, is_on_income_based_jsa and
is_on_income_support now hold, of the claimant and partner, only for the
payee: the one who reports the award, or the claimant where neither does
(is_payee_of_couple_award). Another member's report never moves the payee.
Anyone else is unchanged (their own report while the award is modelled as
paid).

Agreed with #2007, whose round-2 review raised the same point and which
applies the same reading through its is_award_payee helper; the codebase now
reads "who is on" one way.

Effects: in the local working-age CTR schemes a non-dependant couple on one
of these benefits pays the partner's deduction (one deduction per couple, the
higher: Default Scheme Sch para 30(3)); the maintenance-loan ESA proxy follows
the student's own award, not their partner's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis force-pushed the hb-ctr-non-dep-exemptions branch from e92b5d1 to f5aae26 Compare October 2, 2026 14:09
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Composition note from #2078 (stacked on #2009), which makes the CTR means test assess the applicant and partner rather than the benefit unit's claimant and partner.

#2078 changes council_tax_reduction_household_has_non_dep_exemption to read is_council_tax_reduction_applicant_or_partner (new Person variable) in place of is_claimant_or_partner, following "the applicant or his partner" in SI 2012/2885 Sch 1 para 8(6). The two differ where the household head is liable but is not the benefit unit's claimant or partner. Example: a blind grandmother head sharing a benefit unit with a young couple, who were not exempt before.

When this PR's national exemption (council_tax_reduction_non_dep_deduction_exempt) composes with #2078, it should use the same identity for the applicant or partner. #2015 replaces the household flag with a per-family one and gets the same note.

MaxGhenis and others added 6 commits October 3, 2026 07:47
Housing Benefit (SI 2006/213 reg 74; SI 2006/214 reg 55):
- bands include their lower edge, and only a non-dependant in remunerative
  work (16+ hours, reg 6(1)) is banded, on the couple's joint income; anyone
  else pays the reg 74(1)(b) amount;
- one deduction per couple, the higher (reg 74(3));
- no deduction for full-time students, State Pension Credit recipients, or
  under-25s on IS, income-based JSA or UC without earned income
  (reg 74(7), (8), (10)), nor for any non-dependant when the claimant or
  partner is blind or on AA, DLA care, PIP daily living or AFIP (reg 74(6)).

Council Tax Reduction (SI 2012/2885 Sch 1 para 8; WSI 2013/3029 Sch 1
para 3 and Sch 6 para 5; SSI 2012/319 reg 48; SSI 2012/303 reg 67 and
SSI 2021/249 reg 90):
- each national scheme's own weekly scale for 2013-14 to 2026-27, instead
  of the Housing Benefit amounts;
- the same banding and couple rules, with Wales's working-age exception for
  couples on UC;
- the schemes' any-age exemptions, the UC limb from the year each scheme
  added it, and the existing applicant exemption.

Fixes #2004; the non-dependant half of #1934.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s, gross income

- Identify the claimant or partner as the benefit unit's head or any member
  who is not an SSCBA s.142 child or qualifying young person (HB Regs 2006
  regs 2(1) and 19; CTR regs 2(1)), replacing is_adult in the HB and CTR
  claimant exemptions.
- Band a couple on the couple's joint income only, give the couple one
  deduction and any other adult in the family their own, and count benefit
  receipt (IS, JSA(IB), ESA(IR), SPC, UC) only for the award's claimant or
  partner.
- Treat a non-dependant on IS, JSA(IB) or ESA(IR) as not in remunerative work
  for HB (reg 6(6)).
- Take self-employed losses as nil in the UC no-earned-income test
  (UC reg 57(2)).
- Add the family's UC (before the cap), tax credits and child benefit to gross
  income.
- Tests: the review's counterexamples, a non-vacuous SPC exemption case, and
  property-test generators and oracles for dependent young people and losses.

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

- "No earned income" follows UC Regs 2013 regs 52-62: employment income plus
  SSP and SMP less relievable pension contributions (reg 55(4)-(5)),
  self-employed earnings with losses nil and the minimum income floor where
  the UC model applies it (regs 57(2), 62), and other paid work.
- IS, JSA(IB), ESA(IR) and SPC count only for the person they are payable to
  (HB Regs 2006 reg 2(3)-(3A)): the member reporting the award, or the head;
  used for the exemptions and HB reg 6(6). UC still counts for both joint
  claimants.
- Gross income uses UC after the benefit cap (UC reg 81), applying the cap to
  the capped benefits other than HB, which a non-dependant family does not
  receive.
- CTR exempts an adult for whom another person is entitled to child benefit
  (LGFA 1992 Sch 1 para 3, via para 8(8)(b) and equivalents).
- Tests: the review's counterexamples, and property oracles for the payee and
  the child benefit disregard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replaces the local claimant-or-partner helper (benefit unit head, or anyone
not a child benefit child or qualifying young person) with the
is_claimant_or_partner variable: the SSCBA s.137 couple, at most two people.
A partner at school is now a partner, and a third adult in a benefit unit is
deducted separately. Adds the round-2 review cases: a blind 16-year-old
partner (Wales), a non-dependant couple with a partner at school, and a
17-year-old partner's PIP in Merton.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The changelog and the property generator's comment now name
is_claimant_or_partner instead of the SSCBA s.142 definitions, and
is_award_payee says that both members of a joint-claim jobseeker's allowance
couple are on it (HB reg 2(3)(c)), which the model cannot identify.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ap childcare, child benefit claims

- Gross income adds statutory sick, maternity and paternity pay (earnings,
  HB reg 35(1)(i)) and Maternity Allowance to each person's taxable income,
  and the family's IS, JSA(IB), ESA(IR) and Pension Credit to a couple's.
- Earned income for the UC limb adds statutory paternity pay (UC reg
  55(4)(c)), and relievable pension contributions come off self-employed
  earnings when there are no employed earnings (reg 57(2), step 4).
- Scotland's working-age scheme exempts a non-dependant whose partner is on
  IS, JSA(IB) or ESA(IR) and who is not on Universal Credit, from April 2022
  (SSI 2021/249 reg 90(8)(a), reg 4(1)).
- The after-cap UC in gross income keeps the cap's childcare protection (UC
  reg 81(1)-(2)).
- The LGFA 1992 Sch 1 para 3 disregard needs a child benefit claim.
- The property oracle derives the claimant and applicant exemptions from
  the generated inputs, and the generator varies statutory pay, pension
  contributions, the start-up period, the award's reporter and child
  benefit claims.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis force-pushed the hb-ctr-non-dep-exemptions branch from f5aae26 to 084e1f9 Compare October 3, 2026 12:19
MaxGhenis added a commit that referenced this pull request Oct 3, 2026
HB(SPC) Sch 3 para 6 has the working-age premium's conditions and para
12 its amounts, so the pension-age premium is severe_disability_premium
for a family over State Pension age. A Pension Credit reform no longer
moves pension-age HB or CTR. The schedule property keeps the Pension
Credit addition as a cross-check. References add the CTR nomination
rule and the other premium schedules; the conditions YAML notes the
three income-rule defects its awards include (#1908, #2112, #2007).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Takes main's uv.lock, which drops an unintended one-line lock change from
this branch's first commit.

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

MaxGhenis commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

PR_BODY.md

Fixes #2004
Fixes #1934. Main already keeps people on IS, JSA(IB), ESA(IR) or Universal Credit out of the pensioner scheme (#1944). This PR adds the issue's non-dependant deductions. The model lacks inputs for assessment-phase ESA(IR) and a working student's summer vacation.

## Summary

The model now calculates Housing Benefit and national Council Tax Reduction non-dependant deductions under each scheme's own rules. We use legislation.gov.uk's 7 April 2025 text and each year's CTR amending instruments.

**Housing Benefit** (SI 2006/213 reg 74). SI 2006/214 reg 55 sets pension-age HB deductions.

| Rule | Main | This PR |
|---|---|---|
| Banding, reg 74(2) | `right=True` puts £192.00 a week in the band below | Each band runs from "not less than" its lower edge |
| Remunerative work, reg 74(1)-(2), reg 6(1), (6) | For a non-worker the council deducts the lower of the flat amount and their band, so nothing for one with no banding income | For a non-worker, or anyone on IS, JSA(IB) or ESA(IR), the council deducts the reg 74(1)(b) amount whatever their income (£20.40 a week in 2026-27) |
| Couples, reg 74(3)-(4) | The council deducts for each member on their own income | The council makes one deduction for a couple, the higher, on the couple's joint income, and a separate one for any other adult in the family |
| Non-dependant exemptions, reg 74(7), (8), (10) | None | Full-time students; State Pension Credit recipients; under-25s on IS or income-based JSA, or entitled to Universal Credit that DWP calculates on no earned income |
| Claimant exemption, reg 74(6) | None | The council deducts nothing for anyone if the claimant or partner is blind or receives AA, the DLA care component, the PIP daily living component or AFIP |

**Council Tax Reduction.** We model three national schemes:
- England, pension age: SI 2012/2885 Sch 1 para 8 and reg 10.
- Wales: WSI 2013/3029 Sch 1 para 3, Sch 6 para 5 and reg 10.
- Scotland: SSI 2012/319 reg 48 and reg 6; SSI 2012/303 reg 67, then SSI 2021/249 reg 90, reg 10 and reg 4.

The changes:
- **Scales.** Main's national CTR used the Housing Benefit scale. We give each scheme its own weekly scale for 2013-14 to 2026-27 and cite the amending instrument for every year. In 2026-27:
  - England: £5.20 to £15.95, on £279/£485/£605.
  - Wales: £6.70 to £20.05, on the same bands.
  - Scotland: £5.55 to £16.55, on £286/£497/£614.
  - Pension-age and working-age scales match in Wales and in Scotland in every year. From 2027-28 the model uprates each scale's thresholds and amounts by `gov.benefit_uprating_cpi`.
- **Banding, remunerative work and couples.** As for HB. Councils treat recipients of IS, JSA(IB) or ESA(IR) as outside remunerative work under each scheme's reg 10(6) (SSI 2012/319 reg 6(6)).
  - Under Wales's working-age scheme, the council deducts for both members of a non-dependant couple whose own Universal Credit award assesses either of them (Sch 6 para 5(3): "other than where there is an award of universal credit"). Another member's award as a single claimant leaves the couple one deduction. Sch 1 para 3(3), for pension-age applicants, has no such words, so the council deducts both amounts only for a working-age applicant.
- **Non-dependant exemptions, at any age** (para 8(7)-(8) and equivalents): full-time students; non-dependants on IS, income-based JSA, income-related ESA or State Pension Credit; an adult for whom someone else has claimed child benefit (LGFA 1992 Sch 1 para 3 needs another person's entitlement, and SSAA 1992 s.1(1) makes entitlement depend on a claim); and entitlement to Universal Credit that DWP calculates on no earned income, from the year each scheme added it (England and Wales 2015-16, Scotland 2016-17).
  - From 1 April 2022, Scottish councils also exempt, under the working-age scheme, a non-dependant whose partner receives IS, income-based JSA or income-related ESA and who doesn't receive Universal Credit. SSI 2021/249 reg 90(8)(a) exempts a "qualifying income-related benefit claimant", which reg 4(1) defines as "an applicant who is, or who has a partner who is," on one of those benefits. Read literally, "applicant" would leave reg 90(8)(a) no case to apply to, so we read it for the non-dependant.
- **Applicant exemption** (para 8(6) and equivalents). We now apply `council_tax_reduction_applicant_has_non_dep_exemption`, which only the English local schemes read on main, to national CTR calculations.
- **English working-age local schemes** (Merton, Kingston, Newham, Westminster, Oxford) keep their own variables, unchanged.

**New variables:** `housing_benefit_non_dep_deduction_exempt` (Person), `housing_benefit_non_dep_deductions_claimant_exempt` (BenUnit), `council_tax_reduction_non_dep_deduction_exempt` (Person), `uc_benefit_cap_welfare_benefits` (BenUnit).

**New parameters:** HB `income_related_benefit_age_limit` (25); per CTR scheme, `non_dep_deduction/{amount, exempt_universal_credit_without_earned_income}`; Scotland `working_age_exempt_partner_of_income_related_benefit_claimant` (from 2022-04-01). HB `age_threshold` (18) now starts in 2006, when the 2006 Regulations took effect.

**Universal Credit's cap.** The new `uc_benefit_cap_welfare_benefits` holds the welfare benefits the Universal Credit cap counts: those in `benefit_cap_welfare_benefits`, for the same single person or couple, with Housing Benefit at nil (UC reg 80(2A)). `uc_benefit_cap_reduction` reads it where main read the combined total less `housing_benefit_pre_benefit_cap`. The Housing Benefit cap (HB reg 75C) keeps the combined total. A caller who supplies `benefit_cap_welfare_benefits`, a total that includes Housing Benefit, must also supply `housing_benefit_pre_benefit_cap`, which the new variable subtracts, or `uc_benefit_cap_welfare_benefits` itself, with a value for every benefit unit. Otherwise the new variable raises `ValueError` and names both inputs, whether or not any household has a non-dependant. A value a scenario stores with `set_model_value` supplies neither. No baseline formula on `universal_credit`'s route then calculates Housing Benefit, so the deductions can read the award itself. `uc_benefit_cap_reduction_after_floor` keeps main's code. The baseline cap and award arrays match main (invariant 14).

## Gross income

DWP instructs councils to count "earnings, and any other income" as a non-dependant's gross weekly income (HB guidance A5.542), to add savings interest to earnings (A5.570), and to take no business expenses off a self-employed person's total income (A5.551). Reg 74(9) and its equivalents disregard specified disability, compensation and trust payments.

- **A person's own income** comes from the programme's gross-income variable that #2620 added (`housing_benefit_non_dep_gross_income`, `council_tax_reduction_non_dep_gross_income`): earnings, statutory sick, maternity and paternity pay, pensions, the taxable social security benefits, savings and dividend income, miscellaneous income, Maternity Allowance, IIDB, SDA, war widow's pension, and child and spousal maintenance. We add other investment income, ISA interest and ISA dividends, as main's local CTR schemes do. `tax_free_savings_income` counts ISA interest unless the inputs supply a total. The model holds self-employment and property profits rather than the receipts A5.551 describes, so we count each profit and treat a loss as nil.
- **A claimant or partner of the non-dependant's family**: the council bands them on the couple's two incomes plus the family's awards: child and working tax credits, child benefit, Income Support, the couple's income-based JSA and income-related ESA (`claimant_or_partner_jsa_income`, `claimant_or_partner_esa_income`), Pension Credit, and Universal Credit where the award assesses either of them. It bands anyone else in the family on their own income, plus the Universal Credit where `is_uc_claimant` makes them its claimant instead.
- **Universal Credit.** The council counts the family's award after the cap and before debt deductions, so the model reads `universal_credit`, plus `uc_deductions` where the award is positive. DWP awards the maximum amount less the amounts for income (Welfare Reform Act 2012 s.8(1)). DWP then takes advances (SI 2013/383 reg 10), overpayments (SI 2013/384 regs 10-11) and third-party payments (SI 2013/380 Sch 6 para 2(1)) from the sum payable, so those deductions stay the family's income.
  - A stored award, a stored or replaced cap reduction and a reform of `universal_credit`'s formula reach the deduction through `universal_credit` itself.
  - Main's cap leaves out Maternity Allowance, which Parliament lists as a welfare benefit in Welfare Reform Act 2012 s.96(10)(i). #2784 adds it to `CLAIMANT_OR_PARTNER_BENEFITS`, the list `uc_benefit_cap_welfare_benefits` reads, so the deduction counts the allowance once #2784 lands, whichever PR lands first. Until then, for a non-dependant family with £20,000 of Universal Credit before a £14,753 cap and £5,000 of Maternity Allowance, the model counts £14,753 of Universal Credit; with the allowance in the cap it would count £9,753.
  - No baseline formula on Universal Credit's route calculates Housing Benefit, so the deduction reads `universal_credit` without a cycle in any mix of households, and `housing_benefit_non_dep_deductions` needs no early return. A supplied combined total comes with a supplied `housing_benefit_pre_benefit_cap` or `uc_benefit_cap_welfare_benefits` for every benefit unit (invariant 15). A reform can add a read of calculated Housing Benefit to that route; the engine then raises `CycleError` in the deduction, as it does in `universal_credit`.
  - A reform that replaces or neutralizes `benefit_cap_welfare_benefits` changes the Housing Benefit cap alone. To change the benefits the Universal Credit cap counts, a reform replaces `uc_benefit_cap_welfare_benefits`. A reform that neutralizes `housing_benefit_pre_benefit_cap` drops a supplied value, so a caller who supplies the total under it supplies `uc_benefit_cap_welfare_benefits`; the `ValueError` message says so.

## Inputs and choices

- **On a benefit.** The exemptions and the remunerative-work test read #2027's `is_on_income_support`, `is_on_income_based_jsa`, `is_on_income_related_esa` and `is_on_pension_credit`, which ask whether DWP pays the award to that person (HB reg 2(1), (3), (3A)). Universal Credit counts for each claimant its award assesses (`is_uc_assessed_claimant`, the claimant and partner unless `is_uc_claimant` names others). For entitlement we read the award before the cap (`universal_credit_pre_benefit_cap > 0`): UC reg 80(2) uses "the amount to which the claimant is entitled before any reduction under regulation 81".
- **Full-time student**: `current_education != NOT_IN_EDUCATION` or `in_HE`, the helper the local schemes use. With no input, the model gives an 18- or 19-year-old tertiary education, so it counts them as students.
- **Claimant or partner**: `is_claimant_or_partner` (#1896), the benefit unit's claimant and at most one partner (SSCBA s.137).
- **"Earned income" in the Universal Credit limb.** HB reg 74(11) and the CTR provisions adopt UC reg 52's definition of earned income. We read `uc_individual_earned_income`, so UC's deductions, company attribution, trading losses and conditional minimum income floor reach the exemption. DWP takes allowable employment expenses, tax, National Insurance and relievable pension contributions off earnings (UC reg 55(3), (5)). It adds trades' profits and losses together and attributes a company's income to its owner (reg 57(2), reg 77(3)(b)), and it applies the minimum income floor only where reg 62 does, a couple's floor included. The limb asks whether "the award is calculated on the basis that the non-dependant does not have any earned income", and DWP calculates a joint award on combined earnings under reg 22(1)(b). We test the non-dependant's own earned income; we found no DWP guidance on joint claims.

### Not modelled

- Pension-age HB's 26-week deferral of an increase in a deduction (SI 2006/214 reg 59(10)-(13)), and the CTR applicant exemption's protection while DWP suspends AA, DLA or PIP during a hospital stay.
- HB: a working full-time student's summer vacation (reg 74(7)(d)). No input separates assessment-phase income-related ESA from the work-related activity group, so we withhold the reg 74(8)(a) exemption from every under-25 on ESA(IR).
- Wales: Welsh councils deduct for ESA(IR) recipients in the work-related activity group; we exempt every recipient. `esa_includes_support_component` marks the support group only.
- CTR: people whom the council tax discount rules disregard (LGFA 1992 Sch 1), other than students and the para 3 adults. `is_apprentice` has no £195 earnings cap, so we don't read it.
- All schemes: youth training allowances, a normal home elsewhere, long hospital stays, prison and armed forces operations, and the benefit-week and leave rules of reg 6 (annual hours stand in for them).
- Gross income: student support; statutory adoption, shared parental, parental bereavement and neonatal care pay (no inputs).
- Joint-claim JSA couples (HB reg 2(3)(c)): with no joint-claim input, the model counts only the payee as receiving it.
- Scottish disability assistance for the claimant exemption has no separate input.

## How this composes with main

- **Housing Benefit** (`housing_benefit_non_dep_deductions`). For each family we sum each other family's amount (one per couple, the higher, plus one for each other member) over the non-dependants of the household head (#2006). We treat a family that bears any share of the rent as a joint occupier, not a non-dependant (reg 3(2)(d)), even at nil rent. We then multiply the total by the family's `share_of_household_rent` (reg 74(5)) and zero it under the family's own claimant exemption.
- **Council Tax Reduction** (`council_tax_reduction_non_dep_deductions`). Each applicant family takes its deductions under its own scheme (`council_tax_reduction_pensioner`, #2015). The person-level variables report the scheme of the household head's family, which #2009 makes the applicant; for a jointly liable applicant on the other scheme, `_national_non_dependants.py` recomputes the deductions under that scheme. We multiply the total by `council_tax_reduction_joint_liability_share` (para 8(5)) and zero it under the family's own `council_tax_reduction_applicant_has_non_dep_exemption`.
- **#2620's work gate** becomes the full rule: inclusive band edges, the flat amount for every non-worker who isn't exempt, and the exemptions. Its remunerative-work-hours parameters, including the Northern Ireland reference, stay as main has them.

## Invariants

The property tests check these over generated populations (10 examples each, with explicit cases for rare combinations), the exhaustive edge test checks invariant 7, the fixture comparison invariant 8, `test_rent_of_sharers_boarders_and_lodgers_properties.py` invariant 10, differential runs invariant 14, and `test_non_dependant_cycle_contracts.py` and `test_non_dependant_population_consistent_total.py` invariant 15:

1. The council deducts nothing for an exempt non-dependant, for HB and CTR.
2. For an eligible, non-exempt non-dependant the council deducts between the scale's lowest and highest amounts. The council makes no national CTR deduction in English working-age households.
3. For a non-dependant working under 16 hours, or (for HB) on IS, JSA(IB) or ESA(IR), the council deducts the lowest amount whatever their income.
4. The council deducts nothing when the claimant or applicant has an exemption.
5. A family's deductions equal the sum, over the other families in the household, of each family's higher couple amount (both for a Welsh working-age applicant's non-dependant couple whose own Universal Credit award assesses either member) plus each other member's, times the family's share of the rent (HB) or council tax liability (CTR). The generated populations vary `is_uc_claimant`, so another member's award as a single claimant appears.
6. Raising a non-dependant's income never lowers any deduction.
7. In every year 2015-2030 (2019-2030 for HB), the council deducts a band's amount for an income at its lower edge, and the band below's amount for the next lower representable income.
8. Every CTR parameter value equals the enacted sums in `tests/fixtures/council_tax_reduction_non_dependant_deductions.yaml`.
9. The exemption flags equal a reading of the provisions that the test computes from the inputs, with Universal Credit's earned income from invariant 13.
10. HB deductions over the families liable for a household's rent equal the pooled amount (one per couple, the higher, with sharers never in the pool) times the share that families without an exempt claimant bear, and never exceed the sum of individual deductions.
11. For a non-dependant in remunerative work who is neither exempt nor on IS, JSA(IB) or ESA(IR), the council deducts the amount of the band containing the gross income that the test computes from the inputs.
12. Universal Credit's cap counts the Housing Benefit cap's welfare benefits other than Housing Benefit. No baseline formula on Universal Credit's route calculates Housing Benefit: its traced calculation reads no Housing Benefit variable but the reported award, and, where the inputs supply the combined total, the supplied `housing_benefit_pre_benefit_cap`. A reform can add a read of calculated Housing Benefit, and its cycle raises `CycleError`. The Universal Credit in a non-dependant family's gross income equals `universal_credit` plus `uc_deductions`. That sum equals the award before the cap less the cap reduction applied, under current law and under an 85% protected minimum floor, and the floor never lowers an award. Under a reform that scales `universal_credit`, each household's HB and CTR deductions equal those of the same household with the reformed award supplied. The HB deduction equals the band of wages plus that award, so it moves when the award crosses a band.
13. For each claimant the award assesses, the Universal Credit exemption agrees with `uc_individual_earned_income` across employment expenses, trading losses, company income, pension contributions and single and couple floors, and with the inputs wherever they settle it without the tax and National Insurance calculation.
14. At `02cc0896f`, under current law and under the 85% floor, main and that head gave bit-identical `uc_benefit_cap_reduction`, `uc_benefit_cap_reduction_after_floor`, `uc_deductions` and `universal_credit` arrays for 60 families on Universal Credit, and main's cap formulas and that head's gave identical arrays over 4,096 rows of inputs, with and without a supplied cap reduction and a protected floor. At `95fbf3014` the same 60 families give arrays bit-identical to `02cc0896f`'s. Where a family supplies Housing Benefit alongside Universal Credit, which the model's Housing Benefit eligibility rules out when it calculates both, the basket differs from main's combined total less Housing Benefit by at most £0.004, one float32 step.
15. Inputs that supply `benefit_cap_welfare_benefits` also supply `housing_benefit_pre_benefit_cap` or `uc_benefit_cap_welfare_benefits`; otherwise the model raises `ValueError` when it calculates Universal Credit's cap, with or without a non-dependant in the population. A value stored with `set_model_value` supplies neither, and a reform that gives the combined total a formula leaves Universal Credit's basket on its own sum. When the inputs give each benefit unit its own values, each household's Universal Credit, Housing Benefit and HB non-dependant deductions equal its results alone, for any mix of independent households and whichever result the first request asks for. A situation that sets either input for some benefit units stores nil for the rest.

## Tests

- **YAML** (hand-worked expectations from the law):
  - HB band edges in and out of remunerative work (2026-27), 2025-26 cases in four of the six bands, couples, a dependent child's income, each exemption and its age boundary, each claimant exemption (a partner's PIP included), and aggregation, where a family with a 0.5% share of the rent is a joint occupier.
  - CTR band edges for each scheme (2026-27, with 2025-26 checks), every exemption at age 40, the UC-limb start dates, couples including the Welsh UC rule, the applicant exemption, and jointly liable applicants under different schemes.
  - The Universal Credit exemption follows Universal Credit's earned income. Wages cancelled by allowable expenses, or a profit cancelled by a trading loss, leave none, so the council deducts nothing. A company's income attributed to its owner counts, so the councils deduct £1,060.80 (HB) and £348.40 (Welsh CTR) a year. A partner whose earnings meet the couple threshold keeps the minimum income floor off the other member, so the Welsh deduction for the couple comes to £348.40, not £696.80.
  - In Wales, a third adult's own Universal Credit leaves the couple one deduction: £694.20 a year, and £521.30 for each of two jointly liable applicants on different schemes.
  - The gross-income variables count other investment income, ISA interest and ISA dividends (£9,000 of wages, £20,000 of exempt interest and £30,000 of ISA dividends give £59,000), and treat a self-employment or property loss as nil.
  - A family's Universal Credit counts in the income of the claimant it assesses, and another adult's own income-related ESA stays out of the couple's income: £2,121.60 in each case, where counting the award in the parents' income would give £4,407.00.
  - A non-dependant family's Universal Credit counts after the cap: £14,000 under main's cap, which leaves out the partner's £6,000 of Maternity Allowance, so the couple's £660.19 a week falls in the top bands, £6,835.40 HB and £829.40 CTR.
  - Universal Credit's cap leaves out Housing Benefit the family receives. With £15,000 of Universal Credit, £5,000 of child benefit and £10,000 of Housing Benefit, the Housing Benefit cap counts £30,000 and Universal Credit's £20,000, so the award comes to £9,753. A supplied £26,000 total with £6,000 of Housing Benefit gives a £14,753 award.
  - End to end: a 40-year-old claimant paying £6,000 rent with a zero-income full-time student gets £6,000 of HB; a Guarantee Credit pensioner whose son earns £30,000 at 37.5 hours has a £691.60 deduction and £1,308.40 of CTR (#1934's example).
- **Main's tests that change.** Each move follows from the law:
  - `household_benefits_individual_non_dep_deduction.yaml`:
    - "Other investment income does not band a non-worker": £0 becomes reg 74(1)(b)'s £19.65 × 52 = £1,021.80.
    - "Other investment income does not move a worker's band" becomes "A worker's band counts their investment income": £739.02 a week falls in the top band, £126.65 × 52, where main banded the earnings alone at £45.15.
    - The 2018 case keeps HB at £793. Its CTR half moves to Wales's own scale, £4.35 a week from 1 April 2018 (WSI 2018/14), £226.20; England has no national scheme for a working-age household.
    - Every single-person case now holds the family's awards at zero. The council bands a claimant or partner of the non-dependant's family on the couple's income plus those awards (reg 74(4)), so without them the case's stated income wouldn't be its gross income.
  - `housing_benefit_non_dep_remunerative_work.yaml` (#2620): for the nil-income non-worker the council deducts £20.40 × 52 = £1,060.80 (reg 74(1)(b)), not £0; the under-25 on Income Support now reaches £0 through the reg 74(8)(a) exemption, and the case computes eligibility instead of taking it as an input.
  - `council_tax_reduction_non_dep_remunerative_work.yaml` (#2620), 37 cases: each scheme's 2026-27 scale replaces the HB scale. For non-workers the councils deduct £270.40 (England), £288.60 (Scotland) or £348.40 (Wales) a year, and for £403.85 a week in work £551.20, £569.40 or £694.20. For a recipient of IS, JSA(IB) or ESA(IR) they deduct nothing (para 8(8)(a)), and for the nil-income non-worker England's £270.40. Each English case with a 30-year-old non-dependant now gives the household a separate 70-year-old applicant, and the model works out each family's scheme.
  - `council_tax_reduction_non_dep_gross_income.yaml` (#2620), 15 cases: £183.08 a week falls below every scheme's first band edge, so each scheme deducts its 2025-26 lowest amount (England £260, Scotland £278.20, Wales £325 a year) and the reduction comes to £4,000 less that.
  - `test_non_dependant_spousal_maintenance.py`: the national CTR comparison moves to Wales, whose scheme covers working-age applicants: [£348.40, £348.40, £694.20, £348.40].
  - `housing_benefit_non_dep_deductions.yaml`: for the non-worker with investment income the council deducts £19.65 × 52 = £1,021.80, where main's `right=True` gave £0.
  - `council_tax_reduction_individual_non_dep_deduction.yaml`, on England's 2025-26 pensioner scale (SI 2025/39): for a non-worker with £556 a week of investment income the council deducts £5.00 × 52 = £260, and a worker's band counts it, £15.35 × 52 = £798.20.
  - `council_tax_reduction_pension_credit.yaml`: for the son with no hours the council deducts England's £5.20 a week, so CTR is £1,729.60. `claimant_family_scheme.yaml`: for the 40-year-old adult child it deducts £5.20 × 52 = £270.40, so CTR is £1,529.60.
  - `housing_benefit_individual_non_dep_deduction_eligible.yaml`: the 18-year-old "Eligible" case sets `current_education` and `would_claim_uc: false`; otherwise the age default makes them a student, and an income-free 18-year-old has Universal Credit without earnings.
  - `test_rent_of_sharers_boarders_and_lodgers_properties.py`: the HB pool counts one deduction per couple, the higher, and only families without an exempt claimant bear it (invariant 10).
  - `benefit_cap_by_scheme.yaml`, "UC reg 81(2) - no reduction where the childcare costs element exceeds the excess": the case supplies the combined total, so it now supplies the £0 of Housing Benefit the model calculates for it, and its outputs don't change. The other eight YAML cases that supply the total already supply `housing_benefit_pre_benefit_cap`. Before this change only one Python test supplied the total, the one in `test_non_dependant_deduction_properties.py` that this PR rewrites.
  - `pension_age_severe_disability_conditions.yaml`, `claimant_household_head.yaml`, `housing_benefit_non_dep_gross_income.yaml` and `test_housing_benefit_non_dep_history.py` change only in comments, apart from the new ISA case in the gross-income file.
- **`test_non_dependant_deduction_properties.py`**: Hypothesis populations for invariants 1-6, 9 and 11, which vary `is_uc_claimant`, with explicit examples for the rare Welsh-couple and under-25 cases; the exhaustive edge check (7); the fixture comparison (8); and the earned-income comparison (13). For invariant 12, one property compares the basket and the award under current law and an 85% floor with debt deduction rates; its explicit example takes £720.25 of debt and no cap reduction, so the award counts £20,000. Another scales the award by a reform, and its explicit example moves a non-dependant from the 348 band to the 266 band. A third traces Universal Credit, which reads no calculated Housing Benefit, and checks that a supplied total without Housing Benefit raises `ValueError`. The invariant 12 tests build core simulations on one tax-benefit system for the module.
- **`test_non_dependant_cycle_contracts.py`** (invariant 15) uses two independent households: a single Universal Credit claimant with £20,000 before a £14,753 cap and no rent, and a 70-year-old paying £6,000 rent with a non-dependant who earns £9,000 at 20 hours and claims no Universal Credit. For the second's non-dependant, the council deducts £19.65 × 52 = £1,021.80 (HB regs 70 and 74(2)(a), 2025-26), leaving £4,978.20 of Housing Benefit.
  - With the combined total and Universal Credit's basket supplied, the population gives Universal Credit of [£14,753, £0, £0] and Housing Benefit of [£0, £4,978.20, £0], each household's results alone, whether the first request asks for Universal Credit, Housing Benefit or the family deduction.
  - With Housing Benefit supplied instead, the basket counts the total less that Housing Benefit: £26,000 less £6,000 leaves £20,000, and the award comes to £14,753.
  - A supplied total with neither raises `ValueError`, with or without the non-dependant. `set_model_value` supplies neither input, and a reform that gives the total a formula leaves Universal Credit's basket on its own sum.
  - For any mix of up to three independent households, some with a non-dependant on Universal Credit, and Universal Credit's basket supplied, a Hypothesis property checks that each household's Universal Credit, Housing Benefit and deductions equal its results alone.
  - Reforms that add calculated Housing Benefit to Universal Credit's basket or to its award before the cap raise `CycleError`. With nothing supplied, a traced run shows Universal Credit reading only reported Housing Benefit, and the national and five local CTR schemes then read Universal Credit.
- **`test_non_dependant_population_consistent_total.py`** takes each household's combined total and Housing Benefit from its own simulation with nothing supplied (£20,000 and £0; £4,978.20 and £4,978.20). Supplied together for the mixed population, they give each household its results alone for each first request, and the totals without the Housing Benefit raise `ValueError`.
- **`code_health/test_benefit_cap_lists_unique.py`** checks that each name occurs once in the lists the two caps and a non-dependant family's income add up.
- **Mutation testing.** At `97f792542` the tests killed the mutants of the earned-income limb, the Welsh couple rule, either exemption's earned-income test, the ISA income, the joint-occupier share and the partner's PIP. At `86e86d3db` they killed every other mutant of the rules except one that removes CTR's reg 10(6) work exclusion, which changes no result, because each scheme's para (8)(a) exemption already gives such a non-dependant no deduction. At `02cc0896f`, ten mutants of the award and the cap's basket ran, each in a scratch copy under a 2 GB memory cap, and the tests killed nine:
  - the award dropping its debt deductions, rebuilding itself from the cap variables in place of `universal_credit`, or counting Universal Credit before the cap;
  - `uc_benefit_cap_reduction` reading the combined total with Housing Benefit; a supplied combined total never reaching the cap, or keeping its Housing Benefit; the basket leaving out income-based JSA and income-related ESA;
  - `housing_benefit_non_dep_deductions` reading income with no non-dependant, or returning nil for every household.

  The survivor adds debt deductions to a nil award. It changes no result under current law, where a positive deduction always leaves at least a penny of award. The basket leaving out child benefit and the cap ignoring the childcare costs element also failed their tests at `02cc0896f`. This head deletes the early return that the no-non-dependant mutant changed.

  Two mutants of the supplied-total contract ran, each in a scratch copy under a 2 GB memory cap. Restoring the calculated read, so that the basket subtracts `housing_benefit_pre_benefit_cap` from a supplied total whether or not the inputs supply it, makes `test_a_supplied_total_alone_raises[mixed]` and `[single]` raise `CycleError` instead of `ValueError`, and `test_model_values_supply_neither_input` stops raising. Dropping the `ValueError`, so that a supplied total without Housing Benefit counts in full, makes all three stop raising.
- **Differential against main.** In 20 households with 40 people in five English councils (Merton, Kingston, Newham, Westminster and Oxford), each non-dependant earns £9,000 and has nothing else, £20,000 of other investment income, a £5,000 self-employment loss or a £5,000 property loss. At `86e86d3db`, main `2da212abc` and the head gave identical arrays for all five local schemes' reductions, individual and family non-dependant deductions, and the shared eligibility, applicant-exemption, pensioner and joint-liability flags: 19 of 21 arrays. The two national gross-income arrays differed in the 15 investment and loss cases: £9,000 becomes £29,000 with investment income, and £4,000 becomes £9,000 with a loss.
- **Runs**, locally with no survey data, each file alone under a 4 GB memory cap (2 GB for mutants), at `95fbf3014` unless stated. `2667976b8` then adds only a module-scoped fixture that clears `scaled_award_system`'s cache.
  - YAML: `gov/dwp/benefit_cap/` 118 passed, the HB non-dependant directory 139, `gov/local_authorities/council_tax_reduction/` 376, and `benefit_cap_by_scheme.yaml` 22 at `2667976b8`.
  - `test_non_dependant_cycle_contracts.py` 14 passed, `test_non_dependant_population_consistent_total.py` 5, `test_non_dependant_deduction_properties.py` 10 (also at `2667976b8`), `test_uc_deductions.py` 24, `test_benefit_cap_uc_and_hb_properties.py` 1, the sharers properties 6 and the LHA scheme-split properties 7.
  - `code_health/` without `test_changelog_fragments.py`: 2,999 passed and 2 failed. `test_pytest_default_collection_confirms_function_and_class_names` fails only when `PYTEST_ADDOPTS` carries `-n 0` into its nested pytest, and passes without it. `test_every_test_module_cache_is_released` found `scaled_award_system`'s module-level cache, which `2667976b8` releases; at `2667976b8` that file reports 5 passed. `test_changelog_fragments.py`, run on its own with towncrier installed, reports 103 passed.
  - The 60-family cap differential gives bit-identical `uc_benefit_cap_reduction`, `uc_benefit_cap_reduction_after_floor`, `uc_deductions` and `universal_credit` arrays at `95fbf3014` and `02cc0896f`, under current law and under the 85% floor.
  - `ruff format --check`, `ruff check`, `ruff check --select F811,F821` and `git diff --check` against main report nothing.
  - At `4ec0693f7`, whose two commits after `2667976b8` change only documentation strings, comments, the changelog and the `ValueError` message's text (no test asserts the changed wording; with every string literal blanked, each changed Python file parses to the same tree): `test_non_dependant_cycle_contracts.py` 14 passed, `test_non_dependant_population_consistent_total.py` 5 and `gov/dwp/benefit_cap/` YAML 118 at `c2c55086e`; `test_non_dependant_deduction_properties.py` 10, `code_health/test_test_module_caches_released.py` 5 and `test_parameter_descriptions.py` 24 at `4ec0693f7`; `code_health/` with towncrier installed 3,104 passed at `c2c55086e`; `ruff format --check`, `ruff check`, `ruff check --select F811,F821` and `git diff --check` report nothing at `4ec0693f7`.

## Impact (Enhanced FRS 2024-25, certified uk-data 1.58.1)

CI's aggregate impact job ([run 41294688738](https://github.com/PolicyEngine/policyengine-uk/actions/runs/41294688738)) compared main `2da212abc` with head `97f792542` merged into it (`c6e6a19d3b`). It ran each fiscal year as a full microsimulation on both sides, on the certified dataset (sha256 `056e80fd8e91`), and broke no check. Its report on this PR matches the figures below at the displayed precision, as did the run on `86e86d3db` ([41269333819](https://github.com/PolicyEngine/policyengine-uk/actions/runs/41269333819)). Later heads read `universal_credit` in the deduction, give Universal Credit's cap its own basket and add the supplied-total contract. The default calculation supplies no cap total, so the contract changes no default route, and the cap and award arrays stay bit-identical (invariant 14); the figures below should therefore stand at this head, and CI reruns the job on it.

| Change | 2025-26 | 2026-27 | 2027-28 | 2028-29 | 2029-30 | 2030-31 | 2031-32 |
|---|---:|---:|---:|---:|---:|---:|---:|
| Housing Benefit (£m) | +16.1 | +16.8 | +17.4 | +18.1 | +18.5 | +19.4 | +19.0 |
| Council Tax Reduction (£m) | +6.1 | +6.4 | +6.8 | +7.2 | +8.5 | +8.9 | +9.4 |
| Household net income (£m) | +22.2 | +23.2 | +24.2 | +25.3 | +27.0 | +28.3 | +28.4 |
| Housing Benefit caseload | +238 | +238 | +238 | +239 | +239 | +240 | +241 |
| CTR caseload (k) | +6.0 | +6.3 | +6.3 | +6.3 | +6.2 | +6.2 | +6.2 |

Tax receipts and population don't change in any year. In the report, absolute poverty after housing costs falls by 2.2k to 7.2k people a year, and absolute poverty before housing costs rises by up to 1.3k. Relative poverty after housing costs moves in both directions, from a fall of 7.1k (2026-27) to a rise of 12.5k (2025-26). The flat charge for non-workers and the wider gross-income rules leave some households worse off. The job flags two 2030-31 moves in decile means (decile 5 +£145, decile 6 −£119 a household), which compare each side's own deciles.

## Merge order and overlaps

- **On main, and composed here:** #1896, #1925, #2006, #2009, #2015, #2027, #2078, #2089, #2620.
- **#2017** (in-unit non-dependants) stacks on this branch, and **#2077** on #2017. Both now conflict with this head in two files. In `council_tax_reduction_non_dep_deductions.py` the Welsh couple rule tests the couple's own Universal Credit award, and #2017's restack keeps that test. In `housing_benefit_non_dep_deductions.py` this head deletes the early return that read no income when no household had a non-dependant. #2017's restack drops it too and keeps its individual eligibility mask: #2017's `housing_benefit_individual_non_dep_deduction_eligible` admits only non-dependants, and `household_benefits_individual_non_dep_deduction` takes it as `defined_for`, so policyengine-core skips the income formula when no one in the simulation is eligible. After we merge this branch, GitHub retargets #2017 to main.
- **#2030, #2035, #2002 and #1966** conflict with this head in 21, 16, 23 and 2 files. Each owner resolves them when restacking.
- **#1966** (Scotland and Wales working-age CTR) reads `council_tax_reduction_non_dep_deductions`, so it picks this up.
- **#2647** rewrites the documentation of `uc_benefit_cap_reduction` and `uc_benefit_cap_reduction_after_floor`. This head changes only the first one's formula, which now reads `uc_benefit_cap_welfare_benefits`. Git merges it cleanly.
- **#2784** (`fix-2784-maternity-allowance-cap` at `55e015e10`, no PR yet) adds Maternity Allowance to `CLAIMANT_OR_PARTNER_BENEFITS`, which the new basket reads, so Universal Credit's cap and the deduction count it. Git conflicts in one place, the documentation of `benefit_cap_welfare_benefits`: keep this head's sentences on the two baskets and the supplied total, and #2784's sentences on Maternity Allowance and the other welfare benefits, which replace this head's sentence on the benefits the model has no variable for. Whichever lands second moves the review case "A non-dependant family's Universal Credit counts after the benefit cap" to the 485 bands its comment states: £10,890.20 of Universal Credit, £6,232.20 HB and £691.60 CTR.
- **`supplied_inputs.py`**: this head registers `benefit_cap_welfare_benefits` and `housing_benefit_pre_benefit_cap` in `SUPPLIED_INPUT_VARIABLES`, since the new basket asks whether the inputs supply them. Git merges that file cleanly with #1969, #2661, #2765 and #2776. #2265 and #2308 conflict there with main itself.
- **#2346**: its fix (`fix-2346-uc-education`, no PR yet) tests an explicit `is_in_full_time_education` input for Universal Credit, where these exemptions infer full-time study from `current_education` or `in_HE`. Whichever merges second aligns them. Git merges the two cleanly.

## Axiom

axiom: TheAxiomFoundation/rulespec-uk#407 queued

rulespec-uk encodes none of HB reg 74, HB (SPC) reg 55, England's para 8 or Scotland's regs 48 and 90. Its Welsh Sch 1 para 3 and Sch 6 para 5 modules band a supplied gross income at 2026-27 amounts and defer the exemptions, the couple rule and apportionment. #407 holds the encoding brief, which binds the non-dependant's Universal Credit income to the award after the cap and before debt (`uc_pilot_award_amount`); no separately approved billed signed-encoder run exists for it.

Reproducer: `policyengine_uk/tests/test_non_dependant_deduction_properties.py::test_band_edges_are_inclusive`, `policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_non_dep_deduction_chain.yaml`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Generated by Claude Code

@MaxGhenis

MaxGhenis commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

AXIOM_ISSUE.md (an addendum for TheAxiomFoundation/rulespec-uk#407)

# Addendum to #407: non-dependant deductions at policyengine-uk#2007 `4ec0693f7a01dc4347c249b9946db7a68be1e3a9`

This addendum updates https://github.com/TheAxiomFoundation/rulespec-uk/issues/407 for the Housing Benefit and national Council Tax Reduction non-dependant deductions in https://github.com/PolicyEngine/policyengine-uk/pull/2007 at `4ec0693f7a01dc4347c249b9946db7a68be1e3a9`. That head includes main `2da212abcf2086337fc088a4a1236f4ae7b6af63`. It replaces items 1-4 of the 25 October comment's "Known PE deviations", which #2007 resolves, and #407's `review_finding` with the block below.

## Pinned target and execution blocker

Label: `pe-parity`, already on #407. This request runs no encoder: no separately approved billed signed-encoder run exists for it. Record that approval blocker; do not invoke an encoder or write RuleSpec modules by hand.

Target the release that rulespec-uk `main` at `0497350d74f9d0f7272b88e08019e66366d3be58` pins in `.axiom/toolchain.toml`: `uk-rulespec-2026-10-25-pe-child-definitions`, content SHA-256 `76b42156de23f2c000dab657dafacfb5b96ec9fe5670a75563574bd00793048c` (read over the GitHub API on 2026-11-01 UTC). The encoder operator must verify each proposed selector against this release and record the ingest, signed-expression and release bindings when carrying out the separately approved work.

Treat every module path and corpus selector below as a **proposed target**. The state column reports the module tree at `0497350d`, read over the GitHub API on 2026-11-01 UTC. If the encoder needs a corpus update, record its approved input and resulting release pin before parity execution; do not silently substitute a different release.

| Proposed module path | Proposed corpus selector | State at `0497350d` |
|---|---|---|
| `uk/regulations/uksi/2006/213/6.yaml` | `uk/regulation/uksi/2006/213/6` | Absent; confirm ingest |
| `uk/regulations/uksi/2006/213/74.yaml` | `uk/regulation/uksi/2006/213/74` | Absent |
| `uk/regulations/uksi/2006/214/6.yaml` | `uk/regulation/uksi/2006/214/6` | Absent; confirm ingest |
| `uk/regulations/uksi/2006/214/55.yaml` | `uk/regulation/uksi/2006/214/55` | Absent |
| `uk/regulations/uksi/2012/2885/10.yaml` | `uk/regulation/uksi/2012/2885/10` | Absent; confirm ingest |
| `uk/regulations/uksi/2012/2885/schedule/1/paragraph/8.yaml` | `uk/regulation/uksi/2012/2885/schedule/1/paragraph/8` | Absent |
| `uk/regulations/wsi/2013/3029/10.yaml` | `uk/regulation/wsi/2013/3029/10` | Absent; confirm ingest |
| `uk/regulations/wsi/2013/3029/schedule/1/paragraph/3.yaml` | `uk/regulation/wsi/2013/3029/schedule/1/paragraph/3` | Present; it bands a supplied income at 2026-27 amounts and defers the exemptions, couples and apportionment. Extend it |
| `uk/regulations/wsi/2013/3029/schedule/6/paragraph/5.yaml` | `uk/regulation/wsi/2013/3029/schedule/6/paragraph/5` | Present, as above. Extend it |
| `uk/regulations/ssi/2012/319/6.yaml` | `uk/regulation/ssi/2012/319/6` | Absent; confirm ingest |
| `uk/regulations/ssi/2012/319/48.yaml` | `uk/regulation/ssi/2012/319/48` | Absent |
| `uk/regulations/ssi/2021/249/4.yaml` (the "qualifying income-related benefit claimant" definition) | `uk/regulation/ssi/2021/249/4` | Absent; confirm ingest |
| `uk/regulations/ssi/2021/249/10.yaml` | `uk/regulation/ssi/2021/249/10` | Absent; confirm ingest |
| `uk/regulations/ssi/2021/249/90.yaml` | `uk/regulation/ssi/2021/249/90` | Absent |
| `uk/regulations/uksi/2013/376/52.yaml` (the "earned income" definition the UC limb adopts) | `uk/regulation/uksi/2013/376/52` | Absent; confirm ingest. `55.yaml` is absent too; `54.yaml`, `62.yaml`, `77.yaml`, `81.yaml` and `82.yaml` are present |
| `uk/regulations/uksi/2013/380/schedule/6/paragraph/2.yaml` | `uk/regulation/uksi/2013/380/schedule/6/paragraph/2` | Absent; confirm ingest (proof source for part (e)) |
| `uk/regulations/uksi/2013/383/10.yaml` | `uk/regulation/uksi/2013/383/10` | Absent; confirm ingest (proof source for part (e)) |
| `uk/regulations/uksi/2013/384/10.yaml`, `11.yaml` | `uk/regulation/uksi/2013/384/10`, `/11` | Absent; confirm ingest (proof sources for part (e)) |

Wire HB into `uk/policies/housing_benefit_composed_entitlement_pipeline.yaml` and `uk/regulations/uksi/2006/{213/70,214/50}.yaml`. Wire CTR into `uk/policies/govuk/council-tax-reduction.yaml`, `uk/regulations/ssi/2021/249/79.yaml`, `uk/regulations/ssi/2012/319/47.yaml` and the Welsh award modules.

## Universal Credit after the cap and before debt (part (e))

The non-dependant income rule counts the family's Universal Credit award after the benefit cap and before DWP's debt deductions. rulespec-uk at `0497350d` holds three Universal Credit award outputs (read over the GitHub API on 2026-11-01 UTC):

| Output | Module | Entity, period, first version | Corpus binding | Quantity |
|---|---|---|---|---|
| `universal_credit_award_amount` | `uk/statutes/ukpga/2012/5/8.yaml` | Family, Month, `0001-01-01` | `uk/statute/ukpga/2012/5/8` | The s.8(1) balance, before the cap |
| `universal_credit_award_benefit_cap_reduction` | `uk/regulations/uksi/2013/376/81.yaml` | Family, Month, `2013-04-29` | `uk/regulation/uksi/2013/376/81` | The reg 81 reduction, from the inputs `total_welfare_benefits_entitled_in_reference_period`, `relevant_amount_under_regulation_80A`, `benefit_cap_applies_to_assessment_period` and `childcare_costs_element_amount_in_award` |
| `uc_pilot_award_amount` | `uk/policies/universal_credit_composed_award_pipeline.yaml` | Family, Month, `2026-04-01` | `uk/statute/ukpga/2012/5/8` for the award; `uk/regulation/uksi/2013/376/81` for `uc_pilot_benefit_cap_excess` and `uc_pilot_benefit_cap_reduction`; `uk/regulation/uksi/2013/376/80A` for the limits | The award after the cap and before debt, `max(0, uc_pilot_award_before_cap - uc_pilot_benefit_cap_reduction)` |

`uk/policies/universal_credit_award_reconciliation.yaml` treats the first and third as different statutory quantities. Bind the non-dependant income rule to the award after the cap: `uk:policies/universal_credit_composed_award_pipeline#uc_pilot_award_amount` where the pilot applies, and otherwise `universal_credit_award_amount` less `universal_credit_award_benefit_cap_reduction`, at nil if negative. Don't bind it to the payment after debt deductions. The Secretary of State deducts those from the award to discharge the claimant's own liabilities (SI 2013/380 Sch 6 para 2(1); SI 2013/383 reg 10; SI 2013/384 regs 10-11), so they stay the family's income.

No module at `0497350d` encodes SI 2013/380 Sch 6 para 2, SI 2013/383 reg 10 or SI 2013/384 regs 10-11, and the tree holds no standalone Universal Credit module for regs 79, 80 or 83. Proposed corpus selectors, to confirm at ingest: `uk/regulation/uksi/2013/380/schedule/6/paragraph/2`, `uk/regulation/uksi/2013/383/10`, `uk/regulation/uksi/2013/384/10` and `uk/regulation/uksi/2013/384/11`. The rule cites them as proof sources for counting the award before debt; it calculates nothing from them.

Five gaps keep the current pilots from running the companion below unchanged:

- its versions start on 1 April 2026, so it holds no 2025-26 parameters;
- `uc_pilot_benefit_cap_excess` counts the pilot's own pre-cap award as the family's only welfare benefit, so it can't add child benefit or the other benefits regs 79-80 count;
- it calculates by month, so the companion's annual figures need an explicit conversion (`12 * uc_pilot_award_amount`);
- it has no debt-deduction input and no payment output, so the payment assertion needs an adapter input;
- the HB pilot, `uk/policies/housing_benefit_composed_entitlement_pipeline.yaml`, takes `hb_pilot_non_dependant_gross_weekly_income` as an input, so no rule yet carries the award into a non-dependant's gross income.

## What PolicyEngine does at this head

| Rule | PolicyEngine variable or helper | Law |
|---|---|---|
| Lower-inclusive bands ("not less than") | `household_benefits_individual_non_dep_deduction`; `_national_non_dependants.individual_non_dep_deduction` | HB reg 74(2), SPC reg 55(2), each CTR para (2) |
| Flat (1)(b) amount for anyone outside remunerative work, whatever their income, nil included | same | reg 74(1)(b), para 8(1)(b) and equivalents |
| Remunerative work: 16 hours, and never for a person on IS, JSA(IB) or ESA(IR); in Scotland's working-age scheme a "qualifying income-related benefit claimant" | `remunerative_work_hours` parameters; `is_on_income_support`, `is_on_income_based_jsa`, `is_on_income_related_esa` | HB reg 6(1), (6); SPC reg 6; SI 2012/2885 reg 10(6); WSI 2013/3029 reg 10(6); SSI 2012/319 reg 6(6); SSI 2021/249 reg 10(6), reg 4(1) |
| One deduction per couple, the higher, on joint gross income | `_non_dependants.deduction_per_family`, `non_dependant_weekly_gross_income` | reg 74(3)-(4); para 8(3)-(4) and equivalents |
| Both members of a Welsh working-age applicant's non-dependant couple, only where the couple's own Universal Credit award assesses either member; another member's award as a single claimant leaves one deduction | `council_tax_reduction_non_dep_deductions` | WSI 2013/3029 Sch 6 para 5(3) |
| Each CTR scheme's own scale, 2013-14 to 2026-27 | `gov.local_authorities.{england.council_tax_reduction.pensioners,scotland.council_tax_reduction,wales.council_tax_reduction}.non_dep_deduction.amount` | amending instruments listed in each parameter |
| Exemptions | `housing_benefit_non_dep_deduction_exempt`, `council_tax_reduction_non_dep_deduction_exempt`, `housing_benefit_non_dep_deductions_claimant_exempt`, `council_tax_reduction_applicant_has_non_dep_exemption` | reg 74(6)-(8), (10); para 8(6)-(8) and equivalents |
| The Universal Credit limb reads Universal Credit's own calculation of the person's earned income: allowable expenses, tax, NI and relievable pension contributions off, company income attributed to its owner, trading losses set against profits, and the minimum income floor only where reg 62 applies it, a couple's included | `uc_individual_earned_income` | reg 74(8)(b), (11); para 8(8)(c), (11A) and equivalents; UC Regs 2013 regs 52, 55(3), (5), 57(2), 62(2)-(3), 77(3)(b) |
| Gross income: earnings and any other income, with other investment income, ISA interest and ISA dividends; each self-employment or property profit counted, a loss as nil (an approximation: the model holds profits, not receipts) | `housing_benefit_non_dep_gross_income`, `council_tax_reduction_non_dep_gross_income` | reg 74(2), (9); DWP HB guidance A5.542, A5.551, A5.570 |
| A couple's gross income adds the family's tax credits, child benefit, IS, JSA(IB), ESA(IR) and Pension Credit, and, where the award assesses either member, its Universal Credit after the cap and before debt deductions (`universal_credit` plus `uc_deductions`) | `non_dependant_weekly_gross_income`, `universal_credit_award`; the cap's basket without Housing Benefit, `uc_benefit_cap_welfare_benefits` | reg 74(4); UC Regs 2013 regs 79-81, 80(2A); Welfare Reform Act 2012 s.8(1); SI 2013/380 Sch 6 para 2(1); SI 2013/383 reg 10; SI 2013/384 regs 10-11 |

## review_finding (pasteable; replaces #407's)

```
HB Regs 2006 reg 74 / HB (SPC) Regs 2006 reg 55, SI 2012/2885 Sch 1 para 8, WSI 2013/3029 Sch 1 para 3 and Sch 6 para 5, SSI 2012/319 reg 48 and SSI 2021/249 reg 90 set non-dependant deductions in five parts:
(a) the (1)(b) amount for a non-dependant aged 18+ not in remunerative work (16+ hours, and never while on IS, JSA(IB) or ESA(IR): reg 6(6) and equivalents);
(b) income bands, including each lower edge ("not less than"), only for one in remunerative work;
(c) one deduction per couple, the higher, except Wales Sch 6 para 5(3) where the couple's own UC award assesses either member;
(d) exemptions: the applicant's or partner's blindness or disability benefits (para (6)), and the non-dependant's full-time study, SPC, IS/JSA(IB)/ESA(IR), or UC calculated on no earned income as UC Regs 2013 reg 52 defines it (HB: under-25 limbs only);
(e) normal gross weekly income: a couple's joint income plus the family's awards, with its Universal Credit after the benefit cap (UC Regs 2013 reg 81, Housing Benefit at nil under reg 80(2A)) and before debt deductions (WRA 2012 s.8(1); SI 2013/380 Sch 6 para 2; SI 2013/383 reg 10; SI 2013/384 regs 10-11). Consume uk:policies/universal_credit_composed_award_pipeline#uc_pilot_award_amount, or the s.8(1) balance less the reg 81 reduction, never the payment after deductions.
rulespec-uk's HB pilot bands every non-dependant and omits the work, couple, exemption and award-income rules. Its national CTR award spines take supplied deductions. The Welsh deduction modules calculate provisional amounts but defer exemptions, couple consolidation and apportionment. Extend those modules, encode the missing provisions, and wire the deductions into the HB and CTR award spines.
```

## Outputs and companion cases

Per scheme, as #407 lists, plus:

- **`non_dependant_in_remunerative_work`** (person): 16 hours or more and not on IS, JSA(IB) or ESA(IR) for more than 3 days in the week (Scotland working age: not a qualifying income-related benefit claimant).
- **`non_dependant_weekly_gross_income`** (person): the person's own gross income after the para (9) disregards; for a member of a couple, the couple's joint gross income.
- **`non_dependant_weekly_deduction`** (person): the (1)(b) amount outside remunerative work; inside it, the band containing weekly gross income, each lower edge included; 0 when exempt.
- **`non_dependant_deduction_for_couple`**, **`non_dependant_deductions`**, **`non_dependant_deduction_applicant_exempt`**, **`non_dependant_deduction_exempt`**: as #407.

Derive expected values from the statutory tables used for the linked YAML tests in #2007 (`housing_benefit/non_dep_deduction/*.yaml`, `council_tax_reduction/council_tax_reduction_non_dep_*.yaml`); do not copy the engine's outputs. Weekly amounts:

| Provision | Case | Expected |
|---|---|---|
| HB reg 74 (2026-27) | 35 hours, £192.00 a week / £191.99 | 46.85 / 20.40 |
| HB reg 74 (2026-27) | 35 hours, £605.00 | 131.45 |
| HB reg 74 (2026-27) | 0 hours or 15.9 hours, £769.23; 16 hours, £769.23 | 20.40; 131.45 |
| HB reg 74 (2026-27) | 35 hours, nil income | 20.40 |
| HB reg 74 (2026-27) | 0 hours, nil income | 20.40 |
| HB reg 74 + reg 6(6) (2026-27) | 16 hours, £403.85, on IS | 20.40 |
| HB reg 74 (2026-27) | 16 hours: wages £173.08 plus IIDB £230.77 (or child maintenance £230.77) | 105.20 |
| HB reg 74 (2026-27) | couple: worker 35 hours £200 + partner 0 hours £200 | 105.20, one deduction |
| HB reg 74 (2026-27) | age 24 on IS / age 25 on IS | 0 / 20.40 |
| HB reg 74 (2025-26) | 16 hours, £100 / £183.02 / £349 / £578 | 19.65 / 45.15 / 101.35 / 126.65 |
| HB reg 74 (2025-26) | 0 hours, £556 of other investment income | 19.65 |
| HB reg 74 (2025-26) | 37.5 hours, £183.02 wages + £556 other investment income | 126.65 |
| HB reg 74 (2026-27) | 16 hours, wages £300 and a £200 self-employment loss | 64.35 |
| HB reg 74(8)(b), (11) + UC reg 55(3) (2026-27) | age 24 on UC, 0 hours: £1,000 a year of wages and £1,000 of allowable expenses | 0 |
| HB reg 74(8)(b), (11) + UC reg 57(2) (2026-27) | age 24 on UC, 0 hours, minimum income floor not applying: £1,000 profit and a £1,000 trading loss | 0 |
| HB reg 74(8)(b), (11) + UC reg 77(3)(b) (2026-27) | age 24 on UC, 0 hours: £1,000 of company income attributed to its sole owner | 20.40 |
| WSI 2013/3029 Sch 1 para 3 (2026-27) | the same three people, pension-age applicant | 0; 0; 6.70 |
| WSI 2013/3029 Sch 6 para 5 + UC reg 62(3) (2026-27) | UC couple: member aged 24 with no earnings and a floor applying (£10,000 a year), partner earning £30,000 at 15 hours | 0 + 6.70 = 6.70 |
| WSI 2013/3029 Sch 6 para 5(3) (2026-27) | couple of non-claimants earning £10,400 and £5,200 a year at 16 hours; a third adult alone on UC with no earnings | 13.35, one deduction |
| WSI 2013/3029 Sch 1 para 3 and Sch 6 para 5 (2026-27) | the same couple with £520 of child benefit; the third adult earns £9,100 at 20 hours and is the UC claimant (£3,000); pension-age and working-age applicants jointly liable | 20.05 for the household, 10.025 each |
| SI 2012/2885 para 8 (2026-27) | 35 hours, £278.99 / £279.00 / £485.00 / £605.00 | 5.20 / 10.60 / 13.30 / 15.95 |
| SI 2012/2885 para 8 (2026-27) | 16 hours, £403.85 | 10.60 |
| SI 2012/2885 para 8 (2026-27) | age 40 on IS, JSA(IB), ESA(IR) or SPC; UC without earnings | 0 |
| SI 2012/2885 para 8 (2025-26) | 0 hours, £556 of other investment income / 37.5 hours, £739.02 | 5.00 / 15.35 |
| SI 2012/2885 para 8 and HB reg 74 (2025-26) | gross income: wages £9,000 + exempt interest £20,000 + ISA dividends £30,000 a year | £59,000 a year |
| WSI 2013/3029 Sch 6 para 5 (2026-27) | 0 hours; 16 hours £403.85 | 6.70; 13.35 |
| WSI 2013/3029 Sch 6 para 5 (2026-27) | UC couple: worker £300 at 35 hours + partner £19.23 at 0 hours | 13.35 + 6.70 = 20.05 |
| WSI 2013/3029 Sch 1 para 3 (2026-27) | the same couple, pension-age applicant | 13.35 |
| SSI 2012/319 reg 48 (2026-27) | £285.99 / £286.00 in work | 5.55 / 10.95 |
| SSI 2021/249 reg 90 (2026-27) | 16 hours, £403.85; £497.00 / £614.00 in work | 10.95; 13.90 / 16.55 |
| SSI 2021/249 reg 90 (2026-27) | non-dependant whose partner is on IS, neither on UC | 0 |
| SSI 2012/303 reg 67 (2021-22) | the same partner | 4.30 (individual receipt only) |

### Companion for part (e)

Use the 2025-26 HB scale. A non-exempt adult non-dependant works 20 hours a week, earns £4,000 a year, and has no income besides Universal Credit. At the UC award stage, supply £20,000 before the cap, a £14,753 applicable cap, no childcare element and no other counted welfare benefit. DWP takes £720.25 of actual debt deductions. UC reg 81 gives a £5,247 cap reduction, leaving a £14,753 award before debt and a £14,032.75 payment. Count £18,753 annual gross income, or £360.634615… a week. HB reg 74(2)(d) therefore requires £101.35 a week, £5,270.20 over 52 weeks. Counting only the payment would put weekly income at £346.783653… and wrongly give reg 74(2)(c)'s £62, £3,224 a year. Under England's pensioner CTR scheme, Sch 1 para 8(2)(b) requires £10.20 a week, £530.40 over 52 weeks. Select an actual pension-age applicant in a separate family and hold applicant exemptions off. Assert the £14,753 of Universal Credit income and the £18,753 total directly: the CTR amount alone stays in the same band if the encoder wrongly deducts the debt.

| Adapter input or required output | Value |
|---|---|
| `12 * uk:policies/universal_credit_composed_award_pipeline#uc_pilot_award_amount`, or the general reg 81 route above | 14,753 a year |
| `actual_uc_debt_deductions_for_year` (proposed adapter input) | 720.25 |
| `uc_award_after_cap_before_debt_for_year` | 14,753 |
| `uc_payment_after_debt_for_year` | 14,032.75 |
| `non_dependant_uc_income_for_year` | 14,753 |
| `non_dependant_weekly_gross_income` | 360.634615… (18,753 a year) |
| HB `non_dependant_weekly_deduction` (reg 74(2)(d), 2025-26) | 101.35; 5,270.20 over 52 weeks |
| England pensioner CTR `non_dependant_weekly_deduction` (Sch 1 para 8(2)(b), 2025-26) | 10.20; 530.40 over 52 weeks |

This award-stage case supplies the pre-cap quantity and actual debt amount; it does not derive the full household entitlement or debt rate. It uses current law with no hypothetical protected floor. Its annual adapter needs an explicit conversion to the monthly UC output, and its 2025 scale belongs to the requested historical work; the current pilot cannot run it unchanged. The amounts come from decimal arithmetic on the fetched law (reg 81(1)-(3), HB reg 74(2) and England Sch 1 para 8(2) at 7 April 2025, quoted below), not from either engine.

## Known PE deviations (don't encode as law)

1. Annual hours and annual receipt flags stand in for the benefit-week tests ("for more than 3 days in any benefit week"), fluctuating hours and leave (reg 6(2)-(7)).
2. A self-employed non-dependant's gross income counts profit, a loss as nil; the model has no receipts.
3. Student support, statutory adoption, shared parental, parental bereavement and neonatal care pay are not in gross income (no inputs or not added).
4. The model exempts every Welsh ESA(IR) recipient and withholds HB's exemption from every under-25 ESA(IR) recipient. The law treats the assessment phase and work-related activity group differently, and the inputs do not separate them.
5. The Universal Credit "no earned income" limb tests the non-dependant's own earned income as Universal Credit calculates it, not a joint award's combined earned income.
6. Only the payee of a couple's JSA(IB) is on it (no joint-claim input).
7. The model's Universal Credit cap leaves out Maternity Allowance, which Parliament lists as a welfare benefit in Welfare Reform Act 2012 s.96(10)(i), until policyengine-uk#2784 adds it; the after-cap award in gross income follows the model's cap. Encode the statute.

## Operative excerpts bound to the proposed targets

Verbatim from legislation.gov.uk `/data.xml`, fetched 2026-11-01 UTC, with XML markup removed. The DWP guidance at the end is evidence, not law; ingest it separately.

**HB Regs 2006 reg 74**, version at 2026-04-06 (https://www.legislation.gov.uk/uksi/2006/213/regulation/74/2026-04-06; captured original SHA-256 `a2b3e31666e354e3e0f294aa809c22090c6c6344aec2d42724c56b2293782494`):

> (1) Subject to the following provisions of this regulation, the deductions referred to in regulation 70 (maximum housing benefit) shall be— (a) in respect of a non-dependant aged 18 or over in remunerative work, £131.45 per week; (b) in respect of a non-dependant aged 18 or over to whom sub-paragraph (a) does not apply, £20.40 per week.
>
> (2) In the case of a non-dependant aged 18 or over to whom paragraph (1)(a) applies because he is in remunerative work, where it is shown to the appropriate authority that his normal weekly gross income is— (a) less than £192.00, the deduction to be made under this regulation shall be that specified in paragraph (1)(b); (b) not less than £192.00 but less than £279.00, the deduction to be made under this regulation shall be £46.85; (c) not less than £279.00 but less than £365.00, the deduction to be made under this regulation shall be £64.35; (d) not less than £365.00 but less than £485.00, the deduction to be made under this regulation shall be £105.20; (e) not less than £485.00 but less than £605.00, the deduction to be made under this regulation shall be £119.85.
>
> (3) Only one deduction shall be made under this regulation in respect of a couple or, as the case may be, members of a polygamous marriage and, where, but for this paragraph, the amount that would fall to be deducted in respect of one member of a couple or polygamous marriage is higher than the amount (if any) that would fall to be deducted in respect of the other, or any other, member, the higher amount shall be deducted.

At 2025-04-07 (https://www.legislation.gov.uk/uksi/2006/213/regulation/74/2025-04-07; captured `/data.xml` SHA-256 `52cfed4d19152de70b9fe7a528eec60b770e9187856d2f0c1ec581de6cc67d61`), the version the part (e) companion uses, the same text reads £126.65, £19.65, and bands £183.00/£266.00/£348.00/£463.00/£577.00 with £45.15, £62.00, £101.35, £115.45. The verbatim 2025-04-07 text of paragraphs (1)-(2) is pending: quote it from that capture before encoding. Paragraphs (4)-(10) at 2025-04-07:

> (4) In applying the provisions of paragraph (2) in the case of a couple or, as the case may be, a polygamous marriage, regard shall be had, for the purpose of paragraph (2) to the couple's or, as the case may be, all members of the polygamous marriage's joint weekly gross income.
>
> (5) Where a person is a non-dependant in respect of more than one joint occupier of a dwelling (except where the joint occupiers are a couple or members of a polygamous marriage), the deduction in respect of that non-dependant shall be apportioned between the joint occupiers (the amount so apportioned being rounded to the nearest penny) having regard to the number of joint occupiers and the proportion of the payments in respect of the dwelling payable by each of them.
>
> (6) No deduction shall be made in respect of any non-dependants occupying a claimant's dwelling if the claimant or his partner is— (a) severely sight impaired or blind or treated as severely sight impaired or blind by virtue of paragraph 13 of Schedule 3 (additional condition for the disability premium); or (b) receiving in respect of himself either— (i) attendance allowance; … (ia) pension age disability payment; (ii) the care component of the disability living allowance; … (iia) the care component of child disability payment; (iib) the care component of Scottish adult disability living allowance; (iii) the daily living component of personal independence payment; … (iiia) the daily living component of adult disability payment; or (iv) armed forces independence payment.
>
> (7) No deduction shall be made in respect of a non-dependant if— … (c) he is a full-time student during a period of study within the meaning of Part 7 (Students); or (d) he is a full-time student and during a recognised summer vacation appropriate to his course he is not in remunerative work; or (e) he is a full-time student and the claimant or his partner has attained the pensionable age; …
>
> (8) No deduction shall be made in calculating the amount of a rent rebate or allowance in respect of a non-dependant aged less than 25 who is— (a) on income support, an income-based jobseeker's allowance or an income-related employment and support allowance which does not include an amount under section 4(2)(b) of the Welfare Reform Act (the support component and where the non-dependant is not a member of the work-related activity group …); or (b) entitled to an award of universal credit where the award is calculated on the basis that the non-dependant does not have any earned income.
>
> (9) In the case of a non-dependant to whom paragraph (2) applies because he is in remunerative work, there shall be disregarded from his weekly gross income— (a) any attendance allowance, pension age disability payment, disability living allowance, Scottish adult disability living allowance, child disability payment, armed forces independence payment, personal independence payment or adult disability payment received by him; (b) any payment made under the Macfarlane Trust, … ;

**HB Regs 2006 reg 6(1), (6)** at 2025-04-07 (https://www.legislation.gov.uk/uksi/2006/213/regulation/6/2025-04-07):

> (1) Subject to the following provisions of this regulation, a person shall be treated for the purposes of these Regulations as engaged in remunerative work if he is engaged, or, where his hours of work fluctuate, he is engaged on average, for not less than 16 hours a week, in work for which payment is made or which is done in expectation of payment.
>
> (6) A person on income support, an income-based jobseeker's allowance or an income-related employment and support allowance for more than 3 days in any benefit week shall be treated as not being in remunerative work in that week.

SI 2012/2885 reg 10(6), WSI 2013/3029 reg 10(6) and SSI 2012/319 reg 6(6) say the same "for more than 3 days in any reduction week". SSI 2021/249 reg 10(6) (https://www.legislation.gov.uk/ssi/2021/249/regulation/10/2025-04-07):

> (6) A person who is a qualifying income-related benefit claimant for more than 3 days in any reduction week is to be treated as not being in remunerative work in that week.

**SI 2012/2885 Sch 1 para 8**, version at 2026-04-06 (https://www.legislation.gov.uk/uksi/2012/2885/schedule/1/paragraph/8/2026-04-06; captured original SHA-256 `0ce160d67dc7242746425d0a05f539f6109ad3b7b11c645b999f899d95dcbcfa`):

> (1) Subject to the following provisions of this paragraph, the non-dependant deductions in respect of a day referred to in paragraph 7 are— (a) in respect of a non-dependant aged 18 or over in remunerative work, £15.95 x 1/7; (b) in respect of a non-dependant aged 18 or over to whom paragraph (a) does not apply, £5.20 x 1/7.
>
> (2) In the case of a non-dependant aged 18 or over to whom sub-paragraph (1)(a) applies, where it is shown to the appropriate authority that his normal gross weekly income is— (a) less than £279.00, the deduction to be made under this paragraph is that specified in sub-paragraph (1)(b); (b) not less than £279.00 but less than £485.00, the deduction to be made under this paragraph is £10.60 x 1/7; (c) not less than £485.00 but less than £605.00 the deduction to be made under this paragraph is £13.30 x 1/7.

Paragraphs (1)-(2) at 2025-04-07 (https://www.legislation.gov.uk/uksi/2012/2885/schedule/1/paragraph/8/2025-04-07; captured `/data.xml` SHA-256 `4934956bd2d7b525cd9dd36fb7102329b63b8e2c1ccf38c1c7fec45ee40789b2`), the version the part (e) companion uses:

> (1) Subject to the following provisions of this paragraph, the non-dependant deductions in respect of a day referred to in paragraph 7 are— (a) in respect of a non-dependant aged 18 or over in remunerative work, £15.35 x 1/7; (b) in respect of a non-dependant aged 18 or over to whom paragraph (a) does not apply, £5.00 x 1/7.
>
> (2) In the case of a non-dependant aged 18 or over to whom sub-paragraph (1)(a) applies, where it is shown to the appropriate authority that his normal gross weekly income is— (a) less than £266.00, the deduction to be made under this paragraph is that specified in sub-paragraph (1)(b); (b) not less than £266.00 but less than £463.00, the deduction to be made under this paragraph is £10.20 x 1/7; (c) not less than £463.00 but less than £577.00 the deduction to be made under this paragraph is £12.80 x 1/7.

Paragraph (8) at 2025-04-07:

> (8) No deduction is to be made in respect of a non-dependant— (a) who is on income support, state pension credit, an income-based jobseeker's allowance or an income-related employment and support allowance; … (b) to whom Schedule 1 to the 1992 Act applies (persons disregarded for purposes of discount) but this paragraph does not apply to a non-dependant who is a student to whom paragraph 4 of that Schedule refers; or (c) who is entitled to an award of universal credit where the award is calculated on the basis that the person does not have any earned income.

**WSI 2013/3029 Sch 6 para 5(3)** at 2025-04-07 (https://www.legislation.gov.uk/wsi/2013/3029/schedule/6/paragraph/5/2025-04-07; captured original SHA-256 `804582e3c6d28b7bf5922d801da3881a918d53200f5f7635084493d06ce70dfb`):

> (3) Only one deduction is to be made under this paragraph in respect of a couple or, as the case may be, members of a polygamous marriage (other than where there is an award of universal credit) and, where, but for this paragraph, the amount that would fall to be deducted in respect of one member of a couple or polygamous marriage is higher than the amount (if any) that would fall to be deducted in respect of the other, or any other, member, the higher amount is to be deducted.

**SSI 2021/249 reg 90(8)** and **reg 4(1)** at 2025-04-07:

> (8) No deduction is to be made in respect of a non-dependant— (a) who is a qualifying income-related benefit claimant or on state pension credit, (b) to whom schedule 1 of the Act applies (persons disregarded for purposes of discount), but this sub-paragraph does not apply to a non-dependant who is a student to whom paragraph 4 of that schedule refers, or (c) who is entitled to an award of universal credit where the award is calculated on the basis that the non-dependant does not have any earned income, within the meaning given by regulation 52 of the 2013 Regulations.

> "qualifying income-related benefit claimant" means an applicant who is, or who has a partner who is, on one or more qualifying income-related benefits and is not on universal credit,

**UC Regs 2013 reg 52** at 2025-04-07 (https://www.legislation.gov.uk/uksi/2013/376/regulation/52/2025-04-07; captured original SHA-256 `e8827a0c26b5a40928e7ca983f64582dbe0ae0bb483a81ce8162366680ee65f5`), which HB reg 74(11), SI 2012/2885 Sch 1 para 8(11A), WSI 2013/3029 Sch 1 para 3(10) and Sch 6 para 5(10) and SSI 2021/249 reg 90(8)(c) adopt:

> "Earned income" means— (a) the remuneration or profits derived from— (i) employment under a contract of service or in an office, including elective office, (ii) a trade, profession or vocation, or (iii) any other paid work; or (b) any income treated as earned income in accordance with this Chapter.

**UC Regs 2013 reg 62(2)-(3)** at 2025-04-07 (https://www.legislation.gov.uk/uksi/2013/376/regulation/62/2025-04-07; captured original SHA-256 `f9d9f712f351710701eabf441be1e6cc1d695a47f7018fbdea9a5a7d052db9d4`):

> (2) Where this regulation applies to a single claimant, for any assessment period in respect of which the claimant's earned income is less than their individual threshold, the claimant is to be treated as having earned income equal to that threshold.
>
> (3) Where this regulation applies to a claimant who is a member of a couple, for any assessment period in respect of which— (a) the claimant's earned income is less than their individual threshold; and (b) the couple's combined earned income is less than the couple threshold, the claimant is to be treated as having earned income equal to their individual threshold minus any amount by which that amount of earned income combined with their partner's earned income would exceed the couple threshold.

Part (e)'s award, cap and debt-payment law, verbatim from legislation.gov.uk `/data.xml` fetched 2026-11-01 UTC, each the version current on that date:

**Welfare Reform Act 2012 s.8**, version at 2026-07-01 (https://www.legislation.gov.uk/ukpga/2012/5/section/8; `/data.xml` SHA-256 `fc2e379edd0cf935727d046630dca9f22eedf0bfce0ca150e6c139eadd41f7a7`):

> (1) The amount of an award of universal credit is to be the balance of— (a) the maximum amount (see subsection (2)), less (b) the amounts to be deducted (see subsection (3)).
>
> (2) The maximum amount is the total of— (a) any amount included under section 9 (standard allowance), (b) any amount included under section 10 (responsibility for children and young persons), (c) any amount included under section 11 (housing costs), and (d) any amount included under section 12 (other particular needs or circumstances).
>
> (3) The amounts to be deducted are— (a) an amount in respect of earned income calculated in the prescribed manner (which may include multiplying some or all earned income by a prescribed percentage), and (b) an amount in respect of unearned income calculated in the prescribed manner (which may include multiplying some or all unearned income by a prescribed percentage).

**UC Regs 2013 reg 81**, version at 2026-07-16 (https://www.legislation.gov.uk/uksi/2013/376/regulation/81; `/data.xml` SHA-256 `221f87c60bfb4cedd3c9de0781200769aecc409e4a9893d3b8f01ac27a8c8f73`):

> (1) Where the benefit cap applies in relation to an assessment period for an award of universal credit, 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.
>
> (3) The excess is the total amount of welfare benefits that the single person or the couple are entitled to in the reference period, minus the relevant amount determined under regulation 80A.

**SI 2013/380 Sch 6 para 2**, version at 2026-09-22 (https://www.legislation.gov.uk/uksi/2013/380/schedule/6/paragraph/2; `/data.xml` SHA-256 `e695de76930c7ed90385472a5cd635f24230136eab326dffac09e902f33595b9`):

> (1) The Secretary of State may deduct an amount from a claimant's award of universal credit and pay that amount to a third party in accordance with the following provisions of this Schedule to discharge (in whole or part) a liability of the claimant to that third party.

**SI 2013/383 reg 10**, version at 2024-04-01 (https://www.legislation.gov.uk/uksi/2013/383/regulation/10; `/data.xml` SHA-256 `d6cd86bef5a83805d2ad9152e20c7949006e6087264df878a5c73224692de18a`):

> Where it is practicable to do so, a payment on account of benefit— (a) which was made in anticipation of an award of benefit, is to be offset by the Secretary of State against the sum payable to A under the award of benefit on account of which it was made; (b) whether or not made in anticipation of an award, which is not offset under paragraph (a), is to be deducted by the Secretary of State from— (i) the sum payable to A under the award of benefit on account of which it was made; or (ii) any sum payable under any subsequent award of that benefit to A.

**SI 2013/384 reg 10**, version at 2016-04-06 (https://www.legislation.gov.uk/uksi/2013/384/regulation/10; `/data.xml` SHA-256 `daad19d98a4cd8e813e2f6f999e051eaf6f93f312e4d94516127fdbc45f5044f`):

> (1) Subject to regulations 11 to 14, the Secretary of State may recover a recoverable amount from a liable person by deduction from the benefits specified in paragraph (2) which are payable to them.
>
> (2) Those benefits are— (a) benefits under Parts 2 to 5 of the Social Security Contributions and Benefits Act 1992 ; (b) universal credit; (c) jobseeker's allowance; (d) employment and support allowance; (e) state pension credit payable under the State Pension Credit Act 2002 ; ... (f) personal independence payment payable under Part 4 of the 2012 Act; and (g) a state pension under Part 1 of the Pensions Act 2014.

**SI 2013/384 reg 11**, version at 2016-04-06 (https://www.legislation.gov.uk/uksi/2013/384/regulation/11; `/data.xml` SHA-256 `1109c621bc24a3be09a39b5fc584c277e257483e8babe3a606559caa5015faba`):

> (1) The following paragraphs apply where the recoverable amount falls to be recovered by deduction from universal credit payable to the liable person.
>
> (2) Subject to paragraphs (5) to (9), regulation 10 is to apply to the amount of universal credit to which the liable person is presently entitled to the extent that there may be recovered in any one assessment period— (a) in a case to which paragraph (3) applies, an amount equivalent to not more than 40 per cent. of the appropriate universal credit standard allowance; (b) in a case to which paragraph (4) applies but paragraph (3) does not apply, an amount equivalent to not more than 25 per cent. of that allowance; and (c) in any other case, an amount equivalent to not more than 15 per cent.of that allowance.
>
> (7) No deduction made under paragraph (2) is to be applied so as to reduce the universal credit in respect of an assessment period to less than 1 penny.

**DWP HB/CTB guidance manual A5** (https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/236954/hbgm-a5-calculating-benefit.pdf), guidance rather than law; ingest it separately as evidence:

> 5.542 The rate of deduction which applies to non-dependants in remunerative work depends on the non-dependant's gross weekly income, which includes • earnings, and • any other income, but not - DLA or AA - payments from the Macfarlane Trusts, Eileen Trust, London Bombings Relief Charitable Fund (LBRCF) or the Independent Living Funds …
>
> 5.551 The gross income for self employed non-dependants is their total income with no deductions for • business expenses, or • tax and national insurance contributions
>
> 5.570 If the non-dependant declares they receive an income from their savings • average the amount of interest received over the relevant period, and • add the amount to their earnings

Generated by Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6JPH2P5XFEgwSLLAb1
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Ready for hub review

Head: 4ec0693f7a01dc4347c249b9946db7a68be1e3a9. I fast-forwarded hub/2007-r5 to it (2667976b8 → c2c55086e → 4ec0693f7), and the same head sits on claude/apply-2007-r5-nits-ogc2ze. I didn't push hb-ctr-non-dep-exemptions, which stays at 97f792542. The three hub documents are in the three comments above. PR_TITLE.txt is unchanged.

Before starting: the last push to hub/2007-r5 was 2667976b8 at 08:31 UTC, more than 6 hours before I began (14:49 UTC). The last comment was the previous hand-off, and nobody said they were mid-change.

Findings

P3-1 (per-benefit-unit contract).

  • uc_benefit_cap_welfare_benefits.py:26-39: the ValueError message now uses the review's row 2 text ("…with a value for every benefit unit: a situation that sets either for some benefit units stores nil for the rest…").
  • uc_benefit_cap_welfare_benefits.py:52-55: the documentation uses row 3's text.
  • benefit_cap_welfare_benefits.py:66-69: row 4's text.
  • changelog.d/2004.fixed.md: its last sentence is now row 1's text.
  • Body: the "Universal Credit's cap" paragraph uses row 7 and invariant 15 uses row 8. In invariant 15, the "..." stands for the existing set_model_value and reform sentence, which I kept. I also added "for every benefit unit" to the Gross income bullet that cites invariant 15.
  • The three tests that assert on the message use match="housing_benefit_pre_benefit_cap", which still matches, so no test needed a change.

P3-2 (runs, mutants, invariant 14).

  • In the body, the review's runs bullet replaces the old "Runs at this head's code" bullet. That removes the false sentence, "The commits after each run changed only documentation strings, comments and the changelog."
  • The review's mutant paragraph replaces the "wait in scratch copies" paragraph. I kept the old one-line definition of each mutant inline.
  • Invariant 14's 02cc0896f-to-head sentence is now "At 95fbf3014 the same 60 families give arrays bit-identical to 02cc0896f's."
  • The runs bullet gains a last sub-bullet for my runs at this head (below).

P3-3 (Axiom addendum).

  • The header and first paragraph now pin 4ec0693f7a01dc4347c249b9946db7a68be1e3a9.
  • I added England's 2025-04-07 Sch 1 para 8(1)-(2), word for word from the review, with the capture SHA-256 4934956b…89b2 the review gives.
  • Not finished: the verbatim 2025-04-07 HB reg 74(1)-(2). Neither the brief nor the review quotes that text. They give only the summary sentence and the capture hash (52cfed4d…7d61), and legislation.gov.uk doesn't resolve from this session. So I kept the summary sentence, added the hash, and marked the verbatim (1)-(2) text pending, to be quoted from that capture. The hub's r5/law/hb74-2025.xml holds it.
  • The companion's closing sentence now says "quoted below".

P3-4. I added the review's sentence to the ValueError message, at uc_benefit_cap_welfare_benefits.py:36-38: "A reform that neutralizes housing_benefit_pre_benefit_cap drops a supplied value; supply uc_benefit_cap_welfare_benefits instead." I added a matching sentence to the body's reform bullet.

P3-5 (voice table).

  • Rows 1-4 and 7-8: as under P3-1.
  • Row 5: benefit_cap_welfare_benefits.py:70-73.
  • Row 6: benefit_cap_welfare_benefits.py:56 and utils/benefit_cap.py:47. I wrote "The two couples match unless the inputs set is_uc_claimant." instead of the table's "(couples)" parenthesis.
  • Rows 9, 10 (body, and AXIOM_ISSUE.md deviation 7) and 11: in the body and addendum.
  • Row 12: the false sentence, removed under P3-2.
  • Row 13: test_non_dependant_deduction_properties.py:1073-1075.
  • Row 14: AXIOM_ISSUE.md, the "Treat every module path…" line.
  • Row 15: covered by P3-3.
  • Row 16: test_non_dependant_deduction_properties.py:74-76 (invariant 12) and :1149-1156. The docstring states the two-input contract once, so I dropped its old closing sentence, which restated the ValueError with only one input.
  • The 🤖 Generated with line stays.

Merge note. The body names #2784 at 55e015e10. It also says #2784's Maternity Allowance sentences replace this head's sentence about benefits the model has no variable for, which is how the review describes the merge.

Text-only check

c2c55086e and 4ec0693f7 are the only commits after 2667976b8. For every changed Python file at each commit, I blanked every string literal and compared the parsed trees: they match the parent's (True for all four files in c2c55086e and the one in 4ec0693f7). The other changed file is the changelog fragment. The only string that executes is the message that supplied_total_without_housing_benefit returns. The rest are documentation strings, docstrings and comments. 4ec0693f7 only rewraps the invariant 12 docstring paragraph in test_non_dependant_deduction_properties.py.

Tests (no survey data, uv sync --extra dev, -n 4)

Run Head Result
test_non_dependant_cycle_contracts.py, test_non_dependant_deduction_properties.py, test_non_dependant_population_consistent_total.py, code_health/test_test_module_caches_released.py and test_parameter_descriptions.py together c2c55086e 58 passed (14 + 10 + 5 + 5 + 24 by collection)
test_non_dependant_deduction_properties.py, code_health/test_test_module_caches_released.py and test_parameter_descriptions.py together 4ec0693f7 39 passed
test_non_dependant_deduction_properties.py alone first amend of 4ec0693f7 (intermediate) 10 passed
YAML policy/baseline/gov/dwp/benefit_cap c2c55086e 118 passed
code_health/, with towncrier installed c2c55086e 3,104 passed
ruff@0.17.2 format --check ., check ., check --select F811,F821 policyengine_uk, git diff --check 4ec0693f7 clean

Mutation and revert checks

None. These commits change no formula, so there was nothing to revert. The review already reported the contract mutants (M1 and M2, 3 failures each), and the body now states them.

Skipped

  • I didn't rerun the HB non-dependant and CTR YAML directories, the differential or the other property files, because these commits change no formula or YAML. CI covers them.
  • I ran nothing that needs a survey dataset.

Judgment calls

  • I pushed to hub/2007-r5 as well as to the session branch, since the brief allows a fast-forward there.
  • P3-4: of the review's two options, I took "add to the message".
  • HB reg 74(1)-(2) at 2025: I marked it pending instead of rebuilding it from the 2026 text.
  • I edited the posted body and addendum comments in place once to pin the final SHA. The first versions pinned c2c55086e.

Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Ready for hub review

Verdict: APPROVE WITH NITS at 4ec0693f7a01dc4347c249b9946db7a68be1e3a9 (hub/2007-r5), reviewing the delta from 2667976b828e2b622ebc4360971892609d0bca3a: c2c55086e (state the per-benefit-unit contract) and 4ec0693f7 (rewrap the invariant 12 docstring).

I checked out the head detached in a scratch worktree and ran uv sync --frozen --extra dev with HUGGING_FACE_TOKEN and POLICYENGINE_UK_DEFAULT_DATASET unset. I used no survey data.

Findings

No P1 or P2 findings.

P3-1. AXIOM_ISSUE.md: the 2025 HB reg 74 summary sentence is still there beside the verbatim text

The previous round's P3-3 asked for the summary sentence to be replaced by the verbatim 2025-04-07 reg 74(1)-(2). The hub added the verbatim paragraphs but kept the summary sentence ("the same text reads £126.65, £19.65, and bands £183.00/…/£577.00 with £45.15, £62.00, £101.35, £115.45"). The summary and the quote agree (see below), so the sentence is only redundant. Replace the lead-in with:

At 2025-04-07 (https://www.legislation.gov.uk/uksi/2006/213/regulation/74/2025-04-07; captured /data.xml SHA-256 52cfed4d19152de70b9fe7a528eec60b770e9187856d2f0c1ec581de6cc67d61), the version the part (e) companion uses, paragraphs (1)-(2) read:

P3-2 (optional; I can't tell whether this round touched it). AXIOM_ISSUE.md: the excerpt headed "(4)-(10)" stops at (9)(b)

The heading "Paragraphs (4)-(10) at 2025-04-07:" introduces (4), (5), (6), (7), (8) and (9)(a)-(b), and nothing from (10). Change the heading to "Paragraphs (4)-(9)(b) at 2025-04-07, with omissions marked:", or quote (10) from the capture.

Checked and sound

Previous round's findings (review at 2667976b8)

Finding Status Evidence
P3-1 per-benefit-unit contract Closed The message (uc_benefit_cap_welfare_benefits.py:26-39), its documentation (:50-55), benefit_cap_welfare_benefits.py:63-69, the changelog's last sentence, the body's "Universal Credit's cap" paragraph and invariant 15 all carry the review's row 1-4 and 7-8 text. The code agrees: the formula checks supplied_input(...) is not None for the whole population, and a supplied basket replaces the formula outright. My probe of two single-person benefit units, with the inputs set on u1 only: total alone gives ValueError; total plus HB £0 gives basket [20000, 0]; total plus basket £20,000 gives basket [20000, 0]. So the other unit reads nil, as the new text says.
P3-2 body runs, mutants, invariant 14 Closed The review's runs bullet and mutant paragraph are in, the false sentence is gone, and invariant 14 reads "At 95fbf3014 the same 60 families give arrays bit-identical to 02cc0896f's."
P3-3 Axiom 2025 law and pin Partly closed The header and first paragraph pin 4ec0693f7…. England's 2025 para 8(1)-(2) matches the review's quotation character for character (string compare: True). The 2025 HB reg 74(1)-(2) is filled in, but the summary sentence it was meant to replace is still there (P3-1 above).
P3-4 neutralized HB pre-cap Closed The message's last sentence and the body's reform bullet now name it.
P3-5 voice table Closed Rows 1-16 are applied where the hand-off says. Row 6 reads "The two couples match unless the inputs set is_uc_claimant." in both files, which is fine. Row 16's docstring drops the old one-input sentence, and its new last sentence ("Where they supply housing_benefit_pre_benefit_cap, Universal Credit reads that input without calculating Housing Benefit.") is accurate.
Merge note, #2784 at 55e015e10 Closed The body names 55e015e10.

Judgment calls

  • Fast-forwarding hub/2007-r5 is allowed by the brief. git ls-remote shows hub/2007-r5 and claude/apply-2007-r5-nits-ogc2ze at 4ec0693f7, and hb-ctr-non-dep-exemptions still at 97f792542.
  • Of P3-4's two options, the author added the sentence to the message. That is reasonable and touches no logic.
  • The author marked the 2025 reg 74 text pending rather than rebuilding it, which was right. The hub then filled it in (see below).

Text-only check (my own comparison). For each changed .py file at each commit, I blanked every string constant, dropped the constant parts of f-strings and compared ast.dump against the parent. All four files in c2c55086e and the one in 4ec0693f7 give True. The only other changed file is changelog.d/2004.fixed.md. git diff -w --word-diff=porcelain c2c55086e..4ec0693f7 shows no word changes, so the second commit only rewraps. The one executed string that changed is the ValueError message.

The three message-asserting tests still match. test_non_dependant_cycle_contracts.py:328, test_non_dependant_deduction_properties.py:1172 and test_non_dependant_population_consistent_total.py:103 all use match="housing_benefit_pre_benefit_cap", and the new message contains that name. A git grep for the old wording ("for the cap to subtract", "cap counts as nil") finds no other test.

The filled 2025-04-07 HB reg 74(1)-(2)

  • Its figures (£126.65, £19.65; £183.00, £266.00, £348.00, £463.00, £577.00; £45.15, £62.00, £101.35, £115.45) equal the summary sentence and the 2025-04-01 values in parameters/gov/dwp/housing_benefit/non_dep_deduction/amount.yaml.
  • With the figures masked, its wording is identical to the 2026-04-06 quotation.
  • The companion holds: £18,753 / 52 = £360.6346…, which falls in (d)'s £348.00-£463.00 band, so £101.35 × 52 = £5,270.20. £18,032.75 / 52 = £346.7837… falls in (c)'s £266.00-£348.00 band, so £62.00 × 52 = £3,224. Both fall in England's £266.00-£463.00 band, so £10.20 × 52 = £530.40.
  • The body's 2025-26 YAML figures (£100 / £183.02 / £349 / £578 giving 19.65 / 45.15 / 101.35 / 126.65) fit the same bands.

Runs at 4ec0693f7 (uv run pytest … -n 4; uv run policyengine-core test … -c policyengine_uk)

Run Result
test_non_dependant_cycle_contracts.py, test_non_dependant_population_consistent_total.py, test_non_dependant_deduction_properties.py, code_health/test_test_module_caches_released.py, test_parameter_descriptions.py together 58 passed in 238.62 s (14 + 5 + 10 + 5 + 24; the parameter-descriptions file collects 24)
YAML policy/baseline/gov/dwp/benefit_cap 118 passed
YAML benefit_cap_by_scheme.yaml 22 passed
code_health/ (towncrier importable) 3,104 passed in 289.87 s, matching the author's count at c2c55086e
Mutant M2 in the scratch copy (replace raise ValueError(...) with return benunit("benefit_cap_welfare_benefits", period)), then test_non_dependant_cycle_contracts.py Killed: 3 failed (test_a_supplied_total_alone_raises[mixed], [single], test_model_values_supply_neither_input), 11 passed. I restored the file and the worktree is clean.
ruff format --check ., ruff check ., ruff check --select F811,F821 policyengine_uk with ruff==0.17.2 (the version uv.lock pins) clean ("2174 files already formatted", "All checks passed!")
git diff --check 2667976b8 4ec0693f7 clean

Merge with current main. git merge-tree --write-tree origin/main 4ec0693f7, with main at 46a60678ff52d829253572393abdcc6a03ceb6a7 (17 commits past 2da212abc), is clean (exit 0).

Writing. I read every line of text this delta touched: the message, the two documentation strings, the benefit_cap.py docstring, the fixture comment, the invariant 12 docstring, the changelog, the body sections that changed and the Axiom additions. It has no passives and no fix history. Actors are DWP, the model, the formula or a caller. The rewrapped docstring is unchanged word for word.

What I couldn't check


Generated by Claude Code

@MaxGhenis MaxGhenis changed the title Apply Housing Benefit and national Council Tax Reduction non-dependant deductions as legislated Fix HB and national CTR non-dependant deductions Nov 2, 2026
Conflicts: the Housing Benefit notebook's means-test sentence keeps #2007's
non-dependant deduction sentences and main's applicable-income sentences;
shard_durations.toml keeps both sides' timings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6HoH87XjnfRqcGiYmc
main's benefit_cap_reduction_applied.yaml supplies benefit_cap_welfare_benefits
alone. Under this branch's contract a supplied total comes with the Housing
Benefit in it, so each case supplies the nil the model calculates without
rent; the outputs don't change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6HoH87XjnfRqcGiYmc
MaxGhenis pushed a commit that referenced this pull request Nov 4, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6HoH87XjnfRqcGiYmc
@MaxGhenis
MaxGhenis requested a review from juaristi22 November 5, 2026 00:41
MaxGhenis and others added 3 commits November 4, 2026 23:10
Git stopped on one file, the documentation of benefit_cap_welfare_benefits. The merge keeps this PR's sentences on the two baskets and the supplied total, and #2811's sentences on Maternity Allowance and the other welfare benefits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With #2811 on main, DWP's cap counts the partner's 6,000 of Maternity Allowance, so the case's Universal Credit and both deductions move to the figures its comment worked out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Nov 5, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
esa_income_abolition_reached moves a couple reporting income-related ESA to
Universal Credit in 2026, and the award moves the working partner's band. These
cases test how income-related ESA counts in the band, so the couples stay on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Nov 5, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants