Skip to content

Carry unused Income Tax trading losses forward once - #2661

Draft
juaristi22 wants to merge 6 commits into
mainfrom
fix-income-tax-loss-carry-forward-2087
Draft

juaristi22 wants to merge 6 commits into
mainfrom
fix-income-tax-loss-carry-forward-2087

Conversation

@juaristi22

@juaristi22 juaristi22 commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

Addresses #2087
axiom: needed

Track unused Income Tax trading losses as opening stock, consuming relief once. Only a loss explicitly supplied for its tax year enters the current-year claim. Explicit opening balances, including zero, reconcile external history. Class 4 keeps its separate balance.

The follow-up at d3d2bbc55b42cee47fce9b9ce7993439ad257ca0 clarifies the caller-assessed same-trade contract and adds one YAML case. With no other income or pensions, ordinary expense treatment and no capital allowances, an old £10,000 loss from trade A, followed by £8,000 profit from A and a new £4,000 loss from B, leaves £6,000 opening stock for 2027. If both losses belong to A while B earns the profit, identical aggregate inputs cannot identify that association. The docs exclude automatic reconstruction of that case and explain annual reconciliation, zero resets and later reseeding.

The docs also warn conditionally that copying a loss into each later annual input supplies a new loss each year. This makes no claim about current certified-data columns or national effects. The follow-up changes only documentation, one YAML case and the existing Towncrier fragment; production code, dependencies and workflows are unchanged from 3bc00db9.

Correction 16a12b93d0d5c962556cf7d0789ae66a99ffbb34 marks the opening balance as a stock, preserving its annual amount in monthly reads, and removes the temporary YAML label. Annual relief formulas and existing oracles are unchanged. The normal correction push is published and independently reviewed.

Reproducer: policyengine_uk/tests/test_income_tax_loss_carry_forward.py::test_monthly_reads_preserve_the_annual_opening_stock; policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/reliefs/trade_loss_carry_forward.yaml::A loss already relieved against pay is not a new loss next year

Validation:

  • Earlier local stock, provenance/reset and same-trade checks passed on recorded heads and overlays. They do not validate the hosted merge checkout; the lifetime check was diagnostic only.

  • Current hosted Code fails. Two projection-test logs show RecursionError during simulation construction with prior-year loss-relief frames. Aggregate succeeded. The full cause and remedy remain unproved; no local reproduction is established.

  • The docs follow-up ran no new model tests. The PR remains a draft.

This retains the current-year section 64 claim convention and caller-assessed same-trade eligibility. It does not implement previous-year elections, automatic unrelated-trade tracking, terminal/foreign loss rules or full pension claim optimisation, and does not claim full closure of #2087. Later-main dataset/ISA/locked-runtime/H5 qualification remains open in its original scope; this correction integrates no later main and makes no compatibility or population claim.

See docs/engineering/income-tax-trading-losses.md for the contract and official references.

@juaristi22 juaristi22 self-assigned this Oct 25, 2026
@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Aggregate impact on certified data: pass

Base 96315db49f against head 8056c6d88f, both on enhanced_frs_2024_25.h5 (policyengine-uk-data 1.58.1, sha256 056e80fd8e91), fiscal years 2025-26 to 2031-32. Each year is a full microsimulation run on each side.

Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 0 (thresholds in report_spec.yaml; a flag does not fail the job).

Workflow run and full report

Notes

  • In the years a figure from fewer than 10 records is suppressed (spending.disabled_students_allowance), every total containing it, and the decile incomes, are rebuilt from their other components, so no published total includes it. Those totals are marked †. A change is withheld where the two sides leave out different figures and can't be lined up.

Moves flagged for review

None.

Spending by benefit and service

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed

Caseloads

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed

Standard reform costings (change in gov_balance)

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise additional rate by 3pp 0 0 0 0 0 suppressed suppressed

Standard reforms: change in each reform's target

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise additional rate by 3pp: change in income_tax 0 0 0 0 0 suppressed suppressed

Household net income by decile

No household's net income changes in any year.

No change in any year: receipts by tax head; government balance, each component counted once; model totals; household income totals; population; poverty rates; people in poverty; inequality; mean household net income in each side's own deciles.

Checks failing on both base and head (1)
  • VAT excluding private school fees is never negative (sign:vat): 2025-26: 76 units below zero, a weighted £-53,889,156 in total; 2026-27: 76 units below zero, a weighted £-55,420,387 in total; 2027-28: 76 units below zero, a weighted £-56,618,038 in total
Counted-once check

Each of 91 components was raised by £1 per unit in 2026-27, and every head of its totals was read household by household: its own head must rise by exactly £1, no other tax head may move, no other benefit may rise, and the model's own total must move by what its heads moved. A head that breaks a rule is measured again twice: with every other head held at its baseline value, and with every other head held at its value after the first £1 while the component rises again. It fails where either still shows the response. 0 failed; 0 were skipped.

  • widowed_parents_allowance: pension_credit responds only through other heads in fewer than 10 households (other heads that moved there: carers_allowance; gov_spending; not a failure)
  • widowed_parents_allowance: pension_credit responds only through other heads in fewer than 10 households (other heads that moved there: carers_allowance; household_benefits; not a failure)
Provenance
Side Commit Runtime Environment hash Partition hash Time
base main 96315db49f uk 2.162.9 (files 471ff4c944b5), core 3.33.12, py 3.13.17 aarch64 8a99037d2b8f 4f86ac4248d9 1880.72s
head #2661 merged into main 8056c6d88f uk 2.162.9 (files f5d6732cba5a), core 3.33.12, py 3.13.17 aarch64 8a99037d2b8f 4f86ac4248d9 1869.64s

Dataset: hf://policyengine/policyengine-uk-data-private/enhanced_frs_2024_25.h5@2526edbbb687a3b54dbb11272e5389e4b62d2ac2, sha256 056e80fd8e9151d78f432a25cde863340baee81304c6216a37fb575ce8563784 (verified on both sides), policyengine-uk-data 1.58.1.

