Repository navigation
Read Carer's Allowance's pension contribution share from a parameter - #2518
Conversation
carers_allowance_earnings deducted a literal 0.5 of pension contributions. The Computation of Earnings Regulations 1996 set that one-half in regs. 10(4)(b) (employed earners) and 13(1)(b)(ii), (4)(c) and (10)(b) (self-employed earners), unchanged since they came into force on 25 November 1996; Northern Ireland's SR 1996/520 says the same. Add gov.dwp.carers_allowance.pension_contribution_deduction_rate (0.5 from 1996-11-25) citing them and read it in place of the literal, so the share can be reformed. Current-law outputs are unchanged. YAML cases show a reform of the share moving earnings by contributions x the change in share and, through the earnings limit, entitlement; a Hypothesis test states the exactness, locality and eligibility invariants. Fixes #2517 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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. Receipts by tax head
Spending by benefit and service
Caseloads
Government balance, each component counted once
Model totals
Household income totals
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
Household net income by decileMean change in household net income per household, and as a share of the decile's base income; households stay in the base run's deciles.
No change in any year: components the model counts twice; population; poverty rates; people in poverty. 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
|
The case paid Carer's Allowance, which is taxable, so Housing Benefit net earnings carried a share of the resulting income tax (134.64) that the expected value left out. Use the over-limit carer instead: no allowance is paid and no tax is due, so Housing Benefit net earnings are 12,000 less a quarter of 2,000. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The property test promoted carers_allowance_earnings to float64 before repeating the formula's penny rounding. The model stores earnings as float32, so 10,608.52 a year is stored as 10,608.51953125: in float32 the formula counts 20,401 weekly pence (over the £204 limit), but the float64 recomputation floors to 20,400 and expected not gainfully employed. Keep the stored dtype for the rounding check, pin that boundary as an explicit Hypothesis example, and widen the exactness tolerance to 0.05 for float32 spacing at the largest earnings drawn. Found by the GPT-6.1 Sol review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Hand-off to the UK hub (from session local_4a81f29b-42f5-46fb-b59d-3dfbef32d322)
|
|
Merged by the UK hub (session local_8e19f9d5, Opus 5.5) on its merge gates, from the owner session's hand-off (local_4a81f29b). One merge-queue enqueue, pinned to head
🤖 Generated with Claude Code |
Fixes #2517
Status: ready for review. At head 99d9703, the Aggregate impact check passed with no change in any figure, and the independent review approved (see "Review"). CI is finishing at that head.
Summary
carers_allowance_earningsdeducted a hard-coded0.5of pension contributions from earnings for the Carer's Allowance earnings limit. That one-half is nowgov.dwp.carers_allowance.pension_contribution_deduction_rate, cited to the law, so it can be reformed. This is the Carer's Allowance counterpart of #2516 (Council Tax Reduction and Income Support) and #2497 (Housing Benefit). JSA's literal is #140 and is left alone.What the law says
The earnings limit counts earnings under the Social Security Benefit (Computation of Earnings) Regulations 1996 (SI 1996/2745), in force from 25 November 1996 (reg. 1):
How the law was checked:
carers_allowance_earnings.So the parameter is 0.5 from 1996-11-25 and cites both sets of regulations. The law review found no error in the current-law value.
What changes in the model
gov.dwp.carers_allowance.pension_contribution_deduction_rate: 0.5 from 1996-11-25.carers_allowance_earningsmultipliespension_contributionsby that parameter instead of0.5. Its documentation now names the parameter.The diff to
carers_allowance_earnings.pytouches only the documentation sentence and the deduction lines, not the Class 4 lines #2490 rewrites, so the two shouldn't conflict.Invariants
These hold for every input.
test_carers_allowance_pension_contribution_deduction_rate_properties.pystates them and tests them with Hypothesis. The test draws populations of carers in all four UK countries and in 2016, 2020, 2024, 2026 and 2030. Each carer has pay, trading profit (a loss counts as nil), and employee and personal contributions of up to 30% of earnings, so no floor binds.carers_allowance_earningsfalls by exactly contributions × (r − 0.5).Invariant 3 counts pence in float32, the precision the model stores earnings in, as the formula does. An explicit Hypothesis example pins the boundary: pay of £10,608.52 is stored as 10,608.51953125, which is 20,401 weekly pence in float32 (over the limit) but 20,400 in float64.
The YAML cases (
carers_allowance_pension_contribution_deduction_rate.yaml) are hand-derived for 2026-27. With pay of £12,000 (no tax or Class 1) and £3,000 of contributions, earnings are 10,500 (£201.92 a week, under the £204 limit). At a 0.25 share they are 11,250 (£216.35, over), a rise of 750, which is 3,000 × 0.25, and Carer's Allowance goes to nil. A 0.75 share brings a £2,000 contributor from 11,000 (over) to 10,500 (under) and restores the allowance. A trader at a 0.4 share rises by 770, which is 7,700 × 0.1. A reform of HB's share moves HB net earnings and leaves Carer's Allowance earnings alone. That case uses the £2,000 contributor, who is over the limit, so no Carer's Allowance is paid and no income tax is due.Current-law outputs
The new parameter is exactly 0.5, the value of the literal it replaces, in every year the model simulates. The arithmetic is otherwise unchanged.
Re-measured by the UK hub at this head (2026-10-20): run 39383602431. It compares main
47642d1eewith main merged with99d9703f2on certified policyengine-uk-data 1.58.1, and finds no change in any figure in any year, 2025-26 to 2031-32. No household's net income changes. The hub re-ran it because the PR-triggered report at this head compared against a stale base (7e8682590) that left out 10 main commits.The Aggregate impact check at the first head (8991834, merged into main as f39cccb) passed. It found no change in any year from 2025-26 to 2031-32 in receipts by tax head, spending, caseloads, the government balance, household income, poverty, inequality or the standard reform costings. No household's net income changed. The second commit changes only a YAML test.
Tests
CI at the first head passed every check except one YAML case, the Housing Benefit locality case. Its carer was paid Carer's Allowance, which is taxable, so HB net earnings carried the share of income tax that HB attributes to earnings (£134.64). The expected value had left that out. The second commit moves the case to the over-limit carer, who owes no tax.
The
route-a-release-2026-10reservation (to 2026-10-21 04:00Z) allows only light local checks. So the only local run was the new YAML file, against this branch's source, in an existing venv whoseuv.lockis identical to this branch's: all 6 cases pass. The Python tests, including the Hypothesis test, passed in CI at the first head. After the round-1 fix, the property test file was also run locally the same way: 2 passed.uvx ruff@0.17.0 format --check .passes, and so doesruff checkon the changed Python files.Reproducer:
policyengine_uk/tests/policy/baseline/gov/dwp/carers_allowance_pension_contribution_deduction_rate.yaml::A lower share raises earnings by contributions times the change and ends entitlement,policyengine_uk/tests/test_carers_allowance_pension_contribution_deduction_rate_properties.py::test_earnings_fall_by_contributions_times_the_change_in_shareaxiom: n/a: infrastructure — parameter wiring, current-law outputs unchanged
Review
axiom:lines, and the absence of a conflict with Store marginal National Insurance schedules as scales #2490. It confirmed the one-half from the Northern Ireland consolidated text and from legislation.gov.uk's indexed GB text. Direct legislation.gov.uk XML fetches failed in its sandbox.@example, and the exactness tolerance is 0.05, wide enough for float32 spacing (about 0.008) at the largest earnings drawn. Locally the file passes (2 passed). A copy with the float64 conversion restored fails at the boundary example.over_limitrepeats the formula's operations in the same order and dtype under NumPy 2's promotion rules, and that 0.05 leaves more than four times the worst-case float32 error while still catching a wrong share for contributions above about £1 a year. It made two non-blocking notes: one docstring line is longer than 88 characters (E501 is ignored, so CI passes), and the tolerance could be as low as about 0.02.Reviews:
~/reviews/uk-2518-ca-pension-share/review-r1.mdandreview-r2.md(local to the author's machine).🤖 Generated with Claude Code