Skip to content

tax_band compares a Scottish taxpayer's total income with the Scottish thresholds; the Scottish rates apply to non-savings, non-dividend income only #2350

Closed

Description

@juaristi22

What the law says

  • ITA 2007 s. 11A charges the Scottish rates on "the non-savings income of a Scottish taxpayer" (s. 11A(1A)), subject to the dividend rates of s. 13 (s. 11A(5)).
  • So a Scottish taxpayer's savings and dividend income is charged at the UK rates and bands, on top of their other income.
  • ITA 2007 s. 55B(2)(b) lets a person receive the Marriage Allowance only if they are liable at no rate other than the basic rate, the Scottish basic rate, "a Scottish rate below the Scottish basic rate", the Scottish intermediate rate, the dividend ordinary rate and the other rates it lists.

What the model does

tax_band (from 2018-19) takes adjusted_net_income - allowances, which includes savings and dividends, and compares it with the Scottish thresholds for a Scottish taxpayer:

income = ani - allowances
scottish_band = select(
    [income < threshold for threshold in rates.scotland.rates.thresholds[:5]],
    [NONE, STARTER, BASIC, INTERMEDIATE, HIGHER],
    default=ADDITIONAL,
)

The liability itself is right: earned_income_tax applies the Scottish rates to earnings only, and dividend_income_tax applies the UK dividend bands. Only the label is wrong, and what reads it:

  • meets_marriage_allowance_income_conditions refuses a Scottish taxpayer whose earnings sit in the intermediate band and whose dividends are charged at the ordinary rate.
  • Counts of taxpayers by marginal rate put some Scottish taxpayers a band too high: those whose savings or dividends carry their total income over a Scottish threshold.
  • savings_allowance also reads tax_band. That is Personal Savings Allowance uses the Scottish bands for Scottish taxpayers (ITA 2007 s. 12B(8)) #2347, which needs the rest-of-UK bands and is a separate fix.

Minimal reproduction

policyengine-uk 2.122.2, period 2025, one adult aged 40. The formula on main is the same.

  • Scotland, £40,000 of pay and £5,000 of dividends
    • Model: tax_band is HIGHER; meets_marriage_allowance_income_conditions is false.
    • Law: the pay is charged at the starter, basic and intermediate rates (£27,430 of taxable income, below the £31,092 higher-rate threshold). The dividends are charged at the ordinary rate (total taxable income £32,430, below £37,700). The person is liable at no higher rate, so the condition holds.
    • The model's tax is right: £5,582.82 on the pay and £393.75 on the dividends.
  • England, the same incomes: BASIC, and the condition holds.
  • Scotland, £60,000 of pay and £20,000 of dividends
    • Model: tax_band is ADDITIONAL, because £67,430 of total taxable income is above the £62,430 advanced-rate threshold.
    • Law: the pay reaches the Scottish higher rate only (£47,430 of taxable income). The dividends are charged at the upper rate (total below £125,140). No income is charged at the advanced, top or dividend additional rate.
  • England, the same incomes: HIGHER.
from policyengine_uk import Simulation

def band(country, pay, dividends, year=2025):
    situation = {
        "people": {"p": {
            "age": {str(year): 40},
            "employment_income": {str(year): pay},
            "dividend_income": {str(year): dividends},
        }},
        "benunits": {"b": {"members": ["p"]}},
        "households": {"h": {"members": ["p"], "country": {str(year): country}}},
    }
    sim = Simulation(situation=situation)
    return (
        sim.calculate("tax_band", year),
        sim.calculate("meets_marriage_allowance_income_conditions", year),
    )

band("SCOTLAND", 40_000, 5_000)   # HIGHER, False; law: intermediate, True
band("SCOTLAND", 60_000, 20_000)  # ADDITIONAL; law: higher

A possible fix

For a Scottish taxpayer, take the higher of two bands:

  1. the Scottish band of taxable income that is neither savings nor dividends, against the Scottish thresholds;
  2. the rest-of-UK band of total taxable income, which is the highest rate their savings and dividends are charged at.

On the cases above this gives INTERMEDIATE and HIGHER. A Scottish taxpayer with £30,000 of pay and £25,000 of dividends stays HIGHER, which is right: part of the dividends is charged at the upper rate.

Tests to add in scottish_rates.yaml: the two Scottish cases above, with dividends and with savings interest.

Where it showed up

PolicyEngine/microcosm#1198 binds HMRC's counts of Scottish higher and additional rate Income Tax payers (Income Tax liabilities statistics, Table 2.2) on tax_band. An engine test there pins the first case above, so a fix here shows up as a failure of that pin.