Receipts by tax head: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Final charge on cash interest in non-cash ISAs £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Lifetime ISA withdrawal charge £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Income tax £298.70bn £318.31bn £335.60bn £350.52bn £365.79bn £383.53bn £401.03bn
National Insurance (employee and self-employed) £52.37bn £54.71bn £56.48bn £58.15bn £60.22bn £62.11bn £64.44bn
National Insurance (employer) £150.59bn £156.99bn £161.74bn £166.13bn £173.24bn £178.59bn £185.29bn
Employer NI change borne by capital £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Employer NI change borne by consumers £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Capital gains tax £12.61bn £13.05bn £13.53bn £14.02bn £14.52bn £15.05bn £15.58bn
Student loan repayments £7.91bn £8.21bn £8.43bn £8.63bn £8.85bn £9.10bn £9.42bn
VAT excluding private school fees £249.37bn £255.28bn £261.81bn £267.87bn £274.12bn £280.56bn £286.81bn
Change in VAT from its baseline £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
VAT on private school fees £1.95bn £2.00bn £2.05bn £2.10bn £2.21bn £2.21bn £2.31bn
Change in private school VAT from its baseline £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Fuel duties £24.53bn £24.64bn £26.64bn £26.69bn £26.42bn £25.76bn £26.35bn
Alcohol duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Tobacco duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Vehicle excise duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Betting and gaming duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
TV licence fee £4.26bn £4.41bn £4.42bn £4.44bn £4.45bn £4.47bn £4.48bn
Council tax £49.21bn £51.88bn £54.97bn £58.29bn £61.55bn £64.92bn £68.49bn
High value council tax surcharge £0.00bn £0.00bn £0.00bn £0.48bn £0.49bn £0.50bn £0.51bn
Northern Ireland domestic rates £0.55bn £0.58bn £0.60bn £0.63bn £0.66bn £0.69bn £0.72bn
Business rates £34.68bn £38.04bn £38.88bn £39.76bn £43.07bn £43.13bn £44.57bn
Stamp duty land tax on the household's own purchases £6.90bn £7.39bn £7.81bn £8.21bn £8.62bn £9.02bn £9.42bn
Stamp duty land tax paid by companies £4.81bn £5.98bn £6.73bn £7.46bn £8.21bn £8.87bn £9.16bn
Land transaction tax (Wales) £0.07bn £0.08bn £0.09bn £0.09bn £0.10bn £0.10bn £0.11bn
Land and buildings transaction tax (Scotland) £0.31bn £0.32bn £0.34bn £0.35bn £0.37bn £0.38bn £0.40bn
Unspecified tax change borne by capital £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Unspecified tax change borne by consumers £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Wealth tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Wealth tax excluding the main residence £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Land value tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Carbon tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Spending by benefit and service: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Lifetime ISA bonus £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
State Pension £121.64bn £126.07bn £127.08bn £128.87bn £132.52bn £136.31bn £140.08bn
Pension Credit £6.66bn £7.04bn £7.32bn £7.46bn £7.60bn £7.77bn £7.94bn
Winter Fuel Payment £1.86bn £1.86bn £1.80bn £1.78bn £1.79bn £1.79bn £1.80bn
Pension Age Winter Heating Payment (Scotland) £0.19bn £0.20bn £0.20bn £0.21bn £0.21bn £0.22bn £0.22bn
Universal Credit £75.21bn £80.38bn £82.93bn £84.23bn £85.58bn £86.87bn £87.97bn
Housing Benefit £8.55bn £8.89bn £9.17bn £9.31bn £9.58bn £9.84bn £10.10bn
Council Tax Reduction £2.07bn £2.18bn £2.32bn £2.46bn £2.60bn £2.74bn £2.89bn
Employment and Support Allowance (income-related) £0.79bn £0.81bn £0.83bn £0.85bn £0.86bn £0.89bn £0.90bn
Employment and Support Allowance (contribution-based) £5.20bn £5.34bn £5.46bn £5.59bn £5.72bn £5.85bn £5.99bn
Jobseeker's Allowance (income-based) £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Jobseeker's Allowance (contribution-based) £0.29bn £0.30bn £0.30bn £0.31bn £0.32bn £0.32bn £0.33bn
Income Support £0.12bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Working Tax Credit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Child Tax Credit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Incapacity Benefit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Personal Independence Payment £26.94bn £28.06bn £29.13bn £29.83bn £30.54bn £31.26bn £31.96bn
Disability Living Allowance £7.49bn £7.80bn £8.10bn £8.29bn £8.49bn £8.69bn £8.88bn
Attendance Allowance £8.68bn £9.04bn £9.38bn £9.61bn £9.83bn £10.07bn £10.29bn
Carer's Allowance £4.48bn £4.67bn £4.84bn £4.96bn £5.08bn £5.19bn £5.31bn
Carer Support Payment (Scotland) £0.31bn £0.32bn £0.33bn £0.34bn £0.35bn £0.36bn £0.37bn
Carer's Allowance Supplement (Scotland) £0.00bn £0.04bn £0.04bn £0.04bn £0.04bn £0.04bn £0.04bn
Severe Disablement Allowance £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn
Industrial Injuries Disablement Benefit £0.50bn £0.52bn £0.53bn £0.54bn £0.55bn £0.57bn £0.58bn
Armed Forces Compensation Scheme £0.61bn £0.62bn £0.64bn £0.65bn £0.67bn £0.68bn £0.70bn
War Widow's or Widower's Pension £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Child Benefit £16.16bn £16.83bn £17.43bn £17.85bn £18.26bn £18.69bn £19.10bn
Scottish Child Payment £0.49bn £0.52bn £0.54bn £0.55bn £0.56bn £0.57bn £0.56bn
Two Child Limit Payment (Scotland, counterfactual) £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Healthy Start vouchers £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn
Maternity Allowance £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Statutory Maternity Pay £3.30bn £3.39bn £3.47bn £3.55bn £3.63bn £3.72bn £3.80bn
Statutory Paternity Pay £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Statutory Sick Pay £1.12bn £1.15bn £1.18bn £1.21bn £1.24bn £1.27bn £1.29bn
Sure Start Maternity Grant £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn
Bereavement Support Payment £0.67bn £0.69bn £0.70bn £0.72bn £0.73bn £0.75bn £0.77bn
Widowed Parent's Allowance £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Tax-Free Childcare £0.55bn £0.56bn £0.57bn £0.58bn £0.59bn £0.61bn £0.59bn
Extended childcare entitlement £2.99bn £3.50bn £3.60bn £3.67bn £3.77bn £3.85bn £3.94bn
Universal childcare entitlement £2.05bn £2.23bn £2.28bn £2.34bn £2.40bn £2.47bn £2.52bn
Targeted childcare entitlement £0.50bn £0.51bn £0.52bn £0.53bn £0.54bn £0.56bn £0.52bn
Childcare Offer for Wales £0.26bn £0.28bn £0.28bn £0.29bn £0.30bn £0.30bn £0.31bn
Care to Learn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Childcare Grant £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Parents' Learning Allowance £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn
Adult Dependants' Grant £0.06bn £0.06bn £0.04bn £0.04bn £0.04bn £0.04bn £0.04bn
Travel Grant £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed
16 to 19 Bursary Fund £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
NHS spending £203.83bn £204.54bn £205.13bn £205.79bn £206.49bn £207.23bn £207.73bn
State education spending £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn
Transport subsidy spending £25.05bn £25.13bn £25.21bn £25.29bn £25.37bn £25.46bn £25.53bn
Cost-of-living support payment £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Energy Bills Rebate £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Energy Price Guarantee subsidy £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Basic income £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Disability basic income £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Other public spending budget change £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Caseloads: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Lifetime ISA bonus 0k 0k 0k 0k 0k 0k 0k
State Pension 11,261k 11,158k 10,849k 10,746k 10,783k 10,822k 10,848k
Pension Credit 1,485k 1,483k 1,480k 1,461k 1,462k 1,464k 1,460k
Winter Fuel Payment 7,842k 7,793k 7,494k 7,407k 7,432k 7,459k 7,477k
Pension Age Winter Heating Payment (Scotland) 803k 803k 803k 800k 803k 806k 808k
Universal Credit 6,334k 6,341k 6,366k 6,347k 6,345k 6,333k 6,298k
Housing Benefit 1,249k 1,247k 1,250k 1,234k 1,238k 1,240k 1,239k
Council Tax Reduction 3,175k 3,198k 3,209k 3,229k 3,244k 3,257k 3,264k
Employment and Support Allowance (income-related) 71k 71k 71k 71k 71k 72k 72k
Employment and Support Allowance (contribution-based) 719k 721k 723k 726k 728k 731k 732k
Jobseeker's Allowance (income-based) 0k 0k 0k 0k 0k 0k 0k
Jobseeker's Allowance (contribution-based) 67k 67k 67k 68k 68k 68k 68k
Income Support 25k 0k 0k 0k 0k 0k 0k
Working Tax Credit 0k 0k 0k 0k 0k 0k 0k
Child Tax Credit 0k 0k 0k 0k 0k 0k 0k
Incapacity Benefit 0k 0k 0k 0k 0k 0k 0k
Personal Independence Payment 3,566k 3,579k 3,589k 3,601k 3,613k 3,626k 3,635k
Disability Living Allowance 1,291k 1,295k 1,299k 1,303k 1,308k 1,312k 1,316k
Attendance Allowance 1,694k 1,700k 1,705k 1,711k 1,716k 1,723k 1,727k
Carer's Allowance 1,034k 1,038k 1,041k 1,044k 1,048k 1,051k 1,053k
Carer Support Payment (Scotland) 71k 71k 72k 72k 72k 72k 73k
Carer's Allowance Supplement (Scotland) 0k 71k 72k 72k 72k 72k 73k
Severe Disablement Allowance 4k 4k 4k 4k 4k 4k 4k
Industrial Injuries Disablement Benefit 137k 137k 138k 138k 139k 139k 140k
Armed Forces Compensation Scheme 93k 93k 94k 94k 94k 95k 95k
War Widow's or Widower's Pension 0k 0k 0k 0k 0k 0k 0k
Child Benefit 8,102k 8,128k 8,142k 8,165k 8,191k 8,216k 8,232k
Scottish Child Payment 350k 352k 353k 357k 354k 355k 342k
Two Child Limit Payment (Scotland, counterfactual) 0k 0k 0k 0k 0k 0k 0k
Healthy Start vouchers 199k 200k 201k 201k 202k 203k 203k
Maternity Allowance 0k 0k 0k 0k 0k 0k 0k
Statutory Maternity Pay 332k 334k 335k 336k 337k 338k 339k
Statutory Paternity Pay 0k 0k 0k 0k 0k 0k 0k
Statutory Sick Pay 181k 182k 182k 183k 183k 184k 185k
Sure Start Maternity Grant 26k 26k 26k 26k 26k 27k 27k
Bereavement Support Payment 98k 98k 98k 99k 99k 99k 100k
Widowed Parent's Allowance 0k 0k 0k 0k 0k 0k 0k
Tax-Free Childcare 1,014k 1,024k 1,024k 1,027k 1,027k 1,031k 1,013k
Extended childcare entitlement 796k 801k 806k 809k 815k 819k 820k
Universal childcare entitlement 1,071k 1,074k 1,077k 1,080k 1,086k 1,096k 1,097k
Targeted childcare entitlement 165k 156k 154k 155k 155k 156k 144k
Childcare Offer for Wales 39k 39k 39k 39k 39k 39k 39k
Care to Learn 0k 0k 0k 0k 0k 0k 0k
Childcare Grant 0k 0k 0k 0k 0k 0k 0k
Parents' Learning Allowance 98k 98k 99k 99k 99k 100k 100k
Adult Dependants' Grant 18k 18k 12k 12k 12k 13k 13k
Travel Grant 0k 0k 0k 0k 0k 0k 0k
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed
16 to 19 Bursary Fund 0k 0k 0k 0k 0k 0k 0k
Cost-of-living support payment 0k 0k 0k 0k 0k 0k 0k
Energy Bills Rebate 0k 0k 0k 0k 0k 0k 0k
Energy Price Guarantee subsidy 0k 0k 0k 0k 0k 0k 0k
Basic income 0k 0k 0k 0k 0k 0k 0k
Disability basic income 0k 0k 0k 0k 0k 0k 0k
Government balance, each component counted once: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Government receipts, each component once £898.82bn £941.86bn £980.11bn £1,013.84bn £1,052.89bn £1,089.01bn £1,129.10bn
Government spending £528.96bn † £543.83bn † £551.68bn † £558.06bn † £566.57bn † £575.31bn † £583.37bn †
Government balance, each receipt once £369.86bn † £398.03bn † £428.43bn † £455.78bn † £486.32bn † £513.70bn † £545.72bn †
Benefits in household_benefits but not in gov_spending £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Model totals: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
gov_tax £898.82bn £941.86bn £980.11bn £1,013.84bn £1,052.89bn £1,089.01bn £1,129.10bn
household_tax £496.91bn £527.60bn £554.51bn £577.74bn £603.32bn £627.65bn £654.69bn
pre_budget_change_household_tax £489.00bn £519.39bn £546.08bn £568.63bn £593.98bn £618.05bn £644.75bn
gov_spending £528.96bn † £543.83bn † £551.68bn † £558.06bn † £566.57bn † £575.31bn † £583.37bn †
household_benefits £528.96bn † £543.83bn † £551.68bn † £558.06bn † £566.57bn † £575.31bn † £583.37bn †
gov_balance £369.86bn † £398.03bn † £428.43bn † £455.78bn † £486.32bn † £513.70bn † £545.72bn †
Household income totals: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Household market income £1,651.49bn £1,713.03bn £1,764.05bn £1,812.97bn £1,862.19bn £1,918.39bn £1,985.28bn
Household net income £1,644.43bn † £1,688.69bn † £1,719.55bn † £1,750.61bn † £1,781.67bn † £1,821.07bn † £1,867.39bn †
Household net income (HBAI definition) £1,457.30bn £1,504.48bn £1,537.58bn £1,569.36bn £1,602.66bn £1,640.48bn £1,687.04bn
Population: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
People 69,224k 69,466k 69,668k 69,891k 70,128k 70,381k 70,550k
Benefit units 35,100k 35,223k 35,325k 35,438k 35,558k 35,686k 35,772k
Households 31,037k 31,145k 31,236k 31,336k 31,442k 31,555k 31,631k
Poverty rates: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Absolute poverty, before housing costs: everyone 16.0% 15.1% 15.4% 15.5% 15.6% 15.7% 15.7%
Absolute poverty, before housing costs: children 24.0% 21.1% 21.4% 21.3% 21.5% 21.8% 21.8%
Absolute poverty, before housing costs: working-age adults 13.8% 13.5% 13.9% 14.1% 14.1% 14.2% 14.2%
Absolute poverty, before housing costs: pensioners 13.7% 13.2% 13.1% 13.4% 13.5% 13.5% 13.5%
Absolute poverty, after housing costs: everyone 21.4% 20.8% 21.3% 21.5% 21.7% 21.9% 22.2%
Absolute poverty, after housing costs: children 33.0% 30.7% 31.1% 31.1% 31.4% 31.7% 32.3%
Absolute poverty, after housing costs: working-age adults 19.8% 19.6% 20.2% 20.5% 20.7% 20.8% 21.1%
Absolute poverty, after housing costs: pensioners 12.7% 12.4% 12.3% 12.5% 12.8% 12.9% 13.0%
Relative poverty, before housing costs: everyone 17.2% 16.4% 16.5% 16.4% 16.2% 16.3% 16.5%
Relative poverty, before housing costs: children 26.2% 23.5% 23.4% 23.1% 22.7% 22.8% 23.1%
Relative poverty, before housing costs: working-age adults 14.6% 14.4% 14.7% 14.7% 14.6% 14.7% 14.8%
Relative poverty, before housing costs: pensioners 15.0% 14.8% 14.1% 14.1% 14.0% 14.0% 14.2%
Relative poverty, after housing costs: everyone 21.6% 21.0% 21.1% 21.2% 21.3% 21.4% 21.8%
Relative poverty, after housing costs: children 33.3% 31.2% 30.8% 30.7% 31.0% 31.0% 31.5%
Relative poverty, after housing costs: working-age adults 19.9% 19.8% 20.1% 20.3% 20.4% 20.5% 20.7%
Relative poverty, after housing costs: pensioners 12.9% 12.5% 12.1% 11.9% 11.8% 11.9% 12.8%
People in poverty: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Absolute poverty, before housing costs: everyone 11,049k 10,466k 10,736k 10,860k 10,960k 11,054k 11,081k
Absolute poverty, before housing costs: children 3,580k 3,171k 3,211k 3,220k 3,261k 3,305k 3,317k
Absolute poverty, before housing costs: working-age adults 5,845k 5,746k 6,022k 6,123k 6,169k 6,209k 6,219k
Absolute poverty, before housing costs: pensioners 1,625k 1,550k 1,504k 1,517k 1,530k 1,539k 1,545k
Absolute poverty, after housing costs: everyone 14,847k 14,422k 14,808k 14,994k 15,242k 15,402k 15,638k
Absolute poverty, after housing costs: children 4,937k 4,609k 4,671k 4,685k 4,757k 4,814k 4,912k
Absolute poverty, after housing costs: working-age adults 8,406k 8,359k 8,732k 8,898k 9,024k 9,114k 9,240k
Absolute poverty, after housing costs: pensioners 1,504k 1,454k 1,404k 1,411k 1,460k 1,475k 1,486k
Relative poverty, before housing costs: everyone 11,896k 11,403k 11,468k 11,487k 11,391k 11,468k 11,652k
Relative poverty, before housing costs: children 3,918k 3,523k 3,519k 3,491k 3,435k 3,458k 3,516k
Relative poverty, before housing costs: working-age adults 6,202k 6,136k 6,338k 6,400k 6,368k 6,415k 6,515k
Relative poverty, before housing costs: pensioners 1,776k 1,744k 1,611k 1,597k 1,588k 1,595k 1,621k
Relative poverty, after housing costs: everyone 14,957k 14,597k 14,698k 14,825k 14,951k 15,042k 15,351k
Relative poverty, after housing costs: children 4,976k 4,677k 4,630k 4,634k 4,690k 4,712k 4,794k
Relative poverty, after housing costs: working-age adults 8,443k 8,455k 8,680k 8,847k 8,916k 8,977k 9,092k
Relative poverty, after housing costs: pensioners 1,537k 1,465k 1,388k 1,344k 1,346k 1,354k 1,464k
Inequality: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Gini coefficient, equivalised HBAI income before housing costs 0.3085 0.3059 0.3065 0.3071 0.3075 0.3081 0.3094
Mean household net income in each side's own deciles: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Decile 1: mean household_net_income per household £17,781 † £17,906 † £17,832 † £17,905 † £18,142 † £18,339 † £18,482 †
Decile 2: mean household_net_income per household £27,963 † £28,917 † £28,654 † £28,906 † £29,096 † £29,646 † £30,235 †
Decile 3: mean household_net_income per household £34,381 † £35,463 † £36,065 † £36,509 † £36,851 † £37,338 † £38,043 †
Decile 4: mean household_net_income per household £38,785 † £40,713 † £41,873 † £42,413 † £43,144 † £43,906 † £44,167 †
Decile 5: mean household_net_income per household £43,788 † £44,626 † £45,606 † £46,288 † £47,327 † £48,285 † £49,710 †
Decile 6: mean household_net_income per household £48,210 † £49,525 † £50,736 † £51,914 † £52,203 † £53,174 † £54,203 †
Decile 7: mean household_net_income per household £56,219 † £57,224 † £57,764 † £58,010 † £59,040 † £60,061 † £61,765 †
Decile 8: mean household_net_income per household £69,150 † £70,533 † £71,800 † £73,075 † £74,092 † £75,540 † £77,427 †
Decile 9: mean household_net_income per household £79,490 † £81,421 † £82,828 † £84,135 † £85,539 † £87,053 † £89,944 †
Decile 10: mean household_net_income per household £117,581 † £120,296 † £121,933 † £124,163 † £126,045 † £128,342 † £131,110 †
Standard reform costings (change in gov_balance): head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise basic rate by 1pp £6.47bn £6.76bn £6.67bn £6.86bn £7.06bn £7.35bn £7.55bn
Raise higher rate by 1pp £3.69bn £3.98bn £4.03bn £4.29bn £4.46bn £4.78bn £4.91bn
Raise personal allowance by ~800GBP/year £-3.82bn £-3.94bn £-4.01bn £-4.09bn £-4.18bn £-4.18bn £-2.00bn
Raise child benefit by 25GBP/week per additional child £-1.99bn £-1.75bn £-1.56bn £-1.46bn £-1.35bn £-1.23bn £-1.12bn
Reduce Universal Credit taper rate to 20% £-20.39bn £-20.96bn £-21.38bn £-21.61bn £-21.87bn £-22.17bn £-22.60bn
Raise Class 1 main employee NICs rate to 10% £11.02bn £11.42bn £11.77bn £12.07bn £12.37bn £12.75bn £13.07bn
Raise VAT standard rate by 2pp £24.89bn £25.55bn £26.14bn £26.75bn £27.39bn £28.03bn £28.66bn
Raise additional rate by 3pp £2.96bn £3.21bn £3.26bn £3.46bn £3.65bn suppressed suppressed
Raise the Universal Credit standard allowance by 10% £-3.05bn £-3.18bn £-3.30bn £-3.34bn £-3.38bn £-3.42bn £-3.48bn
Raise the State Pension by 10% £-7.79bn £-8.02bn £-8.01bn £-8.12bn £-8.43bn £-8.54bn £-8.87bn
Standard reforms: change in each reform's target: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise basic rate by 1pp: change in income_tax £6.61bn £6.91bn £6.83bn £7.03bn £7.24bn £7.47bn £7.73bn
Raise higher rate by 1pp: change in income_tax £3.69bn £3.99bn £4.04bn £4.25bn £4.47bn £4.74bn £4.93bn
Raise personal allowance by ~800GBP/year: change in income_tax £-3.95bn £-4.08bn £-4.17bn £-4.24bn £-4.34bn £-4.41bn £-1.76bn
Raise child benefit by 25GBP/week per additional child: change in child_benefit £2.33bn £2.14bn £1.95bn £1.84bn £1.73bn £1.62bn £1.50bn
Reduce Universal Credit taper rate to 20%: change in universal_credit £20.39bn £20.95bn £21.37bn £21.62bn £21.87bn £22.22bn £22.61bn
Raise Class 1 main employee NICs rate to 10%: change in national_insurance £11.28bn £11.74bn £12.06bn £12.37bn £12.69bn £13.03bn £13.49bn
Raise VAT standard rate by 2pp: change in vat £24.69bn £25.35bn £25.94bn £26.54bn £27.17bn £27.81bn £28.43bn
Raise additional rate by 3pp: change in income_tax £2.96bn £3.21bn £3.26bn £3.46bn £3.66bn suppressed suppressed
Raise the Universal Credit standard allowance by 10%: change in universal_credit £3.05bn £3.18bn £3.29bn £3.36bn £3.43bn £3.48bn £3.52bn
Raise the State Pension by 10%: change in state_pension £10.68bn £11.18bn £11.31bn £11.55bn £12.00bn £12.44bn £12.94bn

