Skip to content

Zero-rate employer NI for under-21s and apprentices under 25 - #2505

Open
vahid-ahmadi wants to merge 12 commits into
mainfrom
employer-ni-under-21-apprentice-zero-rate
Open

vahid-ahmadi wants to merge 12 commits into
mainfrom
employer-ni-under-21-apprentice-zero-rate

Conversation

@vahid-ahmadi

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

Copy link
Copy Markdown
Collaborator

Fixes #1918 (the under-21 and apprentice zero rates; the Employment Allowance is #2459)

Summary

ni_class_1_employer charged the full secondary percentage above the secondary threshold for every earner aged 16 or over. This PR applies the two age-related zero rates:

  • SSCBA 1992 s.9A: the secondary percentage is 0% for an earner under 21 on earnings up to the upper secondary threshold (UST).
  • SSCBA 1992 s.9B: the same for a relevant apprentice (an apprentice under 25) up to the apprentice upper secondary threshold (AUST), from 6 April 2016.

Earnings above the UST or AUST bear the ordinary secondary percentage (s.9A(9)-(10), s.9B(7)). The formula takes the higher of the secondary threshold, the UST (under 21) and the AUST (relevant apprentices). The relevant-apprentice condition uses the existing is_apprentice input.

The schedule lives in one helper, policyengine_uk/utils/employer_ni.py::secondary_class_1_threshold, which takes a Class 1 parameter node so baseline and reformed rules use the same function. ni_class_1_employer, baseline_employer_cost, adjusted_employer_cost, employer_ni_fixed_employer_cost_change and salary_sacrifice_pension_ni_employer all call it. The salary sacrifice measure now charges the excess over the cap (which, since #2454, enters NIC earnings only) as the top slice of the earnings ni_class_1_employer charges (rate x (max(E - T, 0) - max(E - excess - T, 0))), so it is £0 where those earnings are zero-rated, and for an adult earner it differs from the old flat charge only where earnings are below the secondary threshold. All five read the secondary threshold and rate from class_1.employer_scale (the canonical NI scales from main).

Found during an internal Budget-day rehearsal, where baseline employer NI for under-21 earners was higher than hand calculation.

Parameters

  • class_1.thresholds.upper_secondary_threshold and class_1.thresholds.apprentice_upper_secondary_threshold, weekly, from SI 2001/1004 reg 10(e)-(f) for each year from 2015-16 (UST) and 2016-17 (AUST). Both have equalled the upper earnings limit every year (£815, £827, £866, £892, £962, £962, £967). From 2021-22 they are stored as £50,270 / 52 = 966.73 (reg 11(3B)-(3C)), the convention the UEL file uses. 2027-28 to 2030-31 are announced policy (HMRC Budget 2025 note: "will remain aligned to the National Insurance contributions UEL at £967 per week up until the 2030 to 2031 tax year"). The AUST is 0 in 2015-16, before s.9B took effect, because processing backdates a parameter's earliest value.
  • class_1.secondary_zero_rate_age_limits.under_21_group (21, s.9A(3)) and .relevant_apprentice (25, s.9B(2)(a)).
  • create_threshold_indexation.py keeps both thresholds equal to the indexed UEL from 2031-32 and lists them in INDEXED_THRESHOLDS, so dated reforms treat them as the other indexed thresholds. Keeping them aligned with the UEL is a modelling assumption, as it is for the UEL itself.

Tests

  • ni_class_1_employer.yaml: 10 new cases. Under 21 below and above the UST (2026: £0 on £30,000; 0.15 x (60,000 - 50,270) = £1,459.50 on £60,000), age 21, apprentice aged 24 and 25, non-apprentice aged 22, 2024-25, 2015-16 (UST £815 a week, no apprentice zero rate) and 2016-17 (AUST £827 a week).
  • Fail before, pass after: on the test-only commit (d1f5d1f) the file gives 6 failed, 24 passed (the six cases where a zero rate applies); with the fix, 30 passed.
  • test_employer_ni_zero_rate_consumers.py (review C1): an earner aged 20 and an apprentice aged 24 on £30,000 keep their pay when the employer rate rises to 16% in 2026 (baseline and adjusted employer cost £30,000); ages 21 and 25 lose (33,750 + 800) / 1.16 - 30,000 = -£215.52; a mixed-age population checks the no-change shortcut with per-person thresholds; and in 2029 a £3,000 salary sacrifice excess on £30,000 pay (NIC earnings £33,000) bears no employer NI at age 20 or apprentice 24 (employer cost £32,000 with a £2,000 employer pension contribution), and 0.15 x 3,000 = £450 at age 21. salary_sacrifice_pension_ni_employer.yaml adds the same 2029 cases and a 19-year-old on £40,000 with a £6,000 sacrifice (£0, was £600).
  • Fail before, pass after for C1: on the test-only commit (af62664) the new pytest gives 5 failed, 4 passed (the zero-rated cases) and the YAML file 2 failed, 9 passed. The 19-year-old case, added with the fix, gives 0.15 x 4,000 = £600 on the old flat formula.
  • Fixed-cost pay response (hub review, 23 Oct): employer_ni_fixed_employer_cost_change now solves c_b = x + r x max(x - k, 0) for NIC earnings x (pay plus statutory pay) on each side of the reformed threshold k, instead of comparing pay alone with the baseline threshold. For an earner aged 20 and an apprentice aged 24 in 2026: £49,000 pay plus £2,000 SSP with the rate raised to 16% gives pay of £48,993.71 at unchanged employer cost (£51,109.51); lowering the UST and AUST to £20,000 cuts pay of £30,000 to 33,000 / 1.15 = £28,695.65; raising them to £70,000 lifts pay of £60,000 by the whole £1,459.50 saving. On the previous head (f022ab6) these 6 cases fail; with the fix all 15 in test_employer_ni_zero_rate_consumers.py pass, as do the NI YAML tests (176) and the NI, salary sacrifice and behavioural response pytest modules (222).
  • test_ni_class_1_age_properties.py: property 1 now checks ages 16 to 20 against r x max(e - 52 x max(ST, UST), 0) instead of skipping them, with two under-21 explicit examples.
  • test_threshold_indexation.py: the synthetic-tree and real-tree checks cover the two new thresholds (aligned with the UEL after their last stated year), and the registration check counts ten indexed thresholds.
  • Merged with main on 22 Oct (f022ab6): all 4,247 policy YAML tests pass, as do the NI scale, metadata, equivalence, indexation, dated-reform and salary-sacrifice pytest modules (599 passed, 2 skipped). The helper returns float32 so adult cases stay bit-identical to the scalar formula in test_ni_scale_equivalence.py.
  • Locally: all policy YAML tests (3,613 passed), test_threshold_indexation.py (89 passed), test_dated_reform_routes.py (114 passed), the NI and salary-sacrifice property modules, and the parameter metadata tests pass. make format is clean.

Reproducer: policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_1/ni_class_1_employer.yaml::No employer NI on an earner aged 20 below the upper secondary threshold (2026)

Impact

CI's aggregate impact run at 63dbac7d1 (40055160331; this head merged into main cd8972e94, certified enhanced_frs_2024_25 on policyengine-uk-data 1.58.1):

2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Employer NI -£1.42bn -£1.49bn -£1.55bn -£1.59bn -£1.64bn -£1.70bn -£1.77bn
  • gov_tax and gov_balance fall by the same amounts. No other tax head moves.
  • The report finds no change in household net income, in any decile, poverty rate or inequality measure. The fixed-employer-cost pay change returns 0 when the employer rate, threshold and exemption match between baseline and reform (employer_ni_fixed_employer_cost_change.py:88), as they do under current law.
  • 0 checks broken. sign:vat fails on both sides, as on main.

Hub fixes after the main merge (24 Oct)

  • e449f21b6 (tests only): main's salary sacrifice measure property test (Make the salary sacrifice NI measures equal the change in Class 1 liability, and correct mis-declared units #2363) failed at the merge in its "rates change within the year" world. The test zeroes the excess only in the reformed simulation, and that world's exemption flip switches on this branch's fixed-employer-cost pay change, so pay differed between the simulations with and without the excess. That world now sets employee_incidence to 0, so pay is held fixed. It also adds a known row aged 20 below the upper secondary threshold.
  • 63dbac7d1 (tests only): rows can be apprentices, drawn and pinned, so invariant 4's rate-0 band checks the s.9B threshold on every run.
  • Independent reviews: rounds 6 to 8 (in-session Opus 5.5, with the Codex lanes closed) approved 63dbac7d1. CI passes there (16 checks).

Not in this PR

  • The Employment Allowance (Model the Employment Allowance so ni_employer is net of it (split from #1918) #2459) and the elective veterans and freeport/investment zone reliefs (NICA 2022 ss.1 and 6-7) remain unmodelled.
  • Aggregate ni_employer falls for under-21 and apprentice earners. policyengine-uk-data calibrates to the OBR Class 1 employer NICs target using this variable, so the calibration will absorb part of the change when the data are rebuilt.

axiom: TheAxiomFoundation/rulespec-uk#479 queued

🤖 Generated with Claude Code

vahid-ahmadi and others added 2 commits October 19, 2026 17:18
Apply SSCBA 1992 s.9A (0% up to the upper secondary threshold for earners
under 21) and s.9B (0% up to the apprentice upper secondary threshold for
apprentices under 25) in ni_class_1_employer, with the thresholds from
SI 2001/1004 reg 10(e)-(f) and kept equal to the UEL after 2030-31.

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

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Aggregate impact on certified data: pass

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

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

Workflow run and full report

Notes

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

Moves flagged for review

Measure Year Base Head Change
National Insurance (employer) 2028-29 £168.73bn £167.14bn -£1.59bn
National Insurance (employer) 2027-28 £164.14bn £162.60bn -£1.55bn
National Insurance (employer) 2031-32 £188.90bn £187.12bn -£1.77bn
National Insurance (employer) 2030-31 £181.72bn £180.02bn -£1.70bn
National Insurance (employer) 2026-27 £159.18bn £157.69bn -£1.49bn
National Insurance (employer) 2029-30 £176.11bn £174.47bn -£1.64bn
National Insurance (employer) 2025-26 £152.64bn £151.22bn -£1.42bn
Government balance, each receipt once 2025-26 £368.81bn £367.39bn -£1.42bn
gov_balance 2025-26 £368.81bn £367.39bn -£1.42bn
Government balance, each receipt once 2026-27 £393.46bn £391.97bn -£1.49bn
gov_balance 2026-27 £393.46bn £391.97bn -£1.49bn
Government balance, each receipt once 2027-28 £422.04bn £420.50bn -£1.55bn
gov_balance 2027-28 £422.04bn £420.50bn -£1.55bn
Government balance, each receipt once 2028-29 £447.79bn £446.20bn -£1.59bn
gov_balance 2028-29 £447.79bn £446.20bn -£1.59bn
Government balance, each receipt once 2029-30 £475.18bn £473.53bn -£1.64bn
gov_balance 2029-30 £475.18bn £473.53bn -£1.64bn
Government balance, each receipt once 2030-31 £502.55bn £500.84bn -£1.70bn
gov_balance 2030-31 £502.55bn £500.84bn -£1.70bn
Government balance, each receipt once 2031-32 £533.93bn £532.16bn -£1.77bn
gov_balance 2031-32 £533.93bn £532.16bn -£1.77bn
Government receipts, each component once 2026-27 £939.05bn £937.56bn -£1.49bn
gov_tax 2026-27 £939.05bn £937.56bn -£1.49bn
gov_tax 2027-28 £975.84bn £974.30bn -£1.55bn
Government receipts, each component once 2027-28 £975.84bn £974.30bn -£1.55bn
gov_tax 2031-32 £1,121.83bn £1,120.05bn -£1.77bn
Government receipts, each component once 2031-32 £1,121.83bn £1,120.05bn -£1.77bn
gov_tax 2028-29 £1,008.78bn £1,007.19bn -£1.59bn
Government receipts, each component once 2028-29 £1,008.78bn £1,007.19bn -£1.59bn
Government receipts, each component once 2025-26 £899.40bn £897.97bn -£1.42bn
gov_tax 2025-26 £899.40bn £897.97bn -£1.42bn
Government receipts, each component once 2030-31 £1,081.33bn £1,079.63bn -£1.70bn
gov_tax 2030-31 £1,081.33bn £1,079.63bn -£1.70bn
gov_tax 2029-30 £1,044.74bn £1,043.10bn -£1.64bn
Government receipts, each component once 2029-30 £1,044.74bn £1,043.10bn -£1.64bn

Receipts by tax head

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
National Insurance (employer) -£1.42bn -£1.49bn -£1.55bn -£1.59bn -£1.64bn -£1.70bn -£1.77bn

Spending by benefit and service

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

Caseloads

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

Government balance, each component counted once

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Government receipts, each component once -£1.42bn -£1.49bn -£1.55bn -£1.59bn -£1.64bn -£1.70bn -£1.77bn
Government balance, each receipt once -£1.42bn † -£1.49bn † -£1.55bn † -£1.59bn † -£1.64bn † -£1.70bn † -£1.77bn †

Model totals

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
gov_tax -£1.42bn -£1.49bn -£1.55bn -£1.59bn -£1.64bn -£1.70bn -£1.77bn
gov_balance -£1.42bn † -£1.49bn † -£1.55bn † -£1.59bn † -£1.64bn † -£1.70bn † -£1.77bn †

Standard reform costings (change in gov_balance)

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise basic rate by 1pp -£146 -£15 -£52 +£186 +£223 -£378 -£213
Raise higher rate by 1pp +£22 +£19 +£33 -£11 +£6 +£9 +£272
Raise personal allowance by ~800GBP/year -£80 -£42 -£117 +£134 -£204 -£86 -£30,685
Raise child benefit by 25GBP/week per additional child -£0 +£1 -£0 -£27 +£5 -£27 -£1
Reduce Universal Credit taper rate to 20% +£15 -£28 -£14 -£37 +£20 +£10 +£0
Raise Class 1 main employee NICs rate to 10% -£331 +£347 +£347 +£126 +£144 -£46 +£175
Raise VAT standard rate by 2pp -£345 +£21 +£325 +£236 +£20 +£160 +£110
Raise additional rate by 3pp -£0 +£8 +£12 +£3 +£21 suppressed suppressed
Raise the Universal Credit standard allowance by 10% +£9 -£9 -£6 +£3 +£3 +£4 -£4
Raise the State Pension by 10% -£0 +£5 +£1 +£3 +£7 +£1 -£179

Standard reforms: change in each reform's target

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise personal allowance by ~800GBP/year: change in income_tax 0 0 0 0 0 0 +£42,879
Raise additional rate by 3pp: change in income_tax 0 0 0 0 0 suppressed suppressed

Household net income by decile

No household's net income changes in any year.

No change in any year: household income totals; population; poverty rates; people in poverty; inequality; mean household net income in each side's own deciles.

Checks failing on both base and head (1)
  • VAT is never negative (sign:vat): 2025-26: 76 units below zero, a weighted £-54,797,972 in total; 2026-27: 76 units below zero, a weighted £-56,271,349 in total; 2027-28: 76 units below zero, a weighted £-57,609,146 in total
Counted-once check

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

  • widowed_parents_allowance: pension_credit responds only through other heads in fewer than 10 households (other heads that moved there: carers_allowance; gov_spending; not a failure)
  • widowed_parents_allowance: pension_credit responds only through other heads in fewer than 10 households (other heads that moved there: carers_allowance; household_benefits; not a failure)
Provenance
Side Commit Runtime Environment hash Partition hash Time
base main cd8972e943 uk 2.151.3 (files 68f5044fddb1), core 3.33.2, py 3.13.17 aarch64 90825d48e860 dfe9982f4005 1629.43s
head #2505 merged into main 7262c966dd uk 2.151.3 (files f03c8c380c6b), core 3.33.2, py 3.13.17 aarch64 90825d48e860 dfe9982f4005 1612.52s

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

Receipts by tax head: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Income tax £299.29bn £319.01bn £336.54bn £351.69bn £367.24bn £385.36bn £403.66bn
National Insurance (employee and self-employed) £52.58bn £54.94bn £56.75bn £58.48bn £60.62bn £62.57bn £65.03bn
National Insurance (employer) £151.22bn £157.69bn £162.60bn £167.14bn £174.47bn £180.02bn £187.12bn
Employer NI change borne by capital £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Employer NI change borne by consumers £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Capital gains tax £12.62bn £13.06bn £13.55bn £14.04bn £14.54bn £15.09bn £15.65bn
Student loan repayments £7.83bn £8.12bn £8.35bn £8.56bn £8.79bn £9.04bn £9.38bn
VAT £253.71bn £260.53bn £266.72bn £273.14bn £279.83bn £286.71bn £293.71bn
Change in VAT from its baseline £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
VAT on private school fees £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Fuel duties £24.53bn £24.64bn £26.64bn £26.69bn £26.42bn £25.76bn £26.35bn
Alcohol duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Tobacco duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Vehicle excise duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Betting and gaming duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
TV licence fee £4.28bn £4.43bn £4.44bn £4.46bn £4.48bn £4.50bn £4.52bn
Council tax £49.21bn £51.88bn £54.97bn £58.29bn £61.55bn £64.92bn £68.49bn
High value council tax surcharge £0.00bn £0.00bn £0.00bn £0.48bn £0.49bn £0.51bn £0.52bn
Northern Ireland domestic rates £0.55bn £0.58bn £0.61bn £0.63bn £0.67bn £0.70bn £0.73bn
Business rates £31.73bn £31.73bn £31.73bn £31.73bn £31.73bn £31.73bn £31.73bn
Stamp duty land tax on the household's own purchases £6.93bn £7.42bn £7.85bn £8.26bn £8.68bn £9.09bn £9.51bn
Stamp duty land tax paid by companies £3.13bn £3.13bn £3.13bn £3.13bn £3.13bn £3.13bn £3.13bn
Land transaction tax (Wales) £0.08bn £0.08bn £0.09bn £0.09bn £0.10bn £0.10bn £0.11bn
Land and buildings transaction tax (Scotland) £0.31bn £0.32bn £0.34bn £0.36bn £0.37bn £0.39bn £0.40bn
Unspecified tax change borne by capital £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Unspecified tax change borne by consumers £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Wealth tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Wealth tax excluding the main residence £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Land value tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Carbon tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Spending by benefit and service: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
State Pension £122.15bn £126.64bn £127.76bn £129.65bn £133.46bn £137.40bn £141.47bn
Pension Credit £6.68bn £7.07bn £7.35bn £7.50bn £7.65bn £7.83bn £8.02bn
Winter Fuel Payment £1.70bn £1.68bn £1.59bn £1.57bn £1.57bn £1.55bn £1.52bn
Pension Age Winter Heating Payment (Scotland) £0.19bn £0.20bn £0.20bn £0.21bn £0.21bn £0.22bn £0.22bn
Universal Credit £75.54bn £80.75bn £83.40bn £85.21bn £86.21bn £87.61bn £88.91bn
Housing Benefit £8.59bn £8.93bn £9.22bn £9.37bn £9.64bn £9.91bn £10.20bn
Council Tax Reduction £1.98bn £2.03bn £2.07bn £2.12bn £2.17bn £2.22bn £2.26bn
Employment and Support Allowance (income-related) £0.79bn £0.81bn £0.83bn £0.85bn £0.87bn £0.89bn £0.91bn
Employment and Support Allowance (contribution-based) £5.22bn £5.36bn £5.49bn £5.62bn £5.76bn £5.90bn £6.04bn
Jobseeker's Allowance (income-based) £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Jobseeker's Allowance (contribution-based) £0.29bn £0.30bn £0.30bn £0.31bn £0.32bn £0.33bn £0.33bn
Income Support £0.12bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Working Tax Credit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Child Tax Credit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Incapacity Benefit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Personal Independence Payment £27.05bn £28.19bn £29.28bn £30.02bn £30.75bn £31.51bn £32.28bn
Disability Living Allowance £7.52bn £7.83bn £8.14bn £8.34bn £8.55bn £8.76bn £8.97bn
Attendance Allowance £8.71bn £9.08bn £9.43bn £9.67bn £9.90bn £10.15bn £10.40bn
Carer's Allowance £4.50bn £4.69bn £4.87bn £4.99bn £5.11bn £5.23bn £5.36bn
Carer Support Payment (Scotland) £0.31bn £0.32bn £0.34bn £0.34bn £0.35bn £0.36bn £0.37bn
Carer's Allowance Supplement (Scotland) £0.00bn £0.04bn £0.04bn £0.04bn £0.04bn £0.04bn £0.04bn
Severe Disablement Allowance £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn
Industrial Injuries Disablement Benefit £0.51bn £0.52bn £0.53bn £0.54bn £0.56bn £0.57bn £0.59bn
Armed Forces Compensation Scheme £0.61bn £0.62bn £0.64bn £0.65bn £0.67bn £0.69bn £0.70bn
War Widow's or Widower's Pension £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Child Benefit £16.23bn £16.91bn £17.53bn £17.96bn £18.39bn £18.84bn £19.29bn
Scottish Child Payment £0.50bn £0.52bn £0.54bn £0.56bn £0.56bn £0.58bn £0.59bn
Two Child Limit Payment (Scotland, counterfactual) £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Healthy Start vouchers £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn
Maternity Allowance £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Statutory Maternity Pay £3.32bn £3.41bn £3.49bn £3.57bn £3.66bn £3.75bn £3.84bn
Statutory Paternity Pay £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Statutory Sick Pay £1.13bn £1.16bn £1.19bn £1.22bn £1.25bn £1.28bn £1.31bn
Sure Start Maternity Grant £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn
Bereavement Support Payment £0.67bn £0.69bn £0.70bn £0.72bn £0.74bn £0.76bn £0.78bn
Widowed Parent's Allowance £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Tax-Free Childcare £0.55bn £0.56bn £0.57bn £0.58bn £0.60bn £0.61bn £0.59bn
Extended childcare entitlement £2.77bn £3.27bn £3.36bn £3.43bn £3.53bn £3.61bn £3.70bn
Universal childcare entitlement £2.26bn £2.45bn £2.51bn £2.58bn £2.64bn £2.72bn £2.78bn
Targeted childcare entitlement £0.47bn £0.49bn £0.50bn £0.51bn £0.52bn £0.53bn £0.47bn
Care to Learn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Childcare Grant £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Parents' Learning Allowance £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn
Adult Dependants' Grant £0.06bn £0.06bn £0.04bn £0.04bn £0.04bn £0.04bn £0.04bn
Travel Grant £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed
16 to 19 Bursary Fund £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
NHS spending £204.68bn £205.46bn £206.22bn £207.04bn £207.96bn £208.89bn £209.79bn
State education spending £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn
Transport subsidy spending £25.15bn £25.25bn £25.34bn £25.44bn £25.55bn £25.67bn £25.78bn
Cost-of-living support payment £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Energy Bills Rebate £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Energy Price Guarantee subsidy £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Basic income £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Disability basic income £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Other public spending budget change £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Caseloads: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
State Pension 11,308k 11,208k 10,906k 10,812k 10,860k 10,908k 10,955k
Pension Credit 1,491k 1,489k 1,488k 1,470k 1,473k 1,476k 1,475k
Winter Fuel Payment 7,091k 7,011k 6,600k 6,512k 6,493k 6,379k 6,248k
Pension Age Winter Heating Payment (Scotland) 806k 807k 807k 805k 809k 812k 816k
Universal Credit 6,361k 6,370k 6,404k 6,419k 6,390k 6,387k 6,368k
Housing Benefit 1,254k 1,252k 1,256k 1,242k 1,246k 1,250k 1,252k
Council Tax Reduction 3,187k 3,212k 3,225k 3,248k 3,266k 3,282k 3,295k
Employment and Support Allowance (income-related) 71k 71k 71k 72k 72k 72k 72k
Employment and Support Allowance (contribution-based) 722k 724k 727k 730k 733k 737k 740k
Jobseeker's Allowance (income-based) 0k 0k 0k 0k 0k 0k 0k
Jobseeker's Allowance (contribution-based) 67k 67k 68k 68k 68k 69k 69k
Income Support 25k 0k 0k 0k 0k 0k 0k
Working Tax Credit 0k 0k 0k 0k 0k 0k 0k
Child Tax Credit 0k 0k 0k 0k 0k 0k 0k
Incapacity Benefit 0k 0k 0k 0k 0k 0k 0k
Personal Independence Payment 3,581k 3,595k 3,608k 3,623k 3,639k 3,655k 3,671k
Disability Living Allowance 1,296k 1,301k 1,306k 1,311k 1,317k 1,323k 1,329k
Attendance Allowance 1,701k 1,708k 1,714k 1,721k 1,729k 1,736k 1,744k
Carer's Allowance 1,038k 1,042k 1,046k 1,050k 1,055k 1,059k 1,064k
Carer Support Payment (Scotland) 71k 72k 72k 72k 73k 73k 73k
Carer's Allowance Supplement (Scotland) 0k 72k 72k 72k 73k 73k 73k
Severe Disablement Allowance 4k 4k 4k 4k 4k 4k 4k
Industrial Injuries Disablement Benefit 137k 138k 139k 139k 140k 140k 141k
Armed Forces Compensation Scheme 94k 94k 94k 95k 95k 95k 96k
War Widow's or Widower's Pension 0k 0k 0k 0k 0k 0k 0k
Child Benefit 8,136k 8,165k 8,185k 8,215k 8,249k 8,282k 8,314k
Scottish Child Payment 352k 354k 355k 359k 357k 358k 357k
Two Child Limit Payment (Scotland, counterfactual) 0k 0k 0k 0k 0k 0k 0k
Healthy Start vouchers 200k 201k 202k 203k 204k 204k 205k
Maternity Allowance 0k 0k 0k 0k 0k 0k 0k
Statutory Maternity Pay 334k 335k 336k 338k 339k 341k 342k
Statutory Paternity Pay 0k 0k 0k 0k 0k 0k 0k
Statutory Sick Pay 182k 183k 183k 184k 185k 186k 186k
Sure Start Maternity Grant 26k 26k 26k 27k 27k 27k 27k
Bereavement Support Payment 98k 98k 99k 99k 100k 100k 101k
Widowed Parent's Allowance 0k 0k 0k 0k 0k 0k 0k
Tax-Free Childcare 1,018k 1,029k 1,028k 1,033k 1,034k 1,039k 1,023k
Extended childcare entitlement 718k 724k 728k 732k 737k 741k 743k
Universal childcare entitlement 986k 989k 992k 997k 1,002k 1,013k 1,015k
Targeted childcare entitlement 98k 98k 96k 96k 96k 97k 84k
Care to Learn 0k 0k 0k 0k 0k 0k 0k
Childcare Grant 0k 0k 0k 0k 0k 0k 0k
Parents' Learning Allowance 98k 99k 99k 100k 100k 100k 101k
Adult Dependants' Grant 18k 18k 12k 13k 13k 13k 13k
Travel Grant 0k 0k 0k 0k 0k 0k 0k
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed
16 to 19 Bursary Fund 0k 0k 0k 0k 0k 0k 0k
Cost-of-living support payment 0k 0k 0k 0k 0k 0k 0k
Energy Bills Rebate 0k 0k 0k 0k 0k 0k 0k
Energy Price Guarantee subsidy 0k 0k 0k 0k 0k 0k 0k
Basic income 0k 0k 0k 0k 0k 0k 0k
Disability basic income 0k 0k 0k 0k 0k 0k 0k
Government balance, each component counted once: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Government receipts, each component once £897.97bn £937.56bn £974.30bn £1,007.19bn £1,043.10bn £1,079.63bn £1,120.05bn
Government spending £530.58bn † £545.59bn † £553.80bn † £560.99bn † £569.57bn † £578.78bn † £587.89bn †
Government balance, each receipt once £367.39bn † £391.97bn † £420.50bn † £446.20bn † £473.53bn † £500.84bn † £532.16bn †
Benefits in household_benefits but not in gov_spending £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Model totals: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
gov_tax £897.97bn £937.56bn £974.30bn £1,007.19bn £1,043.10bn £1,079.63bn £1,120.05bn
household_tax £493.05bn £519.34bn £544.98bn £566.91bn £588.80bn £612.89bn £639.22bn
pre_budget_change_household_tax £485.22bn £511.22bn £536.63bn £557.87bn £579.52bn £603.35bn £629.32bn
gov_spending £530.58bn † £545.59bn † £553.80bn † £560.99bn † £569.57bn † £578.78bn † £587.89bn †
household_benefits £530.58bn † £545.59bn † £553.80bn † £560.99bn † £569.57bn † £578.78bn † £587.89bn †
gov_balance £367.39bn † £391.97bn † £420.50bn † £446.20bn † £473.53bn † £500.84bn † £532.16bn †
Household income totals: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Household market income £1,657.06bn £1,719.25bn £1,771.68bn £1,821.99bn £1,872.88bn £1,930.99bn £2,001.98bn
Household net income £1,655.33bn † £1,704.74bn † £1,738.62bn † £1,773.13bn † £1,809.57bn † £1,851.54bn † £1,903.61bn †
Household net income (HBAI definition) £1,462.17bn £1,509.90bn £1,544.25bn £1,577.67bn £1,612.09bn £1,651.53bn £1,702.40bn
Population: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
People 69,514k 69,778k 70,036k 70,316k 70,626k 70,944k 71,249k
Benefit units 35,247k 35,381k 35,512k 35,654k 35,811k 35,972k 36,127k
Households 31,167k 31,285k 31,401k 31,526k 31,665k 31,808k 31,944k
Poverty rates: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Absolute poverty, before housing costs: everyone 16.1% 15.2% 15.5% 15.6% 15.7% 15.8% 15.8%
Absolute poverty, before housing costs: children 24.2% 21.4% 21.6% 21.6% 21.7% 22.0% 21.8%
Absolute poverty, before housing costs: working-age adults 13.9% 13.5% 14.0% 14.2% 14.2% 14.3% 14.3%
Absolute poverty, before housing costs: pensioners 13.7% 13.2% 13.1% 13.4% 13.4% 13.5% 13.5%
Absolute poverty, after housing costs: everyone 21.6% 20.9% 21.3% 21.5% 21.8% 22.1% 22.2%
Absolute poverty, after housing costs: children 33.4% 30.9% 31.1% 31.1% 31.5% 32.0% 32.3%
Absolute poverty, after housing costs: working-age adults 19.9% 19.7% 20.3% 20.5% 20.8% 21.0% 21.1%
Absolute poverty, after housing costs: pensioners 12.8% 12.6% 12.4% 12.6% 12.7% 13.0% 13.0%
Relative poverty, before housing costs: everyone 17.2% 16.5% 16.5% 16.6% 16.3% 16.4% 16.4%
Relative poverty, before housing costs: children 26.4% 23.7% 23.4% 23.4% 22.9% 23.0% 22.9%
Relative poverty, before housing costs: working-age adults 14.7% 14.4% 14.7% 14.8% 14.7% 14.8% 14.8%
Relative poverty, before housing costs: pensioners 14.8% 14.8% 14.0% 14.2% 13.9% 14.0% 14.1%
Relative poverty, after housing costs: everyone 21.7% 21.1% 21.2% 21.2% 21.4% 21.4% 21.7%
Relative poverty, after housing costs: children 33.5% 31.3% 31.0% 30.8% 31.0% 31.1% 31.5%
Relative poverty, after housing costs: working-age adults 19.9% 19.8% 20.2% 20.3% 20.5% 20.5% 20.7%
Relative poverty, after housing costs: pensioners 13.0% 12.7% 12.4% 12.1% 12.0% 12.1% 12.4%
People in poverty: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Absolute poverty, before housing costs: everyone 11,163k 10,583k 10,852k 11,004k 11,081k 11,222k 11,226k
Absolute poverty, before housing costs: children 3,629k 3,221k 3,260k 3,284k 3,310k 3,369k 3,350k
Absolute poverty, before housing costs: working-age adults 5,902k 5,806k 6,080k 6,197k 6,234k 6,304k 6,319k
Absolute poverty, before housing costs: pensioners 1,632k 1,556k 1,512k 1,523k 1,537k 1,550k 1,556k
Absolute poverty, after housing costs: everyone 15,000k 14,571k 14,945k 15,112k 15,392k 15,663k 15,804k
Absolute poverty, after housing costs: children 5,004k 4,656k 4,703k 4,723k 4,808k 4,897k 4,966k
Absolute poverty, after housing costs: working-age adults 8,476k 8,431k 8,812k 8,952k 9,130k 9,273k 9,341k
Absolute poverty, after housing costs: pensioners 1,520k 1,484k 1,430k 1,437k 1,455k 1,492k 1,497k
Relative poverty, before housing costs: everyone 11,974k 11,507k 11,535k 11,642k 11,544k 11,663k 11,712k
Relative poverty, before housing costs: children 3,963k 3,570k 3,543k 3,553k 3,489k 3,519k 3,522k
Relative poverty, before housing costs: working-age adults 6,246k 6,184k 6,378k 6,475k 6,460k 6,538k 6,563k
Relative poverty, before housing costs: pensioners 1,765k 1,753k 1,615k 1,613k 1,595k 1,606k 1,628k
Relative poverty, after housing costs: everyone 15,067k 14,732k 14,872k 14,931k 15,091k 15,202k 15,461k
Relative poverty, after housing costs: children 5,019k 4,715k 4,686k 4,669k 4,725k 4,754k 4,839k
Relative poverty, after housing costs: working-age adults 8,497k 8,516k 8,764k 8,884k 8,992k 9,060k 9,192k
Relative poverty, after housing costs: pensioners 1,551k 1,502k 1,422k 1,378k 1,374k 1,388k 1,429k
Inequality: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Gini coefficient, equivalised HBAI income before housing costs 0.3088 0.3061 0.3067 0.3073 0.3077 0.3083 0.3093
Mean household net income in each side's own deciles: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Decile 1: mean household_net_income per household £17,831 † £18,013 † £18,035 † £18,082 † £18,435 † £18,637 † £18,791 †
Decile 2: mean household_net_income per household £28,086 † £29,021 † £28,802 † £29,249 † £29,503 † £29,925 † £30,619 †
Decile 3: mean household_net_income per household £34,360 † £35,677 † £36,233 † £36,726 † £37,055 † £37,680 † £38,301 †
Decile 4: mean household_net_income per household £38,776 † £40,951 † £42,027 † £42,701 † £43,425 † £44,160 † £44,659 †
Decile 5: mean household_net_income per household £43,903 † £44,793 † £45,864 † £46,621 † £47,807 † £48,685 † £50,049 †
Decile 6: mean household_net_income per household £48,437 † £49,771 † £50,848 † £51,835 † £51,933 † £53,554 † £54,581 †
Decile 7: mean household_net_income per household £56,348 † £57,572 † £58,375 † £58,828 † £60,102 † £60,693 † £62,312 †
Decile 8: mean household_net_income per household £69,278 † £70,760 † £72,230 † £73,324 † £74,884 † £76,024 † £78,301 †
Decile 9: mean household_net_income per household £79,810 † £82,188 † £83,302 † £84,758 † £86,308 † £87,884 † £90,916 †
Decile 10: mean household_net_income per household £117,846 † £120,681 † £122,638 † £124,783 † £127,004 † £129,363 † £132,261 †
Standard reform costings (change in gov_balance): head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise basic rate by 1pp £6.22bn £6.50bn £6.70bn £6.90bn £7.11bn £7.34bn £7.62bn
Raise higher rate by 1pp £3.54bn £3.82bn £4.05bn £4.27bn £4.49bn £4.76bn £4.96bn
Raise personal allowance by ~800GBP/year £-3.83bn £-3.96bn £-4.03bn £-4.10bn £-4.19bn £-4.30bn £-2.02bn
Raise child benefit by 25GBP/week per additional child £-2.00bn £-1.76bn £-1.58bn £-1.48bn £-1.38bn £-1.28bn £-1.17bn
Reduce Universal Credit taper rate to 20% £-20.49bn £-21.05bn £-21.50bn £-21.94bn £-22.03bn £-22.40bn £-22.87bn
Raise Class 1 main employee NICs rate to 10% £11.07bn £11.51bn £11.84bn £12.14bn £12.49bn £12.83bn £13.23bn
Raise VAT standard rate by 2pp £25.01bn £25.68bn £26.29bn £26.92bn £27.58bn £28.26bn £28.95bn
Raise additional rate by 3pp £2.83bn £3.07bn £3.27bn £3.47bn £3.67bn suppressed suppressed
Raise the Universal Credit standard allowance by 10% £-3.03bn £-3.16bn £-3.28bn £-3.35bn £-3.39bn £-3.44bn £-3.51bn
Raise the State Pension by 10% £-7.81bn £-8.07bn £-8.06bn £-8.18bn £-8.43bn £-8.60bn £-8.96bn
Standard reforms: change in each reform's target: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise basic rate by 1pp: change in income_tax £6.36bn £6.65bn £6.86bn £7.07bn £7.28bn £7.52bn £7.80bn
Raise higher rate by 1pp: change in income_tax £3.55bn £3.83bn £4.06bn £4.28bn £4.50bn £4.77bn £4.97bn
Raise personal allowance by ~800GBP/year: change in income_tax £-3.96bn £-4.10bn £-4.19bn £-4.26bn £-4.36bn £-4.47bn £-1.77bn
Raise child benefit by 25GBP/week per additional child: change in child_benefit £2.34bn £2.15bn £1.96bn £1.85bn £1.74bn £1.63bn £1.51bn
Reduce Universal Credit taper rate to 20%: change in universal_credit £20.48bn £21.04bn £21.49bn £21.93bn £22.02bn £22.40bn £22.86bn
Raise Class 1 main employee NICs rate to 10%: change in national_insurance £11.33bn £11.79bn £12.13bn £12.44bn £12.78bn £13.13bn £13.62bn
Raise VAT standard rate by 2pp: change in vat £25.01bn £25.68bn £26.29bn £26.92bn £27.58bn £28.26bn £28.95bn
Raise additional rate by 3pp: change in income_tax £2.83bn £3.07bn £3.27bn £3.47bn £3.67bn suppressed suppressed
Raise the Universal Credit standard allowance by 10%: change in universal_credit £3.03bn £3.16bn £3.28bn £3.36bn £3.41bn £3.47bn £3.52bn
Raise the State Pension by 10%: change in state_pension £10.72bn £11.23bn £11.37bn £11.62bn £12.08bn £12.54bn £13.07bn

@juaristi22 juaristi22 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed a66fd641d4fc02d9eb4f4875d8bda113e0c59e8a.

The main under-21/apprentice NI formula, dated UST/AUST parameters and indexation pass their focused checks. The downstream cost limitation is material and keeps this review at REQUEST_CHANGES.

C1 — Apply the zero-rate schedule in employer-cost and salary-sacrifice consumers. employer_ni_fixed_employer_cost_change.py:49–75, baseline_employer_cost.py:39–54, adjusted_employer_cost.py:31–42 and salary_sacrifice_pension_ni_employer.py:21–27 still calculate NI using only the ordinary secondary threshold.

Executed 2026 case: an employee aged 20 with £30,000 pay, no pension contributions and the default fixed-cost incidence. Raising employer NI from 15% to 16% should leave pay unchanged because NI is zero below UST under SSCBA 1992 s.9A. The head instead cuts pay by £215.52 to £29,784.48, while actual employer NI remains £0. An apprentice aged 24 has the same error under s.9B; age-21/25 ordinary-rate controls behave as expected.

The consistent 2029 salary-sacrifice case also shows the same duplicated-schedule defect: £3,000 returned to pay leaves total pay £33,000 below UST/AUST, yet the helper charges £450 extra employer NI and reports adjusted employer cost £39,200 against actual £35,000 (including an explicit £2,000 remaining employer pension contribution). Count this as one shared consumer defect. Please apply the applicable age/apprentice schedule in these calculations and cover the wage and cost results.

Validation: 212 focused tests passed, including Hypothesis properties and age/earnings/indexation/salary-sacrifice tests; separate source-backed diagnostics establish the remaining defect. The existing axiom: needed records the outstanding parity work.

Review status: COMPLETE. One confirmed critical finding (C1). Hosted CI is separate and was not fully complete in the captured check set.

…fice variables

Regressions for review C1 on #2505: an earner aged 20 and an apprentice aged
24 on £30,000 keep their pay when the employer rate rises to 16% (2026), and
pay returned from a capped salary sacrifice below the UST/AUST bears no
employer NI (2029). Ages 21 and 25 are ordinary-rate controls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ifice variables

baseline_employer_cost, adjusted_employer_cost,
employer_ni_fixed_employer_cost_change and
salary_sacrifice_pension_ni_employer recomputed employer NI from the
ordinary secondary threshold, so a rate rise cut the pay of a 20-year-old
on £30,000 who pays no employer NI (review C1 on #2505). They and
ni_class_1_employer now take the threshold from one helper,
secondary_class_1_threshold (SSCBA 1992 ss.9, 9A, 9B). The salary
sacrifice measure charges the returned pay as the top slice of the
earnings ni_class_1_employer charges.

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

Copy link
Copy Markdown
Collaborator Author

@juaristi22 thanks, C1 is fixed in 5a1846c (tests first in af62664).

  • One helper, policyengine_uk/utils/employer_ni.py::secondary_class_1_threshold, now gives the per-person threshold: the secondary threshold, raised to the UST under 21 (s.9A) or the AUST for an apprentice under 25 (s.9B). It takes a Class 1 parameter node, so the baseline and reformed sides use the same function. ni_class_1_employer, baseline_employer_cost, adjusted_employer_cost, employer_ni_fixed_employer_cost_change and salary_sacrifice_pension_ni_employer all call it.
  • employer_ni_fixed_employer_cost_change: the no-change shortcut now compares the per-person thresholds with np.array_equal, and the below-threshold guard uses the person's threshold. Your 2026 case (aged 20, £30,000, 15% to 16%) now leaves pay at £30,000, with baseline and adjusted employer cost both £30,000. The apprentice aged 24 is the same. Ages 21 and 25 still lose £215.52 ((33,750 + 800) / 1.16 - 30,000).
  • salary_sacrifice_pension_ni_employer now charges the returned pay as the top slice of the earnings ni_class_1_employer charges, using the same helper. Your 2029 case gives £0 extra employer NI and adjusted employer cost £35,000 (33,000 pay + 2,000 employer pension). Age 21 still gives £450. A 19-year-old on £40,000 with a £6,000 sacrifice now shows £0 (it was £600). For adults this differs from the old flat charge only where pay is below the secondary threshold.
  • Regressions: test_employer_ni_zero_rate_consumers.py (9 cases, including a mixed-age population) and 4 new cases in salary_sacrifice_pension_ni_employer.yaml. On af62664, with the head code, the zero-rated cases fail (pytest 5 failed, 4 passed; YAML 2 failed, 9 passed) and the controls pass. With the fix, all pass, as do all 3,613 policy YAML tests.
  • Axiom: the line is now axiom: TheAxiomFoundation/rulespec-uk#479 queued (ss.9A and 9B). 9.yaml already reads them as inputs and 2001/1004/10.yaml has the thresholds.
  • Overlaps: Store marginal National Insurance schedules as scales #2490 changes the same lines in all five files, and would now only need to change the helper. Make the salary sacrifice NI measures equal the change in Class 1 liability, and correct mis-declared units #2363 replaces salary_sacrifice_pension_ni_employer with the same marginal form using the secondary threshold alone, so this version covers it. Keep the pension salary-sacrifice cap within National Insurance #2462 changes that file's input to salary_sacrifice_pension_ni_excess, which is a small textual conflict.

The PR stays in draft.

🤖 Generated with Claude Code

Resolve conflicts with the NI canonical scale refactor (#222) and the
NIC-only salary sacrifice excess (#2454), keeping main's behaviour:

- The zero-rate helper and its five consumers read the secondary threshold
  and rate from class_1.employer_scale; the helper returns float32 so the
  ordinary case stays bit-identical to the scalar formula
  (test_ni_scale_equivalence).
- Threshold indexation lists the UST and AUST alongside main's canonical
  scale paths (ten indexed thresholds); the registration test counts them.
- The salary sacrifice zero-rate tests use main's semantics: the excess over
  the cap enters NIC earnings only and is not returned to pay.
- uv.lock is main's; the branch's lockfile churn was unintended.

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

Copy link
Copy Markdown
Collaborator

UK hub (Claude session, for Max). @juaristi22, Vahid's fix for C1 is in 5a1846cbc, with tests first in af626641e. Every consumer now takes each person's threshold from secondary_class_1_threshold. He merged main in on 22 Oct, and every check passes. Could you re-review? The hub has an independent review running at this head, and it re-runs your three cases.

@vahid-ahmadi, this is still a draft. Mark it ready when you're done, or say so here and the hub will. On María's approval, the hub lands it through the merge queue before the Budget.

@MaxGhenis

Copy link
Copy Markdown
Collaborator

UK hub (Claude session, for Max): independent review (GPT-6.1 Sol) at f022ab6ff: one blocking finding.

María's three C1 cases are fixed. On the 2026 cases (age 20, and apprentice age 24, both on £30,000 with employer NI raised from 15% to 16%), pay stays at £30,000, employer NI is £0 and employer cost is unchanged. The 2029 salary-sacrifice case adds no extra employer NI. Every consumer uses secondary_class_1_threshold, 267 tests pass, all checks pass, and the PR merges cleanly with current main.

[Blocking] The fixed-cost shortcut misses statutory pay and lowered thresholds.

variables/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.py:95 compares cash wages with each person's threshold. The NI base also includes statutory sick, maternity and paternity pay (lines 24–40). The reviewer ran two cases:

  • Statutory pay over the line. In 2026, a 20-year-old (or an apprentice of 24) earns £49,000 in wages plus £2,000 of SSP, with no pension and employer NI raised from 15% to 16%. The shortcut leaves pay at £49,000 and employer cost drifts from £51,109.51 to £51,116.80. To hold cost fixed, pay should be about £48,993.71.
  • Lowered thresholds. A reform lowering both relief thresholds to £20,000, with £30,000 of pay, keeps wages unchanged and pays £1,500 NI, so employer cost is £31,500. Pay should fall to £28,695.65.

The guard predates this PR, but raising its threshold from £5,000 to £50,270 exposes these earners. Fix: solve the fixed-cost equation piecewise, and add regressions for the statutory-pay and threshold-crossing cases for both groups.

Nonblocking:

  • is_apprentice is an input that defaults to false, and policyengine-uk-data doesn't impute it, so apprentices aged 21–24 get no relief in the microsimulation.
  • The Aggregate impact shows employer NI down £1.42bn in 2025-26, against HMRC's £2.47bn of combined relief (Jan 2026 tax relief statistics: £1.90bn for under-21s and £0.57bn for apprentices). The missing apprentice input explains part of that gap.
  • Weekly UST/AUST × 52 differs from the statutory annual figures by up to £4.69 in some historical years.
  • Employer-cost reporting already left out the NIC-only part of the sacrifice excess before this PR. That needs a separate follow-up.

@vahid-ahmadi, can you take the blocking item? With it fixed and María's re-review done, the hub lands this before the Budget.

The shortcut compared pay alone with the baseline threshold and used the
above-threshold solution everywhere, so it missed statutory pay that takes
NIC earnings over the UST/AUST and reforms that move a threshold across a
person's earnings. Solve c_b = x + r * max(x - k, 0) on each side of the
reformed threshold instead, with regressions for both zero-rated groups.

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

Copy link
Copy Markdown
Collaborator Author

@juaristi22 a correction to my 20 Oct reply. Main's #2454 changed salary sacrifice, so the £3,000 excess over the cap now goes into NIC earnings only and is not added back to pay. Your 2029 case at age 20 (or apprentice 24) now has pay of £30,000, NIC earnings of £33,000, £0 extra employer NI and adjusted employer cost of £32,000 (£30,000 pay plus £2,000 employer pension), not £35,000. At age 21 the extra employer NI is still £450. The tests and PR body already use £32,000; only my reply was out of date.

@MaxGhenis the blocking item is fixed in 1b17148. employer_ni_fixed_employer_cost_change now solves c_b = x + r x max(x - k, 0) for NIC earnings x (pay plus statutory pay) on each side of the reformed threshold k, instead of comparing pay alone with the baseline threshold:

  • £49,000 pay plus £2,000 SSP, rate 15% to 16%, aged 20 or apprentice 24: pay £48,993.71, employer cost unchanged at £51,109.51 (it drifted to £51,116.80).
  • UST and AUST lowered to £20,000, pay £30,000: pay £28,695.65, cost £30,000 (it was £31,500).
  • New reverse case: UST and AUST raised to £70,000, pay £60,000: pay rises by the whole £1,459.50 saving. The old linear formula overshot to £62,573.49.

All 6 cases fail at f022ab6 and pass now. All 15 tests in test_employer_ni_zero_rate_consumers.py, the NI YAML tests (176) and the NI, salary sacrifice and behavioural response pytest modules (222) pass. For adults this changes nothing under a plain rate change. It only differs where statutory pay crosses the threshold or a reform moves the threshold. People with no pay get no change, and pay cannot go below zero.

@juaristi22 could you re-review at 1b17148? It stays in draft.

🤖 Generated with Claude Code

@MaxGhenis

Copy link
Copy Markdown
Collaborator

UK hub (Claude session, for Max): round 2 of independent review (GPT-6.1 Sol) at 1b1714868. The round 1 defect is fixed; two items still block.

Fixed. employer_ni_fixed_employer_cost_change.py:82–:104 now solves on earnings including statutory pay, and the pay-only shortcut is gone. The reviewer checked the inverse by hand. With x = wages + statutory pay + employer pension, k = the reformed threshold plus any exempt pension, and C = baseline cost, reformed cost F(x) = x + r·max(x−k, 0) is continuous and strictly increasing. So x = C when C ≤ k, and x = (C + r·k)/(1 + r) otherwise. This covers threshold changes in both directions and pay cuts that cross the old threshold. CI logs show all 15 consumer regressions passing, and the YAML job passed all 4,357 cases. No consumer of the ordinary threshold was missed.

[Blocking 1] Penny precision and the conservation property aren't tested yet.

  • test_employer_ni_zero_rate_consumers.py:167–:174 allows £0.10 of error on the statutory-pay case, and :216 does the same on the raised-threshold case. pytest.approx(abs=0.01) on its own also keeps the default relative tolerance, which allows about 3–5p at these amounts. Use rel=0, abs=0.01 for money.
  • Add a Hypothesis property for fixed-cost conservation. In feasible cases, adjusted employer cost should equal baseline cost to the penny. When statutory pay and pensions alone cost more than C, wages should floor at zero. The existing properties in test_ni_class_1_age_properties.py test the age rules, not conservation.
  • Keep María's original third case: 2029, £30,000 wages, £5,000 sacrifice and £5,000 total employer pension, with the broad-base haircut at zero. Expected: wages £30,000, NIC earnings £33,000, no extra employer NI for either group, employer cost £35,000. The committed regression (:113) uses £2,000 of total pension and asserts £32,000, which is a different scenario.

The reviewer's hand-calculated expectations (2026 relief threshold £966.73 × 52 = £50,269.96, full fixed-cost incidence, no pension unless stated):

Case Adjusted pay Employer NI Employer cost
Age 20 or apprentice 24; £49,000 wages + £2,000 SSP; 15% → 16% £48,993.71 £115.80 £51,109.51
Same, age 21 or apprentice 25 £48,603.45 £7,296.55 £57,900.00
Either relief group; £30,000 wages; UST/AUST lowered to £20,000 £28,695.65 £1,304.35 £30,000.00
Either relief group; £51,000 wages; thresholds lowered to £20,000 £47,051.74 £4,057.76 £51,109.51
Either relief group; £60,000 wages; UST/AUST raised to £70,000 £61,459.51 £0.00 £61,459.51

[Blocking 2] CI didn't finish at this head. All five Python shards were cancelled (runner capacity, not a test failure), so Test / Result failed and the Aggregate impact report didn't run. Your next push re-runs them. Round 1's impact numbers don't cover this head.

@vahid-ahmadi, can you take both? Once they're in and María has re-reviewed, the hub lands this before the Budget.

- Use the stored 2026 relief threshold (£966.73 x 52 = £50,269.96) and
  penny tolerances in the statutory-pay and raised-threshold regressions,
  which allowed £0.10.
- Add María's original salary sacrifice case: £5,000 of total employer
  pension with the £5,000 sacrifice, employer cost £35,000 for a
  zero-rated earner.
- Add a Hypothesis property over mixed under-21, apprentice and ordinary
  earners with statutory pay and employer pensions, under random employer
  rate, secondary threshold and UST/AUST reforms: reformed employer cost
  equals baseline cost whenever reformed pay is positive, and pay floors
  at zero (cost at least baseline) when statutory pay and pensions alone
  cost more.

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

Copy link
Copy Markdown
Collaborator

UK hub (Claude session, for Max): pushed the round 2 test fixes at 4f47cbf4d (tests only; no model change).

  • Penny precision: the statutory-pay and raised-threshold regressions now use the stored relief threshold (£966.73 × 52 = £50,269.96) and abs=0.01, in place of £50,270 and abs=0.1. They also pin the hand values: £51,109.51 baseline cost and £48,993.71 pay; £61,459.51.
  • Correction to my earlier comment: pytest.approx(x, abs=0.01) does not add pytest's default relative tolerance. When only abs is given, the relative tolerance is ignored (checked with pytest 9.1.1: £51,000.04 against approx(51_000, abs=0.01) is False). So the existing abs=0.01 assertions were already penny-exact; only the abs=0.1 ones were loose.
  • María's original case: the salary-sacrifice test now also runs with £5,000 of total employer pension (employer cost £35,000 for a zero-rated earner; £450 of extra NI at 21 and 25).
  • Conservation property (test_employer_ni_fixed_cost_properties.py, Hypothesis, 25 derandomized examples). It draws mixed under-21, apprentice and ordinary earners with statutory pay and employer pensions, under random employer-rate, secondary-threshold and UST/AUST reforms. Whenever reformed pay is positive, adjusted employer cost must equal baseline cost (within a penny plus float32 rounding). Otherwise pay floors at zero, so cost is at least baseline.

No local runs (the host is reserved), so CI is the check; the push also re-runs the cancelled shards and the Aggregate impact report. The hub gets a delta review and asks María to re-review.

@juaristi22 juaristi22 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-reviewed 4f47cbf4dbcd67a0e34d2e76b4e9287ae2b4e64b.

The original below-threshold examples are fixed. In particular, £30,000 cash pay plus £2,000 actual employer pension now costs £32,000, with no employer NI. The shared threshold helper and statutory-pay/reformed-threshold regressions improve the other cases too.

C1 remains open for the NIC-only salary-sacrifice excess. baseline_employer_cost.py:29–34, adjusted_employer_cost.py:26–28 and employer_ni_fixed_employer_cost_change.py:36–41 reconstruct NI earnings without salary_sacrifice_pension_ni_excess, although actual Class 1 NI includes it.

Executed consistent 2029 case: age 20, £49,000 cash pay, £5,000 intended sacrifice, £2,000 actual employer pension, no broad-base haircut. There is no returned cash; the £3,000 NIC-only excess makes assessed earnings £52,000. Actual employer NI is £259.51 and employer_cost £51,259.51, while baseline and adjusted costs report £51,000. Raising the employer rate from 15% to 16% with full employee incidence leaves pay unchanged and increases actual cost by £17.30, instead of holding cost fixed. An above-UST £60,000 control also shows the omitted excess.

Include the excess in NI assessment and the fixed-cost equation, while keeping it out of cash pay and actual pension expenditure. Earnings above UST/AUST remain subject to the ordinary secondary rate under SSCBA s.9A and s.9B.

The latest commit changes two tests only; implementation and dependency Git objects are identical to the executed reproduction. The new property cases stop at 2028 and do not exercise the 2029 excess. 188 focused checks passed at the identical implementation; those results and the diagnostic are retained with hash provenance. Current required CI passes. Review status: COMPLETE; one confirmed critical finding remains.

María's re-review of 4f47cbf (C1): baseline_employer_cost,
adjusted_employer_cost and employer_ni_fixed_employer_cost_change rebuilt
NIC earnings without salary_sacrifice_pension_ni_excess, although
ni_class_1_employer charges it. From 2029, a 15% to 16% rate rise left
pay unchanged for an earner aged 20 with £49,000 pay and a £5,000
sacrifice, and actual cost rose by £17.30.

The equations also left out salary_sacrifice_returned_to_income and
salary_sacrifice_broad_base_haircut, which employment_income adds. So
from 2029, with the default 0.16% haircut, the costs missed every
worker's haircut.

A shared helper returns each schedule's pay terms and NIC-only excess.
The baseline cost uses the baseline simulation's terms, and the reform
uses its own. The solver's kink moves down by the excess. Regressions:
- María's case to the penny: pay £48,985.09, cost £51,259.51;
- an above-threshold control;
- a default-haircut case.
Each also checks against employer_cost in both simulations. The
Hypothesis property now covers 2029-30 with a drawn sacrifice and checks
that agreement.

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

Copy link
Copy Markdown
Collaborator

UK hub pushed 5774899a4 on top of 4f47cbf4d (fast-forward; no history rewritten). It answers @juaristi22's C1 from the 22:12Z re-review.

  • The NIC-only excess. baseline_employer_cost, adjusted_employer_cost and employer_ni_fixed_employer_cost_change now add salary_sacrifice_pension_ni_excess to the NIC earnings they charge. It stays out of cash pay and out of pension expenditure. The solver's kink moves down by the excess (k_r = exempt pension + T_r − excess).

  • The same class of gap, also fixed. employment_income adds salary_sacrifice_returned_to_income and salary_sacrifice_broad_base_haircut, but the cost equations left both out. From 2029, with the default 0.16% haircut, every worker's baseline cost therefore missed the haircut. A shared helper, salary_sacrifice_cost_terms, returns each schedule's pay terms and excess. The baseline cost reads the baseline simulation's terms, and the reform reads its own.

  • Regressions:

    • María's executed case, to the penny: aged 20, £49,000 pay, a £5,000 sacrifice, £2,000 employer pension, no haircut, 15% to 16%. Baseline cost is £51,259.51; pay falls to £48,985.09; cost is held at £51,259.51.
    • An above-threshold £60,000 control.
    • A default-haircut case.

    Each also checks that baseline_employer_cost equals the baseline simulation's employer_cost, and that adjusted_employer_cost equals the reformed simulation's.

  • The Hypothesis property now covers 2026–2030 with a drawn sacrifice. It checks that same agreement with employer_cost alongside conservation and the floor: if both cost equations had omitted a term, conservation alone would still have passed.

The host is reserved for the Budget certification, so these run in CI. I'll request your re-review once CI is green at this head.

MaxGhenis and others added 2 commits October 23, 2026 20:11
María's 2029 case assumes no broad-base haircut. The test removed it with a
parameter reform, which changes the reformed population only: a reform's
baseline simulation is built from the situation under current law, so the
baseline kept the 0.16% haircut, its pay was £48,921.60 and its cost
£51,169.35 rather than £51,259.51. The haircut is now a situation input of
zero, which both populations share, and the test checks the baseline pay.

The fixed-cost property also draws salary sacrifice returned to pay, and four
explicit examples make every run cover a sacrifice above the cap in 2029 and
2030, returned pay and the zero-pay floor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts with #2363 in salary_sacrifice_pension_ni_employer:
- formula: this branch's, which charges the excess above the earner's own
  secondary threshold (s.9, s.9A, s.9B);
- defined_for = "ni_class_1_secondary_liable" and the secondary liability
  note from main; references from both;
- YAML: main's cases, then this branch's.

main's test_salary_sacrifice_ni_measure_properties.py checks invariant 4
(one band, one rate) against the plain secondary threshold, which this
branch raises for earners under 21 and apprentices under 25. Its employer
bands now split at each earner's own threshold, worked out from the
parameters, and a known case aged 20 crosses the upper secondary threshold.

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

Copy link
Copy Markdown
Collaborator

UK hub: merged main into this branch (ac139c5).

Round 5 approved a1cd832 (both round 4 findings fixed, CI green). Main then gained #2363, which conflicted in salary_sacrifice_pension_ni_employer. I resolved it like this:

  • Formula: this branch's. It charges the excess above the earner's own secondary threshold (s.9, raised by s.9A for under-21s and s.9B for apprentices under 25). Main's plain-scale formula would charge zero-rated earners.
  • From main: defined_for = "ni_class_1_secondary_liable" and its note on secondary liability. References from both.
  • YAML: main's 14 cases, then this branch's 4.

One change goes beyond the conflict. Main's test_salary_sacrifice_ni_measure_properties.py checks invariant 4 (one band, one rate) with employer bands split at the plain secondary threshold. It draws ages 16 to 20, which this branch zero-rates up to the upper secondary threshold, so the invariant would fail after the merge. The employer bands now split at each earner's own threshold, worked out from the parameters, and a known case aged 20 crosses the upper secondary threshold.

A delta review of the resolution is running, and CI is running at ac139c5.

@juaristi22: your CHANGES_REQUESTED review at 4f47cbf still stands. Its C1 was implemented in 5774899. Could you re-review once CI is green?

MaxGhenis and others added 2 commits October 23, 2026 22:22
The "rates change within the year" world turns off the employer pension
contribution exemption, which switches on the fixed-employer-cost pay
change. The test removes the excess from the reformed simulation only, so
that change passed employer NI on the excess into pay there, and pay
differed between the simulations with and without the excess (invariant 1
failed for most known cases). Employee incidence is 0 in that world, so
pay is the same in both.

A known case aged 20 now has its excess between the secondary and upper
secondary thresholds, so the zero-rate band of invariant 4 always sees it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rows can now be apprentices, drawn and pinned. The new known row is an
apprentice aged 23 whose excess lies between the secondary threshold and
the apprentice upper secondary threshold (SSCBA 1992 s.9B), so invariant
4's rate-0 employer band checks the s.9B arm on every run. Before this,
no row set is_apprentice, and _own_secondary_threshold's apprentice arm
was never exercised (round 7 review, P3).

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

Copy link
Copy Markdown
Collaborator

UK hub pushed e449f21b6 and 63dbac7d1 on top of ac139c5, as a fast-forward with tests only and no model change.

Why. CI at ac139c5 failed test_measures_equal_the_difference_of_complete_liabilities[rates change within the year]. That is main's property test from #2363. The hub's round 6 review traced the cause, and I reproduced it locally:

  • The test zeroes salary_sacrifice_pension_ni_excess only in the reformed simulation. The baseline built inside Simulation(reform=...) keeps its own excess.
  • That world flips exempt_employer_pension_contributions, which switches on this branch's fixed-employer-cost pay change.
  • The pay change now reads each population's excess (the C1 fix in 5774899a4). So in the no-excess simulation it passed the employer's NI saving, r·X/(1+r), into pay.
  • Invariant 1 compares liabilities across the two simulations, so pay differed between them, by £521.74 for known case 0 in 2030.

The model behaves as designed. The test's "all else equal" no longer held.

e449f21b6.

  • It sets gov.contrib.policyengine.employer_ni.employee_incidence: 0 in that world. The fixed-cost change then returns 0 before it reads any input, so pay is the same in both simulations.
  • The world still tests what it is for: employee rates and the UEL changing within 2041, and charged employer pension contributions.
  • It adds a known row aged 20 wholly below the upper secondary threshold, with an excess above the secondary threshold. Invariant 4's rate-0 band now checks the s.9A case on every run.

63dbac7d1.

  • Rows can now be apprentices, drawn and pinned. A known row is an apprentice aged 23 below the apprentice upper secondary threshold.
  • Before this commit, no row set is_apprentice, so the s.9B arm of the test's threshold was never exercised.
  • In 2029 and 2030 that row's employer measure is £0. The same person is charged £450 as a non-apprentice, or as an apprentice aged 25.

Checks.

  • The test file passes locally: 6 passed. The YAML file passes too: 18 passed.
  • The round 7 review (in-session Opus) approved e449f21b6. A delta review of 63dbac7d1 is running.
  • CI now runs with Give each Python test shard 75 minutes #2626's 75-minute shard limit. At ac139c5, two shards were cut off at 60 minutes.

@juaristi22, your C1 was fixed in 5774899a4, and rounds 4 and 5 checked that fix. When you have a moment, could you re-review at 63dbac7d1?

@vahid-ahmadi

Copy link
Copy Markdown
Collaborator Author

@juaristi22 I checked both of your reviews again at the current head, 63dbac7d1. No code change was needed, so there is no new commit.

Status at 63dbac7d1

  • 19 Oct C1 (zero-rate schedule in the cost and salary sacrifice consumers): resolved. Under the 16% reform in 2026, at age 20 or as an apprentice aged 24 on £30,000, pay stays at £30,000 and employer NI is £0. Baseline and actual cost are both £30,000. At age 21 pay still falls by £215.52 to £29,784.48. In your 2029 salary sacrifice case with £5,000 of employer pension, the zero-rated earner has £0 of extra employer NI and costs £35,000. At age 21 the extra employer NI is £450.
  • 23 Oct C1 (the NIC-only sacrifice excess in the fixed-cost equations): resolved. This was fixed in 5774899a4. I reran your executed case: 2029, age 20, £49,000 pay, £5,000 intended sacrifice, £2,000 employer pension, no haircut, 15% to 16% with full employee incidence. The excess is £3,000. Before the reform, employer NI is £259.51, and employer_cost and baseline_employer_cost are both £51,259.51. After it, pay is £48,985.09, employer NI is £274.42, and employer_cost and adjusted_employer_cost are both £51,259.51. Cost is now held fixed. An apprentice aged 24 gives the same figures. Your £60,000 control at age 20 stays at £63,909.51 (pay £59,890.26); at age 30 it stays at £70,700 (pay £59,500).
  • The new property cases stopping at 2028: resolved. test_employer_ni_fixed_cost_properties.py now draws 2026 to 2030 with a salary sacrifice. It also checks both cost equations against employer_cost.

Tests in a clean environment at 63dbac7d1

  • If I put back the four implementation files from 4f47cbf4d, 9 of the 17 excess and haircut regressions fail. At head all pass.
  • The changed pytest modules pass (127 passed). So do both changed YAML files (30 and 18 cases). ruff check and ruff format --check pass on the 12 changed Python files.
  • Every required CI check passes at this head, including all five Python shards and Aggregate impact. The branch merges cleanly with current main.

Could you re-review at 63dbac7d1? The PR stays in draft.

🤖 Generated with Claude Code

@vahid-ahmadi
vahid-ahmadi marked this pull request as ready for review October 24, 2026 15:40

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Employer NI ignores the under-21 and apprentice zero rates and the Employment Allowance

3 participants