Repository navigation
Conversation
is_uc_eligible now sums working_tax_credit_reported over the claimant and partner only, so a child's or another adult's report no longer opens the reg 60A route on a closure claim. Adds YAML cases A to E from #2805 and Hypothesis properties. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6JcN54KSxCfD7zav9D
|
Ready for hub review Head: Items
Issue script on
|
Aggregate impact on certified data: passBase Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 0 (thresholds in Notes
Moves flagged for reviewNone. Spending by benefit and service
Caseloads
Household net income by decileNo household's net income changes in any year. No change in any year: receipts by tax head; government balance, each component counted once; model totals; household income totals; population; poverty rates; people in poverty; inequality; mean household net income in each side's own deciles; standard reform costings (change in gov_balance); standard reforms: change in each reform's target. Checks failing on both base and head (1)
Counted-once checkEach of 92 components was raised by £1 per unit in 2026-27, and every head of its totals was read household by household: its own head must rise by exactly £1, no other tax head may move, no other benefit may rise, and the model's own total must move by what its heads moved. A head that breaks a rule is measured again twice: with every other head held at its baseline value, and with every other head held at its value after the first £1 while the component rises again. It fails where either still shows the response. 0 failed; 0 were skipped.
Provenance
Dataset: Receipts by tax head: head levels
Spending by benefit and service: head levels
Caseloads: head levels
Government balance, each component counted once: head levels
Model totals: head levels
Household income totals: head levels
Population: head levels
Poverty rates: head levels
People in poverty: head levels
Inequality: head levels
Mean household net income in each side's own deciles: head levels
Standard reform costings (change in gov_balance): head levels
Standard reforms: change in each reform's target: head levels
|
|
Picking this up for the UK hub. Reply here if you're mid-change and I'll stop. Generated by Claude Code |
council_tax_reduction_pensioner now recognises a reg 60A award only where the claimant or partner reports Working Tax Credit, as is_uc_eligible does. Adds Council Tax Reduction cases for Scotland 2026 and England 2024, a UC case with no closure claim and a UC case with a 17-year-old partner's report. Restates P3 as an invariant that needs no reference formula and widens partner draws to ages 16 and 17. Corrects the Tax Credits Act citations and adds reg 60A(1)(a)(iii) to the headers and docstring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6KMhHstAXQyqiYRAcW
|
Generated by Claude Code |
|
Generated by Claude Code |
|
Generated by Claude Code |
|
Ready for hub review Head: Findings
Tests run
Revert and mutation checks
I identified which case failed from the direction of the failure: only that case can move under that mutant. Skipped
Judgment calls
Generated by Claude Code |
|
Ready for hub review Verdict: REQUEST_CHANGES at The code needs no change, and the head can move to #2810's branch. Every focused test, mutant and merge check I reran agrees with the hand-off. The changes I'm asking for are in Setup: a detached worktree at the head, FindingsP2-1.
|
| Tree | meets_pension_credit_age_conditions |
legacy_benefits_closed |
claims_uc_at_legacy_closure |
is_uc_eligible |
|---|---|---|---|---|
| Head, either variant | False | True | True | False |
M1 (main's any-member sum), either variant |
False | True | True | True |
Replacement:
The change also reaches contrived derived cases: a claimant aged 70 with a 17-year-old partner whose Income Support or Child Tax Credit the model closes, and a third member who reports Working Tax Credit. There
main's formula givesis_uc_eligibletrue and this branch false.
The rest of the section matches the code I read:
- tax credits stay open for a Pension Credit-age family with no owner Working Tax Credit report;
wholly_pension_agestops Income Support and income-based JSA closing;- the function never closes ESA.
P3-3. PR_BODY.md, "What the code does now": "after those dates" is off by a year, and nations act
Where: "...Scotland in any year, England before 2025-26 and Wales before 2026-27 assess it under the working-age rules, and England and Wales disregard it after those dates."
The code: council_tax_reduction_pensioner.py:110-112 disregards the award from 2025 in England and from 2026 in Wales. Those are the first scheme years.
Replacement:
Councils assess it under the working-age rules in Scotland in any year, in England before 2025-26 and in Wales before 2026-27, and disregard it in England from 2025-26 and in Wales from 2026-27.
P3-4. changelog.d/2805.fixed.md: a test as actor, and the CTR effect unnamed
Replacement:
The model now opens the reg 60A Universal Credit route on a legacy closure claim only when the claimant or partner reports Working Tax Credit, and the formula for the pension-age Council Tax Reduction test reads the same reports; a report by a child or another adult in the benefit unit no longer opens the route or makes the award a reg 60A award.
P3-5. test_uc_reg_60a_working_tax_credit_properties.py:154-155: the fixed test's docstring is vague
Current: "On a closure claim, zeroing a pension-age claimant's own report closes the route, so the comparison in P3 can tell the two claims apart."
Replacement:
On a closure claim, zeroing a pension-age claimant's own report closes the route, so the zeroing that P3 applies without a closure claim can change is_uc_eligible.
P3-6 (optional). A property can kill M2 and M12; here is one that does
Should a property kill them? Yes. The law gives an invariant that is broader than the 17-year-old YAML case: the couple's award belongs to both members (TCA 2002 s.3(3), s.10(1)). So on a closure claim, moving a Working Tax Credit report from the claimant to the partner, or back, never changes is_uc_eligible.
Why P1-P3 miss them:
- P2 kills only on one kind of draw: a pension-age claimant who reports nothing and a 16- or 17-year-old partner who reports.
families()adds a partner on only half its draws, and the simplest draw (no partner) comes first.- I reran the file under M2 and M12: P1-P3 and the fixed test pass 4 of 4 under each, as the hand-off says.
The property below always draws a partner. Results:
| Tree | Result |
|---|---|
| Head | passes |
| M2 | fails at the assert (assert True == False) |
| M12 | fails at the assert (assert True == False) |
| M1, M10 | passes, so it adds nothing for them |
It reuses the module's helpers, so it can sit in the same file. Command: pytest policyengine_uk/tests/test_uc_reg_60a_working_tax_credit_properties.py <this file> -q. Ten derandomised examples, like the others.
@st.composite
def couples(draw):
"""A claimant and a partner, one of whom reports Working Tax Credit."""
people = {
"claimant": {
"age": draw(ADULT_AGE),
"is_claimant_or_partner": True,
"working_tax_credit_reported": 0,
},
"partner": {
"age": draw(PARTNER_AGE),
"is_claimant_or_partner": True,
"working_tax_credit_reported": 0,
},
}
people[draw(st.sampled_from(["claimant", "partner"]))][
"working_tax_credit_reported"
] = draw(POSITIVE_REPORT)
for index in range(draw(st.integers(0, 1))):
people[f"other_{index}"] = {
"age": draw(OTHER_AGE),
"is_claimant_or_partner": False,
"working_tax_credit_reported": 0,
}
return {"people": people, "capital": WITHIN_LIMIT}
def swapped(family):
people = {name: dict(person) for name, person in family["people"].items()}
claimant, partner = people["claimant"], people["partner"]
claimant["working_tax_credit_reported"], partner["working_tax_credit_reported"] = (
partner["working_tax_credit_reported"],
claimant["working_tax_credit_reported"],
)
return {**family, "people": people}
@PROPERTY_SETTINGS
@given(couples())
def test_moving_report_between_claimant_and_partner_never_changes_closure_eligibility(
family,
):
assert eligible(family, closure_claim=True) == eligible(
swapped(family), closure_claim=True
)If you add it:
- add to the module docstring "P4 On a closure claim, moving a Working Tax Credit report between claimant and partner never changes is_uc_eligible.";
- add a matching line to the body's Invariants;
- add "P4" to the M2 and M12 rows of the body's mutation table.
Cost: the file took 293 s for 4 tests alone and 640 s for 5 with this property, but the 640 s run shared 4 cores with two other runs.
Previous findings
| Finding | Status | Evidence |
|---|---|---|
| F1, CTR reads every member's report | Closed | See "F1 in detail" below. |
F2, axiom: line |
Partly closed | AXIOM_ISSUE.md exists, and the body keeps axiom: hub fills this line, as the author's brief asked. P2-1, P2-2 and P3-1 remain. |
| F3, P3 can't fail; no test of "no route without a closure claim" | Closed | M10 fails the YAML case at line 136 (is_uc_eligible@2026: [1.] differs from 0.0) and P3 (test_working_tax_credit_never_changes_eligibility_without_closure_claim, line 148, assert True == False; 1 failed, 4 passed). The copied formula and its reform are gone. |
| F4, 17-year-old partner | Closed by YAML | M2 and M12 each fail only the line-158 case (is_uc_eligible@2026: [0.] differs from 1.0), and the properties pass 4 of 4 under each. The P3-6 property would also kill both. |
| F5, Survey data | Partly closed | The Aggregate impact comment (run 41342732915, head_sha b442dcbdf) says "No household's net income changes in any year", 0 broken, 0 fixed and 1 failing on both sides. That matches the body. The derived-case sentence is incomplete (P3-2). |
| F6, internal process | Closed | Body: see the "Writing rules" check below. Code: a grep for hub|session|review|harness|claude across the 6 changed files finds nothing. |
| F7, #2806 merge | Closed | See "F7 rerun" below. |
| F8, statute wording | Closed | s.3(3)(a) and s.3(3)(b) are gone. s.10(1) is cited in the code comment, YAML headers and docstring, and s.10 is in both reference tuples. (a)(iii) appears in both YAML headers and the property docstring. |
| F9, title | Closed | Read only the claimant's and partner's Working Tax Credit for reg 60A is 69 characters (wc -c) and names the right set. |
F1 in detail
The code: council_tax_reduction_pensioner.py:94-104 sums working_tax_credit_reported × is_claimant_or_partner, which matches is_uc_eligible.py:96-103. The extra term is & ~working_age_applicant.
CTR revert: I put council_tax_reduction_pensioner.py back to its b442dcb version. Three cases fail (council_tax_reduction_pensioner@2026: [0.] differs from 1.0, the same at @2024):
- "Another adult's ... Scottish pensioner's";
- "A dependant's ... Scottish";
- the English 2024 case.
The partner case and the claimant's-own case pass.
Expected values, worked from the quoted law:
- Cases 1-3: the claimant made no Working Tax Credit claim, so s.10(1) gives them no award, reg 60A(1)(a)(ii) fails and reg 60A doesn't apply.
- In Scotland the award is one the claimant "was not entitled to" (WRA s.4(1)(b) still bars them), which SSI 2021/249 reg 3(2)(b) excludes. That leaves a pensioner:
true. - England 2024 predates SI 2025/39. The pre-2025 SI 2012/2885 reg 3(2) text isn't quoted in this round's inputs, so case 3's
truerests on the formula's existing run-on/error approximation, as the YAML header says.
- In Scotland the award is one the claimant "was not entitled to" (WRA s.4(1)(b) still bars them), which SSI 2021/249 reg 3(2)(b) excludes. That leaves a pensioner:
- Cases 4-5: the claimant, or both joint claimants, hold the award, so reg 60A applies. SSI 2021/249 counts a reg 60A award, so the family isn't a pensioner family:
false.
The hand-off's line references match the file: 86-105, documentation 20-22, reference 44-46.
F7 rerun
#2806's head: GET /repos/PolicyEngine/policyengine-uk/pulls/2806 gives a5d1662666253304de5d3245903f097791b91950, the same commit as before.
Conflict: git merge-tree --write-tree --name-only a5d16626... c643315a0 exits 1. It has one conflict hunk, in is_uc_eligible.py's reference tuple:
- Apply the Universal Credit education condition #2806 adds
adme2.pdf; - this head adds TCA ss.3 and 10.
Merged tree: I merged in a scratch worktree, kept both sides and ran from that tree:
uc_receiving_education.yaml, the reg 60A YAML and the CTR award-owner YAML pass 47 of 47;test_uc_reg_60a_working_tax_credit_properties.pywithtest_uc_receiving_education_properties.pypasses 7 of 7.
Case 35: its comment on #2806 (uc_receiving_education.yaml:918) still reads "Working Tax Credit over every member", so the body's note stands.
Judgment calls
- The CTR comment cites s.3(3) and s.10(1), not "s.3(3)(a)": sound. It follows F8.
- UC Regs 2013 "reg 3(3)" with no sub-paragraph: keep it as is. Nobody quoted reg 3, so a sub-paragraph pin would be a guess. The repository already cites the same point at paragraph level in about ten places, including
is_uc_eligible's own documentation string (line 14) anduc_member_of_couple_claims_as_single_person.py:11. A paragraph-level citation can't be wrong about which sub-paragraph applies. - Widening partner draws to 16 and 17: harmless. As the author found, it doesn't kill M2 or M12 (I confirmed this); P3-6 does.
- The survey section states only what the author read: sound, apart from the gap in P3-2. Leaving out the policyengine-uk-data claims is right, since this round couldn't read that repository.
Checked and sound
My reruns of the hand-off's numbers at the head:
| Run | Result |
|---|---|
| reg 60A YAML plus CTR award-owner YAML | 12 of 12 |
| Whole CTR YAML directory | 336 of 336 |
| UC YAML directory | 570 of 570 |
benefit_cap_by_scheme, child_amount_tax_credit_award, benefit_cap_pension_age |
40 of 40 |
| Property file | 4 of 4 (293 s) |
code_health with test_uc_legacy_closure_properties.py (-n 4) |
3,091; with the property file's 4, the hand-off's 3,095 |
uvx ruff@0.17.2 format --check . |
"2165 files already formatted" |
ruff check . |
"All checks passed!" |
git diff --check b442dcbdf HEAD |
clean |
Mutants: M1 (add over every member), M2 (* claimant), M10 (| reg_60a outside the closure claim) and M12 (* claimant_or_partner * meets_minimum_age).
| Mutant | YAML | Properties |
|---|---|---|
| M1 | fails cases C and D ([1.] differs from 0.0) |
P1 fails (line 129) |
| M2 | fails the 17-year-old case | 4 of 4 pass |
| M10 | fails the no-closure case | P3 fails |
| M12 | fails the 17-year-old case | 4 of 4 pass |
This matches the hand-off and the body's mutation table. I took the failing case names from the test output, not from the direction of failure.
The delta's code:
is_uc_eligible: the formula is unchanged. Only the comment, documentation andreferencetuple changed.- CTR formula: the new sum uses
is_claimant_or_partner, aslegacy_benefits_closedandis_uc_eligibledo.universal_credit_awardkeeps~head_applies_alone, so a lone head applicant isn't affected.
The new tests:
- P3 caps capital and forces a claimant or partner report, so it can now fail (M10 shows it).
- The fixed test asserts both directions.
- The reg 3(3) YAML case expects
is_uc_single_or_joint_claimant [true, false]. That passes on the head, so the household really reaches the reg 3(3) boundary.
PR body:
Fixes #2805is the first line.Reproducer:names an existing case, the one M1 fails.- The CTR test bullet ("three cases ... fail; the claimant's and partner's own reports pass either way") matches the revert.
- 12 × £424.90 = £5,098.80 and 12 × £666.97 = £8,003.64; both cases pass.
Packet:
- Line 1 is
Title:and line 3 isLabels: pe-parity. - Its quote of reg 60A(1) matches Add tax credits validation page #433's quote word for word.
- 60A-F and 60A-G don't clash with Add tax credits validation page #433's ids.
policyengine_beforeis right formain:main's CTR formula usesadd(benunit, period, ["working_tax_credit_reported"]), and itsis_uc_eligibledoes the same.
Writing rules:
- The body ends with the required
🤖 Generated with [Claude Code](https://claude.com/claude-code)line. - None of the 6 changed files names a hub, session, reviewer or harness.
- The comments describe the code as it is now.
What I couldn't check
-
Unquoted law:
- UC Regs 2013 reg 3(3) and its sub-paragraphs;
- TCA 2002 s.3(3) beyond its paraphrase;
- UC Regs 2013 regs 28 and 60(1), which the body's education note cites;
- SI 2012/2885 reg 3(2) before SI 2025/39, behind CTR case 3;
- the rest of SSI 2021/249 reg 3(2) beyond "was not entitled to".
legislation.gov.uk doesn't resolve from this session, so every legal reading above rests on text quoted in the brief, Read the claimants' Working Tax Credit, not every benefit-unit member's, on the reg 60A Universal Credit route #2805, Read only the claimant's and partner's Working Tax Credit for reg 60A #2810 or Add tax credits validation page #433.
-
Corpus status of the TCA 2002 rows (P3-1): rulespec-uk and axiom-corpus are outside this session's access.
-
CI at this head: no run exists until the hub moves the branch. The full YAML and Python suites weren't run.
-
The CTR change on survey data: I ran no survey dataset. The Aggregate impact result covers
b442dcbdfonly, and the CTR change has its first aggregate run when the branch moves. -
The previous round's workspace witnesses (
derived_path.yaml, the batch harness): not on this machine. I rebuilt the derived case myself (P3-2) instead.
Generated by Claude Code
|
Picking this up for the UK hub. Reply here if you're mid-change and I'll stop. Generated by Claude Code |
…ges reg 60A eligibility Add property P4: on a closure claim, moving a Working Tax Credit report between the claimant and partner never changes is_uc_eligible, since both members hold the couple's award. Name the CTR formula in the changelog fragment and make the fixed test's docstring say what P3 needs from it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N2tyHTWc6VfYZtLyAQvnrU
… the reg 60A tests The draws set no carer or limited capability input, so a partner aged 16 or 17 meets the minimum age condition only beside a member under 16. Reword the fixed test's docstring so it doesn't read as contradicting P3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N2tyHTWc6VfYZtLyAQvnrU
|
Picking this up for the UK hub. Reply here if you're mid-change and I'll stop. Generated by Claude Code |
1 similar comment
|
Picking this up for the UK hub. Reply here if you're mid-change and I'll stop. Generated by Claude Code |
|
Picking this up for the UK hub as brief 778, which replaces brief 693. Reply here if you're mid-change and I'll stop. Generated by Claude Code |
juaristi22
left a comment
There was a problem hiding this comment.
Reviewed exact head b442dcbdf79377a18285185e7465dfe483b9ab13. Review COMPLETE. 31 focused checks and the final UC/household-income composition checks passed in the matched locked environment. The claimant/partner ownership mask is consistent with the reviewed statutory award unit.
C1 — Complete the required Axiom parity record. The actual description still says axiom: hub fills this line. The repository's PR requirements require a supported encoded/encoded-correct/queued line, with a dispatch-ready record for a queued policy change. The documented external-contributor fallback is axiom: needed; the current placeholder satisfies neither route.
The actual rulespec-uk #433 body and all three comments cover the general reg 60A route, but do not contain the proposed #2805/#2810 ownership extension, its Tax Credits Act corroboration or companion cases. The AXIOM_ISSUE.md comment remains a proposal, and the c643315 follow-up branch is not this PR's head.
Post the ownership corroboration to the actual queue record (or complete a supported encoded alternative), including the operative sources, qualifying-claim/notice facts, required outputs, source-derived companion judgments and signed-encoder blocker. Then replace the placeholder with the supported line and link. This is an explicit repository completion blocker, so I am requesting changes.
For the 2026 examples, retain the stated annual closure/report or continuing migrated-award interpretation and its historical notice/no-SPC/no-cessation assumptions; those cases do not establish fresh 2026 WTC receipts. The review covers the adopted b442 tree and its affected dependencies; proposed follow-up changes remain separate.
|
Picking this up for the UK hub as brief 1018, which replaces brief 778. Reply here if you're mid-change and I'll stop. Generated by Claude Code |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QmsZJYWxEGz33tNMYHxuwP
|
Generated by Claude Code |
|
Generated by Claude Code |
|
Generated by Claude Code |
|
Ready for hub review Head: Before I started
Brief 608's commits
Findings and what I didP2-1 (
The packet now:
P2-2 (
P3-1. The Corpus bullet is the review's text, and the Ingest line names the same three rows. P3-2. I reran the review's household: 2026, North East, a claimant aged 70 and a partner aged 17 (both
The body's Survey data section uses the review's replacement sentence, which this table supports. P3-3. P3-4.
P3-5. P3-6. The property sits at lines 176-220, and the module docstring's P4 line at 23-24 (from Main. #2806.
With the gate removed from Case 35's comment still says "the model sums reported Working Tax Credit over every member". #2806's property file still copies Tests at the head
The writing grep over Mutants and revertsI made each mutant in a
The CTR award-owner YAML passes 5 of 5 under every mutant, since none of them touches the CTR formula. CTR revert: I put the head's
Skipped
Judgment calls
What I couldn't check
Generated by Claude Code |
juaristi22
left a comment
There was a problem hiding this comment.
Re-reviewed adopted head 8afd8c7fc1f0528024f85eb43b0d688c06dcc5ae. COMPLETE — C1 remains open; one additional nonblocking description finding (A1).
C1 — Complete the actual queued parity record. The actual description still says axiom: hub fills this line; the scoped extension to the filed Axiom record has not been adopted. The proposed AXIOM_ISSUE.md in a PR comment is useful preparation, but the required record must be filed with its operative law, module/corpus targets, outputs and externally sourced companions, and linked from the actual PR. Pending billed encoding is a separate, legitimate queued blocker.
A1 — Update the actual description for this adopted code. It still says council_tax_reduction_pensioner reads every member's WTC and this PR leaves it unchanged. The new head changes that mask to claimant/partner ownership. The actual property/test description also retains the old any-member oracle and omits the added CTR cases/P4. Apply the corrected description currently sitting in the proposed PR_BODY.md comment.
All 28 repository checks passed. The computed UC/household-income case for the excluded 17-year-old UC joint claimant passed. A controlled supplied-award CTR/household-income check passed after its claim-preference setup was corrected; the original diagnostic log is retained and repository tests were not rerun. That CTR case retains its supplied-award/run-on/error assumptions. Official ownership, migration and regional CTR source premises were checked independently. Current-head CI is still running.
Requesting changes for C1; no new wrong-cash result was established.
|
Thanks for the re-review (review).
We changed no code: the head stays |
juaristi22
left a comment
There was a problem hiding this comment.
Re-reviewed 8afd8c7fc1f0528024f85eb43b0d688c06dcc5ae after the actual description and filed record were updated. COMPLETE — C1 and A1 RESOLVED; no open findings or material evidence gaps.
The actual Axiom #433 addendum now supplies the operative law, module/corpus ingestion targets, both outputs and companion cases 60A-F/G. These were independently checked against the official legislation. The actual PR title/description now covers the CTR ownership mask, national rules, P1–P4 and added CTR cases.
The head is unchanged. The prior 28 passing repository checks, computed UC/household-income case and corrected supplied-award CTR proof remain valid; their artifacts and runtime/dependency pins were verified without rerunning tests. The original external-fixture failure and its correction remain qualified in the report. Current-head CI is green.
Approval is withheld because the PR is still a draft. The workflow time targets were exceeded; the original elapsed time is recorded and head, description, review activity, source evidence and CI are refreshed before this publication.
juaristi22
left a comment
There was a problem hiding this comment.
Approving current head 8afd8c7fc1f0528024f85eb43b0d688c06dcc5ae now that this PR is ready. The previous review stands: COMPLETE, C1 and A1 resolved, no open findings or material evidence gaps.
The code, adopted description, dependency pins and filed source evidence are unchanged. I verified the retained validation artifacts (28 passing repository checks plus the native final-result checks) without repeating the code review. The corrected CTR proof remains an explicitly supplied existing-award case, not a fresh UC eligibility proof. Applicable current-head checks pass.
#2806 added DWP's Advice for Decision Making E2 to is_uc_eligible's references, and this branch added Tax Credits Act 2002 ss. 3 and 10 at the same place. The merge keeps all three. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22
left a comment
There was a problem hiding this comment.
Re-reviewed current head 467b51d560eea579a45a988265731259dfcf07dc after the merge from main. COMPLETE — C1 and A1 remain resolved; no open findings or material gaps.
The conflict resolution preserves both sets of references, the claimant/partner Working Tax Credit mask and the pensioner CTR classification. The current education-condition integration is covered. All 70 selected repository tests and two native final-result checks passed in the matching Core 3.34.1 environment. The separate existing-award CTR example retains its supplied-award qualification; it is not a fresh UC eligibility proof.
Approval is pending the current head's remaining CI checks. The completed review can be retained for that CI-only follow-up.
juaristi22
left a comment
There was a problem hiding this comment.
Approving current head 467b51d560eea579a45a988265731259dfcf07dc now that its CI has passed. COMPLETE — C1 and A1 remain resolved; no open findings or material evidence gaps.
The completed review stands: the head, description, dependency pins and source evidence are unchanged, with no new substantive human feedback. I verified the retained 72 passing checks (70 repository cases and two native final-result checks) and the source/artifact bindings without repeating the code review. The existing-award CTR proof keeps its supplied-award qualification. Applicable current-head checks pass.
Fixes #2805
Reproducer: policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/reg_60a_working_tax_credit.yaml::Another adult's Working Tax Credit does not open the reg 60A route for a single claimant aged 70
axiom: TheAxiomFoundation/rulespec-uk#433 queued, cases 60A-F and 60A-G: TheAxiomFoundation/rulespec-uk#433 (comment)
What the code does now
On a closure claim (
claims_uc_at_legacy_closure),is_uc_eligibleopens the SI 2014/1230 reg 60A route when the claimant or partner (is_claimant_or_partner) reports Working Tax Credit and a claimant meets the minimum age condition. The formula sumsworking_tax_credit_reportedover the claimant and partner only, so either member's report counts for both. That includes a partner who can't be a joint claimant of Universal Credit (UC Regs 2013 reg 3(3)), such as a 17-year-old. A report by a child, a qualifying young person or another adult in the benefit unit no longer opens the route.council_tax_reduction_pensionernow reads the same reports to decide whether reg 60A applies to the award. On a closure claim where the claimant or partner reports Working Tax Credit, a wholly pension-age family's Universal Credit award counts as a reg 60A award. Councils assess it under the working-age rules in Scotland in any year, in England before 2025-26 and in Wales before 2026-27, and disregard it in England from 2025-26 and in Wales from 2026-27. Where only another member reports, the formula keeps its approximation for a wholly pension-age award awaiting termination or made in error, so the family stays a pensioner family.The law
SI 2014/1230 reg 60A(1):
In the Tax Credits Act 2002 s.3(3), Parliament provides for a joint claim by the members of a couple "both of whom are aged at least sixteen and are in the United Kingdom", and for a single claim only by a person "not entitled to make a claim under paragraph (a)". In s.10(1) it ties Working Tax Credit entitlement to "the person or persons by whom a claim for working tax credit has been made". So both members hold the couple's award, and the claimant holds no part of an award another member claimed.
Councils in England and Wales disregard a Universal Credit award "where regulation 60A ... applies in respect of the award" (SI 2012/2885 reg 3(2)(b), from SI 2025/39 reg 4; WSI 2013/3029 reg 3(2)(b), from WSI 2026/13 reg 4). Scottish councils count a reg 60A award but not an award the claimant "was not entitled to" (SSI 2021/249 reg 3(2)(b)). So every council asks whether reg 60A applies, and the model answers that question with the same reading of whose award counts in
is_uc_eligibleandcouncil_tax_reduction_pensioner.Cases from #2805
is_uc_eligible(main)is_uc_eligible(this branch)Invariants
is_uc_eligible.is_uc_eligible.is_uc_eligible.Tests
policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/reg_60a_working_tax_credit.yaml: cases A to E, plus a single claimant aged 70 who reports Working Tax Credit without a closure claim (not eligible), and a claimant aged 70 whose 17-year-old partner reports it (eligible, £5,098.80, the partner not a joint claimant). Withmain's formula, C and D fail (is_uc_eligible1, expected 0).policyengine_uk/tests/policy/baseline/gov/local_authorities/council_tax_reduction/ctr_reg_60a_award_owner.yaml: fivecouncil_tax_reduction_pensionercases. Without the change, the three cases where only another adult or a dependant reports (Scotland 2026 twice, England 2024) fail (council_tax_reduction_pensioner0, expected 1); the cases with the claimant's or partner's own report pass either way.policyengine_uk/tests/test_uc_reg_60a_working_tax_credit_properties.py: Hypothesis properties P1 to P4, plus a fixed check that, on a closure claim, zeroing a pension-age claimant's own report closes the route. Partner draws include ages 16 and 17. Withmain's formula, P1 fails.At least one test kills each of these mutations of
is_uc_eligible:main)Survey data
The Aggregate impact run on this PR's earlier head, which changed
is_uc_eligibleonly, found no change to any household's net income in any year from 2025-26 to 2031-32.claims_uc_at_legacy_closurefollowslegacy_benefits_closed, which reads only the claimant's and partner's reports. For a family that meets the Pension Credit age conditions, the model closes tax credits only on the claimant's or partner's Working Tax Credit report, and never closes Income Support or income-based Jobseeker's Allowance for a wholly pension-age family. So on a closure claim the model derives itself, a wholly pension-age family already has the claimant's or partner's report. The change reaches a family when a user suppliesclaims_uc_at_legacy_closure. The change also reaches a derived case: a claimant aged 70 with a 17-year-old partner whose Income Support or Child Tax Credit the model closes, and a third member who reports Working Tax Credit. Theremain's formula givesis_uc_eligibletrue and this branch false.The education gate (#2346, #2806)
At #2806's head (
5961424461ff5ca4b7f87582d37d55314c5411b6), this branch and #2806 conflict only inis_uc_eligible'sreferencetuple; keep both sides' lines. On a merge that keeps both,uc_receiving_education.yaml, the reg 60A YAML and the Council Tax Reduction YAML pass 54 of 54, and the reg 60A and education property files pass 10 of 10. Once only the claimant and partner count, the education gate on this route can't change a result: a claimant or partner on the couple's award doesn't count as a qualifying young person (SI 2014/1230 reg 28), and under SI 2014/1230 reg 60(1), on a closure claim, every other claimant who receives education need not meet the education condition. Without the gate, the merged formula still passes those files and the Universal Credit and Council Tax Reduction YAML directories, so none of those tests reaches the gate. On merge, the comment on case 35 ofuc_receiving_education.yaml("the model sums reported Working Tax Credit over every member") no longer holds, though its expected values do, and whoever lands second rewrites it. #2806's property reference also sums Working Tax Credit over every member, but its draws give a report only to the claimant and partner, so it still agrees with the merged formula.🤖 Generated with Claude Code