@vahid-ahmadi

Copy link
Copy Markdown
Collaborator

Round 1 at 3bc00db94.

Verdict: the law is right and "once" holds in every case I tried. Two shoulds before this leaves draft: one model choice in a year that has a new loss, and CI that has not finished at this head. No musts.

Law checked on legislation.gov.uk

  • ITA 2007 s.83: (1) and (2) carry forward only the part not yet relieved; (3) deduct only from profits of the trade; (4) deduct before any other relief from those profits. s.84 deducts year by year until the loss is used up. loss_relief.py:45-66 follows this: opening stock, less min_(opening, profits) absorbed, plus the new loss left after s.64.
  • s.83 is not in the s.24A(6) list, and the PR does not cap loss_relief. The part of a s.64 claim that the cap blocks carries forward, which matches s.83(1)(b). The YAML "Cap-limited Income Tax loss and Class 4 loss have distinct balances" covers this.
  • s.64(2): current year only. The PR states this, and the previous-year option is still out of scope.
  • Net-pay ceiling (trade_loss_relief_against_general_income.py:46-50): taxable_employment_income is gross of employee contributions (taxable_employment_income.py:17-19 deducts only employment_expenses). So taking off net_pay_pension_relief before the s.25(4) limit is right under FA 2004 s.193(2), and does not double-deduct.

