Skip to content

Include other investment income in total income - #2184

Merged
vahid-ahmadi merged 4 commits into
mainfrom
fix-total-income-other-investment-income
Oct 19, 2026
Merged

vahid-ahmadi merged 4 commits into
mainfrom
fix-total-income-other-investment-income

Conversation

@vahid-ahmadi

@vahid-ahmadi vahid-ahmadi commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #2174.

What was wrong

total_income cites ITA 2007 s. 23, whose Step 1 sums all income charged to income tax. It added eight components but not other_investment_income (National Savings, securities and trust interest). Income tax does charge that income: taxable_savings_interest_income adds it to savings_interest_income, so income_tax and adjusted_net_income include it.

For anyone with other investment income, total_income was lower than the income the model taxes. So post_tax_income subtracted the tax on that income without adding the income, and the Winter Fuel Payment income test missed it.

Separately, the Winter Fuel Payment income test took the payment away at exactly £35,000. The winter fuel payment charge it stands in for applies only where total income "exceeds £35,000" (ITEPA 2003 s.681I(1)(b), inserted by Finance Act 2026 Sch 10).

Fix

  • Add other_investment_income to total_income.adds. It isn't double counted: savings_interest_income is an input that excludes it, and the two are summed only in taxable_savings_interest_income, which doesn't read total_income.
  • housing_benefit_net_earnings loops over total_income.adds, so it picks up the new component without a code change. The denominator of its earnings share now includes the income that the tax being apportioned is charged on.
  • The Housing Benefit and national Council Tax Reduction non-dependant deductions band on total_income less other_investment_income, so they are exactly as on main. Their bands apply only to a non-dependant in remunerative work (HB Regs 2006 reg 74(2), SPC Regs reg 55(2), SI 2012/2885 Sch 1 para 8(2)), and the model doesn't yet test that. Counting the new income there would band non-workers whose only income is investment income. Apply Housing Benefit and national Council Tax Reduction non-dependant deductions as legislated #2007 implements reg 74 in full (work test, exemptions, couples) and can then read total_income directly.
  • The Winter Fuel Payment income test is now <= the limit, not <.
  • Statutory pay (employment_benefits), the related gap noted in the issue, is left for a separate change. housing_benefit_net_earnings adds it on top of total_income, so including it here would need that formula changed too.

Who's affected

Two groups change: people with other_investment_income above zero, and pensioners not on a relevant benefit whose total income is exactly the £35,000 limit. Nobody else's results move.

Reader Effect
post_tax_income Rises by the other investment income, so it's now net of its own tax rather than of tax on missing income.
winter_fuel_payment_eligible The income test counts other investment income, and keeps the payment at exactly £35,000.
housing_benefit_net_earnings Less income tax is attributed to earnings when a person also has other investment income.
bi_individual_phaseout, bi_household_phaseout These contributed reforms now count it.
HB and national CTR non-dependant deductions Unchanged from main for every household (guard tests below).

Unchanged: income_tax and adjusted_net_income, so the personal allowance taper and the High Income Child Benefit Charge.

