Repository navigation
Apply 2022–23 annual employee NI for full-year category A directors - #2493
Conversation
vahid-ahmadi
left a comment
There was a problem hiding this comment.
Fine once the book page keeps its legal references and drops the PR-coordination note.
@juaristi22, the numbers match HMRC's 2022 to 2023 employer rates page. For directors, it gives a £11,908 annual primary threshold, a £50,270 UEL and category A employee rates of 12.73% and 2.73%. I hand-checked two cases. £30,000 gives (30,000 − 11,908) × 0.1273 = £2,303.11. At the UEL, 38,362 × 0.1273 = £4,883.48. The month formulas resolve in_effect correctly for all twelve months of model year 2022, because of the fiscal-year date shift: every month gives £191.93, which sums to £2,303.11. Model year 2023 falls back to the ordinary route.
- should:
docs/book/programs/gov/hmrc/national-insurance-directors.md. The rewrite removes the statutory references the old page had: SI 2001/1004 reg 8 (the annual earnings period for directors), NIM12000, CA44 and SSCBA s.8. Reg 8 is the legal basis of the annual method, and nothing in the new parameters or variables cites it now. Please keep those references on the page, and add reg 8 to theemployee_scale.yamloris_full_year_category_a_directorreferences. - should:
docs/book/programs/gov/hmrc/national-insurance-directors.md:84-88. The paragraph on landing order with #2490 is process information. It will be out of date as soon as either PR merges. Keep it in the PR body only. - nit:
ni_class_1_employee_primary.py:37andni_class_1_employee_additional.pyindex the scale by position (rates[1],thresholds[2]). That works for this three-band scale, but it breaks silently if a band is ever added. A short comment naming the bands, or named threshold parameters, would make it safer. - nit: the parameter folder is year-specific (
full_year_category_a_2022_23). HMRC's 2023 to 2024 page also gives directors a separate annual main rate, 11.5% against the ordinary 12% then 10%. If that year is added later, onedirectors/annualscale with dated values and anin_effecttimeline would avoid a second near-copy of this folder. This does not block this PR.
What I checked
- Read the full diff, the docs page and issue #721. Checked the rates and thresholds against HMRC's 2022 to 2023 and 2023 to 2024 employer rates pages.
policyengine-core testonni_class_1_full_year_director_2022.yaml: 10 passed.pytest test_ni_full_year_director_2022.py: 15 passed. All oftests/policy/baseline/gov/hmrc/national_insurance: 152 passed.- Reverted the two employee component formulas. The new YAML file then fails 10 of 10.
- Simulated by month for 2022 and 2023 with the flag set, to confirm the monthly allocation and the year gate.
ruff checkandruff format --checkon the changed Python files: clean.- Hosted CI on this head: all green.
- Not checked: the combined tree with #2490, or private-data runs.
🤖 Generated with Claude Code
f686e04 to
54b9f73
Compare
|
Restored the director page’s legal and HMRC references, added regulation 8(3) to the annual-scale references, and removed the PR landing-order note from the book page. The PR description retains the #2490 coordination qualification. Rates, dates, formulas and tests are unchanged; the changed page passed HTML export without executing model code. After integration with main at |
Aggregate impact on certified data: passBase Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 0 (thresholds in Notes
Moves flagged for reviewNone. Spending by benefit and service
Caseloads
Household net income by decileNo household's net income changes in any year. No change in any year: receipts by tax head; government balance, each component counted once; model totals; components the model counts twice; household income totals; population; poverty rates; people in poverty; inequality; mean household net income in each side's own deciles; standard reform costings (change in gov_balance); standard reforms: change in each reform's target. Checks failing on both base and head (1)
Counted-once checkEach of 84 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.
Provenance
Dataset: Receipts by tax head: head levels
Spending by benefit and service: head levels
Caseloads: head levels
Government balance, each component counted once: head levels
Model totals: head levels
Components the model counts twice: head levels
Household income totals: head levels
Population: head levels
Poverty rates: head levels
People in poverty: head levels
Inequality: head levels
Mean household net income in each side's own deciles: head levels
Standard reform costings (change in gov_balance): head levels
Standard reforms: change in each reform's target: head levels
|
vahid-ahmadi
left a comment
There was a problem hiding this comment.
@juaristi22 round 2, at 54b9f733f. The branch was rebased onto 8b12bce. The two earlier commits carry no changes (git range-diff shows them as identical), so I reviewed the new commit against round 1.
Verdict: both round-1 shoulds are fixed and the code is unchanged. One process point is left: the axiom: line. I missed it in round 1, sorry.
Round-1 points
- should: legal references on the book page: fixed. The page cites SI 2001/1004 reg 8(3) again, and the References section has reg 8, SSCBA s.8, NIM12000 and CA44 back.
employee_scale.yamlnow cites reg 8(3) as well. I read reg 8(3) on legislation.gov.uk. It makes the earnings period the year for anyone who is a director at the start of that year, which is the case this PR covers. All four links resolve. - should: landing-order note on the book page: fixed. The note is gone from the page and now sits only in the PR body.
- nit: positional indexing (
rates[1],thresholds[2]): not changed. Still optional. - nit: year-specific folder: not changed. Still optional, and fine to leave for whoever adds 2023 to 2024.
New findings
- should: PR body
Axiom: needed. PolicyEngine/.github CONTRIBUTING.md ("Mirror policy changes in Axiom") keepsaxiom: neededfor external contributors. This PR changes NI liability, so as a maintainer you need one ofencoded-correct,encoded,queued(a dispatch-ready rulespec-uk issue labelledpe-parity) orn/a. I found no rulespec-uk issue for reg 8 or the 2022 to 2023 director schedule. rulespec-uk has no module for reg 8 either (uk/regulations/uksi/2001/1004/holds only regs 10 and 100), soencoded-correctis not open to you.queuedwould need a new issue, unless the encoder is free and you can encode it alongside. The Fix evidence check acceptsneeded, which is why CI didn't catch it. No code change is needed. With that line updated, this is ready to approve. - nit: stale PR body. The body still says hosted CI "remains to be checked". All 16 checks now pass on this head.
Landing order
This still fits #2493, then #2452, then #2490:
- It merges cleanly with current main (
410bf54d7) and with #2452 (eeaf911cc). They share no files. - It conflicts with #2490 (
e889563e8) inni_class_1_employee_primary.pyandni_class_1_employee_additional.py, as the body says. Since #2490 lands last, that PR resolves the conflict and keeps the director branch. - Main has moved since
8b12bce(#2184, #2491, #2496 and others). None of those touch NI, and the new parameter-description test passes on the merged tree.
What I checked
- Read the fix commit, your reply, the updated page and the PR body.
- Fresh worktree and uv venv at the head.
policyengine-core test tests/policy/baseline/gov/hmrc/national_insurance: 152 passed.pytest test_ni_full_year_director_2022.py: 15 passed.ruff checkandruff format --checkon the changed Python: clean. - On a local merge of this head into current main: the same NI tests pass (152), and
test_parameter_descriptions.pyplus the director pytest give 39 passed. git merge-treeagainst main, #2452 and #2490, as above.- CI at the head: all 16 checks pass. The aggregate-impact report passes, as expected for an input that defaults to false.
- Not checked: the combined tree with #2490, or private data.
🤖 Generated with Claude Code
|
Addressed the Axiom process request: rulespec-uk#472 is OPEN with Current-head Lint, Test / Result, Fix evidence and documentation checks passed on |
vahid-ahmadi
left a comment
There was a problem hiding this comment.
@juaristi22 round 3, at 54b9f733f (no code change since round 2; the body was edited).
Verdict: approve. The axiom: line now points at a dispatch-ready queued issue, and the stale CI sentence is fixed. Two small nits on rulespec-uk#472, neither blocking.
Round-2 points
- should,
axiom:line: fixed. The body readsaxiom: TheAxiomFoundation/rulespec-uk#472 queued, the same format as merged #2458, and Fix evidence passes. #472 is open withpe-parity, and has the module paths and corpus selectors, the required output, companion cases from HMRC's 2022 to 2023 table, and the blocker (no approved billed encoder run). I checked the quoted law against legislation.gov.uk: SI 2001/1004 reg 8(3), NICA 2022 s.4, HSCL (Repeal) Act 2022 Sch para 5 and reg 11(2A)(b) at 2022-07-06 all match word for word. - nit, stale CI sentence in the body: fixed.
- nit, positional indexing: unchanged, still optional.
- nit, year-specific folder: unchanged, still optional.
New findings
- nit, #472 companion arithmetic. The table gives values with no working. I re-derived all nine rows and they hold, e.g. £30,000: (30,000 − 11,908) × 12.73% = 2,303.1116; £60,000: 38,362 × 12.73% + (60,000 − 50,270) × 2.73% = 4,883.4826 + 265.629 = 5,149.1116. One worked line in the issue would let the encoder check them quickly.
- nit, leftover draft wording in #472. "This draft covers", "Proposed label", "A fresh duplicate/activity guard is still required immediately before creating the ticket" and "Keep the Axiom-needed follow-up open" read as pre-filing notes and predate the switch to
queued. Worth tidying when you next touch the issue.
Landing order
#2493, then #2452, then #2490 still holds:
- Merges cleanly with current main (
47642d1ee) and with #2452 (eeaf911cc). - Same two conflicts with #2490 (
e889563e8) inni_class_1_employee_primary.pyandni_class_1_employee_additional.py, which #2490 resolves as it lands last. - Nothing merged to main since
8b12bcetouchesnational_insurance/class_1.
What I checked
- Head unchanged since round 2, so the round-2 test results (152 NI YAML tests, 15 director pytest cases) still apply.
- Read #472 in full and fetched each cited provision from legislation.gov.uk.
git merge-treeagainst main, #2452 and #2490, as above.- CI at the head: all 16 checks pass, including Fix evidence.
🤖 Generated with Claude Code
…ment-charge No conflicts. programs.yaml auto-merged: main's registry fixes (#2443) and this branch's notes on income tax, the Winter Fuel Payment and PAWHP touch different entries. #2493's 2022 full-year director NI and #2506's aggregate-impact runtime identity read nothing this branch changes; the director tests read income_tax only in 2022, before the charge. Main: 6990350 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #721. This implements the 2022–23 annual employee Class 1 National Insurance schedule for full-year category A directors with one employment. The issue remains open for the remaining cases.
The model currently applies the ordinary employee schedule to these directors. The new
is_full_year_category_a_directorinput defaults to false and selects the historical £11,908 primary threshold, £50,270 upper earnings limit, 12.73% main rate and 2.73% additional rate in model year 2022. For £30,000 of annual NI earnings, employee NI changes from £2,665.90 to £2,303.11. Monthly components allocate one twelfth of annual liability; they do not model interim payroll deductions. Existing liability gates and supplied-output overrides still apply. The existing Class 4 cap consumes employee primary NI, so directors with trading profits can also have an indirect Class 4 change.HMRC’s 2022–23 historical table supplies the amounts and rates; HMRC’s directors guidance supplies annual-method qualifications. Employer NI, appointment-year prorating, category changes, multiple employments, other years and payroll timing remain outside this PR.
Validation: earlier implementation and follow-up evidence covered 159 distinct cases, with unchanged cases carried forward. That historical evidence used Core 3.32.27/microdf 1.2.1. After integration with main at
8b12bce, the focused NI selector passed 72 unique tests on Python 3.13/Core 3.33.2/microdf 1.5.11 in 45.39 seconds; these overlap earlier cases and are not added to the historical total. Independent captured-head CODE/POLICY review verified both requested documentation/reference fixes with no scoped findings or material gaps. Ruff, the trusted current-head fix-evidence check and changed-page HTML export passed. The full suite, private-data tests, combined #2490 tree and encoded parity remain unverified locally; hosted CI at 2026-10-19T18:04:25.909871+00:00 passed on54b9f733f62dd68cf0a70fc1b57b1090f6039f92(Lint, Test / Result, Fix evidence and documentation builds).This shares two employee component files with #2490. A separate synthetic integration preserved that PR’s scale migration: 51 prior test passes remained qualified by unchanged production/test bytes, and the new Class 4 composition case passed on the follow-up integration tree. Coordinate merge order and preserve both changes. This is local compatibility evidence; the combined tree has not received an independent review or hosted CI.
Reproducer: policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_1/ni_class_1_full_year_director_2022.yaml
Landing coordination
Landing plan: #2493, then #2452, then #2490, as requested in the #2490 review. Preserve this PR's director selector and employee contribution components when #2490 integrates its scale migration. Actual shared-component migrations, owner acceptance and fresh combined-tree checks remain conditions of #2490; documenting this order does not complete them.
axiom: TheAxiomFoundation/rulespec-uk#472 queued
The statutory schedule and status selection require a supported encoding/parity follow-up. No encoding or parity result is claimed.