Findings

  1. should: in a year with a supplied loss, the brought-forward stock is set against other trades' profits. trading_loss.py:10 defines self_employment_income in such a year as "profits of their other trades", but loss_relief.py:49 absorbs the opening stock against it. s.83(3) allows a deduction "only from profits of the trade". My check: £10,000 loss in 2025 with no other income, then in 2026 a new £4,000 loss and £8,000 profit from another trade. The model gives 2026 taxable trading income £0, s.64 relief £0 and a 2027 opening stock of £6,000. If the stock came from the trade that lost again in 2026, the law gives s.64 relief of £4,000, taxable income of £4,000 and a stock of £10,000. The total relief is the same; the year and the trade are not. The docs (docs/engineering/income-tax-trading-losses.md:17-18) cover this with the "caller-preassessed pool" assumption. Either stop absorbing the stock in a year with a supplied current loss, or say this case plainly in the docs and add a YAML test that pins the chosen behaviour.
  2. should: CI at 3bc00db94 is red, but no test failed. Python 2/5, 4/5 and 5/5 failed and 3/5 was cancelled, each on "The runner has received a shutdown signal". The logs show 15.6 to 15.8 GB used and 125 to 311 MB free just before (for example, job 121292799781). Other PRs on the same workflow passed in the same window. I cannot say whether the branch caused this. Please re-run, and if it happens again, check whether the new Hypothesis tests raise runner memory. The PR body already says hosted CI at the new head is unverified.
  3. nit: losses supplied every year build up stock. extend_single_year_dataset copies every column into each later year (economic_assumptions.py:255-256), and simulation.py:1207 calls set_input for each one. A future dataset with a trading_loss column would then make each uprated year a new loss. My check: £5,000 loss and £2,000 pay every year gives an opening stock of £3,000, £6,000 and then £9,000. The current enhanced_frs_2024_25.h5 has no trading_loss or loss_relief column, so there is no population effect today, which fits the Aggregate impact check. The Class 4 fold from Charge Class 4 NICs on Chapter 2 profits, after capital allowances, the trading allowance and Schedule 2 losses #1919 has the same exposure, so this predates the PR. One line in the docs would do.
  4. nit: loss_relief.py:27 keeps quantity_type = FLOW on what is now an opening stock. It does no harm for a YEAR variable, but STOCK would match the new label.
  5. nit: the validation ledger in the PR body is hard to review. A short "what ran at this head" list would be enough.

