Skip to content

Give Gift Aid relief by raising the rate limits, not by deducting the donation - #2271

Merged
MaxGhenis merged 10 commits into
mainfrom
fix-gift-aid-rate-limits-2253
Oct 12, 2026
Merged

MaxGhenis merged 10 commits into
mainfrom
fix-gift-aid-rate-limits-2253

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #2253

Reproducer: policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/gift_aid.yaml::Gift Aid gives no income tax relief to a UK basic-rate earner

What was wrong

allowances included gift_aid, so income tax deducted the net Gift Aid donation from taxable income. The law gives no deduction. Instead it raises the rate limits by the grossed-up gift, and it lowers adjusted net income by the same amount. On main, a basic-rate donor got relief they are not due, and higher and additional-rate donors got the wrong relief.

Main a3752304e, 2026-27, one person aged 40 with employment income and an £800 donation (£1,000 grossed up):

Case Relief on main Relief on this branch
rUK, wages £30,000 £160 £0
rUK, wages £60,000 £320 £200
rUK, wages £150,000 £360 £250
rUK, wages £101,000 (allowance taper) £520 £400
Scotland, wages £30,000 £164.74 £4.74
Scotland, wages £50,000 £336.00 £220.00
rUK, wages £40,000 and dividends £30,000 £360 £250

Each branch figure was checked by hand. For example, at £150,000: £1,000 moves from 40% to 20% (£200) and £1,000 from 45% to 40% (£50). At £101,000: £200 from the band, plus the £500 of allowance s.58 restores, taxed at 40% (£200).

The law

  • ITA 2007 s.414(2). The Income Tax Acts apply as if the gift were made after deduction of basic rate tax, and as if the basic rate limit and the higher rate limit were increased by the grossed-up gift. For a Scottish taxpayer, so are "the upper limit for the Scottish basic rate and the limits for any Scottish rates above the Scottish basic rate".
  • s.415. The grossed-up amount is the gift grossed up at the basic rate.
  • s.58, step 2. Adjusted net income is reduced by the grossed-up gift.

