Repository navigation
Conversation
…ldcare and local CTR Fixes #1986. - Benefit cap earnings exception (UC Regs 2013 reg 82): earned income is each person's earnings less their own tax, NI and pension contributions, summed over the unit, without the minimum income floor (reg 82(4)). The threshold is 16 hours a week at the national living wage, x 52 / 12, in whole pounds (reg 6(1A)(za)), x 12; GBP 430 a month before 1 April 2017. - Targeted childcare (SI 2014/2147 reg 1(3)(a)) and the Kingston, Merton, Newham and Westminster CTR schemes (Default Scheme para 37) read earned income before the work allowance, the new uc_earned_income_before_work_allowance, which uc_earned_income now uses. - uc_individual_earned_income_before_mif is copied unchanged from #1973 so the two PRs add identical files. - Adds the missing 2023-24 minimum wage rates (SI 2023/354). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…wn cell - The Class 3 and unearned income property now bumps one adult in every family by a realistic amount and mixes in earners just above the threshold, so it fails against the pre-#1986 formula (it did not before). - Adds a reg 82(4) property: switching every minimum income floor off never changes benefit cap earned income. - Moves the benefit cap earnings exception docs into a separate notebook cell, leaving the means-test cell that #1973 also edits untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…c-earned-income-consumers-1986
…income Follows #1949's f333e17: contributions paid after 75 are not relievable (Finance Act 2004 s. 188(3)(a)), so reg 55(5)(a) does not deduct them. uc_individual_earned_income_before_mif now matches uc_individual_earned_income apart from the minimum income floor. #1973 carries the same file without this change; whichever merges second takes this version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Heads-up on an overlap: #2053 (fixes #2044) adds the same 1 April 2023 minimum wage rates as a standalone fix, cites the S.I. and regulation for every dated rate, and adds a pytest transcribed from the S.I.s. Once #2053 merges, this PR should drop its two 🤖 Generated with Claude Code |
Independent review of b696f8d (legal/code and tests/impact lanes): - The benefit cap is shared with Housing Benefit, so the UC earnings exception also excepted HB claimants; with the NLW-based threshold that newly excepted some in years when the threshold was under 10,152. Reg 82(1) applies "to an award of universal credit"; HB has its own exceptions (HB Regs 2006 regs 75E and 75F, WTC entitlement already in is_benefit_cap_exempt_health_disability). The income test is now uc_benefit_cap_earnings_threshold_met, and the exception needs a UC award before the cap as well. Adds the reviewer's HB regression. - Documents that the cap counts miscellaneous_income as the UC award does (FRS misc is mainly odd-job pay, reg 52(a)(iii)); the reg 66(1)(m) royalties split will move both together. - Tests: exact pins at 10,572 and 15,400, ages 74/75 for relievable contributions, a London BRMA in the counterexample, a floor-dependent childcare case, and a fixed 16,500 -> 16,501 childcare crossing in the monotonicity property (it kills a reversed-inequality mutant the random draws missed). Property 1 compares the income test, and the exception where there is a UC award both ways. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#2053 adds the same S.I. 2023/354 rates with regulation-level citations and tests, so #1999 no longer carries them and the two do not conflict. The benefit cap's 2023-24 threshold follows once #2053 merges (722 a month; 658 until then). The 2023 benefit cap cases move to 2024-25 (793 a month = 9,516 a year): the 9,520/9,500 pair and the Housing Benefit regression (9,600 earnings, cap 25,323). The property tests sample 2024 instead of 2023. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…c-earned-income-consumers-1986 # Conflicts: # policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income.py
After #1896, is_claimant_or_partner is inferred when not given, and an adult under 20 who is 16+ years younger than the other can be presumed a child. The generated families have at most two adults, both claimants, as #1949's fixtures now declare. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Coordination with #2089 (each scheme its own benefit cap), stacked on #2084. #2089 moves the reg 82 earnings exception to Whichever lands second: put this PR's |
…c-earned-income-consumers-1986
…c-earned-income-consumers-1986
|
Note from #1949 (head 8c88c7c, not merged yet): it adds a strict xfail pinned to #1986 that this PR will flip to XPASS.
|
Brings the branch up to main at be877c8, which includes #1949 (fc1ecc4) and #1943. Conflicts: - docs/book/programs/gov/dwp/universal-credit.ipynb: main's notebook with this PR's minimum income floor paragraph re-inserted after the earnings-deductions sentence. - uc_individual_earned_income.py: this PR's reg 62(2)/(3) floor. Main's Finance Act 2004 s.188(3)(a) age filter on pension contributions is ported to uc_individual_earned_income_before_mif, where this PR takes the deductions; the file is copied as is from #1999 (blob 73a9370) so the two PRs merge in either order. - test_uc_earnings_deductions_properties.py: main's file with this PR's start-up-period note, the pre-#1942 gross earnings and invariant 4's start-up setting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four defects in the benefit cap exception logic: 1. The earnings exception subtracted total income tax liability from earnings, so tax on property, savings, dividend and pension income reduced measured net earnings. Regulation 55(5) of the Universal Credit Regulations 2013 deducts only the tax paid in respect of the employment, primary Class 1 NI and relievable pension contributions. Now uses earned_income_tax, national_insurance and pension_contributions. 2. The earnings threshold was hardcoded as 10_152 in three files. Moved to the dated parameter gov.dwp.benefit_cap_earnings_exemption, expressed monthly as reg. 82 and DWP do, with values for 2020-21 to 2026-27 derived from 16 hours a week at the National Living Wage. 3. Armed Forces Independence Payment is a statutory exception but was never checked. Added to the health/disability qualifying benefits. 4. Removed dead copy-pasted code from all three is_benefit_cap_exempt_* variables, keeping each one's return semantics unchanged apart from defect 3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s fix makes it pass The earnings exception to the benefit cap now nets only earned_income_tax off earnings, so tax on dividends no longer removes it (#1986). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: - uc_earned_income.py: main's documentation and references, reading the branch's uc_earned_income_before_work_allowance; the work allowance still comes off it (max(0, before - work allowance)). - is_benefit_cap_exempt_earnings.py: the branch's version for now; it is reworked on top of #1820 in the next merge. Semantic follow-ups to main's claimant rule (is_uc_assessed_claimant, from #1978): uc_earned_income_before_work_allowance and benefit_cap_earned_income now sum over the assessed claimants with add_for_members, as main's uc_earned_income did. uc_individual_earned_income_before_mif is main's (the branch's copy was identical). benefit_cap_earned_income documents that miscellaneous_income is counted as the award counts it (#1961). The CTR helper comment names the Default Scheme regulations correctly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch is stacked on #1820 and must not merge before it. #1820's changes (AFIP exception, dated earnings threshold parameter, earnings-attributable tax in the reg. 82 test, dead-code removal, its YAML tests, changelog fragment and the dividend xfail removal) are Vahid's and land through #1820. Conflicts: - is_benefit_cap_exempt_health_disability.py: main's claimant scoping (not_a_claimant, add_for_members) kept; #1820's removal of the dead earnings block and its AFIP entry applied on top. - is_benefit_cap_exempt_earnings.py: #1820's version. The next commit replaces only its earned income measure for #1986. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1820 measures the benefit cap's earned income as earnings less earned_income_tax, national_insurance and pension_contributions. That still deducts tax on pension and property income, all Class 3 (the #1986 counterexample: a partner's 923 of voluntary Class 3 removed a couple's exception) and contributions paid after 75. Replace only that measure with benefit_cap_earned_income: each claimant's uc_individual_earned_income_before_mif (earnings less their own tax and NI on those earnings and relievable pension contributions), summed over the claimants, without minimum income floor income (reg. 82(4)). #1820's structure, threshold parameter and comments stay; the comments its measure made untrue are edited. Reg. 82(1) excepts "an award of universal credit", so the exception no longer lifts the cap on a Housing Benefit award without Universal Credit (HB's own work exception is WTC entitlement, HB Regs 2006 reg. 75E). Families with neither award keep the income test, so #1820's YAML cases are unchanged. Threshold: drop #1999's own NLW derivation (benefit_cap_earnings_threshold, uc_benefit_cap_earnings_threshold_met and the earnings_exception parameters) in favour of #1820's gov.dwp.benefit_cap_earnings_exemption, extended back with cited values: 430 a month from 29 April 2013 (reg. 82(1)(a) as made), then 520, 542 and 569 for 2017-18 to 2019-20 (S.I. 2017/138; NMW Regs 2015 reg. 4; reg. 6(1A)(za)). A differential test checks every dated value from April 2017 against floor(16 x NLW x 52 / 12) from gov.hmrc.minimum_wage. Tests: the cap YAML moves onto the parameter with boundary pairs for 2016, 2017, 2018, 2019 and 2023 (722 a month); the property file computes the income test from the parameter and loads the model once per module; a rent pin joins #1820's de-xfailed dividend case; the floor childcare case names the responsible carer, since main now defaults the nomination to the elder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
) The cap's earned income shares uc_individual_earned_income_before_mif with the UC award, which counts miscellaneous_income as earned. The FRS input mixes paid work with royalties, trust income and overseas pensions, which reg. 66(1) treats as unearned; #1961 tracks the split. A YAML case pins the shared treatment so the #1961 fix moves the award and the cap together. Also corrects a test comment that still described a fixed 10,152 threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each case now checks is_benefit_cap_exempt_earnings as well as the earned income, so a measure that deducts contributions paid after 75 (not relievable, Finance Act 2004 s. 188(3)(a)) fails the exception test too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
CI run cancelled to free runners (UK hub, 2026-10-11 23:30Z). The PolicyEngine org is at GitHub's limit of 60 concurrent jobs, with about 360 queued. Fleet ops asked the hub to let the Budget-critical #2305 take runners first. This PR's queued run had not started, so I cancelled it. Nothing is wrong with the PR. Please don't re-run it or push just to retrigger CI. The hub re-runs this run itself once #2305 has runners. The record is |
Includes the reform-contract and tax-band fixes; preserves the stack on #1820 without conflicts. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Fixes #1986
Depends on #1820
This draft is stacked on Vahid Ahmadi's #1820 at
ea2b87cece52736992dc6ce1860f16b2a02b0325. It must not merge before #1820. Vahid's work lands through his PR; after it merges, merge main here so this diff reduces to the #1986 changes. Main0bc08f2fd45a4dc5215da7f4a055978226635f36was merged without conflicts as00d4bdb0328e7f915fd6aaea62ff091d67f57141on 12 October 2026.Vahid's #1820 contributes the parameterised earnings threshold, earnings-attributable tax measure, Armed Forces Independence Payment exception, surrounding exception structure, removal of dead code and corrected labels, its 12 YAML cases and
1818.fixed.md, and removal of the strict dividend-property xfail. This branch preserves that work. The earnings measure is the part replaced for #1986.This PR adds:
benefit_cap_earned_income: sum each assessed claimant'suc_individual_earned_income_before_mif, using main's individual definition. Each person deducts their own tax and NI on earnings and relievable pension contributions. Voluntary Class 3, tax on unearned income and contributions paid at 75 do not reduce it; minimum income floor income is excluded.gov.dwp.benefit_cap_earnings_exemptionparameter: £430 monthly from 29 April 2013, then £520, £542 and £569 from April 2017, 2018 and 2019. The old derived threshold variable and its separate parameters are removed. The differential test compares every dated value from April 2017 with the minimum wage parameter and statutory rounding.uc_earned_income_before_work_allowance: preserve main's assessed-claimant member rule and individual minimum income floor treatment;uc_earned_incomesubtracts the work allowance and floors at zero.The pension-tax approximation and pension-age sentence in #1820's earnings-file comments are replaced because the new measure makes them inaccurate. Its AFIP and health/disability changes, dead-code removal, tests and fragment remain. Main's other individual earnings changes, including #2327's miscellaneous-loss treatment, remain intact.
Code at this head: cap income, threshold and pre-cap award scope, pre-allowance claimant sum, childcare, and CTR.
Law and scope
Primary texts were retrieved again on 12 October 2026. Their substantive XML matches the saved point-in-time evidence; all four freshly retrieved official borough PDFs are byte-identical to the saved scheme copies.
UC Regs 2013 reg 82 applies to “an award of universal credit”. Paragraph (1)(a) uses 16 hours at the reg 4 national living wage rate, converted by 52/12; paragraph (4) excludes “income a person is treated as having by virtue of regulation 62”. Reg 6(1A)(za) requires the fractional pound to be disregarded. SI 2017/138 introduced the formula and rounding from 1 April 2017; reg 82 as made gave £430.
NMW Regs 2015 reg 4 April 2017–2026 rates are £7.50, £7.83, £8.21, £8.72, £8.91, £9.50, £10.42, £11.44, £12.21 and £12.71. The corresponding monthly thresholds are £520, £542, £569, £604, £617, £658, £722, £793, £846 and £881. The 2023 rate was already corrected by merged #2053; this PR tests the £722 threshold and carries no #2053 caveat.
HB Regs 2006 reg 75E(2) instead excepts work through entitlement to Working Tax Credit. The model applies that through the existing health/disability exception. Because its reduction currently caps both awards, the UC earnings test must not lift the HB cap. This branch reads both awards before the cap:
is_uc_entitledfor UC andhousing_benefit_pre_benefit_capfor HB. Reading pre-cap awards avoids a circular dependency. With HB and no UC the earnings exception is false. With neither award it returns the earnings test; that fallback preserves #1820's cases and changes no award amount.SI 2014/2147 reg 1 defines the targeted-childcare UC income measure by Part 6 Chapter 2 of the UC Regulations. The work allowance belongs to reg 22, in Part 3. The existing annual model limit remains £15,400; the legal one-, two- and three-assessment-period limits are £1,283.34, £2,566.67 and £3,850. This PR changes the earnings measure, while retaining the model's annual approximation and existing minimum income floor treatment.
Default Scheme Schedule para 37 uses the Secretary of State's UC income calculation and adds the award. Kingston and Merton's 2026–27 para 37 reproduce that provision; Newham's 2026 para 37 refers to the UC entitlement income calculation; Westminster's 2026–27 summary adds the actual award and other income and earnings. None deducts the work allowance. The official scheme URLs are in parameter metadata under
gov/local_authorities/{borough}/council_tax_reduction.Miscellaneous income and review findings
P2: the award and cap share
uc_individual_earned_income_before_mif. Following current main, it countstaxable_miscellaneous_income, so a miscellaneous loss does not reduce employment earnings. Positive miscellaneous income is still classified as earned by the award. The FRS field mixes paid work (reg 52) with royalties, trust income and overseas pensions that reg 66 treats as unearned. #1961 remains open for the split. This limitation is documented in the cap variable and pinned by the synthetic £11,000 YAML case: both measures and the exception agree. A correct classification split needs data work and is outside this PR. No national-impact bound is claimed pending the refreshed runs.P3: this body is audited against current code and sources. The stale #2053 caveat, dead-code claims and earlier-main impact claims are removed. Main has merged #1949 and #1973; their assessed-claimant and individual-income definitions are preserved.
P1: previous population tables and their output-file citations are removed. They used an earlier main and had not passed a joint disclosure audit. The replacement publication script counts distinct survey households, suppresses all change values involving households behind primary-small cells or small subgroup residuals across comparisons and years, and uses common base deciles and only base absolute levels/rates. Its synthetic checks pass. The certified runs and real-output audit remain pending, so this body publishes no national-impact figures. A small cell is never published as a weighted count or amount.
Tests and invariants
Hand-derived YAML includes 29 cap cases, 9 targeted-childcare criterion cases and four borough counterexamples, plus #1820's 12 unchanged cases. It covers the partner's £923 Class 3 counterexample; claimant-only aggregation; exclusion of floor income; relievable contributions at 74 versus 75; tax on property income; the £722 monthly 2023 threshold; and 2026 income £10,571 versus £10,572 at the £881 monthly threshold. Childcare cases include £16,500 pay giving £15,399.60 earned income and £16,501 giving £15,400.32, either side of the annual model limit.
Eight Hypothesis/differential tests in
test_uc_earned_income_consumers_properties.pycheck: unearned tax and voluntary NI invariance; equality with pre-allowance UC income when no floor applies and exclusion of floor income; the exception/HB scope identity; childcare and CTR bounds; childcare monotonicity; the CTR hand reference; and the dated threshold/rounding differential. Dividend and rent property pins exercise the removed xfail and tax-on-unearned-income defect.Current continuation results:
git diff --checkis clean.test_uc_earned_income_consumers_properties.py,test_uc_income_from_capital_properties.py,test_uc_state_pension_properties.py): 20 passed, 1 expected xfailed in 1,278.47 seconds withuv run ... python -m pytest -n 3 -v. All eight consumer properties and both dividend/rent pins pass. The existing strict xfail istest_state_pension_does_not_change_partners_marriage_allowance, tracked separately in Marriage Allowance is booked on the recipient as the transferor's unused allowance, not the fixed s.55B transfer #1947.test_uc_claimant_income_properties.py,test_uc_earnings_deductions_properties.py,test_uc_minimum_income_floor_properties.py,test_benefit_cap_and_ctr_pension_age_properties.py): 19 passed in 636.95 seconds, also with at most three workers. Local Python total: 39 passed, 1 expected xfailed.00d4bdb03: 3,112 YAML cases passed (1 warning, 142.51 seconds) viapolicyengine-core test policyengine_uk/tests/policy -c policyengine_uk, covering every requested related folder. Lint, documentation build checks and Python 3.11–3.14 smoke imports pass. Python shards 1/3 and 3/3 pass; 2/3 remains in progress at the final check. CI run.36f747121. The saved broad run had 1,886 passed before that main merge. These are historical results, not passes claimed for the newest main.Fail-on-main refresh: ten semantic cases assert variables present in base, mid and head, avoiding missing-variable failures. At the new SHAs listed below, base and mid each have 7 failed, 3 passed; head has 10 passed. Main fails cap Class 3, childcare, Merton CTR, the fixed-threshold rounding edge, the historical threshold and tax-on-property/Class-3 cases. Mid fails Class 3, childcare, CTR, historical threshold, HB scope and contributions at 75. The de-xfailed dividend/rent property examples both fail on current main (2 failed, 5 deselected), pass on mid (2 passed, 5 deselected) and pass on head in the complete required Python run. The prior failures are thus reproduced at the refreshed base rather than inferred from old main.
Saved mutation logs kill M1–M11: main's tax/NI measure; #1820's deductions; counting floor income; removing HB scope; omitting threshold rounding; omitting pre-2020 values; counting all members; childcare or CTR after the allowance; reversing childcare's inequality; and requiring a UC award outright. These runs preceded the newest-main merge and are retained as historical evidence. No new mutation pass is claimed here.
Certified-pair impact: refresh blocked
Required dataset: uk-data 1.58.0
enhanced_frs_2024_25.h5, certified SHA-2565ba399ea5ab9a6c7d4e9aaf6c623080beb34d9ad5f330f6244dbad72b8d960b6(the manifest's certified pair uses UK 2.125.1). The refreshed comparison is prepared for:0bc08f2fd45a4dc5215da7f4a055978226635f36.68faf37d00409b755c0ad0e9873f4218d93bf89f, not pushed.00d4bdb0328e7f915fd6aaea62ff091d67f57141.The checkpoint's refreshed impact never executed because its zsh loop collapsed the three labels into one. That runner is corrected. The new foreground attempts at
~/reviews/us-hub/scripts/heavy_run.shexit 75 before taking the lock: the sandbox denies/bin/psand the fallbackos.nice(0). No dataset run starts, and no earlier result is scaled or reused as current impact.Thus 2025–2030 base→mid (#1820), mid→head (#1986) and base→head totals, earnings-exception changes, UC losses/gains, targeted-childcare eligibility, CTR, household income, government balance, poverty and deciles remain unverified. Updated numbers and real-output disclosure verification are required before approval. No current numeric impact table is available.
Overlaps
All listed PRs remain open; none is edited or commented on here. The later lander must preserve:
uc_earned_income_before_work_allowancein CTR'suc_applicable_income; their other gates and deduction changes are separate.benefit_cap_earned_incomeand Fix benefit cap exemption defects (#1818) #1820's parameter intouniversal_credit/benefit_cap/is_uc_benefit_cap_exempt_earnings.py. The shared HB scope gate can then go because the UC and HB caps are separate. Keep the rounded-threshold comment when resolving its shared single-person claim test.Axiom
Checked against rulespec-uk main
b918260427b91eafabdade7fb5630f5d1b4123e6, with all four issues open. #399's stale mapping/ownership text, #400's stale impact/monthly-limit claims, #401's stale population statement/fixture provenance and #57's obsolete encoding claim and missingpe-paritylabel are corrected. Prior national-impact assertions in the earlier #400 and #57 comments are withdrawn. Signed encoding needs separate approval; no encoder ran.axiom: uk:regulations/uksi/2013/376/82 TheAxiomFoundation/rulespec-uk#399 queued— dated thresholds and whole-pound rounding remain outstanding.axiom: uk:regulations/uksi/2015/621/4 TheAxiomFoundation/rulespec-uk#399 queued— historical rates remain outstanding; the current module contains the 2026 rate.axiom: uk:regulations/uksi/2014/2147/1 TheAxiomFoundation/rulespec-uk#400 queued— targeted-childcare income provision remains unencoded.axiom: uk-{kingston-upon-thames,merton,newham,westminster}:policies/*/council-tax-reduction TheAxiomFoundation/rulespec-uk#401 queued— UC income remains an opaque input.axiom: uk:regulations/uksi/2006/213/75E TheAxiomFoundation/rulespec-uk#57 queued— the HB current/recent-work exception remains unencoded.Not verified
Refreshed certified-pair impact, its final disclosure audit and the required heavy-lock broad local YAML rerun are blocked by the sandbox; CI's complete YAML suite passes. The annual model does not implement reg 82's nine-month grace period or childcare's assessment-period look-back. NLW rates after 2026–27 remain absent, so the current threshold stays £881 monthly thereafter. A docs build and full-repository suite were not run locally; CI results are recorded separately above. All local test processes have completed, and scratch exports/dataset-copy directories are removed. The current body and updated parity comments contain no national-impact values; historical edit revisions and external archives have not been certified by this continuation.
🤖 Generated with Claude Code
Reproducer:
policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/claims_as_single_person.yaml,policyengine_uk/tests/policy/baseline/gov/dfe/targeted_childcare_entitlement/meets_universal_credit_criteria_for_targeted_childcare_entitlement.yaml,policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/benefit_cap_earnings_exception.yaml,policyengine_uk/tests/policy/baseline/gov/dwp/benefit_cap/is_benefit_cap_exempt.yaml,policyengine_uk/tests/policy/baseline/gov/local_authorities/council_tax_reduction/council_tax_reduction.yaml,policyengine_uk/tests/test_uc_earned_income_consumers_properties.py::test_benefit_cap_exception_ignores_tax_not_on_earnings