"Once" checks (household, local)

  • 2025: £10,000 loss and £5,000 pay, then trade profits of £3,000 in 2026 and £3,000 in 2027. s.64 relief is £5,000 in 2025. The opening stock is £5,000, then £2,000, then £0. Taxable trading income in 2027 is £1,000. Relief adds up to the loss.
  • An explicit zero loss_relief in 2026 resets the stock: 2027 profits of £5,000 are fully taxed.
  • The PR's conservation property test and the YAML "New loss use does not add the same loss to an old balance" cover the same property.

What I ran: a fresh clone at 3bc00db94, a fresh uv venv, policyengine-core 3.33.12, Python 3.13.

  • PR YAML (trade_loss_carry_forward, trade_loss_net_pay_carry_forward, loss_relief): 16 passed.
  • PR Python (test_income_tax_loss_carry_forward.py, test_ni_class_4_profits_properties.py): 34 passed.
  • All 36 YAML files that touch trading_loss, loss_relief or employee_pension_contributions: 495 passed.
  • Related Python (trading loss, trading allowance, annual allowance, net pay, ANI pension relief, income concepts, uprating declarations): 54 passed.
  • The four household edge cases above. No microsimulation.

Not read or not run: the full Python test file line by line, the rest of extend_single_year_dataset, and the docs index wiring. No parameter values changed, so I did not run the reference check. I did not run the full test suite.