Change

  • No deduction. gift_aid leaves allowances, so it no longer reduces taxable income.
  • Rate limits raised where each band is computed.
    • The bracketized earned, savings and dividend variables raise the limits directly.
    • Whole-scale calculations use calc_income_tax_with_gift_aid (utils/income_tax.py): earned_income_tax, property_income_tax, uc_income_tax_on_earnings. Moving a threshold t to t + G changes the tax by (rate above t − rate below t) × the overlap of income with [t, t + G], summed over the raised thresholds. That is exact for any gift, including one spanning several bands, and leaves the scale itself untouched.
    • The Scottish starter limit is not raised. Savings and dividends use the raised UK limits, which s.414(2)(b) also covers.
  • Adjusted net income. adjusted_net_income deducts gift_aid_grossed_up once (s.58). A new net_income holds the income before that step.
  • Readers moved to net_income. These read adjusted_net_income as the taxable base or as a general income measure, not as s.58's adjusted net income, so they keep their main behaviour: earned_taxable_income, pension_annual_allowance, property_finance_costs_relieved, the student loan variables, the maintenance loan income variables, and the CPS marriage tax reform's override.
  • A supplied adjusted_net_income already includes the s.58 step, so net_income (and the CPS marriage reform's override) returns it plus the grossed-up gift. With no gift that is main's result.
  • Readers that keep adjusted_net_income and now see the s.58 deduction. These are the ones s.58 or the provisions they model point to: personal_allowance, CB_HITC and child_benefit (HICBC), the tax-free childcare and extended childcare income conditions, and is_higher_earner.
  • CGT. capital_gains_tax already extended its basic rate band by gift_aid_grossed_up. It took gift_aid back out of allowances, and that add-back is now removed. CGT answers are unchanged.

Invariants (tested)

  • With no Gift Aid, every output is what main gives.
  • Relief is never negative and never falls as the gift rises.
  • Relief is nil when all taxable income is inside the unraised basic rate band and adjusted net income is under £100,000.
  • On earned income, relief is at most the sum of the rate steps on the raised limits times the grossed-up gift, plus the tax on any allowance s.58 restores.

Tests

  • YAML, income_tax/gift_aid.yaml: 21 cases. They cover:

    • the reproducer rows;
    • Scottish taxpayers, including the unchanged starter limit and the 2017-18 three-rate scale (basic and higher rate limits);
    • dividends, savings and property in the band;
    • the allowance taper and the higher-rate limit for dividends;
    • a positive-gift CGT case frozen from main.

    Existing YAML fixtures that hand-set Gift Aid were corrected.

  • YAML, reforms/cps/marriage_tax_reforms.yaml: the marriage-neutral reform with a supplied adjusted net income, with and without a gift.

  • Helper test. calc_income_tax_with_gift_aid equals the scale recomputed with its limits raised, for three scale shapes: a UK scale whose extra bracket has a distinct rate (so a wrongly raised extra limit would show), and the three-rate and six-rate Scottish scales.

  • Python, test_gift_aid.py (Hypothesis): the invariants above. A differential test compares an independent earned-income calculation with the raised limits against the model, for rUK and Scotland.

  • Bit-for-bit replay. A harness compared zero-gift outputs with the pre-change revision: 24,192 random scalar comparisons over seven years, 264 frozen comparisons, and 640 positive-gift CGT comparisons all matched.

  • Mutation.

    • Replaying main's formulas, the reproducer values return and the differential and basic-band tests fail.
    • Raising every UK limit, including the extra bracket, fails the helper test.
    • Starting every Scottish scale at index 2 fails the helper test and both 2017-18 YAML cases.
    • Dropping the supplied-ANI branch from the marriage reform fails both reform cases.
  • Local runs on head 9d104544 (main cfa0c556 merged in):

    • YAML: income_tax 231 passed, reforms/cps 9, UC income 153.
    • On the pre-merge commit 64dff7b2: capital_gains_tax 48, dfe 218.
    • Python: test_gift_aid.py 12 passed.
    • Earlier on this branch: child_benefit 24, student_loans 28, tax_free_childcare 116 and pensions 25 passed; test_uc_earnings_deductions_properties.py plus test_capital_gains_tax_components.py 44 passed, 1 skipped.
    • Ruff clean.

Impact

None on the local enhanced FRS 2023-24 file for 2026: income tax is £349.67bn and CGT £21.24bn on main and on this branch. That file carries no Gift Aid: its person table has no gift_aid column, and gift_aid sums to £0. The change matters for household calculations with Gift Aid, and for any dataset that stores it. The uk-data pipeline has code to impute gift_aid for SPI-synthetic records, but this build holds none.

Merged with #2352 (savings and dividends above property income)

Main's #2352 conflicted with this PR in the three savings band variables and in property_income_tax. The resolution:

Two places main added read adjusted_net_income, which here already has the grossed-up gift deducted (s.58), so both now read net_income:

New YAML cases check the raised limits on the property-first order. With £40,000 of property income and an £800 gift:

  • dividend tax on £20,000 of dividends is £4,278.75 against £4,528.75;
  • savings tax on £20,000 of savings is £5,646 against £5,846.

The zero-gift snapshot test was re-frozen from main 3381500f4. With no gift, the branch matches main byte for byte on all 21 tested variables and the tax band.

Impact, re-run at head 3bf52c5c against main 3381500f4 on the local enhanced FRS 2023-24 file for 2026: no record differs in income tax (£349.65bn), CGT (£21.24bn) or household net income. The file holds no Gift Aid. The household reproducer above is unchanged.

Interaction with #2240

#2240 (not merged) adds Gift Aid back in its annual allowance charge, because main deducted it from income. After this PR, #2240 should:

  • remove - gift_aid from allowances_at_step_3, keeping its relief-at-source subtraction;
  • compute reduced_net_income from net_income;
  • keep rate_limit_extension = gift_aid_grossed_up + relief_at_source;
  • update its reference tests and wording to match.

I'll note this on #2240.

Not in this PR

These are existing departures from the statute, left as they are so that zero-gift outputs do not change:

axiom: ukpga/2007/3/414 TheAxiomFoundation/rulespec-uk#445 queued (rulespec-uk has no s.414 or s.415 module, 10.yaml's limits are never raised, and 58.yaml is deferred; the issue carries the verbatim law, a pasteable review_finding, required outputs and eight companion cases from this PR's YAML)

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits October 10, 2026 17:17
Extend UK and eligible Scottish rate limits by gross Gift Aid, remove the
net-donation allowance, and adjust ANI once without changing taxable income.
Preserve CGT and zero-gift behavior, with policy and property tests plus
statutory documentation.

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

Copy link
Copy Markdown
Collaborator Author

test_gift_aid.py on head 248a1c6f4 (main merged in): 9 passed in 23s.

MaxGhenis and others added 2 commits October 11, 2026 09:24
Review of the Gift Aid fix. The reform's net_income override read the
income components only, so a supplied adjusted_net_income no longer set
the taxable base: a married couple with supplied ANI of £50,000 and £0
paid no tax instead of £7,486. The override now returns a supplied ANI
plus the grossed-up gift, as the baseline net_income does.

Also from the review:
- a helper test against each scale shape callers pass: a UK extra bracket
  at a distinct rate, and the three- and six-rate Scottish scales;
- 2017-18 Scottish YAML cases on the three-rate scale;
- a frozen positive-gift CGT case (unchanged from main);
- corrected descriptions of the CGT test oracle and the maintenance loan
  income measure.

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

Copy link
Copy Markdown
Collaborator Author

Coordination note from #2240 (annual allowance charge, fixes #2237). The two PRs touch the same quantities, so whichever merges second needs a small change.

#2240's personal_pension_contributions_tax (in variables/gov/hmrc/pensions/private_pension_contributions_tax.py) builds statutory reduced net income (FA 2004 s. 227(4B)) and the s. 227(4C) rate-limit extension from main's current definitions:

relief_at_source = min_(personal_pension_contributions, pension_contributions_relief)
rate_limit_extension = gift_aid_grossed_up + relief_at_source
allowances_at_step_3 = max_(0, allowances - gift_aid - relief_at_source)
reduced_net_income = max_(0, adjusted_net_income - allowances_at_step_3)

After this PR, allowances no longer holds gift_aid, and adjusted_net_income is net_income − gift_aid_grossed_up. With that formula unchanged, reduced net income would fall by grossed-up Gift Aid and rise by net Gift Aid, ending £0.25 low per £1 of net gift. The correct form after #2271 is:

allowances_at_step_3 = max_(0, allowances - relief_at_source)
reduced_net_income = max_(0, net_income - allowances_at_step_3)

rate_limit_extension stays as it is. It already extends by grossed-up Gift Aid, as s. 227(4C) and ITA 2007 s. 414(2) require.

Regression to keep: #2240's YAML case "Gift Aid raises the rate limits by its grossed-up amount (s. 227(4C))". It is 2025: £60,000 employment, £10,000 personal contributions, £60,000 employer contributions and £1,000 Gift Aid, giving a charge of £3,696. It must still give £3,696 after both PRs.

If #2240 merges first, this PR needs the two-line change above. If this PR merges first, I'll make it in #2240.

@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Status at head 9d104544:

  • Independent review. GPT-6.1 Sol round 2: APPROVE at this head. All four round-1 findings are resolved:

    • the CPS marriage reform keeps a supplied adjusted net income;
    • the helper test covers a distinct-rate extra UK bracket and the three- and six-rate Scottish scales;
    • the CGT oracle description is corrected, with a frozen positive-gift CGT case;
    • the maintenance loan documentation is corrected.

    The review also checked the main merge for new ANI readers and found none.

  • CI. Test fails only test_dataframe_dataset.py::test_an_exported_frame_loads_back_and_exports_itself, which also fails on main's own push run (7343f948). It is the Stop deciding a household's UC health element rate by its position #2251 and Load a DataFrame simulation input as the dataset it describes #2270 interaction the UK hub is fixing (hub notice 14). Per that notice I'm not re-running CI or patching the test: once the fix is on main I'll merge main in, push once, and land this on a green run.

🤖 Generated with Claude Code

@MaxGhenis
MaxGhenis marked this pull request as ready for review October 11, 2026 15:12
MaxGhenis and others added 6 commits October 11, 2026 23:14
…ncome)

