Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
aecd34e
Bar income-related ESA at pensionable age and on State Pension Credit
MaxGhenis Oct 31, 2026
bd1d381
Merge origin/main (2da212abc) into fix-2565-esa-pension-credit
MaxGhenis Oct 31, 2026
dcf2969
Test that pensionable age and Pension Credit bar income-related ESA
MaxGhenis Oct 31, 2026
1ca890c
Pin outside reporters beside a Pension Credit couple in the property …
MaxGhenis Oct 31, 2026
d538f82
Add changelog fragment for #2565
MaxGhenis Oct 31, 2026
7e85a89
Bar income-related ESA on Pension Credit worked out without it
MaxGhenis Oct 31, 2026
e7f5b0a
Test the Pension Credit bar against a simulation without the award
MaxGhenis Oct 31, 2026
1971645
Cite pensionable-age rules by their numbers and restate the changelog
MaxGhenis Oct 31, 2026
d37e42d
Open the Pension Credit branch only for a claim the bar could end
MaxGhenis Oct 31, 2026
0d157f0
Name the actor in the Pension Credit bar's comments and changelog
MaxGhenis Oct 31, 2026
b6ddfcf
Read entered or reformed Pension Credit for every couple the ESA bar …
MaxGhenis Nov 1, 2026
d444183
Leave Housing Benefit's override helper as it is on main
MaxGhenis Nov 1, 2026
da6d9ad
Name the claimant or body that acts in the ESA bar's comments
MaxGhenis Nov 1, 2026
2ee68e7
Count only the screen's reads in the simulation itself
MaxGhenis Nov 1, 2026
cef744b
Count the screen's reads for the test year in the simulation itself
MaxGhenis Nov 1, 2026
09e0c08
Compare whole Pension Credit definitions before inferring nil
MaxGhenis Nov 1, 2026
3fc1e1d
Read Pension Credit for every couple when take-up has a formula
MaxGhenis Nov 1, 2026
31d90e9
Name who acts in the ESA bar's comments and test notes
MaxGhenis Nov 1, 2026
cf7153e
Check the awards before the shortcut flag in the new reform tests
MaxGhenis Nov 1, 2026
eec9f64
Name the five Pension Credit variables in the exhaustive guard test
MaxGhenis Nov 1, 2026
abdcede
Name who acts in the remaining new comments and docstrings
MaxGhenis Nov 1, 2026
1d6df1a
Name the model as the payer in the ESA Pension Credit docstrings
MaxGhenis Nov 1, 2026
08129d4
Merge #2813 (fix-2562-esa-abolition at 5a5ac5fb1) into #2807
claude Nov 2, 2026
15b61ba
Merge main at 0080274d7
claude Nov 2, 2026
c3f9cd4
Save the 2026 awards in six pension-age ESA cases from the abolition
claude Nov 2, 2026
d2ce8b5
Hold the pension-age ESA properties with the 2026 abolition in force
claude Nov 2, 2026
71ba275
Wrap the pension-age property docstring
claude Nov 2, 2026
70563bb
Pay a couple on income-related ESA no Pension Credit
claude Nov 2, 2026
a1771bb
Check the outside reporter's row in the pension-age ESA property
claude Nov 2, 2026
254669a
Merge #2813's head 7fe7360fa into the Pension Credit bar
MaxGhenis Nov 3, 2026
562d067
Merge #2813's head 3c9890a19 into the Pension Credit bar
claude Nov 3, 2026
419bccb
Name every route to the ESA-only row and the other reading of para 4(…
claude Nov 3, 2026
d6bf2f6
Add case 18's man to every generated 2016 example
claude Nov 3, 2026
d0ffe7f
Give DWP the verb where an appointee saving or a regulation acted
claude Nov 3, 2026
a8f46e3
Merge #2813's head dc69f3ada into the Pension Credit bar
claude Nov 3, 2026
6cd7081
Add case 26's couple and a 2018 example to the pension-age ESA proper…
claude Nov 3, 2026
0822532
Say which reading of para 4(2)(a) the expected values take, and give …
claude Nov 3, 2026
44ea4c1
Say which reading case 18's values agree with, and turn pension_credi…
claude Nov 5, 2026
9327cec
Merge main at 95dfc62f8 into the Pension Credit bar
claude Nov 5, 2026
741054b
Give DWP or the formula the verb in the PR's remaining comments, and …
claude Nov 5, 2026
3f7735f
Merge main at 47a2817e2 into the Pension Credit bar
claude Nov 5, 2026
eab8ebb
Merge main into fix-2565-esa-pension-credit
MaxGhenis Nov 5, 2026
e2615e8
Merge main into fix-2565-esa-pension-credit
MaxGhenis Nov 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/2565.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The model no longer pays income-related ESA to a claimant who has reached pensionable age, or to a couple that would get State Pension Credit without that award, whether the model's formulas, an input or a reform set that Pension Credit, and it pays no Pension Credit to a couple while it pays the couple income-related ESA (Welfare Reform Act 2007 s.1(3)(c) and Sch 1 para 6(1)(c) and (d)).
88 changes: 75 additions & 13 deletions policyengine_uk/tests/legacy_award_work_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@
against pay (reg 98(11)).
- ESA partner (para 6(1)(f), reg 42(1)): 24 hours or more, unless a carer
(reg 43(2)(c)).
- ESA age (WRA 2007 s.1(3)(c)): no claimant may have reached pensionable age,
their own State Pension age (s.1(6); Pensions Act 1995 Sch 4 para 1). A
caller that reads the model's pensionable age passes it as is_SP_age;
otherwise ages avoid 65 to 69. In the years tested (2025 and 2026) anyone
aged 64 or under was born after 5 October 1960, reaches pensionable age at
66 or later, and so falls below it; anyone aged 70 or over was born before
6 April 1960 and reached it at 66 or earlier.
- ESA and State Pension Credit (para 6(1)(c) and (d)): neither the claimant
nor the other member of the couple may be entitled to it. A caller that
knows passes whether the couple is entitled (on_pension_credit): it would
claim, and it would get Pension Credit with no income-related ESA in the
family. Otherwise no member reports Pension Credit or Housing Benefit, so
no mixed-age couple keeps the SI 2019/37 art 4 saving and the
exclusion of mixed-age couples is in force (SPCA 2002 s.4(1A)). Only a
couple every member of which has reached the qualifying age can then be
entitled, and from 2019-20 that is pensionable age for everyone (SPCA 2002
s.1(6)), so each of them has reached pensionable age and has no claim
under s.1(3)(c), whether or not DWP pays the couple Pension Credit. A
member outside the couple keeps their own claim even when the couple has
an entitlement to Pension Credit.
- JSA claimant (s.1(2)(e), reg 51(1)(a)): 16 hours or more, with no
exception for carers doing unrelated paid work. The other member of the
couple: 24 hours or more (s.3(1)(e), reg 51(1)(b)). In a joint-claim
Expand Down Expand Up @@ -153,17 +173,40 @@ def esa_claimant_in_remunerative_work(adult, parameters):
return not (within_lower_limit or permitted_work)


def reached_pensionable_age(adult):
"""WRA 2007 s.1(3)(c): whether the adult has reached pensionable age."""
if "is_SP_age" in adult:
return bool(adult["is_SP_age"])
assert not 65 <= adult["age"] < 70, adult
return adult["age"] >= 70


def couple_on_pension_credit(couple):
"""Para 6(1)(c) and (d), for a couple with no Pension Credit or Housing
Benefit reports in 2025 or 2026: a couple every member of which has
reached the qualifying age, each of whom has also reached pensionable
age and so has no claim under s.1(3)(c), whether or not DWP pays the
couple Pension Credit."""
assert not any(
adult.get("pension_credit_reported", 0)
or adult.get("housing_benefit_reported", 0)
for adult in couple
), couple
return len(couple) > 0 and all(reached_pensionable_age(a) for a in couple)


def legacy_benefits_closed(couple, others, parameters):
"""Annual closure proxy for the claimant's and partner's own award.

Reports by other members neither close nor shield it (SI 2014/1230
regs 44(1), (4) and 46(1)). This is the existing annual proxy, not a
notice/deadline model. No tax-credit reports are generated here. Ages
avoid the changing Pension Credit qualifying-age boundary.
notice/deadline model. The generators draw tax-credit reports only in
years with tax credits, in which they close nothing. Ages avoid the
changing Pension Credit qualifying-age boundary.
"""
dwp = parameters.gov.dwp
members = couple + others
assert not any(
assert dwp.tax_credits.active or not any(
member.get("child_tax_credit_reported", 0)
or member.get("working_tax_credit_reported", 0)
for member in members
Expand All @@ -187,16 +230,21 @@ def esa_abolition_claim(couple, parameters):
reports (esa_income_report_closed).

The abolition reaches every remaining award on 1 July 2026 in England
(SI 2025/1148 art. 3A), the households' default country, and no member
here has the appointee saving, so it reaches any award reported in a
year with less than all of it payable. would_claim_uc_at_legacy_closure
keeps its default, true. The conditions are those of the other
working-age closures: no Pension Credit report, and not all pension age.
(SI 2025/1148 art. 3A), the households' default country, so it reaches
any award reported in a year with less than all of it payable. Where a
member who reports the award has an appointee saving
(esa_income_appointee_saving), DWP keeps the whole claim payable (art.
3A(2)); like esa_income_payable_share, this function ignores a saving
held only by a member who reports nothing.
would_claim_uc_at_legacy_closure keeps its default, true. The conditions
are those of the other working-age closures: no Pension Credit report,
and not all pension age.
"""
payable = parameters.gov.dwp.ESA.income.awards_payable.great_britain
reports_award = any(m.get("esa_income_reported", 0) > 0 for m in couple)
reporters = [m for m in couple if m.get("esa_income_reported", 0) > 0]
saved = any(m.get("esa_income_appointee_saving", False) for m in reporters)
on_pension_credit = any(m.get("pension_credit_reported", 0) > 0 for m in couple)
if not reports_award or payable >= 1 or on_pension_credit:
if not reporters or saved or payable >= 1 or on_pension_credit:
return False
assert all(not 65 <= adult["age"] < 70 for adult in couple), couple
return not all(adult["age"] >= 70 for adult in couple)
Expand Down Expand Up @@ -233,12 +281,25 @@ def candidates(couple, others, reported, parameters):
]


def esa_screen(couple, others, capital, parameters):
def esa_screen(couple, others, capital, parameters, on_pension_credit=None):
"""esa_income_eligible for a claimant and partner (couple) and other
members of the benefit unit outside the family (others)."""
members of the benefit unit outside the family (others). on_pension_credit
is whether the couple is entitled to State Pension Credit, if the caller
knows it."""
ESA = parameters.gov.dwp.ESA.income
members = couple + others

def barred(i):
# WRA 2007 s.1(3)(c), for every claimant.
if reached_pensionable_age(members[i]):
return True
# Sch 1 para 6(1)(c) and (d), for the couple's claim only.
if i >= len(couple):
return False
if on_pension_credit is not None:
return bool(on_pension_credit)
return couple_on_pension_credit(couple)

def other_member_works(i):
if i >= len(couple):
return False
Expand All @@ -250,7 +311,8 @@ def other_member_works(i):
)

passes = any(
not esa_claimant_in_remunerative_work(members[i], parameters)
not barred(i)
and not esa_claimant_in_remunerative_work(members[i], parameters)
and not other_member_works(i)
for i in candidates(couple, others, "esa_income_reported", parameters)
)
Expand Down
Loading
Loading