Skip to content

Relieve a miscellaneous loss only against miscellaneous income - #2327

Merged
MaxGhenis merged 4 commits into
mainfrom
misc-loss-relief
Oct 12, 2026
Merged

MaxGhenis merged 4 commits into
mainfrom
misc-loss-relief

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #2326

axiom: ukpga/2007/3/152 TheAxiomFoundation/rulespec-uk#452 queued (s. 152 is not yet in the corpus, and the signed encoder run needs its own approval)

What changes

ITA 2007 s. 152(4) allows a miscellaneous loss to be deducted "only from the person's relevant miscellaneous income". Until now, taxable_miscellaneous_income passed a negative miscellaneous_income through to total and adjusted net income. So the loss reduced tax on pay, pensions, savings and dividends: pay of £50,000 with a £10,000 miscellaneous loss paid £5,486 instead of £7,486.

  • taxable_miscellaneous_income is now max(0, miscellaneous_income). The model holds one net miscellaneous figure per person, so a net loss has nothing left to set against in the year.
    • Not modelled: carrying the loss forward.
    • Docstring caveats: s. 152(5) limits relief to income under the same section 1016 provision, so one net figure can undertax a gain and a loss under different provisions. The input also covers income outside s. 1016.
  • total_income (ITA 2007 s. 23 Step 1) reads the floored figure. A loss no longer lowers total income, and so no longer passes a pensioner with £40,000 of pension through the Winter Fuel Payment income test.
  • Universal Credit earned income reads the floored figure too. A loss no longer comes off pay, and the gross earnings match uc_income_tax_on_earnings.
  • Unchanged: whether miscellaneous income should count as UC earnings at all (UC counts royalties, trust income and overseas pensions in miscellaneous_income as earned income #1961).
  • Earlier years: tax credits (tax_credits_applicable_income) still read the raw figure. Tax credits ended in April 2025, so this affects only runs for earlier years.

Impact (real run)

Measured on enhanced_frs_2023_24.h5 (local policyengine-uk-data storage, 6 October build) for 2026: no one has negative miscellaneous income (0 records; £5,116.8m positive in total). The formula is unchanged for non-negative values, so the change is nil on the enhanced FRS.

It matters for household calculations and for datasets that carry losses. The public transfer dataset has negative miscellaneous income for 964 of 58,848 people.

On PolicyBench UK round 2's 200 transfer-data households (every output recomputed on this head, 9e2192d, against policyengine-uk 2.126.7):

Invariants and tests

  • YAML (gov/hmrc/income_tax/bases/taxable_miscellaneous_income.yaml):
    • miscellaneous income is taxed with pay;
    • a £10,000 loss leaves tax on £50,000 of pay at £7,486;
    • a £20,000 loss leaves adjusted net income at £110,000 and Income Tax at £33,432, with the personal allowance tapered;
    • total income ignores the loss;
    • UC earned income on £30,000 of pay stays £25,119.60.
  • Property tests (test_miscellaneous_loss_properties.py, Hypothesis): any miscellaneous loss leaves Income Tax, adjusted net income and total income exactly as with no miscellaneous income, whatever the pay, savings, dividends and pension income. This runs 25 examples plus a fixed example in the taper region. Positive miscellaneous income is taxable in full.
  • Run locally: the full policy YAML suite on this branch passes (3,000 cases).
  • Review: a GPT-6.1 Sol review asked for changes, all addressed here: total income, UC earnings, the docstring, the taper test and the property tests.

🤖 Generated with Claude Code

…007 s. 152(4))

Fixes #2326. taxable_miscellaneous_income added miscellaneous_income as it
stood, so a loss reduced total and adjusted net income and the tax on pay,
pensions, savings and dividends. A net loss now counts as nil; carrying it
forward is not modelled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits October 11, 2026 14:53
Review changes: total_income (ITA 2007 s. 23 Step 1) and Universal Credit
earned income now read taxable_miscellaneous_income, so a loss neither
reduces total income (the Winter Fuel test, non-dependant deductions) nor
widens the UC earnings error in #1961. The docstring states s. 152(5)'s
same-provision limit and the input's wider scope. Tests add the personal
allowance taper, total income and UC earnings; the property test covers
total income, with 25 examples and a taper-region example.

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

Copy link
Copy Markdown
Collaborator Author

CI run cancelled to free runners (UK hub, 2026-10-11 23:30Z). The PolicyEngine org is at GitHub's limit of 60 concurrent jobs, with about 360 queued. Fleet ops asked the hub to let the Budget-critical #2305 take runners first. This PR's queued run had not started, so I cancelled it. Nothing is wrong with the PR.

Please don't re-run it or push just to retrigger CI. The hub re-runs this run itself once #2305 has runners. The record is ~/reviews/uk-hub/ci-yield-2026-10-11.json; see notice 17 in ~/reviews/uk-hub/notices.md.

@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Hand-off to the UK hub (session local_66e4b1fd, PolicyBench UK round 2).

  • Head: 72ec8db6d85d252a763b91ea1b801943ab3847d2 (includes main at ed7b42f95).
  • Review: GPT-6.1 Sol, two rounds, then a delta review of the main merge; APPROVE at this head: ~/reviews/policybench-uk-round2/reviews/pe-uk-merge-delta-2327-2348.md (round 2: pe-uk-2327-review-r2.md).
  • CI: not yet run at this head. The run was queued, and the hub cancelled it to free runners (notice 17, ~/reviews/uk-hub/ci-yield-2026-10-11.json); the hub re-runs it. The last completed run, at 9e2192d1, failed one test only, main's test_dataframe_dataset round-trip (notice 14), with 3,751 others passing. This head includes main's fix for it (Export the identifiers for every year an input frame names, so it round-trips #2349).
  • Tests: the last completed CI run above ran the whole suite at the approved head 9e2192d1. The main merge changed none of this PR's files (pr2327_approved_net.diff and pr2327_merged_net.diff in ~/reviews/policybench-uk-round2/fixes/evidence/ are identical).
  • PolicyBench check at this head: all 2,800 round 2 references recomputed on this head (audit/attribution/refs_misc_final.csv); 16 move, the same cells and values as at the reviewed head. With all five fix heads merged onto main ed7b42f95, 96 move, also unchanged.
  • Impact: nil on the enhanced FRS (it has no negative miscellaneous income); 16 PolicyBench round 2 references move.
  • Depends on: nothing. One interaction to know: with the loss ring-fenced, Council Tax Reduction income deducts the family's whole income tax, not the tax the schemes allow #2090 (Council Tax Reduction deducts tax on income it does not count) shows on transfer-data households with very large negative miscellaneous figures. The PR body and Council Tax Reduction income deducts the family's whole income tax, not the tax the schemes allow #2090 record it; the fix belongs in Council Tax Reduction income deducts the family's whole income tax, not the tax the schemes allow #2090.
  • Decision: none. A bug fix to ITA 2007 s. 152.
  • Follow-ups: none owed. Axiom parity: rulespec-uk#452.

@MaxGhenis
MaxGhenis merged commit 5133c71 into main Oct 12, 2026
7 of 12 checks passed
@MaxGhenis
MaxGhenis deleted the misc-loss-relief branch October 12, 2026 03:07
MaxGhenis added a commit that referenced this pull request Oct 12, 2026
Conflict in uc_individual_earned_income_before_mif: main (#2327,
7145d5b) counts taxable_miscellaneous_income instead of
miscellaneous_income; this branch adds statutory sick, maternity and
paternity pay. Both are kept.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A miscellaneous loss is relieved against all other income, not only miscellaneous income (ITA 2007 s. 152(4))

1 participant