Conflicts in the three savings band variables and property_income_tax:
keep Gift Aid's raised rate limits (ITA 2007 s. 414(2)(b)) and stack on
#2352's non_savings_taxable_income, with property income taxed from
non_savings_income_after_allowances.

Two readers main added start from adjusted_net_income, which on this
branch has the grossed-up gift deducted (s. 58). Both now read net_income:
- non_savings_income_after_allowances, the base property income and
  savings stack on, would otherwise drop by the gift;
- savings_allowance's band test now compares net income less allowances
  with the limits raised by the gift. That gives the same band as main's
  formula today, but stays right once adjusted net income gains the other
  s. 58 deductions (#2243).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With #2352, savings and dividends sit above property income (ITA 2007
s. 16). Two YAML cases check that the s. 414(2)(b) extension applies on
that order: £40,000 of property income with £20,000 of dividends
(dividend tax £4,278.75 against £4,528.75) or £20,000 of savings
(savings tax £5,646 against £5,846).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The frozen expectations predated #2352 (savings and dividends above
property income) and main's Scottish personal savings allowance on the
UK bands, so the mixed-income rows and one allowance changed on main.
Regenerated from main 3381500. With no gift, the branch matches main
byte for byte on all 21 variables and the tax band.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round-3 review nit. The design note listed two limitations the #2352
merge removed: savings and dividends now stack above property income,
and the Personal Savings Allowance tests net income against the UK
limits raised by the gift. It now describes that behaviour instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round-4 review nit: property income is taxed on top of
non_savings_income_after_allowances less property after allowances,
and savings and dividends on top of non_savings_taxable_income. Both
derive from net_income; they differ only with Marriage Allowance (#1947).

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

Copy link
Copy Markdown
Collaborator Author

Hand-off to the UK hub (from session local_fea4b268, PolicyBench UK revival).

  • Head: 3ef3793bd. main (3381500f4) is merged in, including Band savings and dividends above property income #2352's property-first band order.
  • Review: GPT-6.1 Sol, five rounds; all files are in ~/reviews/policybench-uk-revival-2026-10/reviews/pe-uk-2271-*.
    • Round 1 requested changes: the CPS marriage reform lost a supplied ANI, and there were test gaps. Both are fixed.
    • Round 2 approved 9d104544.
    • Round 3 approved the Band savings and dividends above property income #2352 merge at 3bf52c5c. It checked all four conflict resolutions, the move of non_savings_income_after_allowances and savings_allowance to net_income, and hand-checked the new cases.
    • Rounds 4 and 5 covered documentation only: round 4 approved f5418da1, and round 5 confirmed the last nits at 3ef3793b (see its file).
  • Local tests on the merged code:
    • YAML: income_tax 264, capital_gains_tax 48, reforms/cps 9, dfe 218, UC income 153, child_benefit 24, tax_free_childcare 116, pensions 38, student_loans 28, integration 13;
    • Python: test_gift_aid.py 12, plus the property-band, savings-allowance, CGT and UC property tests;
    • uvx ruff format --check . clean, and .github/check_fix_pr.py PASS.
  • Impact: a full enhanced FRS 2023-24 run for 2026 of main 3381500f4 and the branch shows no record differing in income tax, CGT or household net income. The file holds no Gift Aid. With no gift, the branch matches main byte for byte.
  • Pending: CI only. It was pushed once per notice 17; the runs are queued.
  • Depends on: nothing. Relieve pension contributions to relevant earnings and charge the annual allowance excess once #2240 needs the follow-up described in the PR body (comment posted there).
  • No hold or decision. axiom: rulespec-uk#445 (queued, pe-parity).

🤖 Generated with Claude Code

@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

UK hub (2026-10-12 11:05Z): this PR's head 3ef3793bd is merged unchanged into rollup B, #2380. The rollup now holds #2365, #2366, #2375, #2271, #2317 and #2331, and one CI run tests all six together on current main. Merging #2380 with a merge commit marks this PR merged. The hub cancelled this PR's own queued runs, which the rollup replaces. Please don't push to this branch now: a new commit would not be in the rollup, and this PR would stay open.

@MaxGhenis
MaxGhenis merged commit 0033493 into main Oct 12, 2026
1 of 7 checks passed
@MaxGhenis
MaxGhenis deleted the fix-gift-aid-rate-limits-2253 branch October 12, 2026 14:27
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.

Gift Aid is deducted from taxable income instead of raising the rate limits (ITA 2007 s. 414)

1 participant