Datasets that calibrate to HMRC SPI total-income bands (for example Microcosm UK's 47 total_income targets) now band these records by the income they're taxed on.

Microsimulation, 2026, main (e01f2f5) against this branch

Same pinned file and weights for both runs, values mapped to households.

  • Enhanced FRS 2024-25 (sha256 e433e532…): no change to any output. The dataset carries no other_investment_income, and no pensioner's Winter Fuel Payment moves.
  • Microcosm UK 2024-25 (sha256 aa31bdf6…), which does carry it (£2.78bn over 2,305 sample households, 227k weighted):
Variable main Change Households up Households down
income_tax £349.18bn £0 0 0
adjusted_net_income £1,887.13bn £0 0 0
total_income £1,887.98bn +£2,781.3m 227k 0
post_tax_income £1,482.68bn +£2,781.3m 227k 0
housing_benefit £8.32bn £0 0 0
council_tax_reduction £1.20bn £0 0 0
HB and CTR non-dependant deductions £0 0 0
winter_fuel_allowance £1.546bn −£0.28m 0 1k (9 sample households)

post_tax_income rises by exactly each household's other investment income (within float rounding). Every Winter Fuel Payment change is in a household with other investment income that now takes a pensioner over £35,000. Workers, non-workers and zero-income households see no Housing Benefit or CTR change, because the non-dependant deductions are unchanged, and no household's HB moved through the net-earnings apportionment either.

Tests

  • New gov/hmrc/income_tax/total_income.yaml: other investment income only; with savings interest, not double counted; above the £100k taper, where total_income equals adjusted_net_income and the personal allowance is £7,570.
  • New household/post_tax_income_other_investment.yaml: £20,000 of other investment income in 2025-26 gives tax of £286 and post-tax income of £19,714.
  • Non-dependant guards (HB and national CTR): a non-worker with £28,912 (£556 a week) of other investment income gets the same deduction as one with no income, where banding it would give £115.45 a week (£6,003.40 a year); a 37.5-hour worker with £183 a week of earnings and £556 a week of investment income stays in the £45.15 band; a household case for each, HB with a working-age claimant and CTR with a pensioner claimant.
  • Winter Fuel Payment: 2026 cases at £34,999, £35,000 and £35,001 of total income built from pension and investment income, and £35,000 in 2025-26. The existing 2024 reform case at the limit now expects the payment, and the property-test oracle and its income draws include the limit.
  • The total-income, post-tax-income and investment-income Winter Fuel cases fail on main. The non-dependant guards and the £35,000 cases fail on the previous head (21bb4dd, rebased onto main) and pass here; the guards also pass on main, which is what they pin.
  • Full policy YAML suite: 3,073 passed. pytest outside tests/policy: 5,393 passed, 100 skipped, 2 xfailed. ruff format and ruff check are clean. .github/check_fix_pr.py passes on this description.

Reproducer: policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/total_income.yaml, policyengine_uk/tests/policy/baseline/household/post_tax_income_other_investment.yaml, policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/household_benefits_individual_non_dep_deduction.yaml::Other investment income does not band a non-worker, policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_non_dep_deductions.yaml::A non-dependant's investment income does not raise the Housing Benefit deduction, policyengine_uk/tests/policy/baseline/gov/local_authorities/council_tax_reduction/council_tax_reduction_individual_non_dep_deduction.yaml, policyengine_uk/tests/policy/baseline/gov/dwp/winter_fuel_allowance.yaml::Pensioner with total income of exactly £35,000 keeps WFP, policyengine_uk/tests/policy/reforms/parametric/winter_fuel_allowance/taxable_income_test.yaml::Enable income test - other investment income counts towards taxable income

axiom: uk:statutes/ukpga/2007/3/23 encoded-correct (Step 1 sums every income component charged to income tax, so other investment income counts with no special case; companion case 23.test.yaml::taxpayer_liability_after_reliefs_allowances_reductions_and_added_tax); ITEPA 2003 s.681I (the £35,000 "exceeds" boundary) is TheAxiomFoundation/rulespec-uk#420 queued

This PR no longer changes HB Regs 2006 reg 74 or HB (SPC) Regs 2006 reg 55, so it makes no parity claim for them; that work and its parity line belong to #2007.

Should be squash-merged.

🤖 Generated with Claude Code

@vahid-ahmadi vahid-ahmadi self-assigned this Oct 8, 2026
@juaristi22

juaristi22 commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Review of #2184 — Include other investment income in total income

Base repository: PolicyEngine/policyengine-uk
PR number: 2184
Reviewed head SHA: c8cda39
Merge base SHA: c85ffef
Mode: incremental from c8cda39
Scope: changed behavior and affected dependencies
Source evidence: Official sources are linked below; local source copies and validation artifacts were retained.
Review status: COMPLETE

Source Documents

Critical

C1 — Gate Housing Benefit deduction bands on remunerative work (STILL OPEN)

Severity: CRITICAL (Must Fix)
Status: OPEN
Location: policyengine_uk/variables/gov/hmrc/income_tax/total_income.py:22; new regression test policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/household_benefits_individual_non_dep_deduction.yaml:39.

Adding other_investment_income correctly fixes tax/post-tax income, but now feeds investment-only nonworking adults into Housing Benefit's earnings-linked non-dependant bands. The new test explicitly accepts the highest band for £28,912 investment income and no work inputs. Housing Benefit Regulations 2006 regulation 74(1)(b) and (2) place adult non-dependants who are not in remunerative work in the minimum deduction category; the higher gross-income bands are expressly conditional on remunerative work. The downstream deduction formula checks total_income without this work gate. Retain the income inclusion and protect that consumer's category selection, with separate working/nonworking tests.

Executed trigger: model year 2025, 40-year-old existing Housing Benefit claimant in council housing with annual rent £15,000 and reported HB £5,000; separate benefit unit for a 25-year-old adult child with £28,912 other investment income, zero earnings, self-employment income and hours, OTHER_INACTIVE status and no education. The claimant is explicitly a continuing unmigrated HB recipient whose notice/deadline has not ended entitlement, with would_claim_uc=False. That variable is a supplied take-up preference with no formula, not an overridden eligibility result. No eligibility or intermediate deduction is forced. The model computes HB eligibilitytrue for the claimant, identifies the adult child as a non-dependant, and computes zero JSA, ESA, Income Support and Pension Credit for them.

Observed: deduction £124.55/week (£6,476.60/year) and final claimant HB £8,523.40. The source-backed expected category is the minimum, regardless of this nonworker's investment-income level. A named in-memory minimum-category control using the captured model's stored £19.30/week lower tier gives deduction £1,003.60/year and final HB £13,996.40: the current code removes £5,473/year more than its own minimum-category implementation. The model's £19.30 is used only to quantify this conditional modeled difference; the exact statutory 2025 amount was not independently corroborated, and this is not a finding about that rate. Claim-history/migration timing is not encoded, so the reproduction's continuing-award premise is explicit. The material wrong category is established independently by regulation 74.

Reproduction: script (pr-2184-code-hb-diagnostic.py; retained locally), full output (pr-2184-code-hb-diagnostic.log; retained locally), bounded exit 0 / elapsed 4.63s. Source evidence: official extract (policy-source-hb-nondep.txt; retained locally), lines 113–115. This diff exposes the existing missing work gate for the newly included investment-income-only population and codifies the wrong result at the changed test line. The pension-age and 2026 preliminary examples were discarded; only the successful 2025 working-age case above supports this finding.

Should Address

None.

Suggestions

None.

Evidence Gaps

No material gaps reported.

Notes

  • Conditional continuing unmigrated2025HBclaim: reportedHB and UCtake-up preference explicit, eligibility computed. Exactstatutory2025minimum not corroborated; quantified£5,473difference is against capturedmodelminimum-categorycontrol, not claimedactual statutoryloss. Preliminary pensionage/2026examples discarded.
  • Evidence reused after confirming identical reviewed head and merge base. The fetched main branch has advanced; the review remains scoped to this unchanged head. Current CI was captured separately. Local tests were not rerun.
  • Confirmed critical code-1: source-backed nonworking minimum category and executed2025 final-HB loss; owned by code reviewer; do not duplicate. Quantified£5,473 difference uses captured-model minimum control; exact statutory2025minimum not independently verified.
  • Captured base branch: main. Actual fetched base SHA: f1a9a3c. Incremental scope contains no code changes.

Validation Summary

Reused exact-head validation (not rerun in this batch): 240 tests passed (pr-2184-code-tests.log); final2025working-age nonworking-investment-only HBdiagnostic exit0,4.63s, claimantHB8523.40 vs13996.40 named minimum-categorycontrol; pr-2184-code-hb-diagnostic.py/.log. Snapshot import confirmed.

Reused exact-head validation (not rerun in this batch): Three official originals/extracts; reg74work-status rule corroborated. Code final2025case eligibility True, HB£8,523.40 vs minimum-category modelcontrol£13,996.40 (exit0,4.63s). Cache helper passed; source manifest canonical.

CI at c8cda39: Install + smoke-import (py3.11)=SUCCESS, Install + smoke-import (py3.12)=SUCCESS, Install + smoke-import (py3.13)=SUCCESS, Install + smoke-import (py3.14)=SUCCESS, Lint=SUCCESS, Test=SUCCESS.

Detailed code, policy, source and reproduction artifacts were retained locally.

Timing

elapsed seconds: 1151.70s; setup seconds: 269.92s

Review Severity

REQUEST_CHANGES. Open findings: 1 critical, 0 should address, 0 suggestions.

vahid-ahmadi added a commit that referenced this pull request Oct 9, 2026
Addresses María's finding C1 on #2184. Counting other investment income in
total_income pushed non-working non-dependants with investment income into
the Housing Benefit gross-income bands. HB Regs 2006 reg 74(2) (SPC Regs reg
55(2)) applies those bands only to a non-dependant "in remunerative work";
anyone else aged 18 or over takes the reg 74(1)(b) minimum. Reg 6(1) defines
remunerative work as at least 16 paid hours a week.

- Add gov.dwp.housing_benefit.non_dep_deduction.remunerative_work_hours (16)
  and band income only when weekly_hours >= 16; otherwise, and for a worker
  below the first threshold, deduct the first-bracket (reg 74(1)(b)) amount.
- The minimum now also reaches zero-income non-dependants, who previously got
  0 because calc(0, right=True) fell below the first bracket. That exposed the
  missing reg 74(6) / SPC reg 55(6) exemption (claimant or partner blind or on
  AA, DLA care, PIP daily living or AFIP), so add
  housing_benefit_non_dep_deduction_exempt and apply it in
  housing_benefit_non_dep_deductions. The two pension-age severe disability
  tests pass with their original expected values.
- Fix the investment-only test to expect the minimum; add hours to the
  band tests; add non-worker, worker, 16-hour boundary, zero-income and AA
  exemption tests.

UC's non-dependant housing cost contribution is a flat amount and is
unaffected.

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

Copy link
Copy Markdown
Collaborator Author

Thanks, María. You were right on C1, and it's fixed in 291a01c.

What the regulation says

  • Under HB Regs 2006 reg 74(2) (SPC Regs reg 55(2)), the gross-income bands apply only to a non-dependant "to whom paragraph (1)(a) applies because he is in remunerative work".
  • Any other non-dependant aged 18 or over takes the reg 74(1)(b) minimum, whatever their income.
  • Reg 6(1) of both sets of Regulations defines remunerative work as at least 16 paid hours a week.

What changed

  • New parameter gov.dwp.housing_benefit.non_dep_deduction.remunerative_work_hours = 16, citing reg 6(1).
  • household_benefits_individual_non_dep_deduction now bands income only when weekly_hours >= 16. Otherwise it gives the first-bracket amount, which is the reg 74(1)(b) minimum. A worker whose income is below the first threshold, including zero, also gets the minimum, as reg 74(2)(a) requires.
  • other_investment_income stays in total_income. It still counts towards the band of a non-dependant who is in remunerative work.

A knock-on fix

Before this change, a non-dependant with zero income got a £0 deduction rather than the minimum, because calc(0, right=True) falls below the first bracket. Giving them the minimum broke two pension-age severe disability tests. In both, the claimant is on higher-rate Attendance Allowance, and reg 74(6) (SPC reg 55(6)) says no deduction is made at all in that case. The model didn't implement reg 74(6); the old £0 only hid that.

I added housing_benefit_non_dep_deduction_exempt, which covers a claimant or partner who is blind or receives Attendance Allowance, the DLA care component, the PIP daily living component or Armed Forces Independence Payment, and applied it in housing_benefit_non_dep_deductions. Both tests pass with their original expected values. This does widen the change beyond your repro: any non-dependant aged 18 or over who has no paid hours and no exemption now has the minimum deducted, where the model used to deduct nothing.

Evidence

  • Your repro (2025; claimant aged 40, council rent £15,000, reported HB £5,000, would_claim_uc=False; non-dependant aged 25 with £28,912 other investment income and no hours). Before: deduction £6,476.60 (£124.55 a week) and HB £8,523.40. After: deduction £1,003.60 (£19.30 × 52) and HB £13,996.40. That matches your minimum-category control.
  • The line-39 case now has 16 hours a week (hours_worked: 832) and keeps the £124.55 band. A new twin with the same £28,912 and no hours expects the minimum (19.30 × 52). The four existing band tests also have 16 hours a week and keep their values.
  • New tests:
    • a non-worker with £100,000 of investment income gets the minimum;
    • a 37.5-hour worker earning £28,912 stays in the £124.55 band;
    • 831 hours a year (15.98 a week) gets the minimum, and 832 (16.0 a week) gets the band;
    • a 16-hour worker with no income gets the minimum;
    • a household-level case modelled on yours gives a £1,003.60 deduction;
    • a claimant on Attendance Allowance has no deduction, although the non-dependant on their own would take £6,476.60.
  • Six of the new cases fail on c8cda39 and pass here. The band cases pass on both commits, so workers' results are unchanged.
  • Full policy YAML suite: 2,630 passed. pytest outside tests/policy: 3,131 passed, 34 skipped, 2 xfailed. ruff format and ruff check are clean.

Unchanged and out of scope

  • UC: uc_individual_non_dep_deduction is a flat amount and doesn't read income, so it's unaffected.
  • National CTR: council_tax_reduction_individual_non_dep_deduction has the same gap. SI 2012/2885 Sch 1 para 8(2) gates its bands on remunerative work, which reg 10 defines as 16 hours. Its national path also doesn't apply the para 8(6) exemption. The local-authority schemes already gate on hours. Fixing the national variable properly needs both changes, and it reads the HB table even for Welsh councils, so I've left it for a separate PR rather than widen this one.
  • Pre-existing, not changed here:
    • right=True puts income exactly on a threshold (for example £176.00) in the lower band, but reg 74(2) says "not less than £176".
    • The reg 74(7)–(8) exemptions (students, under-25s on income-based benefits, patients, armed forces) aren't modelled.
    • The reg 6(5)–(8) adjustments (absences, income-based benefit receipt, leave) aren't modelled.
    • Couple aggregation under reg 74(3)–(4) isn't modelled.

🤖 Generated with Claude Code

vahid-ahmadi added a commit that referenced this pull request Oct 12, 2026
Addresses María's finding C1 on #2184. Counting other investment income in
total_income pushed non-working non-dependants with investment income into
the Housing Benefit gross-income bands. HB Regs 2006 reg 74(2) (SPC Regs reg
55(2)) applies those bands only to a non-dependant "in remunerative work";
anyone else aged 18 or over takes the reg 74(1)(b) minimum. Reg 6(1) defines
remunerative work as at least 16 paid hours a week.

- Add gov.dwp.housing_benefit.non_dep_deduction.remunerative_work_hours (16)
  and band income only when weekly_hours >= 16; otherwise, and for a worker
  below the first threshold, deduct the first-bracket (reg 74(1)(b)) amount.
- The minimum now also reaches zero-income non-dependants, who previously got
  0 because calc(0, right=True) fell below the first bracket. That exposed the
  missing reg 74(6) / SPC reg 55(6) exemption (claimant or partner blind or on
  AA, DLA care, PIP daily living or AFIP), so add
  housing_benefit_non_dep_deduction_exempt and apply it in
  housing_benefit_non_dep_deductions. The two pension-age severe disability
  tests pass with their original expected values.
- Fix the investment-only test to expect the minimum; add hours to the
  band tests; add non-worker, worker, 16-hour boundary, zero-income and AA
  exemption tests.

UC's non-dependant housing cost contribution is a flat amount and is
unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vahid-ahmadi
vahid-ahmadi force-pushed the fix-total-income-other-investment-income branch from 291a01c to 21bb4dd Compare October 12, 2026 08:15
@MaxGhenis

Copy link
Copy Markdown
Collaborator

Suggested Reproducer: line for the new Fix evidence check; add it to the description, or narrow it to the test that reproduces the bug. It lists the tests this PR adds or changes, and the check (.github/check_fix_pr.py) accepts it against this PR's diff.

Reproducer: `policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/household_benefits_individual_non_dep_deduction.yaml`, `policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_non_dep_deductions.yaml::A non-working adult non-dependant with only investment income takes the minimum deduction`, `policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_non_dep_deductions.yaml::A claimant on Attendance Allowance has no non-dependant deduction`, `policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/total_income.yaml::Other investment income only counts in total income`, `policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/total_income.yaml::Other investment income adds to savings interest, not double counted`, `policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/total_income.yaml::Total income matches adjusted net income above the taper threshold`

The check isn't required yet (decision d1368); it fails fix PRs whose description names no reproducer.

🤖 Generated with Claude Code

@MaxGhenis

Copy link
Copy Markdown
Collaborator

Independent review (GPT-6.1 Sol, UK hub, 2026-10-12): REQUEST_CHANGES at 21bb4dd06.

@vahid-ahmadi: the central change, adding other_investment_income to total_income, is correct under ITA 2007 s. 23, with no double counting. But the reviewer executed four household cases that break nearby rules:

  • zero-income students lose their non-dependant exemption to the new minimum;
  • a non-dependant couple is charged two minimums;
  • national pensioner CTR now charges investment-only non-workers HB rates;
  • Winter Fuel Payment is lost at exactly £35,000.

Full review below. Would you like to fix these yourself, or have the hub push the fixes as new commits on your branch? Say which here. Note that #2007 and #2017 (open drafts) overlap six of the same HB files; finding 6 suggests how to compose them.

VERDICT: REQUEST_CHANGES
Head reviewed: 21bb4dd

  1. [blocker] The new minimum charges exempt non-dependants. household_benefits_individual_non_dep_deduction.py:36 now gives a zero-income, 21-year-old full-time student a £1,021.80 annual deduction in 2025; main gives zero. The student exemption requires zero. Apply non-dependant exemptions before imposing the minimum, including applicable benefit exemptions, and add working-age and pension-age regressions. The claimant disability exemption added here does not cover these cases. Sources: HB reg 74(7)–(8), pension-age reg 55(7)–(9).

  2. [blocker] A non-dependant couple receives two minimum deductions. housing_benefit_non_dep_deductions.py:35 still sums deductions person by person. The executed zero-income couple case produces £2,043.60, although the lawful single minimum is £1,021.80. Calculate joint gross income and count one deduction per couple; test both a zero-income couple and a worker with a zero-income spouse. Sources: HB reg 74(3)–(4), pension-age reg 55(3)–(4).

  3. [blocker] National CTR newly applies an excessive deduction to investment-only non-workers. council_tax_reduction_individual_non_dep_deduction.py:14 reads the increased total_income, applies HB rates and has no remunerative-work gate. The executed pensioner-household case with a non-working adult receiving £28,912 investment income produces £6,003.40 annually. England pensioner CTR requires its own minimum category for a non-worker. Correct the reader’s scheme rates, work condition and exemptions, and add a regression. Calling national CTR “unchanged” is inaccurate. Source: Prescribed Requirements Regulations, Schedule 1 paragraph 8.

  4. [blocker] The Winter Fuel reader wrongly excludes exactly £35,000. winter_fuel_payment_eligible.py:60 uses <. Executed 2026 English pensioner cases with £15,000 pension and varying investment income paid £200 at £34,999, £0 at £35,000 and £0 at £35,001. Recovery applies only above £35,000. Change the comparison to <= and add actual-baseline boundary tests using derived income. The inherited household-wide proxy also remains inconsistent with an individual charge. Source: Finance Act 2026, Schedule 10.

  5. [should-fix] Document and measure the expanded policy change. 2174.fixed.md:2 describes HB entitlement changes beyond investment-income aggregation. The PR description needs a 2026 enhanced-FRS Microsimulation comparison of main against this branch, using identical pinned data and weights. Report weighted totals and counts of increases/decreases for income tax, post_tax_income, HB, CTR and Winter Fuel Payment, separating workers, non-workers, zero-income households and exemptions. Expect modeled income-tax delta zero and post-tax-income delta equal to included investment income. Correct the investment-only scope claim, stale test rates and axiom: n/a statement; the added HB provisions require an applicable parity statement or axiom: needed.

  6. [should-fix] Coordinate the overlapping HB implementation. housing_benefit_non_dep_deduction_exempt.py:4 introduces a BenUnit variable whose exact name/file #2007 uses for a Person exemption. Both Apply Housing Benefit and national Council Tax Reduction non-dependant deductions as legislated #2007 and #2017 are open drafts; they overlap six HB files and the aggregate formula respectively. Rename this claimant-side variable to the coordinated claimant-exemption name, or compose the branches explicitly. The 16-hour gate and claimant exemption are legally supported, but this partial HB expansion should be composed with that fuller work before landing.

The central income addition is correct under ITA 2007 s 23 Step 1. taxable_savings_interest_income already combines ordinary interest and other investment income, subtracting exempt interest. ANI includes that taxable component once; income tax calculates independently of total_income. There is no new double counting. post_tax_income now correctly includes the cash income once.

The only explicit iterator over total_income.adds is housing_benefit_net_earnings.py:64. Other direct readers are HB and national CTR non-dependant deductions, Winter Fuel eligibility, post_tax_income, and the individual and household basic-income phase-outs. HB net earnings gains a more complete denominator, but retains its documented proportional-tax approximation; self-employment tax deductions remain inconsistent with the profit-specific notional calculation in HB reg 39.

The YAML expectations are hand-derived. They cover mixed interest, allowance tapering, post-tax income, the 16-hour boundary and Attendance Allowance, but miss the regressions above. No test is weakened, skipped or xfailed by this diff. No property test covers the new accounting or deduction invariants; the existing Winter Fuel property oracle repeats the strict comparison and omits £35,000.

Required invariants include counting investment income once, unchanged tax/ANI between definitions, and the corresponding post-tax-income increase. Total income should cover charged income in the supported nonnegative domain; a universal assertion would currently fail because statutory pay is omitted and some negative components differ from floored taxable bases. Statutory exemption and one-deduction-per-couple invariants also need coverage.

Other open overlaps are #2002 (total_income), #2035 and #2131 (HB aggregate), and #2066 (Winter Fuel test).

Compressor usage was 21.1 GiB before testing. Serial pytest with the PolicyEngine YAML collector passed 35 changed-file cases plus 11 HB net-earnings cases. gh lacked network access; connected GitHub tools supplied the live PR, comments and overlap checks. The worktree remains unchanged.

@juaristi22 juaristi22 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed 21bb4dd0649f2190af085c3f3a5be3e8100480fb. I found no model-code defect. The 49 focused tests pass, and current-head CI is 10/10 green.

Please update the PR description before merge: (1) it says only households with other_investment_income change, but the new Housing Benefit remunerative-work gate and claimant disability exemption also affect households without that income; (2) its 2025–26 non-dependant deduction examples use stale £124.55/£19.30 values, while current parameters and tests give £115.45/£19.65; (3) axiom: n/a: aggregation... covers income aggregation only, while the diff also implements Housing Benefit regulation 74(2),(6) and State Pension Credit regulation 55 rules. Please state the corresponding RuleSpec parity outcome, or a specific reason those additional rules are inapplicable. These are description and traceability corrections, so I am leaving a comment rather than requesting code changes.

@vahid-ahmadi

Copy link
Copy Markdown
Collaborator Author

@MaxGhenis thanks, I'll fix these myself on this branch today.

vahid-ahmadi and others added 4 commits October 12, 2026 14:41
ITA 2007 s. 23 Step 1 sums all income charged to income tax, and the
model charges other_investment_income through
taxable_savings_interest_income. total_income left it out, so
post_tax_income subtracted tax on income it never added, and the Winter
Fuel taxable-income test and HB/CTR non-dependant bands missed it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses María's finding C1 on #2184. Counting other investment income in
total_income pushed non-working non-dependants with investment income into
the Housing Benefit gross-income bands. HB Regs 2006 reg 74(2) (SPC Regs reg
55(2)) applies those bands only to a non-dependant "in remunerative work";
anyone else aged 18 or over takes the reg 74(1)(b) minimum. Reg 6(1) defines
remunerative work as at least 16 paid hours a week.

- Add gov.dwp.housing_benefit.non_dep_deduction.remunerative_work_hours (16)
  and band income only when weekly_hours >= 16; otherwise, and for a worker
  below the first threshold, deduct the first-bracket (reg 74(1)(b)) amount.
- The minimum now also reaches zero-income non-dependants, who previously got
  0 because calc(0, right=True) fell below the first bracket. That exposed the
  missing reg 74(6) / SPC reg 55(6) exemption (claimant or partner blind or on
  AA, DLA care, PIP daily living or AFIP), so add
  housing_benefit_non_dep_deduction_exempt and apply it in
  housing_benefit_non_dep_deductions. The two pension-age severe disability
  tests pass with their original expected values.
- Fix the investment-only test to expect the minimum; add hours to the
  band tests; add non-worker, worker, 16-hour boundary, zero-income and AA
  exemption tests.

UC's non-dependant housing cost contribution is a flat amount and is
unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main now uses the SI 2025/295 bands and amounts (minimum £19.65, £463-576.99 band £115.45), so the remunerative-work cases expect those.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… at £35,000

Review of 21bb4dd (hub findings 1-3): the remunerative-work gate gave
the reg 74(1)(b) minimum to non-dependants the regulations exempt (full-time
students, reg 74(7)(c)-(e) / SPC reg 55(7)(e)) and charged a non-dependant
couple twice (reg 74(3)-(4)). Doing that properly is #2007's work, so this
drops the gate, the minimum and the claimant exemption from this PR.

Housing Benefit and national CTR non-dependant deductions now band on
total_income less other_investment_income, so they are unchanged from main
for every household until #2007 models the remunerative-work test. That
also stops the national CTR deduction banding a non-worker whose only
income is investment income (hub finding 3, review C1).

Winter Fuel Payment (hub finding 4): ITEPA 2003 s.681I(1)(b) charges only
total income that "exceeds £35,000", so the income test is now inclusive.
The property-test oracle and the 2024 reform case are updated to match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vahid-ahmadi
vahid-ahmadi force-pushed the fix-total-income-other-investment-income branch from 21bb4dd to 8c222fa Compare October 12, 2026 14:02
@vahid-ahmadi

Copy link
Copy Markdown
Collaborator Author

Thanks both. Pushed 8c222fa, rebased onto main (e01f2f5). I've narrowed the PR: the remunerative-work gate, the new minimum deduction and the claimant exemption are gone, and the core total_income change stays.

Why narrow. I reproduced findings 1 to 3 at 21bb4dd, and the law backs all three (HB reg 74(3), (4), (7) and (8); SPC reg 55(3), (4) and (7); SI 2012/2885 Sch 1 para 8). Fixing them properly means student and benefit exemptions, couple aggregation and CTR scheme rates. That is #2007's job almost file for file. Keeping only the 16-hour gate without the minimum would give part-time earners a new wrong result, so I dropped the whole HB expansion rather than half of it.

Max's review

  1. Students get the minimum. Reproduced: a 21-year-old full-time student with no income was charged £1,021.80 a year, with both a working-age and a pension-age claimant; main charges £0. Fixed by removing the minimum. The two non-dependant readers now band on total_income less other_investment_income, so their results match main for every household. The reg 74(7)(c) to (e) exemptions belong in Apply Housing Benefit and national Council Tax Reduction non-dependant deductions as legislated #2007.
  2. Couples charged twice. Reproduced: a zero-income couple paid £2,043.60, and a worker with a zero-income spouse paid £7,025.20 (£6,003.40 + £1,021.80). Now £0 and £6,003.40, both the same as main. One deduction per couple on joint income (reg 74(3) to (4)) is left for Apply Housing Benefit and national Council Tax Reduction non-dependant deductions as legislated #2007, since main doesn't model it either.
  3. National CTR. Reproduced: a pensioner claimant with a non-working adult child whose only income is £28,912 of investment income had £6,003.40 deducted. The CTR reader now leaves that income out of the band, the same as the HB reader, so the deduction is £0, as on main. You're right that calling national CTR "unchanged" was wrong at 21bb4dd; it's true now, and the microsimulation confirms it. Correcting its rates, work test and exemptions is out of scope here and is in Apply Housing Benefit and national Council Tax Reduction non-dependant deductions as legislated #2007.
  4. Winter Fuel Payment at £35,000. Reproduced: £200 at £34,999, £0 at £35,000 and at £35,001. ITEPA 2003 s.681I(1)(b), inserted by FA 2026 Sch 10, says total income "exceeds £35,000", so you're right. The test is now <=, the parameter descriptions say "at or below", and the property-test oracle and its income draws include £35,000. The existing 2024 reform case "pensioner exactly at threshold gets no WFA" encoded the strict test; it now expects £200. I've left the household-wide proxy as it is; per-person charging is Charge the winter fuel payment back through income tax, person by person #2066.
  5. Description and measurement. The description now gives the right scope and current values, and includes a 2026 microsimulation of main against this branch, using the same pinned file and weights for both:
    • Enhanced FRS 2024-25 doesn't change at all. It carries no other_investment_income, and no pensioner's Winter Fuel Payment moves.
    • Microcosm UK 2024-25 carries £2.78bn of it, across 227k weighted households. total_income and post_tax_income each rise by exactly that £2,781.3m. income_tax, adjusted_net_income, HB, CTR and both non-dependant deductions don't change at all. Winter Fuel Payment falls by £0.28m, in 9 sample households (about 1k weighted), all with other investment income.
  6. Overlap with Apply Housing Benefit and national Council Tax Reduction non-dependant deductions as legislated #2007 and Charge non-dependant deductions for non-dependants within the claimant's benefit unit #2017. housing_benefit_non_dep_deduction_exempt and remunerative_work_hours are gone, so the name clash is gone. What's left on HB is a short hunk in each of household_benefits_individual_non_dep_deduction.py and council_tax_reduction_individual_non_dep_deduction.py, which Apply Housing Benefit and national Council Tax Reduction non-dependant deductions as legislated #2007 rewrites anyway. When it lands it can drop the subtraction and read total_income directly. Nothing touches housing_benefit_non_dep_deductions.py now, so there's no overlap with Charge non-dependant deductions for non-dependants within the claimant's benefit unit #2017.

The double-counting analysis and the housing_benefit_net_earnings proportional-tax approximation are unchanged. I haven't added the wider invariant property tests you listed, because the statutory-pay gap you noted would make a universal "total income covers charged income" assertion fail today.

María's description points

  1. Scope: the description now says two groups change, people with other_investment_income and pensioners at exactly £35,000. The HB gate and claimant exemption that touched other households have been removed.
  2. Values: the examples with £124.55 and £19.30 are gone. The remaining figures use the current 2025-26 rates, £115.45 and £45.15 a week.
  3. Axiom: uk:statutes/ukpga/2007/3/23 encoded-correct (Step 1 sums every component charged to income tax; companion taxpayer_liability_after_reliefs_allowances_reductions_and_added_tax), and s.681I is Winter Fuel Payment and PAWHP per person: SI 2025/969 regs 1-4; SI 2024/869 regs 2-4; SI 2000/729 regs 2-3; SSI 2024/351 regs 2, 2A, 5-10 (as amended by SSI 2025/282, 2025/100, 2025/195, 2026/170); ITEPA 2003 s.681I (PE parity: policyengine-uk#2038) TheAxiomFoundation/rulespec-uk#420, queued. HB reg 74(2),(6) and SPC reg 55 aren't changed by this PR any more, so it makes no parity claim for them.

Reproducer. Added, listing the tests in the final diff. .github/check_fix_pr.py passes locally (8 changed executable tests).

Tests

  • New cases: 3 HB and 3 CTR non-dependant guards, 4 Winter Fuel boundary cases, and the updated 2024 reform case. The guards, the two £35,000 cases and the reform case fail on the previous head (rebased) and pass here. The £34,999 and £35,001 cases pass on both heads and pin either side of the limit. The guards also pass on main, because main's result is what they pin.
  • The total-income, post-tax-income and investment-income Winter Fuel cases fail on main.
  • Full policy YAML suite: 3,073 passed. pytest outside tests/policy: 5,393 passed, 100 skipped, 2 xfailed. HB YAML 246, CTR YAML 176 and Winter Fuel YAML 14 + 33 + 17, all passed. The Winter Fuel property tests pass. ruff format and ruff check are clean.

This should be squash-merged.

@juaristi22 @MaxGhenis could you re-review?

@juaristi22 juaristi22 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-reviewed 8c222fa6602915f94c379287aca576235beb7ff5. The narrowed scope resolves the earlier blockers, and I am approving.

other_investment_income enters total income once, while tax and adjusted net income retain their independent taxable-component paths. Both non-dependant band readers subtract the new component, preserving previous HB/CTR inputs; the proposed work gate, minimum deduction and claimant exemption are removed. The underlying HB/CTR gaps remain for #2007. Actual-household controls confirm unchanged final HB/CTR, tax and adjusted net income, with post-tax income increasing by the added investment income.

The Winter Fuel boundary is now inclusive at £35,000, matching ITEPA section681I, which charges only income exceeding that amount. The scope description, examples, Reproducer and axiom status are corrected.

Validation:81 changed/regression cases pass, including the Winter Fuel Hypothesis properties, and the corrected final-accounting diagnostic passes separately (82 distinct passing checks). Two setup errors in my review-only diagnostic were corrected; no PR test failed. Seven official sources checked; captured latest hosted CI passes. Private population tables were not independently rerun.

@vahid-ahmadi
vahid-ahmadi added this pull request to the merge queue Oct 19, 2026
Merged via the queue into main with commit 9647aab Oct 19, 2026
14 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-recheck Re-triggers Fix evidence without changing the PR (UK hub, 2026-10-12)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

total_income leaves out other_investment_income, which income_tax charges

3 participants