Activity

  1. added 3 commits that reference this issue on Nov 1, 2026
  2. MaxGhenis commented on Nov 2, 2026

    @MaxGhenis
    Collaborator

    Ready for hub review

    Head f0f576a4451adb83eee5f7bcccbf58f7ffdb81ea, branch fix-2350-scottish-tax-band (the branch tip when fetched was the same SHA). Environment set up with uv sync --frozen --extra dev; HUGGING_FACE_TOKEN and POLICYENGINE_UK_DEFAULT_DATASET unset. 4 vCPU, 16 GB; no memory trouble, -n 4 throughout.

    # Command Exit Passed Failed Skipped Xfailed Wall
    1 uv run policyengine-core test policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax -c policyengine_uk 0 497 0 0 0 106 s
    2 uv run policyengine-core test <the 11 YAML paths in the brief, one command> -c policyengine_uk 0 145 0 0 0 38 s
    3 uv run pytest -q -o xfail_strict=true -n 4 policyengine_uk/tests/test_tax_band_scottish_properties.py 0 5 0 0 0 56 s
    4 uv run pytest -q -o xfail_strict=true -n 4 policyengine_uk/tests/test_marriage_allowance_cps_switch.py 0 45 0 0 0 249 s
    5a uv run python /tmp/probe_couples.py at head 0 – – – – 11 s
    5b uv run python /tmp/probe_couples.py at main (2da212abcf2086337fc088a4a1236f4ae7b6af63) 0 – – – – 11 s
    6 ISSUE_2015_PARAMETERS at 2da212ab 0 – – – – 13 s
    7 ISSUE_SCOTTISH_2015_16 at 2da212ab 0 – – – – 11 s
    8 ISSUE_SAVINGS_SLICES at 2da212ab 0 – – – – 14 s

    Summary lines: (1) 497 passed, 1 warning in 80.71s; (2) 145 passed, 1 warning in 24.45s; (3) 5 passed in 51.49s; (4) 45 passed in 243.24s (0:04:03). Wall times include uv run start-up. The one warning in 1 and 2 is PytestAssertRewriteWarning: Module already imported so cannot be rewritten; anyio.

    Failures: none. No test skipped for want of a dataset. Entry 4 finished well inside the 90-minute limit, so no -k split was needed; I did not record per-test durations, so I can't say how long test_the_allowance_never_falls_as_the_statutory_rate_rises took on its own.

    Mutants: the brief named none, so none were run. The working tree was clean (git status --porcelain empty) after every run.

    Skipped: nothing.

    main was at 2da212abcf2086337fc088a4a1236f4ae7b6af63 when fetched, so 5b and entries 6–8 all ran at that commit after one uv sync --frozen --extra dev (it reported no package changes). Each script's stdout follows; I removed only uv's UV_NATIVE_TLS deprecation warning line.

    5a. Couples table at head f0f576a4

    /home/user/policyengine-uk/policyengine_uk/__init__.py
    2017 ('SCOTLAND', 40000, 5000, 0, 5000) | earner band BASIC meets True MA 1150.0 | income tax 5470.0 0.0
    2017 ('SCOTLAND', 40000, 0, 5000, 5000) | earner band BASIC meets True MA 1150.0 | income tax 6270.0 0.0
    2017 ('ENGLAND', 40000, 5000, 0, 5000) | earner band BASIC meets True MA 1150.0 | income tax 5470.0 0.0
    2017 ('SCOTLAND', 44000, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 6700.0 0.0
    2017 ('ENGLAND', 50270, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8808.0 0.0
    2017 ('ENGLAND', 50070, 0, 500, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8728.0 0.0
    2017 ('ENGLAND', 50070, 500, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8728.0 0.0
    2017 ('ENGLAND', 50271, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8808.4 0.0
    2018 ('SCOTLAND', 40000, 5000, 0, 5000) | earner band INTERMEDIATE meets True MA 1190.0 | income tax 5745.1 0.0
    2018 ('SCOTLAND', 40000, 0, 5000, 5000) | earner band INTERMEDIATE meets True MA 1190.0 | income tax 6320.1 0.0
    2018 ('ENGLAND', 40000, 5000, 0, 5000) | earner band BASIC meets True MA 1190.0 | income tax 5617.0 0.0
    2018 ('SCOTLAND', 44000, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 6733.5 0.0
    2018 ('ENGLAND', 50270, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8468.0 0.0
    2018 ('ENGLAND', 50070, 0, 500, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8388.0 0.0
    2018 ('ENGLAND', 50070, 500, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8388.0 0.0
    2018 ('ENGLAND', 50271, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8468.4 0.0
    2025 ('SCOTLAND', 40000, 5000, 0, 5000) | earner band INTERMEDIATE meets True MA 1260.0 | income tax 5711.97 0.0
    2025 ('SCOTLAND', 40000, 0, 5000, 5000) | earner band INTERMEDIATE meets True MA 1260.0 | income tax 6118.22 0.0
    2025 ('ENGLAND', 40000, 5000, 0, 5000) | earner band BASIC meets True MA 1260.0 | income tax 5627.75 0.0
    2025 ('SCOTLAND', 44000, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 6493.8 0.0
    2025 ('ENGLAND', 50270, 0, 0, 5000) | earner band HIGHER meets True MA 1260.0 | income tax 7288.0 0.0
    2025 ('ENGLAND', 50070, 0, 500, 5000) | earner band HIGHER meets True MA 1260.0 | income tax 7248.0 0.0
    2025 ('ENGLAND', 50070, 500, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 7500.0 0.0
    2025 ('ENGLAND', 50271, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 7540.4 0.0
    

    5b. Couples table at main 2da212ab

    /home/user/policyengine-uk/policyengine_uk/__init__.py
    2017 ('SCOTLAND', 40000, 5000, 0, 5000) | earner band BASIC meets True MA 1150.0 | income tax 5470.0 0.0
    2017 ('SCOTLAND', 40000, 0, 5000, 5000) | earner band BASIC meets True MA 1150.0 | income tax 6270.0 0.0
    2017 ('ENGLAND', 40000, 5000, 0, 5000) | earner band BASIC meets True MA 1150.0 | income tax 5470.0 0.0
    2017 ('SCOTLAND', 44000, 0, 0, 5000) | earner band BASIC meets True MA 1150.0 | income tax 6270.0 0.0
    2017 ('ENGLAND', 50270, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8808.0 0.0
    2017 ('ENGLAND', 50070, 0, 500, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8728.0 0.0
    2017 ('ENGLAND', 50070, 500, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8728.0 0.0
    2017 ('ENGLAND', 50271, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8808.4 0.0
    2018 ('SCOTLAND', 40000, 5000, 0, 5000) | earner band ADDITIONAL meets False MA 0.0 | income tax 5995.0 0.0
    2018 ('SCOTLAND', 40000, 0, 5000, 5000) | earner band ADDITIONAL meets False MA 0.0 | income tax 6570.0 0.0
    2018 ('ENGLAND', 40000, 5000, 0, 5000) | earner band BASIC meets True MA 1190.0 | income tax 5617.0 0.0
    2018 ('SCOTLAND', 44000, 0, 0, 5000) | earner band ADDITIONAL meets False MA 0.0 | income tax 6733.5 0.0
    2018 ('ENGLAND', 50270, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8468.0 0.0
    2018 ('ENGLAND', 50070, 0, 500, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8388.0 0.0
    2018 ('ENGLAND', 50070, 500, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8388.0 0.0
    2018 ('ENGLAND', 50271, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 8468.4 0.0
    2025 ('SCOTLAND', 40000, 5000, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 5976.57 0.0
    2025 ('SCOTLAND', 40000, 0, 5000, 5000) | earner band HIGHER meets False MA 0.0 | income tax 6382.82 0.0
    2025 ('ENGLAND', 40000, 5000, 0, 5000) | earner band BASIC meets True MA 1260.0 | income tax 5627.75 0.0
    2025 ('SCOTLAND', 44000, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 6493.8 0.0
    2025 ('ENGLAND', 50270, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 7540.0 0.0
    2025 ('ENGLAND', 50070, 0, 500, 5000) | earner band HIGHER meets False MA 0.0 | income tax 7500.0 0.0
    2025 ('ENGLAND', 50070, 500, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 7500.0 0.0
    2025 ('ENGLAND', 50271, 0, 0, 5000) | earner band HIGHER meets False MA 0.0 | income tax 7540.4 0.0
    

    6. ISSUE_2015_PARAMETERS at 2da212ab

    pay 42,386 {'income_tax': 6357.2, 'earned_income_tax': 6357.2, 'savings_income_tax': 0.0, 'savings_allowance': 1000.0, 'dividend_income_tax': 0.0}
    pay 40,600, interest 1,000 {'income_tax': 6000.0, 'earned_income_tax': 6000.0, 'savings_income_tax': 0.0, 'savings_allowance': 1000.0, 'dividend_income_tax': 0.0}
    

    7. ISSUE_SCOTTISH_2015_16 at 2da212ab

    2015 42200 SCOTLAND 31600.0 6340.0 True
    2015 42200 ENGLAND 31600.0 6320.0 True
    2016 43000 SCOTLAND 32000.0 6500.0 True
    2016 43000 ENGLAND 32000.0 6400.0 True
    

    8. ISSUE_SAVINGS_SLICES at 2da212ab

    37500 38000 {'savings_starter_rate_income': 37500.0, 'savings_allowance': 500.0, 'higher_rate_savings_income': 0.0, 'savings_income_tax': 0.0, 'meets_marriage_allowance_income_conditions': 0.0}
    40000 38000 {'savings_starter_rate_income': 37700.0, 'savings_allowance': 500.0, 'higher_rate_savings_income': 0.0, 'savings_income_tax': 0.0, 'meets_marriage_allowance_income_conditions': 0.0}
    

    Generated by Claude Code

  3. MaxGhenis commented on Nov 2, 2026

    @MaxGhenis
    Collaborator

    Picking this up for the UK hub. Reply here if you're mid-change and I'll stop.


    Generated by Claude Code

  4. MaxGhenis commented on Nov 2, 2026

    @MaxGhenis
    Collaborator

    PR_TITLE.txt

    Fix Scottish tax bands and test Marriage Allowance rates directly
    

    Generated by Claude Code

  5. MaxGhenis commented on Nov 2, 2026

    @MaxGhenis
    Collaborator

    PR_BODY.md

    Fixes #2350
    
    ## What and why
    
    `tax_band` compared a Scottish taxpayer's total taxable income, savings and dividends included, with the Scottish limits. Parliament applies Scottish rates to income other than savings and dividends under ITA 2007 ss. 11A and 13. The Scottish Parliament's 2025-26 resolution (S6M-16531) calls this "non-savings and non-dividend income". Parliament puts savings and dividends at the top of income (s. 16) and charges them at the rates they would bear outside Scotland (ss. 11D(6) and 13(5)). The model already calculated non-savings, savings and dividend liabilities separately.
    
    For a Scottish taxpayer, `tax_band` now takes the higher of two bands:
    
    1. the Scottish band of `non_savings_income_after_allowances`, their income other than savings and dividends after allowances, which go to it first, as in `earned_income_tax`;
    2. where the person has taxable savings or dividends, the rest-of-UK band of all taxable income (`net_income - allowances`), since Parliament puts savings and dividends at its top (s. 16).
    
    The model keeps income up to a limit in the lower band. Parliament charges income "up to" a limit at the lower rate (s. 10(2) to (3A)), and the Scottish Parliament charges each Scottish rate "on income above" one limit "and up to" the next. As before, the model raises the Scottish limits from the basic rate's upper limit, and the basic and higher rate limits, by the grossed-up Gift Aid gift, as Parliament requires (s. 414(2)(b)). The model retains nil-rate savings and dividends in their underlying bands. Parliament sets separate rate conditions for the Marriage Allowance (s. 55B).
    
    ### Marriage Allowance rate conditions
    
    `meets_marriage_allowance_income_conditions` no longer reads `tax_band`. It calls `liable_only_at_marriage_allowance_rates`, which applies s. 55B(2)(b) and (ba) to the person's income. A person fails it when any of these holds:
    
    - their income other than savings and dividends, after allowances, exceeds the basic rate limit, or for a Scottish taxpayer the start of the Scottish higher rate;
    - some savings above the basic rate limit lie outside the savings nil-rate slice. Savings up to the limit bear a basic rate or the starting rate for savings, both permitted, since Parliament charges the starting rate only on savings that would otherwise bear a basic rate (s. 12(1)). It charges the savings nil rate on the lowest savings above the starting rate limit for savings, up to the savings allowance (s. 12A(1) to (4)). Dividends sit above savings (s. 16) and don't move that slice;
    - their dividends, counted as if the dividend nil rate did not exist (s. 55B(2)(ba) omits s. 13A), take their income past the basic rate limit, where the dividend upper rate starts (ss. 10(3) and 13(2)).
    
    Under s. 414(2)(b), the model raises each limit by the grossed-up gift. Parliament and the Treasury set the permitted rates year by year:
    
    - 2015-16: the basic rate, the dividend ordinary rate and the starting rate for savings. The Scottish rates of s. 11A start in 2016-17, the year the Treasury appointed (FA 2014 Sch. 38 para. 12, SI 2015/2000 art. 3), so the test holds a Scottish taxpayer to the basic rate limit. Parliament gave the savings nil rate effect from 2016-17 (FA 2016 s. 4(17)), so the test counts no savings nil rate in 2015-16;
    - 2016-17: the Scottish basic rate (the Treasury's SI 2015/1810 art. 14(3)), and the savings and dividend nil rates with s. 55B(2)(ba) (FA 2016 ss. 4(8) and 5(6)). Parliament then charged the Scottish basic rate on the non-savings income that would otherwise bear the basic rate (s. 11A(1) as it stood), so the test again uses the basic rate limit;
    - 2017-18: the Scottish basic rate only, among the Scottish rates, so the test uses the start of the Scottish higher rate, £31,500 of taxable income;
    - from 2018-19: the Scottish starter and intermediate rates too (the Treasury's SI 2018/459 art. 6(2)), so the test uses the start of the Scottish higher rate.
    
    The model's Scottish rate schedule starts in April 2017, and the model reads its first values in earlier years too, so the test sets a Scottish taxpayer's limit by the year, not from that schedule. The test also departs from three historical or reformed parameters that the liability reads. It starts the dividend upper rate at the basic rate limit, where the model's 2015-16 dividend scale starts it at £37,500. It counts no savings nil rate in 2015-16, where the model's Personal Savings Allowance starts in 2005. And it places the savings nil rate above the starting rate limit, where the liability starts it after the income charged at the starting rate. The two agree unless a reform raises that limit above the basic rate limit or changes the starting rate's phase-out, which the test no longer reads.
    
    The model still takes the Marriage Allowance off `earned_taxable_income` (#1947), so `non_savings_taxable_income`, `higher_rate_savings_income`, `add_rate_savings_income` and `savings_starter_rate_income` all read `marriage_allowance`, and the test can't use them. It reads values from before the allowance instead. Parliament orders the calculation so that tax at each rate comes at Step 4 and tax reductions come off at Step 6 (s. 23), and it gives the allowance as a tax reduction (s. 55B(1)). The test takes `non_savings_income_after_allowances`, `taxable_savings_interest_income`, `received_allowances_savings_income`, `savings_allowance` (from 2016-17), the starting rate limit for savings, `taxable_dividend_income`, `received_allowances_dividend_income` and `gift_aid_grossed_up`.
    
    The direct test admits people at a permitted rate limit and people whose savings bear permitted nil rates:
    
    - taxable income exactly at the basic rate limit (or the Scottish higher rate's start, or a limit Gift Aid raised). Parliament charges it at the basic rate (s. 10(2)). `tax_band` outside Scotland still calls it `HIGHER`;
    - from 2016-17, people whose income passes the basic rate limit only by savings the savings nil rate covers. Parliament charges those savings at the savings nil rate (s. 12A(3)) and allows that rate for the Marriage Allowance (s. 55B(2)(b)). Under current limits no Scottish taxpayer reaches this case from 2017-18. The nil-rate slice starts at their non-savings income, so it reaches past the basic rate limit only when that income comes within a savings allowance of the limit, above the start of the Scottish higher rate.
    
    Dividends the dividend allowance covers still fail when, without it, they would bear the dividend upper rate. `tax_band` stays the same for everyone outside Scotland, but the Marriage Allowance conditions move there too. On a grid of 4,816 single adults (3,596 of them outside Scotland), run on `origin/main` and on this branch for each year from 2015-16 to 2028-29, 91 person-years outside Scotland move, all to meeting the conditions:
    
    - 90 have taxable income exactly at the basic rate limit, or at a limit Gift Aid raised, from 2019-20 on;
    - 1 has savings above the limit that the savings nil rate covers, in 2016-17.
    
    In Scotland 841 person-years move: 833 to meeting the conditions, and 8 in 2017-18 to failing them. Every adult's `income_tax` and `savings_allowance`, and every band outside Scotland, match `origin/main`; Scottish bands change in 3,339 person-years. These synthetic households give no population estimate, and the years after 2026-27 use the model's projections.
    
    The `create_expanded_ma_reform` CPS reform calls the same helper. It keeps its rule: people meet its conditions when they meet the statutory ones or the reform's expansion conditions. We ran 400 generated married couples, 187 of them with a child, in 2017-18, 2018-19, 2021-22, 2025-26 and 2026-27. The rule holds for every person-year, under this branch's direct test and under conditions read off this branch's `tax_band`. The statutory conditions move for 21 person-years, the reform's for 20, and the reform's conditions and allowance move for no one whose statutory conditions stay the same.
    
    This PR deletes `gov.hmrc.income_tax.allowances.marriage_allowance.eligible_bands`, which nothing else read. A reform or saved policy that names it now fails with directions to the rate limits (`REMOVED_PARAMETERS`).
    
    ### The rest-of-UK band needs savings or dividends
    
    The issue proposes the higher of the Scottish band of non-savings income and the rest-of-UK band of total taxable income. That second band measures the savings and dividends only when the person has some. Without them it lifts a Scottish starter-rate taxpayer to `BASIC`: in 2018-19, £12,570 of pay leaves £720 of taxable income, all at the 19% starter rate, and the proposal gives `BASIC`. The existing case `Scottish intermediate rate recognised` (2022-23, £14,650 of income, `STARTER`) fails under the proposal. This PR applies the rest-of-UK band only when the person has taxable savings or dividends.
    
    ### Other years
    
    The same band formula now runs from 2017-18. The model dispatches annual formulas at 1 January, so before this PR:
    
    - In 2017-18 the model ran the formula for earlier years and banded Scottish taxpayers on the rest-of-UK limits. The model labelled £44,000 of pay, £1,000 of it at the 40% Scottish higher rate, `BASIC`, and the earner met the Marriage Allowance conditions, though in 2017-18 Parliament allowed only the Scottish basic rate (s. 55B(2)(b)).
    - In 2018-19 it ran the 2017-18 formula, which read the first three of the five Scottish limits as the basic, higher and additional limits. So the model labelled every Scottish taxpayer with more than £12,150 of taxable income `ADDITIONAL`.
    
    ### Labels
    
    `TaxBand` has no advanced or top label, so `ADDITIONAL` covers the Scottish advanced and top rates and the savings and dividend additional rates. `HIGHER` covers the Scottish higher rate and the savings higher and dividend upper rates. `STARTER` and `INTERMEDIATE` come from the Scottish band only.
    
    ### What reads `tax_band`, and what moves
    
    - `meets_marriage_allowance_income_conditions` and the CPS reform no longer read it.
    - `marriage_allowance` and `income_tax` move for married couples whose earner now meets, or no longer meets, the conditions. For an earner whose spouse earns £5,000, run on this branch and on `origin/main`:
    
      | Year and earner's income | Earner's band | Marriage Allowance | Earner's income tax |
      |---|---|---|---|
      | 2025-26, Scotland: £40,000 pay, £5,000 dividends | `HIGHER` → `INTERMEDIATE` | £0 → £1,260 | £5,976.57 → £5,711.97 |
      | 2025-26, Scotland: £40,000 pay, £5,000 savings | `HIGHER` → `INTERMEDIATE` | £0 → £1,260 | £6,382.82 → £6,118.22 |
      | 2018-19, Scotland: £40,000 pay, £5,000 dividends | `ADDITIONAL` → `INTERMEDIATE` | £0 → £1,190 | £5,995.00 → £5,745.10 |
      | 2017-18, Scotland: £44,000 pay | `BASIC` → `HIGHER` | £1,150 → £0 | £6,270.00 → £6,700.00 |
      | 2025-26, England: £50,270 pay | `HIGHER` | £0 → £1,260 | £7,540.00 → £7,288.00 |
      | 2025-26, England: £50,070 pay, £500 savings | `HIGHER` | £0 → £1,260 | £7,500.00 → £7,248.00 |
      | 2025-26, England: £50,070 pay, £500 dividends | `HIGHER` | £0 | £7,500.00 |
      | 2025-26, England: £40,000 pay, £5,000 dividends | `BASIC` | £1,260 | £5,627.75 |
    
      The spouse pays no income tax in any row. The model deducts the Marriage Allowance from earned income, so it saves the earner 21% of it in the first three rows (£264.60 and £249.90) and 20% in the English rows (£252). In 2017-18 it saves £430, since it takes £1,000 out of the 40% band and £150 out of the 20% band. Parliament gives a tax reduction of the basic rate, or the Scottish basic rate, on the transferable amount (s. 55B(1) and (3)): £252 in 2025-26. #1947 tracks that, and #1963 fixes it. These synthetic households give no population estimate.
    - Counts of taxpayers by marginal rate (PolicyEngine/microcosm#1198 binds HMRC's Scottish higher and additional rate counts on `tax_band`): Scottish taxpayers move to the band their income reaches. In the 4,816-adult grid, 3,129 of the 3,339 Scottish band changes move down; the grid gives no population shares. The other 210 move up: 8 Scottish taxpayers in 2017-18 above the Scottish higher-rate limit, and 202 starter-rate taxpayers with some savings or dividends, whom the model now bands `BASIC`. The model charges savings that low at the 0% starting rate for savings and such dividends at the nil or ordinary rate, and the band counts nil-rate income where it sits.
    - `savings_allowance` does not read `tax_band`. Since the fix for #2347 it sets the Personal Savings Allowance on the rest-of-UK bands itself (s. 12B(8)), and its documentation now says so. This PR does not move it: `test_savings_allowance_does_not_read_the_band` forces every band as an input and the allowance stays the same, and the 4,816-adult differential above finds no change in it.
    
    Microcosm's engine test `test_scottish_savings_and_dividends_count_against_the_scottish_thresholds` pins the issue's first case (Scotland, £40,000 of pay and £5,000 of dividends, `HIGHER`). With this change the model gives `INTERMEDIATE`, so that test fails until Microcosm updates it.
    
    ### Existing tests changed
    
    - The eleventh row (index 10) of the `test_gift_aid.py` bit-for-bit snapshot, a Scottish taxpayer in 2026-27 with no pay, £10,000 of property income, £20,000 of savings and £10,000 of dividends, moves from `INTERMEDIATE` to `BASIC`. The model sets the allowance against all the property income, so no income bears a Scottish rate (s. 11A), and the remaining £27,430 of savings and dividends falls below the £37,700 UK basic rate limit (ss. 11D(6) and 13(5)). Its other eleven rows and every other snapshot value stay the same.
    - On `origin/main`, the six cases in `meets_marriage_allowance_income_conditions.yaml` set `tax_band` as an input. They now set the income that puts a person in each band; every expected value stays the same.
    - `test_marriage_allowance_cps_switch.py`'s invariant 3 read `eligible_bands`. It now checks that no one with taxable income above the basic rate limit, or above £31,092 (the start of the Scottish higher rate) for a Scottish taxpayer, meets the conditions or has the allowance, and that everyone else meets them.
    
    ### Known limits, unchanged here
    
    - Outside Scotland, from 2018-19 `tax_band` puts taxable income exactly at a limit in the band above, and pay equal to the personal allowance in `BASIC`, where Parliament charges income up to a limit at the lower rate (s. 10). #1963 pins the first at £50,270 of pay. The Marriage Allowance no longer reads the band, so this affects counts by marginal rate only. #2782 tracks it.
    - The model tests only the receiving spouse. Parliament also bars an election by a spouse who, with their allowance cut by the transferable amount, would be liable above the permitted rates (s. 55C(1)(c)); #1947 tracks this and #1963 models it.
    - For 2018-19 the model's Scottish higher-rate limit and rate (£31,500 of taxable income and 40%, which the model keeps until 2021-22) differ from the Scottish Parliament's 2018-19 resolution (£31,580 and 41%, S5M-10397). #2783 corrects the historical Scottish rates and limits; this branch's cases sit clear of the changed limit.
    - The code tells the Scottish schedules apart by their number of limits (three in 2017-18), as `first_extended_scottish_threshold` already does, so a reform that changes the number of Scottish brackets gets wrong labels.
    - The model's Scottish rate schedule starts in April 2017, and the model reads its first values in 2015-16 and 2016-17 too. So `earned_income_tax` charges a Scottish taxpayer 40% above £31,500 of taxable pay in those years, where Parliament taxed that pay on the rest-of-UK limits. In 2015-16, £42,200 of pay leaves £31,600 of taxable pay, below the statutory £31,785 limit, and the model charges a Scottish taxpayer £6,340 against £6,320 in England. In 2016-17, £43,000 of pay leaves £32,000, the basic rate limit Parliament set for 2016-17 (FA 2015 s. 4(1)(a), as Finance (No. 2) Act 2015 s. 6(a) amended it), and the model charges £6,500 against £6,400. The Marriage Allowance test uses the basic rate limit in those years. A Scottish taxpayer can therefore meet this income test while the model charges some of their income at 40% in 2015-16 or 2016-17. The property test leaves those Scottish taxpayers out (<Scottish 2015-16 issue>).
    - Parliament set the basic rate limit at £31,785 for 2015-16 (s. 10(5)); the model uses £32,000. In 2015-16 the model's dividend scale also starts the dividend upper rate at £37,500, and its Personal Savings Allowance applies from 2005. The model's liability keeps these values, and the Marriage Allowance test follows the statute instead (<2015 parameters issue>).
    - When a reform raises the starting rate limit for savings above the basic rate limit, the model's savings liability starts the savings nil rate where the starting rate ends, and Parliament starts it at the limit (s. 12A). Under a £40,000 limit, with £12,570 of pay and £38,000 of interest, the test fails the conditions. The model charges no savings tax there, where Parliament charges £120 on the £300 above the basic rate limit (ss. 11D and 12A) (<savings slices issue>).
    
    ### Overlap
    
    #1963 (draft) turns the Marriage Allowance into a Step 6 tax reduction. Both branches define `liable_only_at_marriage_allowance_rates` in the same file. This branch reads income before the allowance. #1963 also deletes `eligible_bands.yaml`, and its `test_taxable_income_at_the_basic_rate_limit_still_meets_the_conditions` pins the same reading at £50,270 of pay. Against `origin/main` (2da212abc) and `origin/fix-2783-scottish-rates` (9aa5ee4c9), git merges this branch cleanly. Against #1963's head (69c919f6), git conflicts in that function and its variable, the CPS reform, `meets_marriage_allowance_income_conditions.yaml` and `test_marriage_allowance_cps_switch.py`. #1963 also conflicts with `origin/main` itself in `income_tax_subtractions.yaml`, `uses_property_allowance.py` and `property_finance_cost_relief.py`. #2670 also edits `create_expanded_ma_reform` (its child condition), and git merges that file cleanly with this branch; #2670 conflicts with `origin/main` in `policyengine_uk/simulation.py`, which this branch doesn't touch. No other open PR touches these files.
    
    On 1,000 generated households in all four nations (607 married couples and 393 single adults), run for each year from 2015-16 to 2028-29, this branch's test and #1963's at 69c919f6 agree in 22,497 of 22,498 person-years, couples included. In the one difference, in 2016-17, a single Scottish taxpayer with £20,519 of pay, £22,267 of property income and £1 of interest has £31,786 of taxable income other than savings and dividends. This branch holds them to the £32,000 basic rate limit and finds the conditions met, where #1963 uses the £31,500 that the model's Scottish schedule shows in that year.
    
    #### Merge notes
    
    - When #1963 rebases onto this branch, it keeps this branch's helper, both callers, the removed-parameter migration and the regression cases. The helper carries the pre-2017-18 year rule for Scottish taxpayers, the strict comparisons at each limit, the Gift Aid extensions, the dividend test on the basic rate limit, and the savings nil rate from 2016-17 placed above the starting rate limit. #1963 brings the reduced allowance for the electing spouse, the full statutory transfer and the capped Step 6 tax reduction, and drops the earned-income deduction.
    - The helper's docstring on this branch replaces the two paragraphs it shared with #1963. #1963 should take this wording:
    
      > Apply the rates Parliament permits for the tax year (s. 55B(2)(b) and s. 55C(1)(c)). From 2016-17 these include the savings nil rate; from 2018-19 the Scottish list includes rates below basic and the intermediate rate. Where s. 55B(2)(ba) and s. 55C(1)(ca) apply, test dividends as if s. 13A were omitted.
      >
      > Parliament extends the rate limits by the grossed-up Gift Aid gift (s. 414(2)(b)); the gift does not reduce taxable income. Before 2017-18 this test uses the basic rate limit for Scottish taxpayers, even though the model's historical Scottish liability currently reads the 2017-18 schedule. The test also follows the statute where the model's liability departs from it: it starts the dividend upper rate at the basic rate limit (ss. 10 and 13), counts no savings nil rate before 2016-17 (FA 2016 s. 4(17)), and places the nil-rate slice above the starting rate limit for savings (s. 12A).
    - `non_savings_income_after_allowances`' documentation on this branch reads: "The model subtracts savings income, dividend income and allowances from net income and floors the result at zero, so it sets allowances against this income first. Parliament treats savings and dividends as the highest part of income, above this income (ITA 2007 s. 16). It raises the rate limits by the grossed-up Gift Aid gift (s. 414(2)(b)), which does not reduce this taxable income." The #2106 bullet below says how to merge it.
    - #2106 (`fix-2106-allowance-order`, `ea880ff76`) merges with this branch with one textual conflict, in `non_savings_income_after_allowances.py`: take #2106's file and add this branch's readers to its documentation ("the Marriage Allowance rate test and, for Scottish taxpayers, tax_band read it"). Its least-tax search leaves `marriage_allowance` out of its inputs, so the merge computes `income_tax` with no cycle, and it moves allowance onto savings where that lowers tax (s. 25(2)), so the pound-of-savings case uses pay at the basic rate limit to stay at 40p under either allocation.
    
    ## Invariants
    
    The property tests assert these on generated cases:
    
    1. With the starting rate for savings, the Personal Savings Allowance and the dividend allowance set to zero, every single Scottish taxpayer pays tax at their band's rates on some income and at no higher band's rates; with no band they pay no tax.
    2. With the baseline nil-rate amounts, no single Scottish taxpayer pays tax at the higher or additional rates above their band.
    3. With the nil-rate amounts set to zero, the Marriage Allowance income conditions hold for a single Scottish taxpayer exactly when they pay nothing at the higher or additional rates.
    4. With the baseline nil-rate amounts and with reformed limits and amounts, the conditions hold for every single person in England, and in Scotland from 2017-18, exactly when the liability charges no income at a rate s. 55B(2)(b) forbids and, without the dividend allowance, no dividends at the dividend upper or additional rate. The reforms change the start of the Scottish higher rate, the basic rate limit (with the dividend upper rate moved to it, where Parliament starts it (s. 13(2))), the starting rate limit for savings up to the basic rate limit, the Personal Savings Allowance or the dividend allowance. So savings at a nil rate never stop the allowance.
    5. From 2019-20, the band never rises against the formula before this PR, except from `STARTER` to `BASIC` where savings or dividends sit below the Scottish starter limit: they sit in the rest-of-UK basic band.
    6. With the CPS expanded reform on, its conditions equal the statutory conditions or its expansion conditions.
    
    For everyone outside Scotland, `tax_band` stays the same. For single people anywhere, `income_tax` and `savings_allowance` stay the same.
    
    Invariant 4 compares the test with the model's liability, so its reforms hold the starting rate limit at or below the basic rate limit, the range in which the model's liability places the savings nil rate where Parliament does (s. 12A). It also leaves out Scottish taxpayers before 2017-18, and its years start in 2016-17. YAML cases worked from the statute cover those: the 2015-16 dividend and savings cases at the model's £32,000 limit and at the statutory £31,785, and the two cases under a raised starting rate limit.
    
    ## Test plan
    
    - [x] YAML tests whose expected values come from an external source: new cases in `scottish_rates.yaml` and `meets_marriage_allowance_income_conditions.yaml`, which we worked by hand from ITA 2007, FA 2016, SI 2015/1810, SI 2018/459 and the Scottish rate resolutions. Both files pass: 36 and 31 cases.
    - [x] Scoped Ruff format and lint checks, the repository F811/F821 check, and `git diff --check`.
    - [x] Changelog fragment `changelog.d/2350.fixed.md`.
    
    Results, run locally with no survey data:
    
    - `scottish_rates.yaml`: 36 passed.
    - `meets_marriage_allowance_income_conditions.yaml`: 31 passed.
    - We ran `liable_only_at_marriage_allowance_rates` on 200,000 generated sets of non-savings, savings and dividend income, basic rate limits, starting rate limits, savings allowances, Gift Aid, years and nations. It agrees in every case with a hand-worked oracle of ss. 10, 12, 12A and 13 and FA 2016 s. 4(17).
    - HMRC income tax YAML directory (`policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/`): 497 passed.
    - `marriage_tax_reforms.yaml`: 10 passed.
    - `integration/income.yaml`: 8 passed.
    - `test_tax_band_scottish_properties.py`: 5 passed.
    - `test_marriage_allowance_cps_switch.py`: 45 passed, in four `-k` shards (40, 3, 1 and 1).
    
    One-line mutations of the Marriage Allowance test and of `tax_band`, against both YAML files:
    
    | Mutation | Marriage Allowance YAML (31 cases) | `scottish_rates.yaml` (36 cases) | Cases that fail |
    |---|---|---|---|
    | Dividend upper rate read from the model's dividend scale | 2 fail | all pass | the two 2015-16 dividend cases |
    | Savings nil rate counted in 2015-16 | 2 fail | all pass | the two 2015-16 savings cases |
    | Savings nil-rate slice starts at non-savings income, not at the starting rate limit | 1 fails | all pass | the £37,500 starting rate limit case |
    | Scottish limit before 2017-18 read off the 2017-18 schedule | 2 fail | all pass | the 2015-16 and 2016-17 Scottish cases |
    | Savings test on all taxable income instead of non-savings income and savings | all pass | all pass | none (see below) |
    | `tax_band` without the Gift Aid rise in the UK limits | all pass | 1 fails | Scottish 2025 dividends at the Gift Aid limit |
    | `tax_band` puts income at the UK additional limit above it | all pass | 1 fails | Scottish 2025 savings at the higher rate limit |
    | `tax_band` reads `earned_taxable_income` | all pass | 2 fail | both property-income cases at the higher rate limit |
    
    Under the £5,000 starting rate limit, a slice that starts at non-savings income and one that starts at the larger of it and the limit give the same answer: where they differ, both end below the basic rate limit. So only a raised starting rate limit separates the slice mutation, and the YAML case under a £37,500 limit does. The savings-test mutation changes the savings result only where dividends sit above the savings and take income past the basic rate limit. There the dividend test already fails the person. So no input whose net income equals the sum of its non-savings, savings and dividend income separates the mutation at the helper's output, and the 200,000-case differential above finds no difference with it either. A supplied `adjusted_net_income` below the income components breaks that equality and separates them.
    
    Reproducer: `policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/allowances/meets_marriage_allowance_income_conditions.yaml::Under a raised Scottish higher rate, nil-rate savings meet the conditions`; `policyengine_uk/tests/test_tax_band_scottish_properties.py::test_marriage_allowance_follows_the_liability_with_nil_rates`
    
    axiom: <rulespec-uk issue> queued
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    

    Generated by Claude Code

  6. MaxGhenis commented on Nov 2, 2026

    @MaxGhenis
    Collaborator

    AXIOM_ISSUE.md

    Title: Derive the s. 55B(2)(b) and (ba) rate conditions from income, with Scottish non-savings income on the Scottish limits
    
    Labels: pe-parity
    
    PolicyEngine UK now applies the Marriage Allowance rate conditions to income directly, in PolicyEngine/policyengine-uk#2350 (PR: <PE PR>). The income conditions no longer read `tax_band`. A person meets them when none of these holds:
    
    - their income other than savings and dividends, after allowances, exceeds the basic rate limit, or for a Scottish taxpayer the start of the Scottish higher rate (ITA 2007 ss. 10 and 11A);
    - some savings above the basic rate limit lie outside the savings nil-rate slice. Parliament keeps the starting rate for savings within the basic rate band (s. 12) and charges the savings nil rate on the lowest savings above the starting rate limit for savings, up to the savings allowance (s. 12A);
    - their dividends, counted as if the dividend nil rate (s. 13A) did not exist, take their income past the basic rate limit (s. 55B(2)(ba)).
    
    Savings sit above non-savings income and dividends above savings (s. 16), so dividends never fill the savings bands. Savings and dividends take the rest-of-UK limits for a Scottish taxpayer too (ss. 11D(6) and 13(5)). PolicyEngine raises each limit by the grossed-up Gift Aid gift (s. 414(2)(b)). PolicyEngine now also bands a Scottish taxpayer's savings and dividends on the rest-of-UK limits in `tax_band`.
    
    rulespec-uk does not yet derive this. On `main` (`0497350`):
    
    - `55B.yaml` does not compute the rate condition from income: `uk/statutes/ukpga/2007/3/55B.yaml` takes `individual_not_liable_above_basic_rate` as a case input.
    - `uk/statutes/income_tax/individual/scottish_income_tax_oracle_pipeline.yaml` charges a Scottish taxpayer's earned income on the 2026-27 Scottish limits, and states that it leaves savings and dividends out.
    - No module encodes s. 11A itself; `11D.yaml` and `13.yaml` exist.
    
    ## Blocker
    
    Max must approve an OpenAI-billed signed-encoder run. Axiom's workflow also requires approval in its production-signing environment.
    
    rulespec-uk `main` (`0497350`) pins release `uk-rulespec-2026-10-25-pe-child-definitions` (content sha256 `76b42156de23f2c000dab657dafacfb5b96ec9fe5670a75563574bd00793048c`, axiom-corpus commit `9d174d88119fd6e6ace02d7c94eec7bb47e363f0`). The locked provision files cover the statutes and resolution below. The release lacks s. 414 and earlier Scottish resolutions.
    
    - It holds rows for ITA 2007 ss. 6, 8, 10, 11A, 11D, 12, 12A, 12B, 13, 13A, 16, 23, 35, 55B, 55C and 58, Scotland Act 1998 s. 80C, and the 2026-27 Scottish rate resolution, `uk/guidance/scottish-government/scottish-rate-resolution-2026-27/draft-motion/block-1`.
    - It holds no row for ITA 2007 s. 414, so the encoder cannot cite the Gift Aid rise in the Scottish, basic rate and higher rate limits (s. 414(2)(b)) until Axiom ingests it and pins a release that holds it.
    - It holds no row for ITA 2007 s. 25, under which Parliament deducts reliefs and allowances at Steps 2 and 3 "in the way which will result in the greatest reduction in the taxpayer's liability to income tax" (s. 25(2)).
    - It holds no Scottish rate resolution before 2026-27, so the encoding covers 2026-27 only.
    
    ## Provisions
    
    | Proposed module path | Corpus citation | What it adds |
    |---|---|---|
    | `uk/statutes/ukpga/2007/3/11A.yaml` (new) | `uk/statute/ukpga/2007/3/11A`, with `uk/guidance/scottish-government/scottish-rate-resolution-2026-27/draft-motion/block-1` | Scottish non-savings, non-dividend income charged at each named Scottish rate, and the amount at rates s. 55B permits |
    | `uk/statutes/ukpga/2007/3/55B.yaml` (existing) | `uk/statute/ukpga/2007/3/55B`, with `uk/statute/ukpga/2007/3/10`, `11D`, `12`, `12A`, `12B`, `13`, `13A` and `16` | Derive `individual_not_liable_above_basic_rate` from income |
    | Step 3 income slices (deferred inputs, or co-encoded) | `uk/statute/ukpga/2007/3/23`, `35` and `58`; s. 25 has no row in the pinned release | Non-savings, savings and dividend income after ordinary Step 3 allowances, before the Marriage Allowance |
    | Gift Aid extension (deferred input) | none in the pinned release: ITA 2007 s. 414 | The UK and Scottish limits as s. 414(2)(b) extends them |
    
    We propose these paths as targets; the encoder binds the source expressions.
    
    ### Source and deferred-input plan
    
    The initial encoding covers 2026-27 and one taxpayer with zero Gift Aid.
    
    - It takes the non-savings, savings and dividend amounts after ordinary Step 3 allowances as deferred inputs with exactly that scope. Grounding the allowance amounts and their allocation instead needs ss. 23, 25, 35 and 58, and so an ingested and pinned s. 25. Parliament gives the Marriage Allowance as a tax reduction (s. 55B(1)) and takes tax reductions off at Step 6 (s. 23), so it must not reduce these inputs.
    - It declares the extended UK and Scottish limits under s. 414(2)(b) as deferred inputs, and the companion cases set Gift Aid to zero, until Axiom ingests and pins s. 414.
    - It reuses an existing module only if the signed encoder can import its manifest; otherwise it co-encodes the provision or declares the dependency as deferred.
    
    ### The law
    
    The following passages quote the pinned 2026-27 provisions and corpus resolution. The pinned rows for ss. 11A, 12A and 16 carry no subsection numbers, so the labels follow legislation.gov.uk's text of the same provisions.
    
    - ITA 2007 s. 11A(1A): "Income tax is charged at Scottish rates on the non-savings income of a Scottish taxpayer." (4): "For the purposes of this section, “non-savings income” means income which is not savings income." (5): "This section is subject to— section 13 (income charged at the dividend ordinary, upper and additional rates: individuals), and any provisions of the Income Tax Acts (apart from section 10) which provide for income of an individual to be charged at different rates of income tax in some circumstances." (6): "Section 16 has effect for determining which part of a Scottish taxpayer’s income consists of savings income."
    - s. 13(1): "Income tax is charged at the dividend ordinary rate on an individual's income which— (a) is dividend income, (b) would otherwise be charged at the ... basic rate, ... or the Welsh basic rate". (2): the dividend upper rate on dividend income which "would otherwise be charged at the higher rate". (5): "In relation to an individual who is a Scottish taxpayer, references in this section to income that would otherwise be charged at a particular rate are to be read as references to income that would, if the individual were not a Scottish taxpayer, be charged at that rate (and subsection (4) is to be read accordingly)."
    - s. 11D(1) and (2): the savings basic and higher rates on savings income which "would otherwise be charged at the basic rate or the default basic rate" or "at the higher rate or the default higher rate". (6): "In relation to an individual who is a Scottish taxpayer, references in this section to income which would otherwise be charged at a particular rate are to be read as references to income that would, if the individual were not a Scottish taxpayer (but were UK resident), be charged at that rate".
    - s. 16(5): "the savings income and dividend income are together treated as the highest part of the person's total income, and the dividend income is treated as the higher part of that part of the person's total income".
    - s. 10(2): "Income tax on an individual's income up to the basic rate limit is charged at the basic rate". (3): "Income tax is charged at the higher rate on an individual's income above the basic rate limit and up to the higher rate limit." (5): "The basic rate limit is £37,700".
    - s. 12(1): "Income tax is charged at the starting rate for savings ... on so much of an individual's income up to the starting rate limit for savings as— (a) is savings income, and (b) would otherwise be charged at the basic rate or the default basic rate". (3): "The starting rate limit for savings is £5,000".
    - s. 12A(1): "This section applies in relation to an individual if— (a) the amount of the individual's Step 3 income is greater than £L, where £L is the amount of the starting rate limit for savings, and (b) when the individual's Step 3 income is split into two parts— (i) one (“the individual's income up to the starting rate for savings”) consisting of the lowest £L of the individual's Step 3 income, and (ii) the other (“the individual's income above the starting rate limit for savings”) consisting of the rest of the individual's Step 3 income, some or all of the individual's income above the starting rate limit for savings consists of savings income (whether or not some or all of the individual's income up to the starting rate limit for savings consists of savings income)." (2): "In this section— £A is the amount of the individual's savings allowance (see section 12B), “the excess” is so much of the individual's income above the starting rate limit for savings as consists of savings income, and £X is the amount of the excess."
    - s. 12A(3): "If £X is less than or equal to £A, income tax is charged at the savings nil rate (rather than the basic, higher or additional rate or the default basic, default higher or default additional rate) on the excess." (4): "If £X is more than £A, income tax is charged at the savings nil rate ... on the lowest £A of the excess." (7): "For the purposes of this section, an individual's “Step 3 income” is the individual's net income less allowances deducted at Step 3 of the calculation in section 23."
    - s. 12B(3): "If— (a) any of the individual's income for the year is higher-rate income, and (b) none of the individual's income for the year is additional-rate income, the individual's savings allowance for the year is £500." (8)(b)(ii) counts as higher-rate income "income on which income tax would be charged at the higher rate , or default higher rate, but for section 12A".
    - s. 13A(2), Step 1, Rule 1A: "If D is more than £500, the first £500 of D is charged at the dividend nil rate (rather than the dividend ordinary rate)".
    - s. 55B(2)(b): "the individual is not, for the tax year, liable to tax at a rate other than the basic rate, the default basic rate, the savings basic rate, the dividend nil rate, the Scottish basic rate, a Scottish rate below the Scottish basic rate, the Scottish intermediate rate, the Welsh basic rate, the dividend ordinary rate , the savings nil rate or the starting rate for savings". (ba): "if for the tax year the individual is liable to tax at the dividend nil rate, the individual would for that year neither be liable to tax at the dividend upper rate, nor be liable to tax at the dividend additional rate, if section 13A (dividend nil rate) were omitted". Parliament adds the property basic rate and the Welsh property basic rate to s. 55B(2)(b) for 2027-28 and later years (Finance Act 2026 s. 6(8) and Sch. 1 para. 19); they stay out of the 2026-27 rule.
    - Scottish rate resolution 2026-27 (S6M-20844): "for the purposes of section 11A of the Income Tax Act 2007 (which provides for Income Tax to be charged at Scottish rates on certain non-savings and non-dividend income of a Scottish taxpayer to be charged above the personal allowance), the Scottish rates and limits for the tax year 2026-27 are as follows— (a) a starter rate of 19%, charged on income up to a limit of £3,967, (b) the Scottish basic rate is 20%, charged on income above £3,967 and up to a limit of £16,956 (c) an intermediate rate of 21%, charged on income above £16,956 and up to a limit of £31,092, (d) a higher rate of 42%, charged on income above £31,092 and up to a limit of £62,430, (e) an advanced rate of 45%, charged on income above £62,430 and up to a limit of £125,140, and (f) a top rate of 48%, charged on income above £125,140."
    
    ## Outputs and companion cases
    
    ### Required outputs
    
    For one taxpayer in 2026-27 with zero Gift Aid, from the non-savings, savings and dividend income left after ordinary Step 3 allowances:
    
    - a Money output for each named Scottish rate: the Scottish non-savings, non-dividend income charged at that rate under s. 11A and the resolution;
    - `non_savings_income_above_limit`: non-savings income exceeds £37,700, or £31,092 for a Scottish taxpayer;
    - `savings_above_basic_rate_limit_at_a_positive_rate`: some savings bear the savings higher or additional rate. Count from the top of non-savings income and stop before dividends. Parliament charges the starting rate only within both the starting rate limit and the basic rate band (s. 12), and the savings nil rate on the lowest £A of savings above the starting rate limit (s. 12A);
    - `dividends_above_basic_rate_limit_without_nil_rate`: some dividends remain, and non-savings income, savings and dividends together, with dividends the dividend nil rate covers counted where they sit, exceed £37,700 (s. 55B(2)(ba));
    - `individual_not_liable_above_basic_rate`: none of the three Judgments holds.
    
    ### Companion tests
    
    The companion cases use 2026-27 law for a taxpayer aged 40 with a £12,570 personal allowance and zero Gift Aid, with values from S6M-20844 and ITA 2007 ss. 10(5), 12(3), 12B, 13A and 35(1). They match the cases in `scottish_rates.yaml` and `meets_marriage_allowance_income_conditions.yaml`. The three Judgment columns give `non_savings_income_above_limit`, `savings_above_basic_rate_limit_at_a_positive_rate` and `dividends_above_basic_rate_limit_without_nil_rate`.
    
    | Case | Taxpayer | Pay | Savings | Dividends | Non-savings taxable | All taxable | Non-savings | Savings | Dividends | `individual_not_liable_above_basic_rate` |
    |---|---|---|---|---|---|---|---|---|---|---|
    | Intermediate pay, dividends below the basic rate limit | Scottish | £40,000 | 0 | £5,000 | £27,430 | £32,430 | false | false | false | true |
    | Higher-rate pay and savings | Scottish | £60,000 | £20,000 | 0 | £47,430 | £67,430 | true | true | false | false |
    | Both limits exactly | Scottish | £43,662 | 0 | £6,608 | £31,092 | £37,700 | false | false | false | true |
    | £1 of dividends above the basic rate limit | Scottish | £43,662 | 0 | £6,609 | £31,092 | £37,701 | false | false | true | false |
    | Allowance beyond pay goes to dividends | Scottish | £5,000 | 0 | £30,000 | £0 | £22,430 | false | false | false | true |
    | Pay at the start of the Scottish higher rate | Scottish | £43,662 | 0 | 0 | £31,092 | £31,092 | false | false | false | true |
    | £1 of pay into the Scottish higher rate | Scottish | £43,663 | 0 | 0 | £31,093 | £31,093 | true | false | false | false |
    | Savings nil rate above the basic rate limit | rest of UK | £50,070 | £500 | 0 | £37,500 | £38,000 | false | false | false | true |
    | £1 of savings at the savings higher rate | rest of UK | £50,270 | £501 | 0 | £37,700 | £38,201 | false | true | false | false |
    | Pay at the basic rate limit | rest of UK | £50,270 | 0 | 0 | £37,700 | £37,700 | false | false | false | true |
    | Dividend nil rate above the basic rate limit | rest of UK | £50,070 | 0 | £500 | £37,500 | £38,000 | false | false | true | false |
    | Savings below the basic rate limit, dividends above it | rest of UK | £40,000 | £1,000 | £10,000 | £27,430 | £38,430 | false | false | true | false |
    
    In the fifth case the allowance takes all £5,000 of pay and then £7,570 of dividends, so no income bears a Scottish rate and £22,430 of dividends sit below £37,700. In the eighth, Parliament cuts the savings allowance to £500 because income exceeds £37,700 (s. 12B(3)) and charges all £500 of interest at the savings nil rate, since it lies above the £5,000 starting rate limit (s. 12A(3)). In the eleventh, Parliament charges all £500 of dividends at the dividend nil rate (s. 13A); without s. 13A, £300 of them would bear the dividend upper rate. In the ninth, pay sits at the basic rate limit, so no other share of the allowance lowers the tax (s. 25(2)): the savings allowance covers £500 of the £501 of interest and the last pound bears the savings higher rate. In the last, savings stop at £28,430, below £37,700, though all taxable income exceeds it; the dividends take the income past the limit.
    
    These three scenarios reform parameters; the baseline law stays the pinned 2026-27 schedule:
    
    | Scenario | Taxpayer | Pay | Savings | Starting rate for savings | Savings nil rate | Savings at the savings higher rate | `individual_not_liable_above_basic_rate` |
    |---|---|---|---|---|---|---|---|
    | Starting rate limit £37,500 | rest of UK | £12,570 | £38,000 | £37,500 | £500 | 0 | true |
    | Starting rate limit £40,000 | rest of UK | £12,570 | £38,000 | £37,700 | 0 | £300 | false |
    | Scottish higher rate from £37,702 | Scottish | £50,270 | £1 | 0 | £1 | 0 | true |
    
    Under a £40,000 limit, Parliament stops the starting rate at the £37,700 basic rate limit (s. 12(1)(b)) and charges nothing at the savings nil rate, since Step 3 income of £38,000 never exceeds £L (s. 12A). The £500 savings allowance amount (s. 12B(3)) stays distinct from the income Parliament charges at the savings nil rate. In the Scottish scenario pay leaves £37,700 of non-savings taxable income, below the reformed £37,702 start of the Scottish higher rate, and Parliament charges the £1 of interest at the savings nil rate (s. 12A).
    
    ### review_finding (paste as-is)
    
    > Encode ITA 2007 s. 11A as `uk/statutes/ukpga/2007/3/11A.yaml` (corpus `uk/statute/ukpga/2007/3/11A`), with the 2026–27 limits from `uk/guidance/scottish-government/scottish-rate-resolution-2026-27/draft-motion/block-1`. Then re-encode `uk/statutes/ukpga/2007/3/55B.yaml` (corpus `uk/statute/ukpga/2007/3/55B`) to derive its opaque input `individual_not_liable_above_basic_rate` from the income charged at each permitted or forbidden rate. Entity `Person`, period `Year`, initial tax year **2026–27 only**. Preserve s. 55B's other entitlement conditions. The output is the rate condition, not a spouse election or a generic tax-band enum. Do not take PolicyEngine `tax_band`, a supplied “highest rate” or a supplied eligibility boolean as evidence of the law.
    >
    > Required outputs, each with verbatim proof excerpts from the cited corpus provision:
    >
    > 1. **Money**, GBP: Scottish non-savings, non-dividend Step 3 income charged at each named Scottish rate, under s. 11A and the resolution. The Scottish basic rate, rates below it and the Scottish intermediate rate are permitted; the Scottish higher, advanced and top rates are forbidden by s. 55B(2)(b).
    > 2. **Judgment**, `non_savings_income_above_limit`: whether any non-savings income after ordinary Step 3 allowances reaches a rate that s. 55B(2)(b) forbids. Income exactly at the permitted upper limit passes. Use the UK basic rate limit for non-Scottish taxpayers and the end of the Scottish intermediate band for Scottish taxpayers: £37,700 and £31,092 respectively under the 2026–27 parameters, before any statutory extension.
    > 3. **Judgment**, `savings_above_basic_rate_limit_at_a_positive_rate`: whether any savings income is charged at the savings higher or additional rate after ss. 12, 12A, 12B and 11D. Savings sit above non-savings income and below dividends under s. 16; dividends do not fill the savings bands first. Section 12 permits only savings within the starting-rate limit that would otherwise bear the basic or default basic rate. Under s. 12A(1)–(4), the savings nil-rate slice is the lowest savings income **above the starting-rate limit**, not simply the next pounds after the amount actually charged at the starting rate. A savings allowance amount does not itself establish that any savings qualify for the nil rate.
    > 4. **Judgment**, `dividends_above_basic_rate_limit_without_nil_rate`: whether dividends after ordinary Step 3 allowances, counted before s. 13A, reach the dividend upper or additional rate under s. 13 with s. 13A omitted, as s. 55B(2)(ba) requires. Stack dividends above non-savings and savings. Use the UK basic rate limit for Scottish taxpayers too, under s. 13(5). Do not treat PolicyEngine's dividend-scale breakpoint as an independent statutory basic rate limit.
    > 5. **Judgment**, `individual_not_liable_above_basic_rate`: none of outputs 2–4 holds. Derive this output and replace the current opaque input in s. 55B's existing entitlement rule.
    >
    > Source scope and dependencies: use ss. 10, 11D, 12, 12A, 12B, 13, 13A and 16 for rate treatment. If deriving the result from gross pay, savings and dividends, ground the Step 3 allowance amounts and allocation under ss. 23, 25, 35 and 58. Otherwise declare the non-savings, savings and dividend amounts **after ordinary Step 3 allowances** as deferred inputs with exactly that scope. Marriage Allowance is a Step 6 tax reduction and must not reduce those inputs. The pinned release lacks s. 414: ingest and repin it before deriving Gift Aid extensions, or declare the statutory extended UK and Scottish limits as deferred inputs and scope the initial companion cases to zero Gift Aid. State each deferred dependency in the module. Reuse an existing module only if the signed encoder can import its manifest; otherwise co-encode it or declare the dependency as deferred. Do not copy PolicyEngine's current allowance allocation or liability results as proof of law.
    >
    > Use the pinned release `uk-rulespec-2026-10-25-pe-child-definitions`, content SHA256 `76b42156de23f2c000dab657dafacfb5b96ec9fe5670a75563574bd00793048c`, at corpus commit `9d174d88119fd6e6ace02d7c94eec7bb47e363f0`. Quote the pinned provisions verbatim and distinguish any later or prospective text. In particular, the property basic-rate additions to s. 55B effective from 2027–28 do not belong in the operative 2026–27 rule. Retain the signed-encoder blocker until Max approves the OpenAI-billed run and the protected workflow receives its required review.
    >
    > Companion tests must derive their expected values from the quoted law and resolution, independently of PolicyEngine outputs. Retain the packet's 2026–27 exact-limit, dividend-counterfactual and savings nil-rate cases. Also assert the component outputs in these cases, all with zero Gift Aid:
    >
    > - **Savings below the basic limit, dividends above it:** an English taxpayer with pay £40,000, savings £1,000 and dividends £10,000, with a £12,570 personal allowance. The savings output is false although total taxable income exceeds £37,700; the dividend output is true and the final rate condition is false.
    > - **Valid starting-rate example:** where the harness accepts parameter reforms, use pay £12,570, savings £38,000 and a starting-rate limit of £37,500. Section 12 covers £37,500 and s. 12A covers the remaining £500 above that limit. Savings at a forbidden rate are zero and the final rate condition is true.
    > - **Starting-rate limit above the basic limit:** with the same income and a starting-rate limit of £40,000, s. 12 covers £37,700, s. 12A has no excess above £40,000, and £300 of savings bears the savings higher rate. The final rate condition is false. Keep the £500 statutory savings allowance amount distinct from the income actually charged at the savings nil rate.
    > - **Scottish nil-rate witness:** in 2026–27, reform the Scottish higher-rate threshold to £37,702 and use pay £50,270 plus interest £1. Non-savings taxable income is £37,700 and the £1 interest bears the savings nil rate, so the final rate condition is true.
    >
    > Mark reformed-parameter fixtures as policy scenarios; the baseline law remains the pinned 2026–27 schedule. Do not extend Scottish coverage to earlier years until the dated provisions and resolutions have been ingested and pinned.
    
    ## Completion boundary
    
    Create or link the tracking ticket, and keep the Axiom follow-up in the policyengine-uk pull request. An approved follow-up must ingest and pin ITA 2007 ss. 25 and 414 and the earlier Scottish rate resolutions, run the signed encoder and supply paired parity evidence.
    

    Generated by Claude Code

  7. MaxGhenis commented on Nov 2, 2026

    @MaxGhenis
    Collaborator

    FOLLOWUPS.md

    # Follow-ups from #2350 (not fixed here; for the hub to file)
    
    Each item names its existing issue or the draft in this folder. The hub files every draft and replaces the placeholders in `PR_BODY.md`. We ran the reproduction scripts in `ISSUE_2015_PARAMETERS.md`, `ISSUE_SCOTTISH_2015_16.md` and `ISSUE_SAVINGS_SLICES.md` exactly as each draft prints them, alone, on `main` at 2da212abc and on #2350's head. The 2015 and Scottish scripts print their tables' model figures on both. The savings script prints its table's model columns on `main`; its last column comes from #2350's head, since `main` reads the conditions off `tax_band` and finds them not met in both rows. Each draft names its run under its table.
    
    ## 1. Rest-of-UK `tax_band` from 2018-19 puts income at a limit in the band above: open #2782
    
    `tax_band.formula_2018` compares `income < threshold` for taxpayers outside Scotland, so it calls taxable income exactly at the basic rate limit `HIGHER`, and taxable income of exactly nil (pay equal to the personal allowance) `BASIC`. Parliament charges income "up to the basic rate limit" at the basic rate and only income "above" it at the higher rate (ITA 2007 s. 10(2) and (3)). #2782 reports the same defect with the same reproduction (£50,270 of pay, `HIGHER`; £12,570 of pay, `BASIC`). Link it; no new issue. The direct Marriage Allowance test in #2350 removes the eligibility effect, but the band stays wrong outside Scotland; the Scottish band now uses `>`.
    
    ## 2. Scottish 2018-19 higher-rate limit and rate: open #2783
    
    `parameters/gov/hmrc/income_tax/rates/scotland/rates.yaml` keeps a £31,500 Scottish higher-rate limit and a 40% higher rate from April 2017 to April 2021. In resolution S5M-10397 the Scottish Parliament set 21% up to £31,580 of taxable income and 41% above it for 2018-19 (`review/witness-r2/srr-S5M-10397.txt`). #2783 and its branch `fix-2783-scottish-rates` correct this; no new issue.
    
    ## 3. Axiom
    
    rulespec-uk takes the s. 55B(2)(b) rate condition as an input. `AXIOM_ISSUE.md` holds the packet for the hub to file with the `pe-parity` label; the signed-encoder run it asks for needs Max's approval.
    
    ## 4. The electing spouse's rate condition (s. 55C(1)(c)): open #1947, fixed by #1963
    
    Parliament lets a person elect only if, with their own personal allowance cut by the transferable amount (s. 55B(6)), they would not be liable above the permitted rates (s. 55C(1)(c)). #2350 tests only the receiving spouse and never cuts the elector's allowance. #1947 reports that the transferor's allowance never falls and describes the fix, and #1963 checks both spouses, the donor after the cut. No new issue. On 1,000 generated households, `r3/compare_1963.log` and `r4/d1963_head.log` record where #2350 and #1963 differ (see `PR_BODY.md`).
    
    ## 5. 2015-16 parameters: `ISSUE_2015_PARAMETERS.md`
    
    The model reads three historical parameters wrongly: its 2015-16 basic rate limit (£32,000 against the statutory £31,785), its 2015-16 dividend scale (upper rate from £37,500) and its Personal Savings Allowance (from 2005, though Parliament introduced the savings nil rate for 2016-17 in FA 2016 s. 4). #2350 corrects its own test against the statute in each case: the dividend test reads the basic rate limit, and the savings test counts no savings nil rate before 2016-17. The liability defects stay for this issue. No open issue covers them; #1775 (the roadmap) names historical coverage in general, and #1822 aligned the dividend thresholds with the main-rate limits from 2016-17 only.
    
    ## 6. 2021-22 Scottish limits: attach to #2783
    
    `rates/scotland/rates.yaml` gives 2021-04-06 starter and basic upper limits of £2,306 and £13,991 of taxable income. In resolution S5M-24225 the Scottish Parliament set £2,097 and £12,726 for 2021-22, with the intermediate rate up to £31,092 (`review/witness-r2/srr-S5M-24225.txt`). `fix-2783-scottish-rates` corrects both. Add this to #2783; no separate issue.
    
    ## 7. Scottish taxpayers in 2015-16 and 2016-17 pay the 2017-18 Scottish higher rate: `ISSUE_SCOTTISH_2015_16.md`
    
    The model's Scottish rate schedule starts in April 2017, and the model reads its first values in 2015-16 and 2016-17 too, so `earned_income_tax` charges a Scottish taxpayer 40% above £31,500 of taxable pay in those years. The Scottish rates of s. 11A started in 2016-17 (FA 2014 Sch. 38 para. 12, SI 2015/2000 art. 3), and that year Parliament charged the Scottish basic rate on income that would otherwise bear the basic rate (s. 11A(1)), so the rest-of-UK limits applied. At £32,000 of taxable pay in 2016-17, the model charges the Scottish taxpayer £6,500 and the English taxpayer £6,400, while both meet the income conditions. The Marriage Allowance test in #2350 uses the basic rate limit for Scottish taxpayers before 2017-18; the liability stays wrong. `fix-2783-scottish-rates` starts in 2017 and doesn't fix these years. No open issue covers them.
    
    ## 8. Savings nil-rate slice under a starting rate limit above the basic rate limit: `ISSUE_SAVINGS_SLICES.md`
    
    `higher_rate_savings_income`, `basic_rate_savings_income` and `add_rate_savings_income` start the savings nil rate where the starting rate for savings ends. Parliament charges it on the lowest savings above the starting rate limit (s. 12A). The two agree whenever the starting rate limit stays at or below the basic rate limit, as under current law, and part when a reform raises the limit above it or changes the starting rate's phase-out. Under a £40,000 limit, with £12,570 of pay and £38,000 of interest, the model charges no savings tax where Parliament charges £120. The Marriage Allowance test in #2350 follows s. 12A; the liability stays for this issue. No open issue covers it.
    
    ## 9. #2106 interaction
    
    Merged with `fix-2106-allowance-order` at `ea880ff76`, this branch computes `income_tax` with no cycle, since #2106's least-tax search leaves `marriage_allowance` out of its inputs. The pound-of-savings Marriage Allowance case uses pay at the basic rate limit, so it stays a higher-rate witness under the least-tax allocation. The PR body's merge notes carry the #2106 bullet.
    

    Generated by Claude Code

  8. MaxGhenis commented on Nov 2, 2026

    @MaxGhenis
    Collaborator

    ISSUE_2015_PARAMETERS.md

    Title: Correct the 2015-16 basic rate limit, dividend upper-rate threshold and Personal Savings Allowance
    
    ## What the model does
    
    On `main` (2da212abc):
    
    - `policyengine_uk/parameters/gov/hmrc/income_tax/rates/uk.yaml` sets the basic rate limit at £32,000 from 2015-04-05.
    - `rates/dividends.yaml` starts the dividend upper rate at £37,500 from 2015-04-01 and at the basic rate limit from 2016-04-06. `dividend_income_tax` reads that scale, so in 2015-16 it charges the dividend ordinary rate on dividends up to £37,500 of income.
    - #1822 aligned the dividend thresholds with the main-rate limits from 2016-17 and left 2015-16 at £37,500.
    - `allowances/personal_savings_allowance/basic.yaml`, `higher.yaml` and `additional.yaml` start in 2005 (£1,000, £500 and nil), so `savings_allowance` and the savings liability apply a savings nil rate in 2015-16.
    
    ## What the law says
    
    - [ITA 2007 s. 10(5)](https://www.legislation.gov.uk/ukpga/2007/3/section/10/2015-04-06), as it stood on 6 April 2015: "The basic rate limit is £31,785." For 2016-17, Parliament set £32,000 (FA 2015 s. 4(1)(a), as Finance (No. 2) Act 2015 s. 6(a) amended it), the value the model uses for both years.
    - Parliament charges the dividend upper rate on dividend income which "would otherwise be charged at the higher rate" ([s. 13(2)](https://www.legislation.gov.uk/ukpga/2007/3/section/13/2015-04-06)), and the higher rate on income "above the basic rate limit" (s. 10(3)). So the dividend upper rate starts at the basic rate limit in 2015-16 too.
    - Parliament inserted the savings nil rate and ss. 12A and 12B in [FA 2016 s. 4](https://www.legislation.gov.uk/ukpga/2016/24/section/4/enacted), with effect "for the tax year 2016-17 and subsequent tax years" (s. 4(17)). 2015-16 had no Personal Savings Allowance.
    - Parliament inserted the dividend nil rate (s. 13A, £5,000) in [FA 2016 s. 5](https://www.legislation.gov.uk/ukpga/2016/24/section/5/enacted) and changed the dividend ordinary rate from 10% to 7.5% and the dividend additional rate from 37.5% to 38.1%, also from 2016-17 (s. 5(10)). The model's 2015-16 dividend rates and its dividend allowance, which it reads from its first 2016-17 value, follow 2016-17 law; this issue doesn't check the rest of the 2015-16 dividend regime.
    
    ## Reproduction
    
    ```python
    """2015-16 parameters: basic rate limit, dividend upper-rate threshold, PSA.
    
    England, aged 40, no survey data; one simulation per case so a reform
    stays on its own system.
    """
    import policyengine_uk.simulation as uk_simulation
    from policyengine_uk import Simulation
    
    system = uk_simulation.CountryTaxBenefitSystem()
    uk_simulation.CountryTaxBenefitSystem = system.clone
    Y = 2015
    CASES = [
        ("pay 42,386", dict(employment_income=42_386), {}),
        ("pay 40,600, interest 1,000", dict(employment_income=40_600, savings_interest_income=1_000), {}),
    ]
    for name, inputs, reform in CASES:
        sim = Simulation(
            situation={
                "people": {"p": {"age": {Y: 40}, **{k: {Y: v} for k, v in inputs.items()}}},
                "benunits": {"b": {"members": ["p"]}},
                "households": {"h": {"members": ["p"], "country": {Y: "ENGLAND"}}},
            }
        )
        for path, value in reform.items():
            sim.tax_benefit_system.parameters.get_child(path).update(period=str(Y), value=value)
        sim.tax_benefit_system.reset_parameter_caches()
        sim.reset_calculations()
        print(
            name,
            {
                v: round(float(sim.calculate(v, Y)[0]), 2)
                for v in [
                    "income_tax",
                    "earned_income_tax",
                    "savings_income_tax",
                    "savings_allowance",
                    "dividend_income_tax",
                ]
            },
        )
    ```
    
    | Case (England, 2015-16) | Model | Statute |
    |---|---|---|
    | Pay £42,386: taxable pay £31,786 | income tax £6,357.20, all at 20% | £6,357.40: the £1 above £31,785 bears 40% |
    | Pay £40,600 and interest £1,000: taxable pay £30,000 | savings tax £0 under a £1,000 Personal Savings Allowance | £200: no savings nil rate, and the £30,000 of pay leaves no starting-rate band, so the £1,000 bears 20% |
    
    Run on `main` at 2da212abc, the script prints these model figures. The dividend upper-rate threshold needs no run: `rates/dividends.yaml` reads £37,500 for 2015-16 against £32,000 in `rates/uk.yaml`.
    
    ## Proposed fix
    
    - Set the 2015-16 basic rate limit in `rates/uk.yaml` to £31,785, citing s. 10(5) as it stood on 6 April 2015, and keep £32,000 for 2016-17, citing FA 2015 s. 4(1)(a) as Finance (No. 2) Act 2015 s. 6(a) amended it.
    - Start the 2015-16 dividend upper rate in `rates/dividends.yaml` at the basic rate limit.
    - Give the Personal Savings Allowance parameters nil before 2016-17, citing FA 2016 s. 4(17), and check the 2015-16 dividend rates and dividend allowance against FA 2016 s. 5 in the same change.
    - Add the rows above as YAML cases.
    
    The Marriage Allowance rate test (`liable_only_at_marriage_allowance_rates`, #2350) already reads the basic rate limit for dividends and counts no savings nil rate before 2016-17, so it follows the statute on these cases whatever the liability does; its YAML cases under a reformed £31,785 limit show the statutory result. The fix moves only 2015-16 calculations.
    

    Generated by Claude Code

  9. MaxGhenis commented on Nov 2, 2026

    @MaxGhenis
    Collaborator

    ISSUE_SAVINGS_SLICES.md

    Title: Place the savings nil rate above the starting rate limit for savings when a reform raises that limit past the basic rate limit
    
    ## What the model does
    
    `basic_rate_savings_income`, `higher_rate_savings_income` and `add_rate_savings_income` (`policyengine_uk/variables/gov/hmrc/income_tax/bracketized_savings_income/`) each set `zero_rate_savings = min_(savings, savings_starter_rate_income + savings_allowance)` and charge savings from `non_savings_taxable_income + zero_rate_savings` upwards. `savings_starter_rate_income` keeps the starting rate for savings within the basic rate band, and cuts the starting rate limit by `gov.hmrc.income_tax.rates.savings_starter_rate.phase_out` (1 under current law) for each pound of `non_savings_taxable_income`. So the model puts the savings nil rate on the savings straight after the income it charges at the starting rate, and a reform of the phase-out moves both.
    
    ## What the law says
    
    [ITA 2007 s. 12(1)](https://www.legislation.gov.uk/ukpga/2007/3/section/12):
    
    > Income tax is charged at the starting rate for savings ... on so much of an individual's income up to the starting rate limit for savings as— (a) is savings income, and (b) would otherwise be charged at the basic rate or the default basic rate.
    
    Parliament applies the savings nil rate only where Step 3 income "is greater than £L", the starting rate limit for savings ([s. 12A(1)](https://www.legislation.gov.uk/ukpga/2007/3/section/12A)). It then charges "the lowest £A of the excess", the savings "above the starting rate limit for savings", where £A is the savings allowance (ss. 12A(2) to (4) and 12B).
    
    So the starting rate never reaches past the basic rate limit, and the savings nil rate covers the lowest £A of savings above £L. With non-savings income N, savings S and starting rate limit L, the nil-rate slice runs from max(N, L) to max(N, L) + A, cut at N + S. Dividends sit above savings (s. 16) and don't move it.
    
    While L stays at or below the basic rate limit, as under current law (£5,000), the model's slice starts at the same place and the liability matches the statute. A reform that raises L above the basic rate limit separates them: savings between the basic rate limit and L bear the savings higher rate, and the nil rate starts at L. A reform of the phase-out separates them too, since the model then starts the nil rate where the reduced starting-rate band ends.
    
    ## Reproduction
    
    ```python
    """Savings nil rate under a starting rate limit above the basic rate limit.
    
    2026, England, aged 40, pay at the personal allowance, no survey data.
    """
    import policyengine_uk.simulation as uk_simulation
    from policyengine_uk import Simulation
    
    system = uk_simulation.CountryTaxBenefitSystem()
    uk_simulation.CountryTaxBenefitSystem = system.clone
    Y = 2026
    for limit, interest in [(37_500, 38_000), (40_000, 38_000)]:
        sim = Simulation(
            situation={
                "people": {
                    "p": {
                        "age": {Y: 40},
                        "employment_income": {Y: 12_570},
                        "savings_interest_income": {Y: interest},
                    }
                },
                "benunits": {"b": {"members": ["p"]}},
                "households": {"h": {"members": ["p"], "country": {Y: "ENGLAND"}}},
            }
        )
        sim.tax_benefit_system.parameters.get_child(
            "gov.hmrc.income_tax.rates.savings_starter_rate.allowance"
        ).update(period=str(Y), value=limit)
        sim.tax_benefit_system.reset_parameter_caches()
        sim.reset_calculations()
        print(
            limit,
            interest,
            {
                v: float(sim.calculate(v, Y)[0])
                for v in [
                    "savings_starter_rate_income",
                    "savings_allowance",
                    "higher_rate_savings_income",
                    "savings_income_tax",
                    "meets_marriage_allowance_income_conditions",
                ]
            },
        )
    ```
    
    | Starting rate limit | Pay | Interest | Model: starting rate | Model: `savings_income_tax` | Under ss. 12 and 12A | Marriage Allowance income conditions (#2350) |
    |---|---:|---:|---:|---:|---|---|
    | £37,500 | £12,570 | £38,000 | £37,500 | £0 | £37,500 at the starting rate, £500 at the savings nil rate: £0 | met (on `main`, not met: `tax_band` puts £38,000 of taxable income in `HIGHER`) |
    | £40,000 | £12,570 | £38,000 | £37,700 | £0 | £37,700 at the starting rate, £300 at the 40% savings higher rate: £120 | not met |
    
    Run on `main` at 2da212abc, the script prints the model columns. The last column comes from #2350's head, where the Marriage Allowance test reads income directly.
    
    ## Proposed fix
    
    - Compute the savings nil-rate slice once, from max(N, L) to max(N, L) + A and cut at N + S, and have the three savings band variables charge the savings outside the starting-rate and nil-rate slices at the basic, higher and additional rates.
    - Add the rows above as YAML cases under a reformed `gov.hmrc.income_tax.rates.savings_starter_rate.allowance`.
    - `test_tax_band_scottish_properties.py::test_marriage_allowance_follows_the_liability_with_nil_rates` holds a reformed starting rate limit at or below the basic rate limit, since the liability and the statute part above it. Once the liability follows s. 12A, drop that limit so the property draws L above the basic rate limit too.
    
    The rate test for the Marriage Allowance (`liable_only_at_marriage_allowance_rates`, #2350) already places the slice where Parliament does (s. 12A), so under a £40,000 limit it finds the £300 at the savings higher rate that the liability leaves untaxed. The fix moves calculations only under a reform that raises the starting rate limit above the basic rate limit or changes the phase-out; under current law the slices agree.
    

    Generated by Claude Code

  10. MaxGhenis commented on Nov 2, 2026

    @MaxGhenis
    Collaborator

    ISSUE_SCOTTISH_2015_16.md

    Title: Charge Scottish taxpayers on the rest-of-UK limits in 2015-16 and 2016-17
    
    ## What the model does
    
    `policyengine_uk/parameters/gov/hmrc/income_tax/rates/scotland/rates.yaml` starts on 2017-04-06, with limits of 0, £31,500 and £150,000 at 20%, 40% and 45% for 2017-18. policyengine-core reads a parameter's first value at any earlier date, so in 2015-16 and 2016-17 the model holds that 2017-18 schedule too. `earned_income_tax` charges a Scottish taxpayer's earned income on the Scottish scale (`pays_scottish_income_tax`), so in those years it charges them 40% from £31,500 of taxable pay, where it charges a taxpayer in England 20% up to the £32,000 basic rate limit.
    
    ## What the law says
    
    - Parliament gave ITA 2007 s. 11A effect from the tax year the Treasury appointed under Scotland Act 2012 s. 25(5) ([FA 2014 Sch. 38 para. 12](https://www.legislation.gov.uk/ukpga/2014/26/schedule/38/paragraph/12)), and the Treasury appointed 2016-17 ([SI 2015/2000 art. 3](https://www.legislation.gov.uk/uksi/2015/2000/article/3/made)). No Scottish rate applied in 2015-16.
    - In 2016-17 Parliament charged the Scottish basic rate on a Scottish taxpayer's non-savings income that "would otherwise be charged at the basic rate" ([s. 11A(1)](https://www.legislation.gov.uk/ukpga/2007/3/section/11A/2016-04-06)), and the Scottish higher and additional rates in the same way (s. 11A(2) and (3)), so the rest-of-UK limits set the bands.
    - From 2017-18 the Scottish Parliament sets its own limits by resolution.
    
    ## Reproduction
    
    ```python
    """Scottish taxpayers before 2017-18 (no survey data)."""
    import policyengine_uk.simulation as uk_simulation
    from policyengine_uk import Simulation
    
    system = uk_simulation.CountryTaxBenefitSystem()
    uk_simulation.CountryTaxBenefitSystem = system.clone
    CASES = [(2015, 42_200), (2016, 43_000)]
    people, benunits, households = {}, {}, {}
    for i, (year, pay) in enumerate(CASES):
        for country in ["SCOTLAND", "ENGLAND"]:
            key = f"{country}_{i}"
            people[key] = {"age": {year: 40}, "employment_income": {year: pay}}
            benunits[key] = {"members": [key]}
            households[key] = {"members": [key], "country": {year: country}}
    sim = Simulation(situation={"people": people, "benunits": benunits, "households": households})
    names = list(people)
    for i, (year, pay) in enumerate(CASES):
        tax = sim.calculate("income_tax", year)
        meets = sim.calculate("meets_marriage_allowance_income_conditions", year)
        taxable = sim.calculate("earned_taxable_income", year)
        for country in ["SCOTLAND", "ENGLAND"]:
            j = names.index(f"{country}_{i}")
            print(year, pay, country, float(taxable[j]), round(float(tax[j]), 2), bool(meets[j]))
    ```
    
    | Year | Pay | Taxable pay | Model: Scottish taxpayer's income tax | Model: English taxpayer's income tax | Income conditions (#2350) |
    |---|---:|---:|---:|---:|---|
    | 2015-16 | £42,200 | £31,600 | £6,340 | £6,320 | both met |
    | 2016-17 | £43,000 | £32,000 | £6,500 | £6,400 | both met |
    
    The Scottish taxpayer pays £20 and £100 more: the model charges £100 and £500 of their taxable pay at 40% instead of 20%, above its £31,500 Scottish limit. In 2015-16 £31,600 sits below the statutory £31,785 basic rate limit, so Parliament charged all of it at 20%. Run on `main` at 2da212abc, the script prints these figures; #2350's head gives the same.
    
    ## Proposed fix
    
    - Give `rates/scotland/rates.yaml` explicit values before 2017-04-06 that follow the rest-of-UK limits, or have `earned_income_tax` charge Scottish taxpayers on the rest-of-UK scale before 2017-18. The 2015-16 limit then follows the corrected basic rate limit (<2015 parameters issue>).
    - Check the 2016-17 Scottish rates against the Scottish Parliament's 2016-17 resolution; this issue covers the limits only.
    - Add the rows above as YAML cases.
    - Drop the pre-2017-18 Scottish mask in `test_tax_band_scottish_properties.py::test_marriage_allowance_follows_the_liability_with_nil_rates` once the liability follows the law.
    
    The Marriage Allowance rate test (`liable_only_at_marriage_allowance_rates`, #2350) already uses the basic rate limit for Scottish taxpayers before 2017-18, so a Scottish taxpayer can meet the income conditions while the model charges some of their income at 40%. `fix-2783-scottish-rates` (#2783) starts in 2017-18 and doesn't change these years.
    

    Generated by Claude Code

  11. MaxGhenis commented on Nov 2, 2026

    @MaxGhenis
    Collaborator

    Ready for hub review

    Head c74ca6424849beb39525a83230785ce0fab37ed3 on branch claude/apply-2350-review-round-3-kxns4x. It sits on f0f576a4451adb83eee5f7bcccbf58f7ffdb81ea and adds two commits:

    • 0713b7c9 changes the P3-0 case and nothing else.
    • c74ca642 changes text only.

    My harness only lets me push claude/... branches, so fix-2350-scottish-tax-band is still at f0f576a44. To move it, fast-forward it to c74ca642. No PR is open and this branch started no CI. The hub documents are in the seven comments above this one.

    Findings

    P3-0. I changed meets_marriage_allowance_income_conditions.yaml lines 98-111 to employment_income: 50_270 and savings_interest_income: 501. The outputs are unchanged (£0.40 of savings tax, conditions false), and the comment is the review's. I worked it by hand first, for 2026-27 in England:

    • 50,270 − 12,570 = 37,700 of pay, exactly at the basic rate limit. Taxable income is 38,201.
    • That income is above the limit, so the PSA is 500 (s. 12B(3)). Pay already passes £5,000, so no starting rate applies.
    • The nil rate covers 37,700 to 38,200 (s. 12A). The last pound bears 40%: 40p, and the conditions fail.
    • Under s. 25(2), moving £x of personal allowance from pay to the interest puts x of pay into the 40% band (+0.4x). It saves 0.4x of savings tax. That is a tie at best. At x = 1 the pay passes the limit, so the conditions still fail.

    Model runs (England 2026-27; savings tax, conditions met, income tax):

    Pay, interest This head Merged with #2106 ea880ff76 Merged with #2106's current tip 74fdacf1
    £50,070, £501 (old case) £0.40, false, £7,500.40 £0.00, true, £7,500.20 £0.00, true, £7,500.20
    £50,270, £501 (new case) £0.40, false, £7,540.40 £0.40, false, £7,540.40 £0.40, false, £7,540.40
    £50,070, £500 £0.00, true £0.00, true £0.00, true

    How I built the merged trees: the head merged with each #2106 commit, taking #2106's non_savings_income_after_allowances.py. I checked that policyengine_uk.__file__ resolved to each merged tree.

    Tree Marriage Allowance YAML scottish_rates.yaml
    Merged with ea880ff76 31 passed 36 passed
    Merged with 74fdacf1 31 passed 36 passed
    ea880ff76 merge with the old f0f576a44 YAML (control) 1 failed, 30 passed not run

    #2106 has moved on since the review (ea880ff76 → 74fdacf1). P3-0 holds on both.

    P3-1. I used the review's replacement for both sentences, in the PR body's Overlap paragraph.

    P3-2. I added ## Completion boundary after the review_finding block in AXIOM_ISSUE.md, with the review's text. I dropped the comma after "£16,956" in the S6M-20844 quotation.

    P3-3. I used the review's replacement for the S2 sentence, at the end of the PR body's mutation paragraph.

    P3-4. I used the review's replacement for the "The two agree…" sentence in the PR body.

    • ISSUE_SAVINGS_SLICES.md "What the model does" now names the phase-out. I read savings_starter_rate_income first: it cuts the starting rate limit by phase_out × non_savings_taxable_income, and the parameter holds 1 from 2014.
    • I added a matching sentence to "What the law says" and the closing paragraph of that draft, and to FOLLOWUPS.md item 8.

    P3-5.

    P3-6. I applied every row, across these files:

    • the body;
    • the helper docstring, comments and documentation;
    • savings_allowance.py, non_savings_income_after_allowances.py and tax_band.py;
    • marriage_tax_reforms.py, test_marriage_allowance_cps_switch.py and test_tax_band_scottish_properties.py;
    • both YAML headers and case comments, and the two renamed cases;
    • the Axiom packet outside quotations, FOLLOWUPS.md and the three drafts.

    Statutory quotations, the review_finding block, the axiom: line, the Reproducer: line and the footer are unchanged. I rewrapped the comment blocks I touched to 79 columns, without breaking citations or sums across lines. The body quotes the new docstring and documentation.

    Judgment calls

    1. Axiom companion row. The review didn't flag this one. The "£1 of savings at the savings higher rate" row used £50,070 of pay and £501 of interest. By P3-0's s. 25(2) reasoning, at that pay a pound of allowance on the interest lowers tax by 20p, so the statutory answer is "met". I moved the row to £50,270 (non-savings £37,700, all taxable £38,201, Judgments false/true/false, final false). I added a sentence on the ninth case, and the row now matches the YAML again.
    2. Extra voice fixes in the same pattern, which no row named:
      • scottish_rates.yaml header: "The Scottish rates charge only…" became "Parliament charges the Scottish rates only…".
      • The £125,140 case: "Income of 125,140 withdraws…" became "At income of 125,140 Parliament withdraws…".
      • The 2017-18 case: "The 2017-18 s. 55B(2)(b) allows…" became "In 2017-18 Parliament allows… (s. 55B(2)(b))".
      • FOLLOWUPS.md items 2 and 6 now say "In resolution S5M-… the Scottish Parliament set". Item 5 says "Parliament introduced the savings nil rate … in FA 2016 s. 4".
    3. Rows I reworded:
      • The 2015-16 dividend YAML case: I split the review's text into two sentences, because the "which starts at" clause could attach to the wrong rate.
      • The body's line 132: "where the model's liability places … where Parliament does" became "the range in which the model's liability places the savings nil rate where Parliament does (s. 12A)".
      • The FA 2015 nit: written with Parliament as the actor, "the basic rate limit Parliament set for 2016-17 (FA 2015 s. 4(1)(a), as Finance (No. 2) Act 2015 s. 6(a) amended it)".
      • Body merge note on non_savings_income_after_allowances: I replaced the "should reconcile" sentence with a pointer to the Allowances and reliefs are set against savings before dividends, not in the order giving the greatest reduction (ITA 2007 s.25(2)) #2106 bullet.
    4. s. 16(5) quotation. The packet's s. 16 line now quotes s. 16(5) in the review's words. I couldn't read the pinned corpus row from here, so please check that quotation against the row.
    5. origin/main. It has moved past the held 2da212abc to 46a60678. This head merges with it cleanly (git merge-tree). The body's SHAs stay at 2da212abc, the commit the runs used.

    Tests at c74ca642

    No survey data, -n 4.

    Test Result
    meets_marriage_allowance_income_conditions.yaml 31 passed
    scottish_rates.yaml 36 passed
    HMRC income tax YAML directory 497 passed
    marriage_tax_reforms.yaml + integration/income.yaml 18 passed
    test_tax_band_scottish_properties.py (5), test_marriage_allowance_cps_switch.py (45), test_gift_aid.py (12), test_removed_marriage_allowance_eligible_bands_parameter.py (3), test_parameter_descriptions.py (24) 89 passed
    policyengine_uk/tests/code_health 3,086 passed
    Lint: uvx ruff@0.17.2 format --check ., check . and check --select F811,F821 policyengine_uk; git diff --check all pass

    The pytest and two-file YAML runs started before my final comment rewrap. The comparison below shows the rewrap left the code unchanged, and I reran the three YAML runs at the final head.

    Issue scripts, run exactly as printed, alone, at main 2da212a and at this head:

    • The 2015 and Scottish scripts print the drafts' figures at both commits.
    • The savings script gives starting-rate income of £37,500 and £37,700, a £500 savings allowance, and £0 of higher-rate savings and savings tax at both commits. The conditions come out met then not met at this head, and not met in both rows on main.

    Code-change check (AST and parsed YAML). I compared Python ASTs with docstrings and documentation/label strings blanked, and parsed YAML with case name/description dropped.

    • f0f576a44..c74ca642: every Python file has an equal AST. scottish_rates.yaml parses equal. In the Marriage Allowance YAML exactly one case differs, "One pound of savings at the savings higher rate fails the conditions", where employment_income goes from 50070 to 50270. That file has 31 cases at both ends.
    • 0713b7c9..c74ca642: every file is equal, so that commit is text only.

    Mutations and reverts

    Skipped


    Generated by Claude Code

  12. MaxGhenis commented on Nov 2, 2026

    @MaxGhenis
    Collaborator

    Ready for hub review

    Verdict: APPROVE WITH NITS at c74ca6424849beb39525a83230785ce0fab37ed3 (branch claude/apply-2350-review-round-3-kxns4x, on f0f576a4451adb83eee5f7bcccbf58f7ffdb81ea).

    This review covers the delta f0f576a44..c74ca6424 (two commits: 0713b7c9 P3-0, c74ca642 text) and the hub documents posted above. There's no P1 or P2. The code is unchanged apart from one YAML input. Every test I reran passes, both at this head and merged with #2106's current tip. The findings below change text only. Two of them come from branches that have moved since round 3: #2670 and #2106.

    Findings

    P3-1. The body's #2670 sentence is stale: #2670's head now merges cleanly with main and with this branch

    #2670's head has moved from a63f18e67 to 3f67112c ("Merge main into structural reform period fixes"). The body's Overlap paragraph says "#2670 conflicts with origin/main in policyengine_uk/simulation.py". That no longer holds.

    gh api repos/PolicyEngine/policyengine-uk/pulls/2670 --jq .head.sha   # 3f67112c834ae8b26c42357fe7e7854657bd2a94
    git merge-tree --write-tree c74ca6424 3f67112c8   # exit 0
    git merge-tree --write-tree 46a60678f 3f67112c8   # exit 0 (current main)
    git merge-tree --write-tree 2da212abc 3f67112c8   # exit 0

    #2670 still edits create_expanded_ma_reform: it adds _parameter_driven and reads the child-age cap from parameters.

    Replacement for "#2670 also edits create_expanded_ma_reform (its child condition), and git merges that file cleanly with this branch; #2670 conflicts with origin/main in policyengine_uk/simulation.py, which this branch doesn't touch.":

    #2670 also edits create_expanded_ma_reform (its child condition). Git merges its head (3f67112) cleanly with this branch and with origin/main.

    The #1963 sentences still hold at #1963's unchanged head 69c919f6. Against this head, git conflicts in the same seven files. Against main 46a60678, it conflicts in the same three: income_tax_subtractions.yaml, uses_property_allowance.py and property_finance_cost_relief.py.

    P3-2. The #2106 merge bullet names an old SHA, overstates "either allocation" and leaves the merge instruction loose

    PR body, Merge notes, last bullet; FOLLOWUPS.md item 9.

    The bullet has three problems:

    • It names an old SHA. It names ea880ff76, but fix-2106-allowance-order now points to 74fdacf1d4933f759f94d15999a28e64b4cc73e2 (gh api repos/PolicyEngine/policyengine-uk/branches/fix-2106-allowance-order --jq .commit.sha).
    • "Under either allocation" is wrong. The new case is an exact tie under s. 25(2), not a case where the savings tax stays 40p under every allocation. By hand (England 2026-27, pay £50,270, interest £501, personal allowance £12,570):
      • In the default order, non-savings income is £37,700, exactly the basic rate limit. The £501 of interest sits above the limit, so the savings allowance is £500 (s. 12B(3)). Non-savings income already exceeds £5,000, so no starting rate applies. The savings nil rate covers £500, and £1 bears 40%. Tax is £7,540 + £0.40 = £7,540.40, and the conditions fail.
      • Setting £x (0 < x ≤ 1) of the allowance against the interest instead puts £x of pay above the limit, at +40p a pound. It also takes x of the last pound of interest into the nil rate, at −40p a pound. The tax stays £7,540.40. This is a tie, so no allocation lowers the tax. At x = 1 the savings tax is £0, and £1 of pay bears the higher rate. The conditions fail under every share.
      • So the case keeps its £0.40 of savings tax on the merged tree only because Allowances and reliefs are set against savings before dividends, not in the order giving the greatest reduction (ITA 2007 s.25(2)) #2106 keeps the old order unless a share saves more than TOLERANCE (utils/income_tax_allocation.py:111-113 and :594-598 at 74fdacf1). The conditions output (false) holds under any allocation.
    • The merge instruction is loose. Allowances and reliefs are set against savings before dividends, not in the order giving the greatest reduction (ITA 2007 s.25(2)) #2106's documentation ends "property_income_tax and taxable_property_income_after_allowances both read it." The bullet should say what replaces that sentence.

    Runs (England 2026-27; non-savings after allowances, savings tax, income tax, conditions):

    Pay, interest This head Head merged with 74fdacf1
    £50,070, £501 (old case) £37,500, £0.40, £7,500.40, false £37,501, £0.00, £7,500.20, true
    £50,270, £501 (new case) £37,700, £0.40, £7,540.40, false £37,700, £0.40, £7,540.40, false
    £50,070, £500 £37,500, £0.00, £7,500.00, true same
    £50,271, £501 £37,701, £0.40, £7,540.80, false same

    Replacement bullet:

    FOLLOWUPS.md item 9: change "at ea880ff76" to "at ea880ff76 and at its current tip 74fdacf1".

    P3-3. The packet's s. 16(5) quotation drops the lead-in and the paragraph letters without marking the cut

    AXIOM_ISSUE.md, "The law", s. 16 bullet (judgment call 4). Checked against the hub's fetch of ITA 2007 s. 16:

    (5) If a person has both savings income and dividend income— (a) the savings income and dividend income are together treated as the highest part of the person's total income, and (b) the dividend income is treated as the higher part of that part of the person's total income.

    The packet quotes "the savings income and dividend income are together treated … and the dividend income is treated as the higher part…" as one run of text. It drops "If a person has both savings income and dividend income—", "(a)" and "(b)", with no ellipsis. The words of (a) and (b) match. The packet asks the encoder to "quote the pinned provisions verbatim", so the quotation should be verbatim too.

    Replacement:

    • s. 16(5): "If a person has both savings income and dividend income— (a) the savings income and dividend income are together treated as the highest part of the person's total income, and (b) the dividend income is treated as the higher part of that part of the person's total income".

    I couldn't compare it with the pinned corpus row. The hub's text is legislation.gov.uk's current text, and the packet says the pinned s. 16 row has no subsection numbers.

    P3-4. Axiom companion row 9: say that the alternative share ties

    AXIOM_ISSUE.md, the paragraph after the companion table. Judgment call 1 is right, and I worked it independently:

    • The old row (£50,070, £501). Setting £1 of allowance against the interest costs 20p of tax on pay (non-savings £37,501, still within the limit). It saves 40p of savings tax. So s. 25(2) gives non-savings £37,501 and savings £500, all at the nil rate, and the statutory answer is "met". The old row's "false" was wrong.
    • The new row (£50,270, £501). It is a tie, as in P3-2, and the final condition is false under every share.

    The packet's sentence says "no other share of the allowance lowers the tax", which is true. But the row's component columns (non-savings false, savings true) hold only for the order the table uses. The tied share gives non-savings true and savings false.

    Replacement for "In the ninth, pay sits at the basic rate limit, so no other share of the allowance lowers the tax (s. 25(2)): the savings allowance covers £500 of the £501 of interest and the last pound bears the savings higher rate.":

    In the ninth, pay sits at the basic rate limit, so no share of the allowance lowers the tax (s. 25(2)). Setting £1 of it against the interest instead takes £1 of pay into the higher rate and saves the same 40p. The table keeps the allowance against pay: the savings allowance covers £500 of the £501 of interest, and the last pound bears the savings higher rate. Under either share some income bears a higher rate, so the final condition is false.

    P3-5. Two citations the voice rewrite left thin

    • tax_band.py:46-47. The rewrite dropped the old "each resolution" without citing it. Write: "…and the Scottish Parliament charges each Scottish rate "on income above" one limit "and up to" the next (each Scottish rate resolution)."
    • scottish_rates.yaml:132-133. "Parliament charges the Scottish rates only on income that is neither savings nor dividends (ITA 2007 s. 11A(1A); …)". Section 11A(1A) and (4), as the packet quotes them, exclude only savings income. Section 11A(5) makes the section "subject to— section 13" for dividends. Write: "(ITA 2007 s. 11A(1A), (4) and (5), and s. 13; S6M-16531 for 2025-26)."

    Previous round's findings

    Round 3 finding At this head Evidence
    P3-0 pound-of-savings case Closed. meets_marriage_allowance_income_conditions.yaml:98-111 now uses pay £50,270 and £501 of interest, with the outputs unchanged and the review's comment. My hand working is in P3-2. The YAML gives 31 passed at head, and 31 passed on head merged with #2106's tip 74fdacf1. The probe table in P3-2 matches the author's table value for value. Mutation: setting savings_at_forbidden_rates to always false fails 4 cases, the new case among them (killed; restored).
    P3-1 overlap attribution Closed for #1963, but stale for #2670 (P3-1 above). git merge-tree against current heads
    P3-2 Completion boundary and comma Closed. The section follows the review_finding block, and the S6M-20844 quotation reads "£16,956 (c)". the packet text
    P3-3 mutant S2 sentence Closed, with the review's text. the body's mutation paragraph
    P3-4 phase-out Closed. It is in the body, ISSUE_SAVINGS_SLICES.md and FOLLOWUPS.md item 8. The helper doesn't read phase_out (grep finds only savings_starter_rate.allowance, line 80). savings_starter_rate_income subtracts phase_out × non_savings_taxable_income, and phase_out.yaml holds 1 from 2014-04-06. The review's example (pay £49,570, interest £1,200, 2026) reruns: phase-out 1 gives starting-rate £0, higher-rate savings £500, savings tax £240, false; phase-out 0.1 gives £700, £0, £0, false.
    P3-5 placeholders and run records Closed. The test-results paste, the #2106 bullet (see P3-2), FOLLOWUPS.md lines 3 and 39, the three drafts' run records, the #1822 sentence and the savings draft's corrected cell are all in. The three issue scripts, run as printed at this head: 2015 gives £6,357.20 (all earned) and £0 of savings tax under a £1,000 allowance. Scottish gives £31,600 → £6,340 and £6,320; £32,000 → £6,500 and £6,400; all met. Savings gives starting-rate £37,500 and £37,700, allowance £500, higher-rate £0, savings tax £0, conditions met and not met. All match the tables.
    P3-6 voice Closed, apart from the two citations in P3-5 above. the full text diff 0713b7c9..c74ca642
    Item 6 FA 2015 s. 4(1)(a) nit Closed, in the body and in ISSUE_2015_PARAMETERS.md. —

    Judgment calls

    1. Axiom row moved to £50,270. Right. The old row's statutory answer under s. 25(2) is "met", and the new row's is "false" (P3-4 has the working and a clarifying sentence).
    2. Extra voice fixes. These are sound. One nit: the scottish_rates.yaml header citation (P3-5).
    3. Reworded rows. These are sound. The 2015-16 dividend split reads correctly.
    4. s. 16(5). The words match the hub's fetch, but the quotation drops the lead-in and the paragraph letters (P3-3).
    5. main moved. This head merges cleanly with 46a60678 (git merge-tree exit 0). The commits since 2da212abc touch the State Pension, the programme registry and the Pension Tax Allowance, and none of the scripts' cases.

    Checked and sound

    What I couldn't check

    • Numbers from earlier rounds, which this delta didn't touch. These are the 4,816-adult grid, the 1,000-household Model Marriage Allowance as the statutory s.55B transfer and Step 6 reduction #1963 comparison, the 400-couple CPS check, the eight couple rows and the 200,000-case differential. The scripts and arrays for them sit on the hub's machine.
    • test_marriage_allowance_cps_switch.py (45 cases). Its code is AST-equal to round 3, where all 45 passed. I didn't rerun it, given its runtime of about 85 minutes.
    • The pinned Axiom corpus rows. I couldn't reach them, so the s. 16(5) check in P3-3 rests on the hub's legislation.gov.uk fetch.
    • FA 2015 s. 4(1)(a) and Finance (No. 2) Act 2015 s. 6(a). No text of either is quoted in the brief, so the precision fix rests on round 3's reading.
    • The author's runs at main 2da212abc. I ran the issue scripts at this head only.

    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions