Repository navigation
Annualise the employer NI secondary threshold to the statutory £5,000 - #2165
Conversation
From 2025-26 store the secondary threshold as 5,000 / 52 a week, the convention SI 2001/1004 reg 11(3A)(c) uses and the model already applies to the primary threshold and upper earnings limit. 52 weeks now give the reg 11(3A)(b) annual £5,000 instead of 52 x £96 = £4,992. The strict xfail in test_ni_threshold_freeze.py becomes an ordinary test. Fixes #1967 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22
left a comment
There was a problem hiding this comment.
Reviewed commit fe705bb41119436965fc7017b46371d7353e1e74. The £5,000 annual threshold correction is sound; no introduced formula defect found. All four annual threshold readers are consistent with the new internal weekly equivalent. Regulation 11(3A)(b) supports the annual amount; statutory weekly £96 and monthly £417 remain separate payroll-period amounts.
A1 — Please clarify the incidence setting behind the aggregate impact table (nonblocking).
The described comparison restores £96 through a reform with “nothing else changed”. With default employee incidence of 1, that reform also changes gross wages, so it does not isolate the parameter correction at fixed earnings.
Reproduced for a 2026 worker with £40,000 supplied through employment_income_before_lsr:
| Run | Calculated gross wages | Employer NI |
|---|---|---|
| Corrected baseline | £40,000.000 | £5,250.000 |
| Restore £96, default employee incidence | £39,998.957 | £5,251.043945 |
| Restore £96, employee incidence explicitly 0 | £40,000.000 | £5,251.200195 |
Please state the actual setting used for the £32.0m/£32.1m table. If it used the default, label it a wage-adjusted reform comparison or rerun at fixed gross earnings. If incidence was already zero, adding that detail resolves this finding. I did not inspect the original aggregate script or independently rerun that table. Phrase the £1.20 as the employer-liability difference at fixed annual earnings above £5,000, rather than an employee paying employer NI.
For a population analysis, employee incidence of zero does not itself disable consumer/capital incidence; those settings should also be explicit if reporting household effects or a fully incidence-free comparison. This is the documentation issue raised in #2168.
Validation: 92 NI YAML cases passed; 15 independent household diagnostic rows completed, including five baseline liability assertions around the threshold. Local Hypothesis-dependent suites could not collect because that dependency is absent. GitHub Test, Lint and Python 3.11–3.14 install checks are successful at the reviewed commit.
The code correction is supported; the remaining finding concerns the description's impact methodology.
|
Thanks, María. You were right on A1. The original setting. The −£32.0m / −£32.1m table compared this branch with a £96 reform under the default incidence settings, with no incidence parameter changed. So Rerun at fixed gross earnings. Same dataset (
This is £1.20 times the number of employees liable, plus a negligible amount from people earning between £4,992 and £5,000. For reference, under the default settings Description updated. The impact section now states the incidence settings, shows the fixed-earnings table first and labels the old figures as a wage-adjusted comparison. The £1.20 now reads as the difference in employer liability at fixed annual earnings of £5,000 or more, not as something the employee pays. 🤖 Generated with Claude Code |
juaristi22
left a comment
There was a problem hiding this comment.
Reviewed fe705bb. The annual £5,000 secondary-threshold correction remains sound. A1 is resolved: the description now separates the fixed-earnings comparison (all three employer NI incidence parameters set to zero) from the wage-adjusted comparison under default incidence, and describes £1.20 as the employer-liability difference. CI is green at this head. I did not independently rerun the population table. Approving.
Enhanced FRS impact (post-merge check)The impact above used the microcosm drill-2 file. This repeats it on the Enhanced FRS 2024-25 (private build, SHA-256
2031 is the first CPI-uprated year after the freeze. It now uprates from £5,000 rather than £4,992. These are close to the drill-2 figures above (−£36.7m in 2026, −£36.9m in 2027). Checks run on every one of the 113,617 person records in every year:
About the dataset: it predates the fix for PolicyEngine/policyengine-uk-data#504, which gave SPI-synthetic under-16s employment income. That does not affect this comparison. Secondary Class 1 needs an earner over 16 ( The scripts and outputs (aggregates only) are kept outside the repo. Follow-up: #2178 corrects the 2015-16 and 2016-17 secondary threshold (£155 → £156) and pins the annual threshold to reg 11(3A)(b) for every year from 2015-16. |
…ertion docs/book/policy/model-baseline.md still said the model annualises the secondary threshold as 52 x £96 = £4,992, which #2165 fixed. At the annual threshold itself, the stored 5,000 / 52 annualises to £4,999.999992, so employer NI there is exactly zero only after float32 rounding. Assert less than half a penny instead of exact zero (review finding). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #1967
Summary
From 2025-26,
gov.hmrc.national_insurance.class_1.thresholds.secondary_thresholdheld the rounded weekly £96 from SI 2001/1004 reg 10(d).ni_class_1_employermultiplies it byWEEKS_IN_YEAR(52), so the model's annual threshold was £4,992. For an annual earnings period, the law gives £5,000:The primary threshold and upper earnings limit already use this convention (12,570 / 52 and 50,270 / 52).
docs/book/policy/model-baseline.mdalready says the threshold was reduced "to £5,000 annually". Before 2025-26 the two conventions agree (£175 × 52 = £9,100).Change
secondary_threshold.yaml: the values from 2025-04-06 to 2030-04-06 are now 96.153846 (= 5,000 / 52). The 2025-04-06 entry now also cites reg 11(3A)(b), and the header comment explains the convention. The value is a single number, so CPI uprating from 2031-32 now starts from £5,000 a year instead of £4,992.period: week, so it will now show as £96.15 a week (5,000 / 52) instead of the statutory reg 10(d) £96. The primary threshold already shows a derived weekly figure (£241.73) in the same way. The other option, keeping £96 and having the formula read an annual £5,000, was not taken because Employer NI annualises the secondary threshold as 52 × £96 = £4,992, not the statutory £5,000 #1967 specifies the annual / 52 convention.ni_class_1_employerand the three contrib employer-NI incidence variables (adjusted_employer_cost,baseline_employer_cost,employer_ni_fixed_employer_cost_change) all multiply the same parameter by 52, so they now use £5,000 too.changelog.d/1967.fixed.md.Impact
For an employee with fixed annual earnings of £5,000 or more, employer NI liability is £1.20 a year lower than under the old £4,992 threshold (15% × £8). Example: at £40,000 in 2026-27, employer NI is £5,250.00, not £5,251.20. Between £4,992 and £5,000 the difference is smaller (15% of the earnings above £4,992).
Aggregate check on the drill-2 dataset (
microcosm_uk_2024_25.h5, SHA-256aa31bdf6…), weighted totals, model years 2026 and 2027. Each run compares this branch (£5,000) with the same code plus a reform restoring £96 a week (£4,992 a year) from 2025-04-06.Fixed gross earnings. Both runs set all three employer NI incidence parameters to 0:
gov.contrib.policyengine.employer_ni.employee_incidence,consumer_incidenceandcapital_incidence. Gross employment income is identical in the two runs (largest per-person difference: £0).ni_class_1_employergov_balanceAt fixed earnings nothing else moves, so
gov_balancechanges by the same amount as employer NI. The change is £1.20 times the number of employees liable, plus a negligible amount from the £4,992–£5,000 band (about 150 weighted employees in 2026 and 7,100 in 2027). The model holds one employment per person, so these are counts of people.Wage-adjusted comparison (default incidence). The first version of this description reported −£32.0m (2026) and −£32.1m (2027). Those figures used the default settings (
employee_incidence = 1), so the £96 reform also lowered gross wages to keep employer cost fixed. They are not a fixed-earnings comparison. For reference, under the default settings:ni_class_1_employeremployment_incomegov_balanceHere the correction raises wages by close to the employer NI saved, so part of it comes back as income tax and employee NI.
Tests
test_ni_threshold_freeze.py: the statute table now holdsFraction(5_000, 52). The strict xfailtest_annual_secondary_threshold_is_statutoryis now an ordinary test covering 2025 and 2026–2030, at ±£0.01. The edge cases now include both £4,992 and £5,000.ni_class_1_employer.yaml: £5,251.20 / £8,251.20 / £45,251.20 become £5,250 / £8,250 / £45,250. New cases: £5,000 (2025-26) → 0; £4,996 (2026-27) → 0; £6,000 → £150; £40,000 in 2025-26 → £5,250.ni_threshold_freeze.yaml: expectations now use £5,000.main's parameter, 12 of the NI YAML cases fail. On this branch they pass.policyengine-core test policyengine_uk/tests/policy -c policyengine_uk(full YAML suite: 2,562 passed).pytest test_ni_threshold_freeze.py test_ni_class_1_age_properties.py(57 passed).pytest policyengine_uk/tests/ -m "not microsimulation" -n 6: 3,084 passed, 4 skipped, 2 xfailed (the five Employer NI annualises the secondary threshold as 52 × £96 = £4,992, not the statutory £5,000 #1967 strict xfails are now ordinary passing tests). Microsimulation-marked tests were not run because they need the full dataset.ruff format/ruff checkon the touched Python file: clean.axiom: TheAxiomFoundation/rulespec-uk#438 queued
🤖 Generated with Claude Code