Axiom encoding of Income Tax Act 2007 s.35 surfaced a mismatch in personal_allowance.
Section 35(3) says that if the allowance remaining after the adjusted-net-income taper is not a multiple of £1, it is rounded up to the nearest £1. PE-UK currently applies the s.35(2) half-excess taper but returns the fractional post-taper amount directly.
Repro on current main:
from policyengine_uk import Simulation
sim = Simulation(situation={
"people": {"p": {"adjusted_net_income": {"2026": 100001}}},
"benunits": {"bu": {"members": ["p"]}},
"households": {"h": {"members": ["p"]}},
})
print(float(sim.calculate("personal_allowance", 2026)[0]))
Actual: 12569.5
Expected under ITA 2007 s.35(3): 12570.
Likely fix: apply np.ceil (after the nonnegative floor) in policyengine_uk/variables/gov/hmrc/income_tax/allowances/personal_allowance.py, and add a regression test for odd-pound adjusted net income just above £100,000.
Axiom encoding of Income Tax Act 2007 s.35 surfaced a mismatch in
personal_allowance.Section 35(3) says that if the allowance remaining after the adjusted-net-income taper is not a multiple of £1, it is rounded up to the nearest £1. PE-UK currently applies the s.35(2) half-excess taper but returns the fractional post-taper amount directly.
Repro on current
main:Actual:
12569.5Expected under ITA 2007 s.35(3):
12570.Likely fix: apply
np.ceil(after the nonnegative floor) inpolicyengine_uk/variables/gov/hmrc/income_tax/allowances/personal_allowance.py, and add a regression test for odd-pound adjusted net income just above £100,000.