Repository navigation
Conversation
…ncome uc_earned_income subtracted benunit_tax, the whole benefit unit's income tax and NI on all income, from the unit's gross earnings. UC Regs 2013 reg. 55(5)(b) and reg. 57(2) step 3 deduct only tax and NI paid by the person in respect of their employment or trade. - uc_income_tax_on_earnings: income tax on the person's earnings, taken as the lowest slice of their non-savings income after their allowances (savings and dividends sit above it under ITA 2007 s. 16, property income above it in the engine and under s. 16A from 2027-28). - uc_national_insurance_on_earnings: Class 1 employee, Class 2, Class 4. - uc_individual_earned_income: gross earnings less the person's own pension contributions, tax and NI, floored at nil. - uc_earned_income: sum over the unit, less the work allowance. Fixes #1942. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- test_uc_earnings_deductions_properties.py: unearned income never changes earned income; UC is non-increasing in every kind of unearned income; the tax deducted equals the tax on earnings alone; earned income is never lower, and UC never higher, than under the pre-fix formula. - test_uc_state_pension_properties.py: the #1942 strict xfail now passes and loses its marker; pound-for-pound and the property-income equivalence cover families with earnings; a new strict xfail pins the Marriage Allowance transfer, which the model books on the recipient as the transferor's unused allowance (ITA 2007 s. 55B gives a fixed reduction). - Document the earnings deductions in the UC docs page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The transferor's election cuts their own personal allowance by the fixed transferable amount (s.55B(4)-(6)); the gaining party gets a Step 6 tax reduction at the basic or Scottish basic rate, capped at their Step 5 tax (s.55A(2), s.55B(1), (3), s.29(2)). The couple elects only when the conditions in s.55B(2) and s.55C(1)(b)-(c) hold, no married couple's allowance is claimed, it lowers their combined income tax, and the elder spouse's take-up draw is true. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…roperties YAML cases from ITA 2007 ss.55A-55E, hand-computed, including the issue's worked example and each departure it lists. The State Pension and earnings-deduction UC properties now run with Marriage Allowance claimed, holding each couple's election at its first-run choice; the strict xfail on the partner's Marriage Allowance now passes, and a new case pins the point where the couple stops electing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ential The couple's tax never rises with the model's election, which matches the cheapest allowed option found by running each election fixed; the gaining partner's reduction does not depend on the elector's income; transfers balance and reductions stay within s.55B(1)/(3) and s.29(2); couple tax never falls with more income. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dataset draws would_claim_marriage_allowance independently per person at 50%. Reading the elder spouse's draw reshuffled take-up across about 0.65m couples on the Enhanced FRS for no legal reason. The gaining spouse's draw is the one that decided take-up before, so the same couples claim. Also add the changelog fragment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…est comments s.55B(2)(ba) asks whether dividends would reach the dividend upper rate, which starts at the basic rate limit (s.13); the dividends scale's own threshold lags it before 2026-27. State the married premise in the UC property tests, and correct the comments on why the couples do not elect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tie case no longer asserts income tax, which depended on the model's fixed allowance order rather than s.25(2). The married couple's allowance case uses spouses old enough to claim it. The ordering case cites s.27(2) and s.29(2)-(3); the limit notes s.27(6) for double taxation relief; the single election per couple is described as a modelling choice, since s.55E does not bar elections both ways. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e rate test s.16 puts dividends above property income as well as earnings, so a landlord whose dividends cross the basic rate limit is liable at the dividend upper rate (s.55B(2)(ba)). Note that a spouse outside the benefit unit is not modelled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve the election-independent income tax inputs before branching, so a reform that branches inside adjusted_net_income gives the same result whichever variable is asked for first, and free the branch arrays afterwards. Only a spouse can give allowance up, and spouses must be old enough to marry (16, the Scottish minimum). On a tie the lower-income spouse elects. The CPS expanded reform lifts the income condition only when remove_income_condition is set. Refresh stale comments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…riage Allowance The locked policyengine-uk predates PolicyEngine/policyengine-uk#1963. A lock bump changes the dataset build, so it belongs in its own release. The test runs as soon as the installed model defines marriage_allowance_relinquished. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… at #1947 The docs edit left 'plus all of its unearned income' hanging off the earnings taper, which read as if unearned income were tapered. State the two deductions separately. The strict xfail's reason and the module docstring now cite #1947. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Heads-up from #1949, which this stacks on: its head moved from d0fc22f to 514132f with one added commit and no history rewrite. The commit changes two lines in |
|
Update from #1949: its head is now c98c53c, a merge of #1943's new head ce85220, which has main 7b9fc37 merged in. No history rewrite and no formula change. Rebasing or merging onto c98c53c brings in current main, so this PR's impact base moves too. The one conflict #1949 had was the UC docs means-test sentence. |
…able pension contributions Review of #1949 found two defects in the new deductions. - uc_income_tax_on_earnings capped the tax on the earnings slice at the person's income_tax. When a tax reduction (the married couple's allowance) made that cap bind, the pension annual allowance charge and the High Income Child Benefit Charge raised the deduction, and so did more pension income. Reductions now come off the earnings slice first, as allowances do, so the deduction never includes a charge and does not move with other income. - uc_individual_earned_income deducted every pension contribution. Contributions paid after 75 are not relievable (Finance Act 2004 s. 188(3)(a)), so reg. 55(5)(a) does not deduct them. Adds YAML cases for both, draws a married couple's allowance in the property tests, and says in the test docstring that payroll giving (reg. 55(5)(c)) is not modelled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…roperty tests The random generator rarely produced an earner old enough for the married couple's allowance, so the properties did not exercise tax reductions. A dedicated family shape now does. With it, restoring the old min(tax, income_tax) cap fails both the invariance and the differential property. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
uc_income_tax_on_earnings hard-coded savings, dividends and property as the incomes outside earned_taxable_income. A reform that adds another income to gov.hmrc.income_tax.earned_taxable_income_exclusions, such as one exempting pensions, then had that income subtracted twice, and the tax on earnings fell to nil. The formula now reads the list. Adds a YAML case with the reform as a parameter input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iew's counterexamples - test_uc_unearned_benefits_properties.py (added on main by #1958) had a strict xfail pinned to #1942, which this fix turns into a pass. Remove it and widen the earnings-free invariants to all families, with Marriage Allowance off as in the State Pension file (#1947). - The tests review found that random draws missed two mutations. Pin its minimized counterexamples as @example cases: a pound of State Pension for a pensioner just above the personal allowance with an earning partner, and a pound of pension for an earner just above it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> # Conflicts: # policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/income/uc_earned_income.yaml # policyengine_uk/tests/test_uc_unearned_benefits_properties.py # policyengine_uk/variables/gov/dwp/universal_credit/income/uc_earned_income.py
… property fixtures #1896 presumes that a member under 20 who is at least 16 years younger than the claimant is the claimant's child. The State Pension and earnings property tests generate couples such as 67 and 18 without relationship inputs, so the presumption would read them as a parent and child and the tests would pass without exercising a couple (including the tests review's pinned 67-and-18 example). Set is_claimant_or_partner from each generated role, as #1896 did for main's fixtures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- #1950: rent and dividends are not UC unearned income (reg. 66(1) is a closed list), so the single parent's unearned income is nil (UC 11,308.50) and the self-employed claimant's is the pension alone (UC 3,975.29). - #1881: without gross receipts, a trade profit above the trading allowance gets no allowance (ITTOIA 2005 s. 783AI), so the tax on the trade is (18,000 - 12,570) x 20% = 1,086. The tax and NI this PR deducts are unchanged in method; the cases' gaps against the pre-fix formula are still 0.55 x the tax on other income. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… unchanged The widened award clause of test_interest_dividends_and_rent_leave_the_award_unchanged gives one adult up to 270,000 of interest, dividends and rent beside up to 30,000 of earnings. Above 100,000 of adjusted net income the personal allowance tapers (ITA 2007 s. 35(2)), which raises the tax on the person's earnings and so the reg. 55(5)(b) deduction: the exception this PR states. Compare only benefit units whose personal allowances the income leaves unchanged, as test_uc_earnings_deductions_properties.py does by keeping adjusted net income under the taper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…anged Review of c6412ee found the widened capital-income award invariance still false for the award after the cap. The model's earnings exception to the cap (reg. 82(1)(a)) nets tax on all of a person's income off their earnings (#1986), so dividends can switch the cap on while allowances and UC earned income are unchanged: a single London claimant earning 11,000 with 10,000 of dividends loses 2,081.80 to the cap. - Compare universal_credit only where is_benefit_cap_exempt is also unchanged; the other variables keep the personal allowance filter. - Pin the case as a strict xfail against #1986. - Qualify the YAML header's ITA 2007 s. 16 ordering comment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ce-election Take #1949's uc_income_tax_on_earnings, which now sets every Step 6 reduction (income_tax_subtractions, which this branch extends with the Marriage Allowance reduction) against the tax on earnings first. Identify spouses as the married claimant and partner (#1896), dropping the two-eldest rule and the minimum-age parameter. Keep #1896's Marriage Allowance and CPS tests, adapted to the transfer model; carry the pinned elections into #1949's married couple's allowance and property-income properties. Drop the branch-array clearing now that core shares branch arrays until they are written. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hold each couple's Marriage Allowance election at the base run's choice in the UC capital-income properties too, in place of switching Marriage Allowance off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #1947.
Stacks on #1949 (the #1942 fix, which stacks on #1943). The base is
mainso CI runs, and the diff includes #1943 and #1949 until they merge.What the law says and what changes
ITA 2007 ss. 55A-55E (legislation.gov.uk, read 2026-09-30; verbatim text in the issue):
would_claim_marriage_allowancewould_claim_marriage_allowance, so each couple has one draw (the dataset draws it per person at 50%, so take-up stays at 50%)The rate test reads the model's own liabilities. Two points follow from the statute's wording:
Scottish savings and dividends are banded on UK rates, so an intermediate-rate Scottish taxpayer with savings stays eligible. The old band test made them ineligible.
Variables
New, all Person, YEAR:
marriage_allowance_transferable_amount: s. 55B(4)-(5).is_marriage_allowance_spouse: the two eldest married members of a benefit unit that has exactly two. This also works before Replace generic child and adult flags with each programme's legal definitions #1896 lands, whilemarital_statusstill marks children in a married benefit unit as married.makes_marriage_allowance_election: the couple's choice; users can set it as an input.marriage_allowance_relinquished: s. 55B(6).marriage_allowance_appropriate_percentage: s. 55B(3).marriage_allowance_tax_reduction_limit: s. 29(2)-(3).marriage_allowance_tax_reduction: s. 55B(1). Added toincome_tax_subtractions.Changed:
marriage_allowanceis now the amount received from the spouse's election, and only the gaining spouse has it. It no longer comes off taxable income: it was removed fromearned_taxable_income_exclusions.personal_allowancesubtractsmarriage_allowance_relinquished.meets_marriage_allowance_income_conditionsis the rate test above.Removed:
partners_unused_personal_allowance: it is not part of the law and nothing else used it.eligible_bandsparameter.The CPS expanded-MA contrib reform now overrides
meets_marriage_allowance_income_conditionsandmarriage_allowance_transferable_amount. Its mechanics follow the statute, so a higher-rate recipient saves the basic rate × the amount, not their marginal rate.How the election is computed
The election depends on the couple's tax with and without it, and the tax depends on the election. So
makes_marriage_allowance_electionruns two policyengine-core branches, the pattern PE-US uses for itemisation:For each spouse, the saving from electing is the partner's reduction (capped at their Step 5 tax left) minus the elector's extra tax. It is rounded to pennies. The couple elects if the saving is positive and the conditions hold. The spouse with the larger saving elects; on a tie, the elder. Branches are dropped after use.
Overhead: ~20% on small household simulations (noisy, on a loaded machine). Enhanced FRS: see below.
Universal Credit
uc_income_tax_on_earnings(from #1949) takes the recipient's reduction off the tax on their earnings first. That is how HMRC delivers it: gov.uk's tax code letters say code M means "you've received a transfer of 10% of your partner's Personal Allowance". So a pensioner's State Pension no longer changes the earning partner's UC earned income once the couple has elected. The strict xfailtest_state_pension_does_not_change_partners_marriage_allowancenow passes.Other Step 6 reductions (married couple's allowance,
other_tax_credits) keep #1949'smin(tax, income_tax), which sets them against other income first. That is the opposite order, and a comment says so. Married couple's allowance needs a birth before 6 April 1935, so it almost never reaches UC.Known interaction (intended): the election weighs the couple's income tax, not their UC. At the point where electing stops saving tax, the couple stops electing. The earner's tax then rises by up to £252, and their UC rises by up to 55% of that.
test_couple_stops_electing_when_it_no_longer_saves_taxpins an example: 2026, State Pension £12,500 → £12,600, UC £5,509.26 → £5,547.86. A choice rule that also weighed UC would behave differently; that is a separate modelling decision.Departures in the issue, each now a YAML test
Also covered:
meets_marriage_allowance_income_conditions.yamlis rewritten on incomes, not tax-band inputs. It includes s. 55C(1)(c) with the cut allowance (50,000 earnings: eligible with the full allowance, ineligible once it falls to 11,310).Invariants and property tests
test_marriage_allowance_properties.py(Hypothesis). It generates couples and singles with every taxable income type, in England, Wales and Scotland, with children and occasional married couple's allowance claims, for 2019, 2026 and 2027:UC properties now run with Marriage Allowance claimed.
test_uc_state_pension_properties.pycovers pound-for-pound, non-increasing, and the private-pension and property-income equivalences.test_uc_earnings_deductions_properties.pycovers invariants 1-3; the old docstrings said these "switch Marriage Allowance off until this is fixed". Where a property compares two runs, each couple's election is held at its first-run choice, because the election legitimately responds to income.Mutation testing of these tests is running; results will be added here before the PR leaves draft.
Enhanced FRS impact
Real
Microsimulationruns on a private copy of the Enhanced FRS 2024-25 (enhanced_frs_2024_25.h5, sha256e433e532…). Base is #1949's head d0fc22f and head is this branch (a1a4822); both trees were clean. There are 4 runs per year: base, head, and each with no one claiming (would_claim_marriage_allowancefalse for all). £m, head minus base:"MA cost" is income tax with no one claiming minus income tax as modelled. Housing Benefit and CTR move by less than £0.01m. Poverty rates (BHC and AHC; all, children, pensioners, working age) are unchanged to 0.001pp in 2026 and 2027, and move by at most 0.011pp in other years.
Differential check: with no one claiming, base and head agree exactly on all 32 recorded person, benefit-unit and household arrays in every year. So this changes nothing but Marriage Allowance.
2026 decomposition (aggregates only):
Take-up: the dataset draws
would_claim_marriage_allowanceper person at 50%. The couple's election uses the gaining spouse's draw, the same draw that decided take-up before. A first version used the elder spouse's draw. That reshuffled take-up across ~0.65m couples for no legal reason, so it was dropped; those runs are kept in the review folder.Runtime: the election's branches add about 0.4–1 s per year to a full Enhanced FRS run, on a heavily loaded machine.
Not modelled
The rate test inherits the model's own income tax stacking. Chips filed for pre-existing bugs found on the way:
Axiom
axiom: TheAxiomFoundation/rulespec-uk#362 queued (s.55C, extended for this PR with s.55C(1)(ca) and s.55B(2)(ba) and seven more companion cases) + the s.55B re-encode queued in rulespec-uk
bulk/worklist.yaml(batch PE-UK-CHILD-DEFS, pending), which already requires the fixed transferable amount, the s.55B(6) cut and the s.55B(1) reduction.rulespec-uk's s.55B module still caps the transfer at the partner's unused allowance: it encoded the same departure as PolicyEngine. The worklist entry and #362 both called the PolicyEngine side "a separate follow-up"; this is it.
Review
Independent verification (hand computation from the statute, adversarial law and robustness reviews) is running; results will be added before this leaves draft.
🤖 Generated with Claude Code