🤖 Generated with Claude Code

@vahid-ahmadi

Copy link
Copy Markdown
Collaborator

Round 2 at d3d2bbc55.

Status of round 1 (round 1). The new commit changes only the docs, one YAML case and the changelog fragment; the model code is the same as at 3bc00db94.

  1. should, resolved by documentation. The code still sets the opening stock against all trading profits in a year with a new loss (loss_relief.py:45-49). The commit takes the second route we offered: docs/engineering/income-tax-trading-losses.md:57-63 now works through both cases, says the old-loss-and-new-loss-in-the-same-trade case is outside what the model can rebuild, and tells callers to supply reconciled balances there. The new YAML case (trade_loss_carry_forward.yaml:251) pins the behaviour: £10,000 stock against £8,000 profit, £0 s.64 relief, £6,000 carried to 2027. I think that is acceptable. The model has no trade identity, so it cannot know which trade a stock belongs to. The case needs two trades with a profit and a loss in the same year, and the certified data has no trading_loss column, so there is no population effect today. One caveat for the docs, not a blocker: the total relief is only the same if trade A later makes enough profit. If A never does, the law never relieves that old stock, while the model has already used it against B.
  2. should, still open. CI at this head is red again, and again no test failed. Python 2/5, 3/5, 4/5 and 5/5 each ended with "The runner has received a shutdown signal", after memory reached 15.6 to 15.9 GB of 16 GB (49 MB free on shard 5 just before). Test / Result then fails because those shards did not finish. Details below.
  3. nit, fixed. income-tax-trading-losses.md:65-67 now warns that copying trading_loss or loss_relief into later years creates a new loss or a new override each year.
  4. nit, open. loss_relief.py:27 still has quantity_type = FLOW on an opening stock.
  5. nit, partly fixed. The PR body has a "Validation" list now, but it is still hard to follow (for example "nine scalar outputs are not nine tests"). A plain list of what ran at this head would do.

