Skip to content

Narrow the legacy award readers property: an adult non-dependant ends the severe disability premium - #2294

Merged
MaxGhenis merged 3 commits into
mainfrom
legacy-readers-severe-disability-invariant
Oct 11, 2026
Merged

MaxGhenis merged 3 commits into
mainfrom
legacy-readers-severe-disability-invariant

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #2293

Summary

test_legacy_award_readers_properties.py::test_other_members_awards_never_change_the_readers claimed that adding a benefit-unit member who is not the claimant, the partner or a dependant never changes a reader of the legacy income-related awards. That is false: an added adult aged 18 or over is a non-dependant, and a non-dependant ends the family's severe disability premium. The test passed on main only because its 15 derandomised examples did not draw the case, so any edit to the test could have surfaced it in an unrelated PR.

This PR changes the test only. The model's behaviour for the severe disability premium is right and is unchanged. The review of this PR found a second way an added member changes a reader, which is a model defect: the benefit cap counts the member's own awards. That is #2298; this PR holds the cap at zero in the one property until it is fixed (see "The benefit cap" below).

The counterexample

On main (9475c95), 2025-26: a claimant aged 18, not in education, disabled (pip_dl £2,000), with a carer benefit and £200 of reported Income Support, in England with no savings. The added member is an 18-year-old with no awards.

Variable Without the added member With
severe_disability_premium £4,729.28 £0
income_support £13,771.55 £9,042.27
council_tax_reduction_applicable_income (a family reader) £25,074.71 £20,345.43

Each falls by the premium. No other family reader moves.

The law

IS Regs 1987 Sch. 2 para. 13(2)(a)(ii): a single claimant is severely disabled only if "he has no non-dependants aged 18 or over normally residing with him or with whom he is normally residing". The parameter gov.dwp.disability_premia.severe_non_dependant_age cites that paragraph and its Housing Benefit counterpart, HB Regs 2006 Sch. 3 para. 14(2)(a)(ii).

Change

The property now builds each family three ways (without the other member, with them reporting no award, with them as drawn) and checks two things that are true:

  1. What the member reports never changes a reader. The family readers, the member readers and the premium are equal between "reporting nothing" and "as drawn". This is the claim in the test's name.
  2. Their presence changes a reader only by ending the premium. Where the premium is the same with and without the member, every reader is the same. Where it differs, it has fallen to zero from a positive amount, and the member is at least severe_non_dependant_age (18). Even then, the family's income-related ESA and income-based JSA awards (claimant_or_partner_esa_income, claimant_or_partner_jsa_income) and whether a member is on one of them (is_on_income_related_esa, is_on_income_based_jsa) are the same: they read reports and capital only. Whether a member is on Income Support can change, when the lost premium takes the award to zero.
  3. Income Support falls by at most the premium. It never rises, and its fall is never more than the premium lost. It can be less: an award is never negative, so a family whose Income Support is already below the premium loses only what it had, and an ineligible family loses nothing.

The counterexample is pinned three ways as @examples: the 18-year-old with no award, the same with £3,000 of Income Support, and a 17-year-old with £3,000 of income-related ESA. A fourth @example pins the review's benefit cap family (below).

A new fixed test, test_adult_non_dependant_ends_the_severe_disability_premium, states the rule on that family: a 17-year-old leaves everything unchanged; an 18-year-old ends the premium, income_support and council_tax_reduction_applicable_income fall by exactly the premium, and every other family reader is the same in all three.

Of the issue's three options this is the first and third together: the family's own award is held fixed for the awards claim, and the premium rule gets its own statement. The second (a generator that cannot draw a non-dependant) would have dropped most adult members from the property.

The benefit cap

benefit_cap_reduction sums jsa_income and esa_income, which are the whole benefit unit's awards, so an added member's own income-based JSA or income-related ESA raises the family's cap reduction, and Universal Credit and the council tax reduction income fall. On main, a couple on £3,000 of JSA each with two children and a lodger: an added member aged 20 reporting £3,000 of JSA takes the cap reduction from £0 to £1,047.52 and Universal Credit from £15,118.92 to £14,071.40. In law the cap applies to the welfare benefits "to which a single person or couple is entitled" (WRA 2012 s.96(1)). #2298 has the details and the fix.

The cap is not one of the readers this test is about, so the property holds benefit_cap_reduction at zero, with a comment naming #2298, and pins that family as an @example. When #2298 is fixed, removing the hold makes the example check the fix. One other property compares families with and without a member, the Merton non-dependant one; it fixes Universal Credit at zero, which closes this channel, so it needs no hold. The fixed severely disabled family is exempt from the cap.