CI at d3d2bbc55 (run 40318044087)

Job Result Cause
Python 2/5 failed at 54 min runner shutdown at 83% of tests; 15.7 GB used shortly before
Python 3/5 failed at 44 min runner shutdown at 77%; 15.6 GB used
Python 4/5 failed at 58 min runner shutdown at 86%; 15.7 GB used
Python 5/5 failed at 68 min runner shutdown at 95%; 15.9 GB used, 49 MB free
Test / Result failed follows from the four shards above

None of these is a test failure or a timeout (limit 75 min). All tests in the logs up to the shutdowns passed, including the PR's Class 4 property tests.

Is it the branch? I cannot prove it, but the pattern points that way:

  • Every head of this branch has lost runners. At 4400b67e1, shard 1/5 had a real test failure (later fixed) and shards 2 to 5 were cancelled (I did not read their logs); at 3bc00db94 and now d3d2bbc55, four of five shards lost their runners each time.
  • In the same window (25 Oct 09:00 to 26 Oct 13:10) the workflow had 90 successful runs. I checked every other failed run: each was a real test failure (the test step ended in "failure", not "cancelled"), or, in one case, a run cancelled as a whole. None lost a runner.
  • The suite is close to the limit anyway. Passing runs today peaked at 11.4 to 15.3 GB per shard (Uprate reported Council Tax Reduction with household bills #2680, Extend fiscal parameter conversion beyond 2040 #2649's queue run, fix-uc-advance-cash-2410), and the workflow comment cites The Python test shards run out of memory under sys.monitoring coverage #2330 for worker memory growth.
  • What I could not find: a reason in the code. A household simulation computes the same 1,008 variables at this head and at the merge base, with the same traced peak memory. One run of three unrelated property test files in a single process peaked at 3.48 GB RSS at this head and 3.00 GB with the base model code. That is one run each, so treat it as a hint, not proof.

Suggestion: re-run the failed jobs once. If they lose their runners again, compare the memory lines in the logs with a main run, shard by shard, before marking ready. A green run is still needed before this leaves draft.

New finding

  1. nit: trade_loss_carry_forward.yaml:248 starts the new case with "# PROPOSED ONLY". It is not clear what that means in a baseline test. If it means the trade identities are assumptions of the test, the next two comment lines already say so.

What I ran, at d3d2bbc55 in a fresh worktree and venv (Python 3.13):

  • PR YAML (trade_loss_carry_forward, trade_loss_net_pay_carry_forward, loss_relief): 17 passed.
  • All YAML under income_tax/reliefs: 74 passed.
  • PR Python (test_income_tax_loss_carry_forward.py, test_ni_class_4_profits_properties.py): 34 passed, 0.88 GB peak RSS.
  • Memory checks above: test_marriage_allowance_cps_switch.py, test_uc_income_from_capital_properties.py and test_mixed_age_couple_properties.py (52 passed with both versions), and one household simulation for 2026, 2028 and 2030.
  • The CI logs of all four failed shards, the memory lines of 15 passing shards from three other runs, and the job results of every failed run of the workflow in the window.

Not read or not run: no microsimulation and no full suite run. I did not re-read the model code, which is unchanged since round 1, and I did not find the cause of the higher CI memory.

Verdict: should 1 is resolved by a clear, tested statement of the assumption. Should 2 still blocks: CI needs a green run. Still a draft, so this is a comment, not an approval.

🤖 Generated with Claude Code

@vahid-ahmadi

Copy link
Copy Markdown
Collaborator

Round 3 at 16a12b93d.

Status of round 2 (round 2). The new commit changes loss_relief to a stock, adds one test, drops the YAML label and extends the changelog fragment. The docs are unchanged.

  1. should, still open (docs only). The same-trade assumption is settled as round 2 said, but the caveat we asked for is not in yet. docs/engineering/income-tax-trading-losses.md:63 says the old stock is "unrelieved" in the A/A/B case. It does not say that, under s.83, that relief is lost for good if trade A never makes enough profit again, while the model has already used it against B's profit. One sentence at line 63 would close this.
  2. should, still open. CI at this head is red again (run 40393666784). Details and a correction below.
  3. nit, fixed (in round 2).
  4. nit, fixed. loss_relief.py:27 is now STOCK, and test_income_tax_loss_carry_forward.py:179 pins £8,000 for the year, January and December. That is the right behaviour for an opening balance. The PR body says January returned £666.67 before the change, so this was a real bug, not just a label.
  5. nit, still open. The PR body is now longer and harder to follow. The "Validation" list reads as a run log ("1/1/1 exits", "D3D", "artifact-unqualified"). A short list of what ran at this head and what passed would do.
  6. nit, fixed. trade_loss_carry_forward.yaml:248 no longer says "PROPOSED ONLY".

CI and memory

At this head, Python 2/5, 3/5 and 5/5 lost their runners ("The runner has received a shutdown signal"), 4/5 was cancelled (its log is no longer available), and 1/5 passed. Again, no test failed.

Shard This PR, peak Passing runs the same evening (pr-2679 queue, esa-permitted-work PR)
1/5 13,979 MB, passed 13,989 / 13,952 MB
2/5 15,874 MB, lost 15,008 / 14,304 MB
3/5 15,586 MB, lost 14,983 / 13,315 MB
4/5 log gone 12,495 / 12,713 MB
5/5 15,752 MB, lost 14,022 / 15,028 MB

A correction to round 2: I said no other run in the window had lost a runner. That no longer holds. The merge-queue run for #2105 (run 40395541241, 26 Oct 21:27) lost Python 2/5 at 15,501 MB. So the suite on main is now at the 16 GB limit too. This branch still loses more shards (three or four of five on each head, against one of five there), and its peaks are 0.6 to 0.9 GB above passing runs, but I can no longer say the problem is only this branch.

I also looked for a cause in the code and did not find one:

  • The certified dataset (1.58.1, enhanced_frs_2024_25.h5) has no trading_loss or loss_relief column, so supplied_input_periods finds no earlier years and the carry-forward loop in loss_relief.py:29-66 never runs in a microsimulation. It does not build simulations per year, and the per-year values it reads go into the ordinary cache, so it is linear in years, not recursive over all years.
  • The PR's own tests are small: test_income_tax_loss_carry_forward.py and test_ni_class_4_profits_properties.py together peaked at 0.91 GB RSS in one process.
  • Those tests run in every shard, including 1/5 (three loss and four Class 4 tests there), and shard 1/5 did not move. So "the PR's tests land in the full shards" does not explain it either.

My view: this looks like the general worker memory growth the workflow already notes (#2330, and the comment above the Python job), with this branch tipping it over. That is a CI capacity question rather than a defect in this PR. I would not ask for code changes for it. It still needs a green run before this leaves draft, and that may need a sixth shard or a fix on the CI side first.

New findings

None.

What I ran, at 16a12b93d in a fresh clone and venv (Python 3.13), without data:

  • test_income_tax_loss_carry_forward.py and test_ni_class_4_profits_properties.py: 35 passed, 0.91 GB peak RSS.
  • All YAML under income_tax/reliefs: 74 passed.
  • The column list of the certified dataset (names only).
  • The logs of shards 1, 2, 3 and 5 of run 40393666784, the shard peaks of two passing runs from the same evening, and the failed shard of the Uprate the Scottish Carer Supplement by September CPI with 5p rounding #2105 queue run.

Not run: no microsimulation and no full suite. I started a base-against-head memory run on test_dated_reform_routes.py but stopped it after 14 minutes on a shared machine, so I have no paired number this round.

Verdict: the code changes since round 2 are right and add no new findings. Two things remain: the one-sentence docs caveat (1), and a green CI run (2), which now looks like a CI capacity problem more than a PR problem. Still a draft, so this is a comment, not an approval.

🤖 Generated with Claude Code

@vahid-ahmadi

Copy link
Copy Markdown
Collaborator

Round 4 at bcf0fb7cd.

Status of round 3 (round 3). The only change since 16a12b93d is one sentence in the docs. No new comments from you since then, so I have read the commit and the PR body.

  1. should, fixed. docs/engineering/income-tax-trading-losses.md:63 now says that if A never earns enough future profits, the excess of its old loss stays unrelieved under ss.83 to 84, while the aggregate calculation has already used part of it against B's profit. That is the caveat we asked for. Thank you.
  2. must, open (was should). CI at this head is red again, and this time it is not the runners. Details below. We were wrong in round 3 to call this only a CI capacity issue: the runner shutdowns hid real test failures.
  3. nit, fixed (in round 2).
  4. nit, fixed (in round 3).
  5. nit, still open. The "Validation" list in the PR body still reads as a run log ("16A Code attempt 1", "COMPLETE / APPROVE; local verification is CLEAN"). Please also drop "COMPLETE / APPROVE": no GitHub reviewer has approved this PR, and readers may take it that way.
  6. nit, fixed (in round 3).

CI at bcf0fb7cd

Code run 40424136853 failed: Python shards 2, 3, 4 and 5 failed, shard 1, YAML, lint and docs passed. Aggregate impact passed. All four shards ran to the end (67 to 71 minutes) and each failed on RecursionError in Microsimulation tests. CI tests this head merged with current main (refs/pull/2661/merge, 8056c6d):

  • shard 2: test_projection_horizon.py::test_legacy_reported_capital_expires_only_after_the_horizon
  • shard 3: test_tax_credits_state_pension_step_one_properties.py::test_state_pension_change_preserves_claimant_scope_and_deductions, plus an error in test_projection_horizon.py::test_a_year_past_the_horizon_is_served_from_the_last_projected_year
  • shard 4: test_projection_horizon.py::test_tax_free_savings_formula_takes_over_after_the_horizon
  • shard 5: errors in test_projection_horizon.py::test_every_year_to_the_horizon_is_a_dataset_year and ::test_simulation_serves_projected_values_to_the_horizon

The printed stack is only the last frames, but in shards 2 and 4 it runs loss_relief 2025 → trade_loss_relief_against_general_income 2024 → net_income_before_trade_loss_relief 2024 → the full 2024 income and benefit chain, inside a pension credit branch. That cross-year link is new in this PR: on main loss_relief is a plain input with no formula. Here loss_relief.py:40-60 walks back over every earlier year with a supplied loss, calls loss_relief for that year (:45) and reads trade_loss_relief_against_general_income for it (:58-60), which pulls in that year's whole income chain. My guess is that the stack depth then grows with the number of years walked back, on top of an already deep pension credit branch, and the horizon tests project far enough out to pass the limit. I have not proved this. In shards 3 and 5 the visible frames are all UC and pension credit, so I cannot show the PR's variables there, but the failures are the same kind.

The same tests pass without this PR: the #2690 run 40422010249, on recent main plus the shard change, passed all shards. So this looks caused by the PR's carry-forward chain once the dataset has supplied losses in earlier years. Two things would help: reproduce one of the test_projection_horizon.py failures on the merge with main, and see whether the earlier-year relief can be computed without nesting a full prior-year calculation inside the current one (or cap the years it walks back).

At round 3 (16a12b93d, run 40393666784) shards 2, 3 and 5 hit runner shutdowns near 15.7 GB and shard 4 was cancelled, before the logs show these tests. The code is the same at both heads, so these failures were most likely there then too.

What I ran at bcf0fb7cd (fresh checkout and uv venv, Python 3.13):

  • pytest policyengine_uk/tests/test_income_tax_loss_carry_forward.py policyengine_uk/tests/test_ni_class_4_profits_properties.py: 35 passed.
  • policyengine-core test on loss_relief.yaml, trade_loss_carry_forward.yaml and trade_loss_net_pay_carry_forward.yaml: 17 passed.
  • I did not run the failing Microsimulation tests locally (no microsimulation on the shared laptop), so the cause above is from the CI logs, not a local reproduction.

Verdict: not ready. Item 2 is a must: the PR's tests pass, but the full suite fails on this head merged with main, with recursion through the new carry-forward chain. It is still a draft, so no approval.

🤖 Generated with Claude Code

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

Development

Successfully merging this pull request may close these issues.

2 participants