Invariants

  1. A member outside the claimant's family reports any Income Support, income-based JSA or income-related ESA: no reader changes, for the family or its existing members.
  2. Adding such a member changes a reader only if it ends the family's severe disability premium, and never changes the family's ESA or JSA awards or whether a member is on one of them.
  3. Only a member aged 18 or over ends it, and it ends in full. (IS Sch. 2 para. 13(3) ignores a non-dependant on a qualifying disability benefit, a blind one and some carers; the test's added members are none of those.)
  4. Income Support falls by at most the premium lost, and never rises.

Tests

The new invariant was stressed before it replaced the old one. The old property passed for months by not drawing its counterexample, so the narrowed one was run on 600 random examples (four seeds of 150, not derandomised, the test's own strategies): all passed. That run did not draw the benefit cap family the review found, so it was evidence, not proof. After the review it was stressed again at 4380010, with the cap held and the new bounds, in a log that records the commit and directory (stress_2294_r1.log): 600 random examples, all passed.

Fail on the old claim. The pinned examples are the issue's counterexample: with the old assertion (readers equal with and without the member) the first one fails on council_tax_reduction_applicable_income, £25,074.71 against £20,345.43, as the issue's repro shows on main.

Run locally at 4380010 (the later commit changes the docstring and a comment only):

  • pytest policyengine_uk/tests/test_legacy_award_readers_properties.py: 12 passed (the four pinned examples run first in the changed property).
  • ruff format --check and ruff check on the file: clean.

The full suites run in CI.

Microsimulation impact

None: the PR changes one test file and no model code, so no simulation result can differ.

Note for #2279

#2279 edits other_members in the same file (it sets the 16th-birthday date fact false). The two changes touch different functions and merge cleanly; the pinned examples here are aged 17 and 18, so they do not depend on that fact.

axiom: n/a: test-only change, no policy is added, updated or fixed

🤖 Generated with Claude Code

test_other_members_awards_never_change_the_readers claimed that adding a
benefit-unit member who is not the claimant, the partner or a dependant
never changes a reader of the legacy income-related awards. An added adult
aged 18 or over is a non-dependant, which ends the family's severe
disability premium (IS Regs 1987 Sch 2 para 13(2)(a)(ii)); Income Support
falls by the premium and so does the council tax reduction income that
counts it. The test passed only because its 15 derandomised examples missed
that case.

The property now builds each family three ways (without the member, with
them reporting nothing, with them as drawn) and checks:
- what the member reports never changes a reader or the premium;
- their presence changes a reader only where it ends the premium, and only
  a member aged 18 or over does that.

The counterexample is pinned as explicit examples, with a fixed test that
Income Support and the council tax reduction income fall by exactly the
premium and no other reader moves.

Fixes #2293

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits October 10, 2026 22:38
… property

Review round 1 of #2294:

- The benefit cap sums the whole unit's income-based JSA and
  income-related ESA, so an added member's own award can raise the cap
  reduction and lower Universal Credit and the council tax reduction
  income. That is a model defect, filed as #2298. The property holds the
  cap at zero until it is fixed and pins the family the review found.
- Income Support falls by at most the lost premium (an award is never
  negative), not always by all of it. The docstring says so and the test
  asserts the bound. The awards the family's own members report, and
  whether a member is on one, must not change even when the premium ends.
- The adult non-dependant rule is qualified to these fixtures (para 13(3)).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 2 of #2294: whether a member is on Income Support can change
when the lost severe disability premium takes the award to zero. The
docstring and the PREMIUM_FREE_READERS comment now say that only the ESA
and JSA awards and receipt flags never change. Docstring and comment only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 11, 2026 12:25
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Hand-off to the UK hub (from the QYP follow-ups session, local_1b6fef51-8f78-48d7-addf-36982ce71831).

@MaxGhenis
MaxGhenis merged commit 7343f94 into main Oct 11, 2026
6 checks passed
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

UK hub: landed on the gates at head 757e4061749f57e744a6ce391bf36bf5a0d7dfe5 (merge commit).

@MaxGhenis
MaxGhenis deleted the legacy-readers-severe-disability-invariant branch October 11, 2026 13:24
MaxGhenis added a commit that referenced this pull request Oct 11, 2026
…able-simulated-benefits-scoped-awards

No conflicts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 11, 2026
Brings in #2258 (120-minute Test job), #2180 (NICs thresholds indexed by
September CPI after 2030-31), #2063 (salary sacrifice needs pay), #2038
(winter payments to the member a benefit is paid to), #2259, #2248,
#2251, #2262, #2266, #2270, #2285, #2288, #2294 and #2021.

Conflict: test_legacy_award_readers_properties.py imports. Kept both
hypothesis.event (this branch's screened-out events) and
hypothesis.example (#2294's found families). The rest of the file merged
cleanly: #2294's without/silent/with-other comparison, the severe
disability premium bound and the held benefit cap sit alongside this
branch's work generators, screened reference and outside-member tests.
Two comments now say the ESA and JSA readers also read the members' work.

_legacy_award_payee.py merged cleanly: #2038's can_claim (Pension Credit)
and this branch's own_report_is_paid are independent. The shared NI
parameters already match main (#2058); #2180 changes only 2031-32 onward.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 11, 2026
#2294 holds the benefit cap at zero in test_legacy_award_readers_properties.py
by setting benefit_cap_reduction, which Universal Credit read on main. Here
Universal Credit reads uc_benefit_cap_reduction and Housing Benefit reads
housing_benefit_benefit_cap_reduction, and benefit_cap_reduction is only their
sum, so that input no longer held either award. Hold both scheme reductions
instead, keeping the hold's purpose (#2298).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 11, 2026
…efit-cap-75f-claimant-partner

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 13, 2026
…riance test

Vahid Ahmadi's review of d4984fb asked for a YAML case pinning an entry of
false for `uc_limited_capability_for_WRA`. `uc_limited_capability_for_work` is
LCWRA or the disability flag, so a claimant flagged disabled with LCWRA
entered as false keeps limited capability for work: the work allowance
(4,932 in 2025-26), the work preparation group and UC of 10,094.28, where
main gives no work allowance and UC of 7,381.68.

#2294 held `benefit_cap_reduction` at zero in
`test_other_members_awards_never_change_the_readers` because the cap counted
another member's own income-based JSA and income-related ESA (#2298). This
branch counts only the couple's, so the hold goes. With it gone the test
passes here, and on main 1d3abec it fails on its pinned family (the
council tax reduction income moves when a member with 3,000 of JSA joins).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Property test claims an added non-dependant never changes the legacy award readers; the severe disability premium is a counterexample

1 participant