Repository navigation
Conversation
Add the person input is_in_full_time_education (false unless set), the reg 12 test uc_receiving_education, the reg 14(1) and SI 2014/1230 reg 60 exceptions in uc_is_excepted_from_education_condition, and the WRA 2012 s.4(1)(d) basic condition meets_uc_education_condition. is_uc_eligible needs one claimant to meet it, as reg 3(2)(b) provides for a couple. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Twenty-two cases: the issue's Plymouth student with the input true (nil) and false (£6,348.91), a single parent student, couples with one or two students, a student couple with a child or a foster parent, mixed-age couples, PIP with and without limited capability for work, a qualifying young person, loans and grants on non-advanced courses, a reg 3(3) single claim, a legacy closure claim, Northern Ireland and 2016. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drawn families, each in a household of its own, check that the input never raises Universal Credit, that unflagged families match main's eligibility formula (kept verbatim), that eligibility matches a reference implementation of regs 3(2)(b), 12 and 14, that excepted claimants keep their award, and that only the couple's flags count. A further test pins the intended effect through non-dependants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rewords docstrings and test comments; no formula or expected value changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… reg 28 - is_uc_eligible: one claimant must meet the age condition and the education condition together, as DWP's ADM E2016 reads reg 3(2), on the ordinary, closure-claim and reg 60A routes. - has_education_maintenance_funding: a person input for reg 12(2)(b), read in place of student_loans and education_grants. - is_qualifying_young_person_for_uc_education: applies SI 2014/1230 reg 28 before regs 12(1A) and 60 read qualifying young person status. - uc_is_excepted_from_education_condition: states the limited capability for work approximation and corrects the limb (a) and (b) wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and reg 28 Cases 7 and 8 now expect no award under ADM E2016, cases 15 and 16 use has_education_maintenance_funding, and cases 23 to 33 cover the other two mixed-age patterns, the closure-claim and reg 60A routes, Attendance Allowance, Armed Forces Independence Payment and DLA alone, an equipment grant without maintenance funding, and reg 28. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reference implementation now applies ADM E2016's common-member rule, reg 28 and the maintenance funding input, and the generator draws Attendance Allowance, AFIP, DLA, the new input and reported Housing Benefit and Working Tax Credit. New invariants: one member meets both conditions, loans and grants don't decide receiving education, and no one reporting an existing benefit counts as a qualifying young person. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A reform keeps this branch's is_uc_eligible and gives benefit units flagged uses_main_eligibility main's formula, so each drawn case needs one simulation instead of two, and the explicit examples share one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Case 34 gives case 32's 18-year-old a closure claim: reg 28 sends them to reg 12(2), so reg 60(1) lifts the condition. Case 35 puts Working Tax Credit on another adult in a qualifying young person's benefit unit: reg 60A waives only s.4(1)(b), so the claimant still fails s.4(1)(d). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
uc_limited_capability_for_work takes uc_limited_capability_for_WRA as well as is_disabled_for_benefits, so a user can set either assessment input for an ESA determination. The documentation also names the actor in each sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The copy drops main's comments, and the formatter re-wrapped two of its statements, so "verbatim" overstated it. The module docstring also names the actor for regs 12 and 14 and for Schedule 4 para 9(2)(f). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Apply the voice review to the documentation of the condition, receiving education, the reg 28 status, both new inputs and the changelog fragment. The maintenance input no longer says education_grants holds childcare or disability grants: the FRS build allocates those to their own variables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rewords the YAML case comments and the property file's docstrings so that DWP, the decision maker or the Secretary of State acts under each provision, and replaces be-verb identities with verbs. No test input, expected value or executable line changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Case 6 names the reg. 14(1)(c) exception, case 26 puts reg. 60(1) first, case 28 says reg. 66 doesn't list Armed Forces Independence Payment, case 30 states when reg. 12(2)(b) treats a claimant as receiving education, and case 32 drops "no longer". Comments only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
Standard reform costings (change in gov_balance)
The report suppresses a reform's costing and target change in these years, for the reasons below (
Standard reforms: change in each reform's target
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. 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
|
juaristi22
left a comment
There was a problem hiding this comment.
Reviewed exact head 4d363920686117ae58ce23fa284918964ab86bbc. Review PARTIAL: no confirmed implementation finding, but policy checks remain incomplete, so this is not an approval.
All 164 selected tests passed, including final-UC cases for the full-time student exclusion, parent and partner exceptions, mixed-age couples and legacy-closure routes. The core GB and NI education provisions, DWP's couple guidance and 2026–27 standard rates were checked. The actual rulespec-uk#514 handoff also states its signed-encoder/corpus blockers.
Before completing the policy review, please provide or identify:
- The operative Northern Ireland transitional provisions corresponding to GB SI 2014/1230 regs 28, 48 and 60, including their application dates. The NI education regulations alone do not establish managed-migration parity.
- Source-backed temporal cases for the unversioned reg 60 protection, including the qualifying migration claim, course continuity and cessation. The annual closure proxy and the documented missing parental-support, LCW-date and placement facts do not establish every statutory outcome merely because represented-case tests pass.
The existing passing tests are retained; no broad suite was repeated during recovery. The declared input/data limitations remain explicit, and I am not treating them as verified legal entitlement rules.
The change whichever of #2845 and #2806 lands second needs#2845 (branch #2845 applies SI 2014/1230 reg. 28 in
Test runs on a tree that merges both heads
On the merged tree with the change, we ran a single claimant aged 18 on an upper-secondary course (North East, owned outright) who reports £3,000 of one benefit:
The two reg. 28 readings now agree in every row; before the change, #2806's variable gave false in the first three. Diff against #2806's head (
|
|
Thanks. These are the Northern Ireland provisions you asked for, with their dates from legislation.gov.uk:
The NI text matches the GB text, with NI cross-references: Article 9(1)(d) of the 2015 Order for section 4(1)(d) of the 2012 Act, reg 6 of the NI Universal Credit Regulations for reg 5, reg 47(4) for reg 46(4), and regs 57 and 58 for regs 56 and 57. So the model's GB routes carry over to NI unchanged. The model protects a student only where The next push adds the NI references to the three variables, plus temporal YAML cases worked from the law by hand:
The body will say where the annual proxy departs from the law, 60(2) included. |
juaristi22
left a comment
There was a problem hiding this comment.
Source follow-up at unchanged head 4d363920686117ae58ce23fa284918964ab86bbc: review COMPLETE, with no findings or material evidence gaps in this bounded review. The prior NI and dated-GB source questions are resolved.
I independently checked the NI counterparts, including reg 29, reg 49 and reg 61, their commencement/amendment chain, and the dated historical GB reg 60 text. The applicable NI provisions substantively support the reviewed GB premises. The actual Axiom #514 record remains valid for the queued work.
One qualification to the reply: the claim that protection is reachable only from 2025 applies to default-derived closures. The public input contract also accepts explicit historical closure flags, and the existing properties use them in 2019/2021/2024. Please preserve that distinction in the planned wording.
The body already discloses the annual proxy's course/history/cessation limitations. With the operative law now verified, those known limits and the absence of the five proposed additional tests do not leave a material source question unresolved. This review validates the stated annual model contract; it does not certify exact longitudinal statutory outcomes or the promised next push.
The prior 164 passing checks are retained at the identical head/body/tree; no model tests were repeated. Current exact-head CI passes. This remains a draft, so I am posting the completed review without approving it yet.
|
Picking this up for the UK hub. Reply here if you're mid-change and I'll stop. Generated by Claude Code |
…and 60 Add SR 2016/226 regs 29, 49 and 61 to the references of the education condition variables, with GB reg 48, and name the Northern Ireland regulation beside each GB one in their documentation. No formula changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WR8LHfDXcSTTbY2JgCMXBk
Cases 36 to 41 follow one full-time student through the closure of tax credits: the claim at closure in 2025, the same course in 2026, the end of the course in 2027, a claim outside the migration, a claim before the closure in 2024, and the Northern Ireland protection under SR 2016/226 reg 61. Each case works its result from SI 2014/1230 regs 44 to 60 and DWP's rates, and none sets claims_uc_at_legacy_closure, so the closure runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WR8LHfDXcSTTbY2JgCMXBk
For single people aged 25 to 59 with no other exception, in model years 2020 to 2028, uc_is_excepted_from_education_condition holds exactly when they report a legacy benefit whose active parameter reads false and their family claims at closure. The test reads the closure dates from the parameters and batches each example into one simulation, with explicit grids for 2024, 2025 and 2026. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WR8LHfDXcSTTbY2JgCMXBk
Ground each case comment in the regulation text: the notice DWP issued, the claim after the final deadline in case 39, the text in force before 27 January 2025 in case 40, the minimum income floor in case 37, and the end of the course in case 38. Name the Universal Credit Regulations 2013 reg 5 and the 2014 Regulations' reg 48 in the documentation. Put every grid combination in North East England and in Northern Ireland, scope the property's invariants to closures the model derives, describe the 30 April parameter snapshot, and check that the grid years straddle each closure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WR8LHfDXcSTTbY2JgCMXBk
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WR8LHfDXcSTTbY2JgCMXBk
|
Generated by Claude Code |
|
Generated by Claude Code |
|
Generated by Claude Code |
|
Ready for hub review Head: Before starting. What the push does
Independent reviewFour reviewers, each blind to my reasoning, read the GB law in the cases, the NI addendum and docstrings, the body against the code, and the property test. They raised 44 findings; none touched a formula or an expected value. I fixed all but one in
Tests at
|
| Run | Result |
|---|---|
uc_receiving_education.yaml |
41 passed |
person_legacy_benefits_closed.yaml |
3 passed |
legacy_closure_ineligible_partner.yaml, legacy_closure_pension_route.yaml, legacy_closure_migrant_nations.yaml |
1, 4 and 9 passed |
pytest -n 4 on test_uc_receiving_education_properties.py, test_uc_legacy_closure_properties.py and test_legacy_closure_scope.py, in one run |
30 passed (5, 4 and 21) in 202 s; no run reached the 40-minute cap |
pytest policyengine_uk/tests/code_health -n 4 |
3,095 passed |
uvx ruff@0.17.2 format --check ., ruff check ., ruff check --select F811,F821 policyengine_uk |
clean (2,177 files formatted) |
git diff --check 4d3639206..HEAD |
clean |
.github/check_fix_pr.py (main's copy) on the final title and body, with the changes since the merge base with main, as pr_fix_evidence.yaml runs it |
PASS, 2 changed executable tests |
git merge-tree --write-tree --name-only HEAD origin/main (52b30809f) |
clean, no conflict |
Scratch merge of this head with #2845's d5d32074d, uc_receiving_education.yaml |
3 failed, 38 passed: cases 31, 33 and 34, each on is_qualifying_young_person_for_universal_credit, as composition.md expects; no new case fails |
I also ran, for the body's table: tests/policy/baseline/gov/dwp/universal_credit/ 57 passed; tests/policy/baseline/finance/benefit/family/universal_credit/ 563 passed; test_parameter_descriptions.py 24 passed. On main at 2da212abc, the new YAML file gives 41 failed, and the property file 4 failed and 1 passed (the grid guard reads only parameters). At c8767e3c, the new property also passed on the #2845 merge (137 s).
Revert checks at f06a9c63
Each in a scratch worktree, which I removed afterwards; the working tree stayed clean.
- Drop
migrated_studentfrom the return ofuc_is_excepted_from_education_condition: 7 of 41 cases fail (19, 25, 26, 34, 36, 37 and 41), so 36, 37 and 41 fail as the brief expects. The legacy closure YAML files pass. In the property file, 3 of 5 fail (24 s):test_reg_60_protection_follows_the_closure_dateson its 2025 and 2026 grids,test_education_condition_invariantson an explicit example, andtest_examples_reach_the_cases. - Make
legacy_benefits_closedignore theactiveparameters, by passinglegacy_award_is_closeda stand-in with all three closed in that variable alone: case 40 alone fails, as the brief expects. The legacy closure YAML files pass. In the property file, 1 of 5 fails (77 s):test_reg_60_protection_follows_the_closure_dateson its 2024 and 2025 grids. In the legacy closure property files, 4 of 25 fail (442 s):test_uc_legacy_closure_properties.py::test_closure_claims_and_the_end_of_legacy_awards,::test_the_closure_flag_only_decides_closure_claimsand::test_the_tax_credit_closure_ends_awards_paid_the_year_before, andtest_legacy_closure_scope.py::test_preclosure_report_does_not_end_outside_own_award.
The same runs at c8767e3c, before the review fixes, gave the same failures.
Skipped
- Survey-data tests: none of this work needs them.
- The body's differential row (two hand-built datasets): I didn't re-run it, and the body now says it comes from an earlier head; no formula has changed since.
- The full suite: CI covers it once the hub moves the PR branch.
- rulespec-uk#514 lies outside this session's GitHub access, so I couldn't read it. The addendum names no module path, and it opens "Above, we asked for…", on the reading that PolicyEngine wrote Remove y axis title from validation chart #514.
Judgment calls
- gov.uk and legislation.gov.uk resolved from this session, against the brief's note. I used them to check £400.14 (DWP's 2025 to 2026 table: "Single 25 or over 393.45 400.14"), to find the NI amounts (SR 2025/64 Schedule 13, £400.14; SR 2026/54 reg 3(3)(a), £424.90 from 6 April 2026), to fetch NI UC Regs reg 3, and to fetch every data.xml again. Every hash matched the brief's except NI reg 47 (
36c0fcac…today,73e9c123…in the brief), whose text and notes matched; the addendum quotes reg 47(4) only as a supporting provision, under the hash of the file I fetched. - Case 41 asserts £4,801.68, from SR 2025/64, not DWP's GB table.
- The cases assert a few outputs beyond the brief's list:
uc_receiving_educationin 36, 37 and 41;legacy_benefits_closedandclaims_uc_at_legacy_closurein 37, 39 and 40;claims_universal_credit,uc_receiving_educationandmeets_uc_education_conditionin 39 and 40. - Cases 36, 37 and 41 state their assumptions: 12 assessment periods at the year's rate and no transitional element (reg 55). The model has no transitional element, and the brief doesn't hold regs 50 to 55, so I state it as an assumption rather than a result.
- The property draws a full-time flag beyond the brief's list and also asserts eligibility: the model finds a degree student eligible exactly when the exception holds. Its exception assertion pins the model's proxy for people off a course (the "same course" departure), and its docstring says so.
- With 40 derandomised examples, Hypothesis drew no 2025 case, so explicit grids for 2024, 2025 and 2026 cover each closure boundary, and
test_closure_grids_straddle_each_closurefails if theactivedates move off those years. - In the addendum I kept the brief's "(61)" style for regulation numbers, and marked each omitted paragraph
[...]. - The body's departures go beyond the brief's minimum: reg 56(3A), the reg 3(3) exception, a joint claimant without a notice, a course started after the migration day, and Income Support and income-based JSA migrants before 2026-27. "Overlapping work" adds
mainat52b30809f, which I checked withmerge-tree, along with the SHAs the body already cites. - The remote branch didn't exist (only a stale local tracking ref at
main), so the first push created it; I never force-pushed.
Generated by Claude Code
|
The description, invariant 9 and the property's docstrings keep your distinction. Where the model derives the closure, the protection starts no earlier than 2025-26. Where a user or dataset sets |
juaristi22
left a comment
There was a problem hiding this comment.
Re-reviewed f06a9c6362bc03b4247f275e9062d532296451f5, incrementally from 4d363920686117ae58ce23fa284918964ab86bbc. COMPLETE: no findings or material evidence gaps.
The new NI references, temporal cases and actual Axiom #514 addendum are corroborated. The NI qualifying-claim/migration-day/student provisions and cessation cross-references support the stated examples. I checked £400.14 against NI's 2025 Schedule 13 as made and its 7 April assessment-period commencement, rather than relying on the now-omitted revised schedule.
The expanded description correctly distinguishes default-derived closure protection (model 2025/2026) from explicit historical closure/migration inputs. Notice/deadline, same-course and cessation history remain disclosed annual-model limitations; the new properties test that proxy rather than claim full longitudinal statutory implementation.
Validation: 43 adopted checks passed, including all 41 YAML cases and both new closure tests; the prior 164 passing checks remain applicable because production formula ASTs and prior property functions are unchanged. Corrected ordinary-input GB/NI paired diagnostics also give final UC and household-income changes of £4,801.68; the income comparison allows for the unchanged default TV licence charge. Author mutation/population claims were not reproduced.
The PR remains a draft, and hosted Python checks were pending at capture. This is a completed review comment, not an approval; I will retain the review and check CI/readiness on a later run.
|
Ready for hub review Verdict: REQUEST_CHANGES at Two P2s, no P1.
No formula, expected value or count at this head is wrong. Every count I reran matches the body, and both of my mutants are killed. A run of my own in 2021 confirms juaristi22's qualification. I worked cases 36 to 41 again from legislation.gov.uk, and their law and amounts hold. CI is green at the head. The P3s are fixes to citations, timing and wording in the cases, the body and the addendum. legislation.gov.uk and gov.uk resolved from this session, so I fetched each provision below (data.xml, current and point-in-time) rather than relying on the addendum's quotations. Prior round, item by item
FindingsP2-1. Invariant 9 and both new docstrings promise eligibility to "anyone else", and case 15 contradicts thatWhere:
The stated population ("single people aged 25 to 59 with no child, no listed disability benefit, no foster role, no capital and no other benefit report") sets no limit on the course. So it takes in a full-time student on a non-advanced course with maintenance funding. That student receives education under reg 12(2)(b) and, with no exception, isn't eligible. Case 15 is such a person: aged 30, single, The test never draws such a person. Both docstrings also scope the property to "where the model derives claims_uc_at_legacy_closure itself" ( Replacements:
Editing the test docstring changes the drawn examples. Hypothesis 6.170.11 seeds P2-2. Overlapping work leaves out #2845, and the prescribed composition change would remove the reg 60(1) protection from a young closure migrant"Overlapping work" never mentions #2845. On a scratch merge with f379377,
Cases 36 to 41 pass. Comment 6594856385 then prescribes the change for whichever PR lands second: I applied that formula change on the merge and ran
M's nil contradicts this PR's own rule: "One with maintenance funding receives education under reg 12(2)(b), and on a closure claim keeps the award under reg 60(1) (case 34)". It also contradicts the law that rule rests on. Reg 60(1) asks whether, on the migration day, the person failed the condition "because the person is undertaking a full-time course (see regulation 12(2) and 13 ...)". Then it disapplies the condition "while they are continuing to undertake that course". Reg 8(2)(a) ends the Working Tax Credit award "on the day before the first date on which the claimant is entitled to universal credit", which is the migration day itself (reg 49). So reg 28 still applied that day, and the student took reg 12(2)(b). Nothing would catch the change. No YAML case has a young claimant whose own closed report derives the closure. Case 34 uses Housing Benefit and sets the flag, and The comment also says the patch "applies at either head". At f06a9c6, "Overlapping work" also cites stale heads. Replacements in "Overlapping work":
P3-1. The explicit-flag route:
|
| Setup, 2021 | claims_uc_at_legacy_closure |
excepted | is_uc_eligible |
UC |
|---|---|---|---|---|
A: claims_uc_at_legacy_closure set true, no report |
True | True | True | £4,938.12 |
B: legacy_benefits_closed set true, would_claim_uc_at_legacy_closure default |
True | True | True | £4,938.12 |
C: legacy_benefits_closed set true, would_claim_uc_at_legacy_closure false |
False | False | False | £0 |
| D: neither set, reports £1,000 of Working Tax Credit | False | False | False | £0 |
E: claims_uc_at_legacy_closure set false, Working Tax Credit £1,000 |
False | False | False | £0 |
2019 and 2024 give the same pattern (A and B get £3,813.84 and £4,721.40). In 2025, D also gets £4,801.68, from the derived closure. So the qualification holds for claims_uc_at_legacy_closure. For legacy_benefits_closed it holds only where would_claim_uc_at_legacy_closure holds, and datasets draw that input at DWP's claim rates (would_claim_uc_at_legacy_closure.py documentation). The model never protects a qualifying young person (uc_is_excepted_from_education_condition.py:132-136).
The law before 2025-26 also differs by year:
- Reg 60 exists only from 24 July 2019.
uksi/2014/1230/regulation/60/2019-06-01/data.xmlreturns 404, and the note reads "Pt. 4 inserted (24.7.2019) by ... (S.I. 2019/1152), regs. 1(2), 3(7) (with reg. 2)". - From 27 January 2025 it reaches only "a person who has made a qualifying claim".
- In Northern Ireland, SR 2016/226 reg 45(7), in its version to 31 July 2022, read: "The Department must not issue a migration notice while the restriction in regulation 2 (managed migration pilot: limit on number of cases migrated) of the Universal Credit (Managed Migration Pilot and Miscellaneous Amendments) Regulations 2019 is in force". That restriction was revoked on 25 July 2022 (S.I. 2022/752 reg 10).
Replacements:
- Departures intro, second sentence: "Unless a user or dataset sets the flag, the model derives it from
legacy_benefits_closedandwould_claim_uc_at_legacy_closure, and unless a user or dataset setslegacy_benefits_closed, it takes that closure from theactiveparameters." - Migration day, last sentence: "Where, for a year before 2025-26, a user or dataset sets
claims_uc_at_legacy_closuretrue, astest_education_condition_invariantsdoes in 2019, 2021 and 2024, or setslegacy_benefits_closedtrue for a family whosewould_claim_uc_at_legacy_closureholds, the model protects every claimant and partner in the family who doesn't count as a qualifying young person. DWP protected no one under reg 60 before 24 July 2019, when SI 2019/1152 inserted it, then only a notified person until 26 January 2025, and from 27 January 2025 only a person who had made a qualifying claim (reg 48); in Northern Ireland the Department for Communities could issue no migration notice before 25 July 2022 (SR 2016/226 reg 45(7)). So a partner whom DWP never notified gains, and so does everyone the model protects before 24 July 2019, or in Northern Ireland before 25 July 2022." - Which years, second paragraph: "A user or dataset can also set
claims_uc_at_legacy_closuretrue for any year, orlegacy_benefits_closedtrue for a family whosewould_claim_uc_at_legacy_closureholds (its default), and the model then applies the protection in that year: ..." - For the next reply to juaristi22, since 6602652027 is already posted: "Where a user or dataset sets
claims_uc_at_legacy_closuretrue, or setslegacy_benefits_closedtrue for a family whosewould_claim_uc_at_legacy_closureholds (its default), the model applies it in any year, astest_education_condition_invariantsdoes withclaims_uc_at_legacy_closurein 2019, 2021 and 2024."
P3-2. The text in force before 27 January 2025 loses "as a consequence of a claim", and case 40 understates it
GB reg 60(1) at uksi/2014/1230/regulation/60/2024-06-01/data.xml reads: "Where a notified person does not meet the basic condition in section 4(1)(d) of the Act (not receiving education) on the day on which all awards of any existing benefit are to terminate as a consequence of a claim for universal credit because the person is undertaking a full-time course ..., that condition is not to apply in relation to the notified person while they are continuing to undertake that course." When a claim ends a tax credit award, reg 8(2)(a) ends it on the migration day (reg 49).
- Body, Migration day. The words "under which DWP applied reg 60(1) to a notified person on the day the existing awards were to end" drop the causal clause, so the paraphrase reads as covering a notified person whose awards ended at the deadline because they didn't claim (reg 46(1)). Replacement: "The model also never applies the text in force before 27 January 2025, under which DWP lifted the condition for a notified person who was on a full-time course "on the day on which all awards of any existing benefit are to terminate as a consequence of a claim for universal credit" (SI 2025/3 reg 6(9))."
- Body NI table, reg 61 row, and the same cell in the addendum. This also stops a paragraph acting ("turns on"). Replacement: "From 24 July 2019 (SR 2019/152). The Department for Communities has applied paragraph (2) since 1 August 2022 (SR 2022/194 reg 11). Since 27 January 2025 it has applied paragraph (1) to a person who has made a qualifying claim and is on the course on the migration day; before then it applied it to a notified person on the course on the day on which all their existing awards were to terminate as a consequence of a claim for universal credit (SR 2025/3 reg 6(9))."
uc_receiving_education.yaml:1129-1133(case 40). The comment states only the text in force from 27 January 2025. But DWP protected such a notified student for the whole of 2024-25, so the model departs from the law more widely than the comment says. Replacement for the sentence beginning "A family that claimed by the final deadline": "A family that claimed by the final deadline after DWP issued it a migration notice in 2024-25 did make a qualifying claim (reg. 48). Until 26 January 2025 DWP applied reg. 60(1) to a notified person in it who was on the course on the day their existing awards were to terminate as a consequence of the claim, which for a tax credit award ended by the claim is the migration day (regs. 8(2)(a) and 49); from 27 January 2025 it applied reg. 60(1) to a full-time student in it who was on the course on the migration day."
P3-3. Case 36: two citations, and the Working Tax Credit premise
:975, "Working Tax Credit, an existing benefit (reg. 44(1))". Reg 2(1) holds the definition ("“existing benefit” means income-based jobseeker's allowance, income-related employment and support allowance, income support, housing benefit and child tax credit and working tax credit under the 2002 Act"); reg 44(1) is the power to issue a notice. Replacement: "DWP issued the claimant a migration notice (reg. 44(1)) while they had an award of Working Tax Credit, an existing benefit (reg. 2(1)), so they became a notified person (reg. 44(6)).":982, "no transitional element (reg. 55)". Reg 52(1) decides whether DWP includes an element at all ("A transitional element is to be included in the calculation of an award if the total amount of any awards of existing benefits ... is greater than ... the indicative UC amount"). Reg 55(1) sets "The initial amount of the transitional element". Replacement: "no transitional element (reg. 52(1))".:967-969, the shared profile "aged 30 and single, with no child, no listed disability benefit and no earnings". Such a claimant couldn't hold Working Tax Credit while they had no earnings. For a single claim, the Working Tax Credit (Entitlement and Maximum Rate) Regulations 2002 reg 4(1), second condition, requires that the person "is aged at least 25 and undertakes not less than 30 hours work per week" (version at 2024-06-01). Case 37's step that "their earned income didn't reach the single administrative threshold in the first assessment period" needs the work to have stopped before the award began. Add: "While they had Working Tax Credit they worked at least 30 hours a week (Working Tax Credit (Entitlement and Maximum Rate) Regulations 2002 reg. 4(1)); they stopped before their Universal Credit award began, so they have no earned income from its first assessment period on."- Addendum, shared premise of the companion cases: "the Department may issue a migration notice to a person entitled to an award of tax credit (reg 45(1) and (3B))". Reg 45(3B) only allows a 6 April 2025 deadline day. Replacement: "Before the migration they had an award of Working Tax Credit, an existing benefit (reg 2(1)), and the Department may issue a migration notice to a person entitled to an award of an existing benefit (reg 45(1))."
P3-4. Case 36's migration day falls in 2024-25, not 2025-26
The name "a full-time student whose family claims when its tax credits close in 2025" reads as a claim in 2025-26. In law, a tax credit migrant whose deadline day falls on or before 6 April 2025 has a migration day on or before 5 April 2025:
- reg 6A(9): "a person may not make, or be treated as making, a claim for a tax credit in respect of the whole, or any part, of the tax year beginning on 6th April 2025";
- reg 44(3B): for a notice "issued on or after 27th January 2025 and before 6th April 2025, the deadline day may be 6th April 2025";
- reg 46(3): for a claim after the deadline day but by the final deadline, "the award is to commence on the deadline day".
So 2025-26 is a later year of the award, and DWP protects the student in it under the text in force from 27 January 2025. The result stands.
- Name: "Case 36, a full-time student whose family claimed when DWP moved it off tax credits keeps the award in 2025."
- Comment: after "(reg. 60(1))." add "No one could claim a tax credit for 2025-26 (reg. 6A(9)), and an award on a claim made by the final deadline starts no later than the deadline day (reg. 46(3)), so with a deadline day on or before 6 April 2025 (reg. 44(3B)) the migration day fell on or before 5 April 2025; in 2025-26 DWP applies the text of reg. 60(1) in force from 27 January 2025."
P3-5. Reg 56(3)(b) also covers a start-up period
Reg 56(3)(b) counts a claimant as reaching the threshold in any assessment period in which "regulation 62 (minimum income floor) of the Universal Credit Regulations applies to that claimant or would apply but for regulation 62(5) of those Regulations (minimum income floor not to apply in a start-up period)". UC Regs reg 62(1)(a) applies the floor only to a claimant who "is in gainful self-employment".
uc_receiving_education.yaml:1014-1017(case 37). Replacement: "The claimant has no earned income and isn't in gainful self-employment, so the minimum income floor neither applies to them nor would apply but for a start-up period (Universal Credit Regulations 2013 reg. 62(1) and (5)); so even under reg. 56(3)(b) their earned income didn't reach the single administrative threshold in the first assessment period, and DWP can't apply reg. 56(2) (reg. 56(2)(a)(ii))."- Body, Reg 60(2), first sub-bullet: replace "counting a claimant on the minimum income floor as reaching it" with "counting a claimant to whom the minimum income floor applies, or would apply but for a start-up period, as reaching it".
- Addendum companion case 2 has the same gap in NI reg 57(3)(b) ("or would apply but for regulation 63(6)"). Replace "and the Department applies no minimum income floor to them" with "and the minimum income floor in reg 63 of the Universal Credit Regulations (Northern Ireland) 2016 neither applies to them nor would apply but for reg 63(6) (start-up period)".
P3-6. The departures intro scopes the gains and losses too narrowly
The intro reads: "Each gain or loss concerns a full-time student with no reg 14(1) exception whose family has no other claimant who meets the condition". is_uc_eligible needs one claimant who meets the age and education conditions together. In case 25's mixed-age couple, the 70-year-old meets the education condition but not the age condition, so reg 60(1) still decides the award. Case 25 asserts uc_is_excepted_from_education_condition: [true, true], and dropping migrated_student fails it (row 10 of the mutant table). Replacement: "Each gain or loss concerns a full-time student with no reg 14(1) exception whose family has no other claimant who meets both the age and the education conditions, such as the single claimant of cases 36 to 41 or the younger member of case 25's mixed-age couple."
P3-7. The mutant paragraph contradicts its own table
Body, Test plan, the paragraph that opens "We ran each mutant on a copy of the code". It goes on: "first against the 35 YAML cases and then against the property file. The YAML runs and the last mutant's property run used db140e0. The other property runs used 13bbf82". Rows 10 and 11 ran on 41 cases at f06a9c6, as the paragraph's last sentence says. So "each mutant", "the last mutant" and "the other property runs" no longer fit the eleven-row table. The hub's edit kept the author's wording here. Replacement for those three sentences: "We ran each mutant on a copy of the code, first against the YAML cases and then against the property file. For the first nine mutants, the YAML runs, against the 35 cases, and the ninth mutant's property run used db140e0; their other property runs used 13bbf82, and the two with shrinking off used 27e004a." My rerun of the YAML half of rows 10 and 11 reproduces both rows (Checked and sound).
P3-8. is_qualifying_young_person_for_uc_education.py:43-49 doesn't cite the NI regulation its new sentence names
:38-40 name "reg. 6 of the Universal Credit Regulations (Northern Ireland) 2016" in place of UC Regs reg 5. The tuple cites GB reg 5 but not NI reg 6, while the other two variables cite every NI regulation they name. The URL resolves: HTTP 200, "Meaning of “qualifying young person” ... 6 1 A person who has reached the age of 16 but not the age of 20 ...". Replacement for the tuple's last lines:
"https://www.legislation.gov.uk/nisr/2016/226/regulation/29",
"https://www.legislation.gov.uk/nisr/2016/216/regulation/6",
)P3-9. The grids aren't "every combination", the guard isn't tied to the grid years, and "tests 2020 to 2028" claims more than the draws reach
- Body, the first bullet under invariant 9's test: "with every combination in North East England and in Northern Ireland".
closure_grid(:826-843) crosses region, no report or exactly one, the closure flag and full-time.would_claim_ucis always the opposite of the closure flag, and no person has two reports. The second gap matters: 2025 is the only year with tax credits closed while Income Support and income-based JSA stay open. So a person with Child Tax Credit and Income Support in 2025 is tested only if a drawn case lands there. Replacement: "40 drawn cases plus explicit grids of 40 people for 2024, 2025 and 2026, the years either side of each closure: in North East England and in Northern Ireland, each legacy report alone and none, withwould_claim_uc_at_legacy_closuretrue or false (andwould_claim_ucthe opposite), on a full-time degree or not;". A grid row with two reports would close the gap. test_closure_grids_straddle_each_closure(:941-945) checks its own literals 2024, 2025 and 2026, not the@exampleyears (:873-875). My mutant A2 moves the Income Support closure to 2027-04-01. It passes all five YAML files and the main property, and only this guard fails (:945). So here the guard alone pins the Income Support and income-based JSA dates. Sharing the years keeps the guard and the grids in step:
ALL_OPEN, TAX_CREDITS_CLOSED, ALL_CLOSED = 2024, 2025, 2026
@example(closure_grid(ALL_OPEN))
@example(closure_grid(TAX_CREDITS_CLOSED))
@example(closure_grid(ALL_CLOSED))
...
assert active(ALL_OPEN, "tax_credits.active")
assert not active(TAX_CREDITS_CLOSED, "tax_credits.active")
for path in ("income_support.active", "JSA.income.active"):
assert active(TAX_CREDITS_CLOSED, path)
assert not active(ALL_CLOSED, path)- Which years: "
test_reg_60_protection_follows_the_closure_datestests 2020 to 2028". The strategy draws from 2020 to 2028, but the source fixes the 40 derandomised examples. I logged each case's year fromclosure_situation, a helper, so the test's seed stays the same.uv run pytest …::test_reg_60_protection_follows_the_closure_datesgave1 passed in 255.66sover 43 calls: 2020 (6), 2021 (7), 2023 (8), 2024 (8 + grid), 2025 (7 + grid), 2026 (3 + grid) and 2027 (1). No case fell in 2022 or 2028, and the P2-1 edit changes the draws again. Replacement: "andtest_reg_60_protection_follows_the_closure_datesdraws its model year from 2020 to 2028, besides its grids for 2024 to 2026."
P3-10. Two citations in the body that don't fit what they cite
- The Same course bullet: "keeps the exception whatever the course, or none (case 38)". Case 38 doesn't assert the exception, and its comment says so. The property test pins the proxy. Replacement: "keeps the exception whatever the course, or none, as case 38's comment notes and
test_reg_60_protection_follows_the_closure_dateschecks". - Test plan, first checkbox: "The YAML tests use ... the non-dependant contribution from SI 2026/148 art. 32(3)".
grep -n "96.55\|non-dependant\|2026/148" uc_receiving_education.yamlfinds nothing. The £96.55 comes fromtest_student_head_moves_non_dependant_to_joint_tenant. Replacement for the end of the sentence: "and the taper from reg 22(1)(b);test_student_head_moves_non_dependant_to_joint_tenanttakes the non-dependant contribution from SI 2026/148 art. 32(3)."
P3-11. The body's Northern Ireland section and the shared table
- The section's first paragraph, which probably predates this push, says "The Department for Communities made the Universal Credit Regulations (Northern Ireland) 2016". Their preamble reads "The Secretary of State makes the following Regulations ... Those powers are exercisable by the Secretary of State by virtue of Article 4(1)(a) of the Welfare Reform (Northern Ireland) Order 2015" (
nisr/2016/216/introduction/made). Replacement: "The Universal Credit Regulations (Northern Ireland) 2016, which the Secretary of State made under Article 4(1)(a) of the 2015 Order, have GB reg 3(2) as their reg 3(1), and GB reg 13 and reg 14(1) under the same numbers." - The reg 29 row, in the body and the addendum: "which came into operation when Article 6 of the Welfare Reform (NI) Order 2015 first came into force for any part of NI (their reg 1)". SR 2016/216 reg 1 makes the Regulations "come into operation on the day on which Article 6 of the Welfare Reform (Northern Ireland) Order 2015 (universal credit) comes into force for all purposes in respect of the whole or any part of Northern Ireland". SR 2017/190 art 4(3) brought Article 6 in only "in relation to the case of a claim referred to in paragraph (4)", which legislation.gov.uk records as "for specified purposes". legislation.gov.uk dates SR 2016/216 reg 1, SR 2016/226 reg 1 and reg 29 from 2017-09-27, so the date holds. Replacement for the clause: "which come into operation on the day on which Article 6 of the Welfare Reform (NI) Order 2015 "comes into force for all purposes in respect of the whole or any part of Northern Ireland" (their reg 1); legislation.gov.uk dates both from 27 September 2017, when Article 6 came into force for claims in the first districts (SR 2017/190 art 4(3) to (5) and Schedule 1)."
- The SR 2025/64 link, in the body's last NI paragraph and the addendum's companion-case intro, now opens a revoked Order: "Order revoked (5.5.2026) by The Social Security Benefits Up-rating Order (Northern Ireland) 2026 (S.R. 2026/59), arts. 1(1)(o), 34", with Schedule 13 shown as dots. Link https://www.legislation.gov.uk/nisr/2025/64/schedule/13/made instead, and say "as made".
- Addendum, the paragraph after the table: "The Department has applied reg 50 ... since 24 July 2019". Reg 50 has been in operation since then, but reg 45(7) (P3-1) barred any migration notice, and so any qualifying claim, until 25 July 2022. Replacement: "Reg 50, meaning of "migration day", which matches GB reg 49 word for word, has been in operation since 24 July 2019, when Part 4 came in (SR 2019/152), but until 25 July 2022 reg 45(7) barred the Department from issuing a migration notice, so no one in Northern Ireland could make a qualifying claim before then."
P3-12. Addendum: the verbatim blocks
- SR 2025/3 reg 6(9). The block has four spaces the source doesn't have. The data.xml (SHA-256
97ee2227…, the addendum's own hash) reads "completed)<FootnoteRef …/>—", "…qualifying claim”;", "…migration day”;" and "“person”.". The block has "completed) —", "claim” ;", "day” ;" and "“person” .". Replacement block:
(9) In regulation 61(1) (protection for full-time students until course completed)—
(a) for “notified person”, in the first place it occurs, substitute “person who has made a qualifying claim”;
(b) for “on the day on which all awards of any existing benefit are to terminate as a consequence of a claim for universal credit” substitute “on the migration day”;
(c) for “notified person,” in the second place it occurs, substitute “person”.
- Reg 29. Its only note is commencement information (
<Commentary … Type="I">), not an amendment note. Label it "Commencement information (legislation.gov.uk):". - Hashes. Every hash reproduced for me, but a hash identifies one rendering of the file. A reviewer fetched reg 47 cached and uncached and got
36c0fcac…ande47f5312…, whose canonical XML is identical. That explains the hand-off's reg 47 mismatch. The intro could say each hash covers the file as served on 3 November 2026.
P3-13. Addendum: companion cases 1 and 5
- Case 1: "We assume an award whose 12 assessment periods all start on or after 7 April 2025" leaves the end open. SR 2026/54 reg 1(4) gives £424.90 for "assessment periods commencing on or after" 6 April 2026. Replacement: "We assume an award whose 12 assessment periods all start on or after 7 April 2025 and before 6 April 2026, with no transitional element."
- Case 5 rests "in every later one" on reg 57(1) alone. When the claimant forms the couple, their award ends and the couple are treated as claiming jointly. SR 2016/220 reg 8(8) reads: "A couple who are joint claimants are to be treated as making a claim for universal credit where— (a) one of them ceased to be entitled to an award of universal credit ... on the formation of that couple; and (b) the other member of the couple did not have an award ...". So the later assessment periods belong to a new award, where reg 58(1)(a) bars a transitional element; reg 58(3) treats the new award as a continuation only where reg 58(2) applies. The partner's missing notice matters because it stops the joint claim being a qualifying claim (reg 49), so the claim brings no fresh reg 61(1) protection. The case mentions the notice only beside reg 3(2), which doesn't depend on it. The expected values still follow. Replacement for the sentence that begins "So the Department applies reg 57(4)(b)": "The claimant's award ends when they form the couple, and since the partner has no award the couple are treated as claiming jointly (reg 8(8) of the Universal Credit, Personal Independence Payment, Jobseeker's Allowance and Employment and Support Allowance (Claims and Payments) Regulations (Northern Ireland) 2016). Had the claimant had a transitional element, it would stop from the assessment period in which they became a couple (reg 57(4)(b) and (1)), and no later award could include it, since earned income didn't end the claimant's award (reg 58(1)(a) and (2)(a)); so under reg 61(2) the Department doesn't apply reg 61(1) to the claimant in any of those assessment periods. The joint claim isn't a qualifying claim, since the partner isn't a notified person (regs 45(6) and 49), so it brings no new migration day and no protection under reg 61(1) for either member."
- Case 5, last sentence. The "only" comes from Article 8(2)(a) of the Order ("Joint claimants are jointly entitled to universal credit if— (a) each of them meets the basic conditions"), which reg 3(1)(b) relaxes. Replacement: "Joint claimants are entitled only if each meets the basic conditions (Article 8(2)(a) of the Order), and reg 3(1)(b) relaxes that for Article 9(1)(d) only "if the other member does meet that condition or is excepted from it"; neither does here, so the couple has no joint entitlement."
P3-14. Writing
- Body, Reported receipt, last sentence. The student keeps the protection, not the user or dataset. Replacement: "Cases 37 and 38 keep the report in 2026 and 2027 to stand for the award that closed, and where a user or dataset sets
claims_uc_at_legacy_closure, the student keeps the protection without a report." uc_receiving_education.yaml:1171-1172(case 41) attaches "the Northern Ireland amount in SR 2025/64 Schedule 13" to £4,801.68, where Schedule 13 sets the monthly £400.14. Replacement: "With the assumptions of case 36, and the monthly £400.14 that the Department for Communities set in SR 2025/64 Schedule 13, 12 x £400.14 = £4,801.68."
Checked and sound
Setup. I ran everything in a scratch clone detached at f06a9c6, after uv sync --frozen --extra dev, with HUGGING_FACE_TOKEN and POLICYENGINE_UK_DEFAULT_DATASET unset. The host has 4 CPUs and also ran my other jobs, so my times exceed the body's.
Tests at the head
| Run | Result | Body |
|---|---|---|
uc_receiving_education.yaml |
41 passed | 41 passed |
person_legacy_benefits_closed.yaml |
3 passed | 3 |
legacy_closure_ineligible_partner.yaml, legacy_closure_pension_route.yaml, legacy_closure_migrant_nations.yaml |
1, 4 and 9 passed | 1, 4 and 9 |
pytest -n 4 on test_uc_receiving_education_properties.py, test_uc_legacy_closure_properties.py and test_legacy_closure_scope.py |
30 passed in 408.33 s (test_reg_60_protection_follows_the_closure_dates 270.62 s) |
30 passed in 202 s |
tests/policy/baseline/gov/dwp/universal_credit/ |
57 passed (41 + 3 + 5 + 8 cases) | 57 |
tests/policy/baseline/finance/benefit/family/universal_credit/ |
563 passed in 67.96 s | 563 in 36 s |
test_parameter_descriptions.py; ::test_examples_reach_the_cases |
24 passed; 1 passed | 24; 1 |
pytest policyengine_uk/tests/code_health -n 4 |
3,095 passed | 3,095 |
main at 2da212a with this head's two new test files |
YAML: 41 failed. Property file: 4 failed, and test_closure_grids_straddle_each_closure passed |
the same |
uvx ruff@0.17.2 format --check .; ruff check .; ruff check --select F811,F821 policyengine_uk; git diff --check 4d3639206..f06a9c636 |
2177 files already formatted; all checks passed; all checks passed; clean | clean |
.github/check_fix_pr.py from main (the same file at 0080274 and 1d4d020), run as pr_fix_evidence.yaml runs it: live title and body, empty labels, git diff --name-status --no-renames -z from the merge base 0080274, a base checkout as --repo-root, --head-sha f06a9c6 |
Fix evidence: PASS ('Apply the Universal Credit education condition') - Fix PR has valid reproducer evidence. Changed executable tests: 2. |
PASS, 2 |
My mutants, and the author's last two rows. Each ran in a throwaway worktree, against the five YAML files and the property file with -n 4.
| Mutant | YAML | Property file | Verdict |
|---|---|---|---|
B: claims_uc_at_legacy_closure returns legacy_benefits_closed alone, dropping would_claim_uc_at_legacy_closure |
Case 39 fails (claims_uc_at_legacy_closure@2025: [1.] differs from 0.0). One legacy_closure_pension_route.yaml case fails ("Protected mixed-age couple declining Universal Credit keeps the same Housing Benefit reclaim route"). The rest pass. |
1 failed, 4 passed in 147.60 s: test_reg_60_protection_follows_the_closure_dates at :917, with "Mismatched elements: 8 / 40" on an explicit grid and "1 other explicit example also failed" |
Killed by the new property |
A: tax_credits/active.yaml 2025-04-06: false changed to 2026-04-06: false |
Cases 36, 39 and 41 fail on legacy_benefits_closed@2025. In person_legacy_benefits_closed.yaml and the three legacy_closure_*.yaml files, 1 of 3, 1 of 1, 4 of 4 and 4 of 9 fail. |
1 failed, 4 passed in 266.05 s: only test_closure_grids_straddle_each_closure (:942). The main property passes, because it reads the moved date. |
Killed by the guard and the cases. The main property follows the parameters by design. |
A2 (extra): income_support/active.yaml 2026-04-01 changed to 2027-04-01 |
All pass (41, 3, 1, 4, 9) | 1 failed, 4 passed in 293.87 s: only the guard (:945) |
Killed by the guard alone (P3-9) |
Body row 10: drop migrated_student |
7 of 41 fail: 19, 25, 26, 34, 36, 37 and 41. The legacy closure files pass. | not rerun | Matches the body |
Body row 11: legacy_benefits_closed with all three active closed in that variable alone |
1 of 41 fails: case 40. The legacy closure files pass. | not rerun | Matches the body |
Invariant 9 and the grids. Apart from P2-1, invariant 9 states what the test asserts. The exception holds exactly when a reported legacy benefit's active reads false and would_claim_uc_at_legacy_closure holds; eligibility then equals ~full_time | expected. For this population the expected array reproduces legacy_award_is_closed: there is no Pension Credit or age route below 60, the claimant's own reports decide, no reg 14(1) limb applies, and no branch depends on region. The grids reach each side of each closure: tax credits are open in 2024 and closed in 2025, and Income Support and JSA are open in 2025 and closed in 2026. That follows from convert_to_fiscal_year_parameters sampling 30 April (utils/parameters.py:171), which the comment at :894-897 describes correctly. Only claims_universal_credit.py reads would_claim_uc, so tying it to the opposite of the closure flag costs invariant 9 nothing.
The law in cases 36 to 41. All from legislation.gov.uk data.xml, with point-in-time texts where dates matter.
- Text in force. GB reg 60(1) read "notified person … as a consequence of a claim" from 24 July 2019 to 26 January 2025, with para (2) from 25 July 2022. From 27 January 2025 it reads "a person who has made a qualifying claim … on the migration day" (SI 2025/3 regs 1(2) and 6(9)(a) to (c)). NI reg 61(1) made the same change from 27 January 2025 (SR 2025/3 reg 6(9)), with para (2) from 1 August 2022. Cases 36, 39 and 41 (2025-26), 37 (2026-27) and 38 (2027-28) fall wholly under the current texts. Case 40 (2024-25) spans both, and its claimant, who claimed before any notice, is protected under neither.
- Case 36. A notified person (reg 44(6)) who claims by the final deadline (reg 46(4)) makes a qualifying claim (reg 48), so the migration day falls under reg 49 and reg 60(1) protects them. 12 × £400.14 = £4,801.68, from gov.uk "Benefit and pension rates 2025 to 2026": "Single 25 or over | 393.45 | 400.14". It holds, subject to P3-3 and P3-4.
- Case 37. Reg 56(2) needs first-period earnings at or above the threshold (reg 56(2)(a)(ii)). Reg 56(3A) needs a reg 60A(1) award, which needs State Pension Credit age. Neither reg 56(4)(b) nor reg 57 arises. 12 × £424.90 = £5,098.80, from gov.uk 2026 to 2027: "Single 25 or over | 400.14 | 424.90". It holds, subject to P3-5.
- Case 38. "while they are continuing to undertake that course" is verbatim, and off the course reg 12(2) isn't met. The model reads only
is_in_full_time_educationfor the full-time test (uc_receiving_education.py), andmigrated_studentignores the course, so leaving the exception unasserted is right. gov.uk/government/publications/benefit-and-pension-rates-2027-to-2028returns 404, and the newest edition gov.uk lists is 2026 to 2027. So "no rates for 2027 to 2028 by November 2026" holds today. - Case 39. Reg 48 counts a claim only "where the claim is made on or before the final deadline". For a tax credit notified person in 2025-26, the flags (
would_claim_uctrue,would_claim_uc_at_legacy_closurefalse) can only mean a late claim. The body's The claim bullet discloses the other reading. - Case 40. It holds under both texts; P3-2 widens its closing sentences.
- Case 41. It follows SR 2016/226 regs 45(1), 45(6), 47(4), 49, 50 and 61(1), and Working Tax Credit is an existing benefit under NI reg 2(1). £400.14 comes from SR 2025/64 art 32(2) and Schedule 13 (as made), from the first assessment period on or after 7 April 2025 (art 1(1)(n)). It holds.
- UC Regs 2013 reg 56 is "Employee involved in trade dispute", and no case relies on it. The cases' "reg. 56" is SI 2014/1230's, whose threshold comes from UC Regs reg 99(6)(a).
The variables. git diff 4d3639206 f06a9c636 -- policyengine_uk/variables touches three files (+23 −6), only in documentation strings and reference tuples. A reviewer's AST comparison with those removed finds the classes identical. Each provision the new text cites says what the text says:
- NI reg 29 refers to "regulation 6 of the Universal Credit Regulations", and SR 2016/216 reg 6 matches UC Regs reg 5 apart from NI names.
- NI reg 61 matches GB reg 60, with Article 9(1)(d) and regs 57 and 58 in their places. NI reg 49 matches GB reg 48, with reg 47(4) for reg 46(4).
- GB reg 48 defines the qualifying claim.
- NI UC reg 12(1) treats every qualifying young person as receiving education, with no training-scheme exception.
- No formula has a regional branch or any reg 60(2) or 61(2) logic, so "the model doesn't apply reg. 61(2) either" holds.
The hand-off's line references are right: :36-40, :48, :29-34, :44, :23-25, :52-58 and :65-70.
The addendum.
- All ten SHA-256 values reproduce.
- Every block matches its data.xml word for word, curly quotes, "(2) , (3A)" and reg 61(2)'s closing ”. included, except for P3-12. That ”. is in legislation.gov.uk's own text of SR 2022/194 reg 11.
- Each
[...]stands for a real omitted paragraph, and nothing else is left out. - The dates hold: 27 September 2017, 24 July 2019 (SR 2019/152 regs 1(2) and 2(8)), 1 August 2022 (SR 2022/194 regs 1(1) and 11) and 27 January 2025 (SR 2025/3 reg 1(2), which defers only regs 6(2)(b) and 6(8)).
- The cross-references hold, and NI reg 50 matches GB reg 49 word for word.
- The companion cases' expected values follow:
- £400.14 (SR 2025/64 Schedule 13, as made) and £424.90 (SR 2026/54 regs 1(4) and 3(3)(a));
- case 3: a claim after the final deadline isn't a qualifying claim (reg 49), reg 46 lets the Department move the deadline day only "on the Department's own initiative" or on a request made "before the deadline day", and reg 48 was revoked from 1 August 2022;
- case 4 under both texts;
- case 5 to £0 (Article 8(2)(a)).
The description.
- The live title and body are the brief's hub version (invariant 9 reads "if and only if").
- The hub's three edits change no fact: "exactly when" becomes "if and only if" twice, "and anyone else" becomes "and finds anyone else", and the rewritten opening of the mutant paragraph keeps the author's facts (P3-7 is the author's).
- Every backticked name exists at the head (a script ran
git grepandgit ls-treeover each one). The exception isesa_income_abolition_reached, which lives onfix-2562-esa-abolition(dc69f3a), as the body says. - In Which years, cases 19, 25, 26, 34 and 35 set
claims_uc_at_legacy_closure: truefor 2026, andtest_education_condition_invariantsdraws it inYEARS = [2019, 2021, 2024, 2026](:100,:225). - The departures bullets hold against the code and the law, apart from P3-1, P3-2, P3-5 and P3-6.
- The test-plan counts match my reruns (table above).
- My run reproduces the body's "Young people on legacy benefits" Working Tax Credit row at this head (£5,112.96; P2-2).
Merges. git merge-tree --write-tree --name-only f06a9c636 X exits 0 with no conflicted file for each of these:
| X | Tree |
|---|---|
main 1d4d020 (moved past 52b3080; #2776 merged) |
b4efb8250b |
| 52b3080 | 35f8ffb3f5 |
| #2845 f379377 | 68b757b79b |
| #2279 c5b9850 | 1b2989076d |
#2813 / fix-2562-esa-abolition dc69f3a |
eff04b1893 |
fix-2120-uc-carer-awards 260f6a7 |
fff3f53cc7 |
On the #2845 merge, uc_receiving_education.yaml gives 3 failed and 38 passed: cases 31, 33 and 34 fail on is_qualifying_young_person_for_universal_credit, and cases 36 to 41 pass. That matches the hand-off's account at d5d3207. P2-2 covers the follow-up change.
CI at f06a9c6. Every check has completed. Of 21 check runs, 20 succeeded:
- Changes, Lint, Documentation build, Release build records the commit, and Install + smoke-import on Python 3.11 to 3.14;
- Test / YAML (11:37Z), Test / Python 1/7 to 7/7 (the last at 12:38:23Z) and Test / Result (12:38:34Z);
- Changelog fragments, Fix evidence and Aggregate impact (12:40:14Z).
One run, Fix evidence 41627920552, shows cancelled. Two pull_request_target events arrived at 11:29:25Z, and the workflow's concurrency group (cancel-in-progress for pull_request_target) cancelled the earlier run. Its successor, 41627920585, passed. Nothing failed.
What I couldn't check
- rulespec-uk#514 lies outside this session's GitHub access, so I checked the brief's copy of the addendum (
AXIOM_514_NI.md), not the posted comment. - I didn't rerun the body's differential row (two hand-built datasets), or the property halves of the eleven mutant rows. I reran the YAML halves of rows 10 and 11 and my own three mutants.
- Merges with 2da212a, Value means-tested capital less 10% sale expenses and secured debt #1969 and Add overseas and other real estate to property wealth and capital #2688 weren't in this round's list, so I didn't rerun them.
- Whether DWP set any tax credit deadline day after 6 April 2025, through reg 44(3) notices issued before 27 January 2025 or reg 45 extensions. P3-4 holds for deadline days on or before 6 April 2025.
- Whether DWP read the pre-2025 reg 60(1) as covering a notified person who claimed after the deadline day but by the final deadline, whose awards ended under reg 46(1). The explanatory memorandum to SI 2025/3 doesn't say.
- When the Department for Communities first issued migration notices in practice. I checked only the legal bar in reg 45(7).
- The legal outcome for the body's W row after Apply SI 2014/1230 reg 28 to the UC qualifying young person test #2845 (P2-2). It turns on UC Regs reg 5(5), which no one in this round fetched and quoted.
Generated by Claude Code
juaristi22
left a comment
There was a problem hiding this comment.
Re-reviewed the new detailed reply at unchanged head f06a9c6362bc03b4247f275e9062d532296451f5. COMPLETE — no confirmed criticals; two nonblocking findings remain.
- A1 — Narrow invariant 9 and both closure-test docstrings. The generator fixes the course at
TERTIARY; “anyone else” overstates its population. A full-time non-advanced student with maintenance funding is already in case 15 and is correctly ineligible. Say “anyone among them not on a full-time course”, and exclude both explicitly supplied closure inputs when describing the fully derived route. - A2 — Complete the source references and precision edits. Add NI UC regulation 6 to the reference tuple that names it. In case 36, regulation 2(1) defines an existing benefit; regulation 44(1) supplies the notice power. Preserve the causal UC-claim condition when describing the pre-27 January 2025 regulation 60 text, and link the 2025 NI rates as made. These edits leave the verified monthly/annual amounts unchanged.
The #2845 concern is a useful integration handoff: the reported lost award needs a future merge plus an additional proposed formula change. It does not establish a wrong award at this head. When integrating that change, preserve migration-day status and cover the young tax-credit migrant; I have not certified the proposed patch or executed that future tree.
Prior passing code/final-UC/household-income evidence was retained after checking the unchanged code, dependency and artifact hashes. I checked the new source claims against the 56 retained official/queue records rather than rerunning unchanged tests. Exact-head CI is green (the cancelled duplicate Fix evidence run has a passing successor). Approval remains withheld while A1/A2 are open; the PR is also still a draft.
|
Picking this up for the UK hub. Reply here if you're mid-change and I'll stop. Generated by Claude Code |
…ons after review Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6FJANF2Y9hDYWaUWXU
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6FJANF2Y9hDYWaUWXU
|
Generated by Claude Code |
|
Generated by Claude Code |
|
Generated by Claude Code |
|
Generated by Claude Code |
|
Ready for hub review Head: Before starting. Findings
All fourteen P3s applied; I skipped none. ExamplesHypothesis's seed for
Tests at
|
| Run | Result |
|---|---|
uc_receiving_education.yaml |
42 passed |
person_legacy_benefits_closed.yaml |
3 passed |
legacy_closure_ineligible_partner.yaml, legacy_closure_pension_route.yaml, legacy_closure_migrant_nations.yaml |
1, 4 and 9 passed |
pytest -n 4 on the three property files |
30 passed in 245.71 s; none reached the 40-minute cap |
tests/policy/baseline/gov/dwp/universal_credit/ |
58 passed |
tests/policy/baseline/finance/benefit/family/universal_credit/ |
563 passed in 42.67 s |
test_parameter_descriptions.py |
24 passed |
main 1d4d020 with this head's YAML and property file |
42 failed; 4 failed, 1 passed |
pytest policyengine_uk/tests/code_health -n 4 |
3,095 passed |
uvx ruff@0.17.2 format --check .; ruff check .; --select F811,F821 policyengine_uk; git diff --check f06a9c636..HEAD |
2177 files already formatted; all passed; all passed; clean |
.github/check_fix_pr.py from main 1d4d020, as pr_fix_evidence.yaml runs it (merge base 0080274, empty labels) |
Fix evidence: PASS, changed executable tests: 2 |
Scratch merge with #2845 f379377f9, uc_receiving_education.yaml: no change / composition.md's change / my patch |
3 failed, 39 passed (31, 33, 34) / 1 failed, 41 passed (42) / 42 passed |
| the same merge, property file with my patch | 5 passed in 179.62 s |
git merge-tree --write-tree --name-only HEAD X, no conflict for any: main 1d4d020 (tree 9dc14e992c), 2da212a, 52b3080, #1969 b4e8722, #2279 c5b9850, #2688 0d760fc, #2845 f379377 (c32c24b16e), fix-2120-uc-carer-awards 260f6a7, #2813 dc69f3a. #1969 and #2688 have moved since the body's SHAs; at the old #2688 798491c the merge still conflicts in uprating_indices.yaml, but at 0d760fc it doesn't, so the body now lists both new heads as clean.
Mutant A2
income_support/active.yaml 2026-04-01 changed to 2027-04-01, at this head: all five YAML files pass (42, 1, 4, 3, 9). Property file: 1 failed, 4 passed in 175 s, only test_closure_grids_straddle_each_closure at :957 (assert not active(ALL_CLOSED, path)). The main property and the new two-report row follow the moved parameter by design, so the guard alone still kills A2; it now asserts on the shared constants the grids use.
Skipped
- Survey-data tests, the full suite (CI), the body's differential row, and the property halves of the old mutant rows.
- Citing reg 8 for Income Support, income-based JSA or Housing Benefit: the patch's migration-day variable reads those reports as Apply the Universal Credit education condition #2806 already does, but no one has quoted when DWP ends those awards on a migration.
- rulespec-uk#514: outside this session's access.
Judgment calls
- The body keeps W's £5,112.96 and says DWP's outcome depends on reg 5(5) rather than changing the row.
- The patch gates on the migration-day status only, as the brief asks. It doesn't also require that the person received education under reg 12(2) on the migration day, which would take W's exception away but would still leave W's final award to reg 5(5).
- Case 42 keeps the Working Tax Credit report in 2026 to stand for the closed award, as cases 37 and 38 do. It doesn't say how an 18-year-old held Working Tax Credit (WTC Regs reg 4(1)); the review's M has the same premise.
- Case 42 asserts
is_qualifying_young_person_for_uc_education: [false]at this head, so the guard fails first on that variable; M's award under the earlier change also drops to £0 (checked with a script on the merge). - The grid row has two reports; the body now says 48 people.
- The body's test-plan rows now give this head's counts and
mainat 1d4d020.
Generated by Claude Code
|
We pushed
Case 42 covers the young tax-credit migrant. An 18-year-old on a full-time non-advanced course with maintenance funding, whose closure the model derives from a Working Tax Credit report, keeps 12 × £338.58 = £4,062.96 in 2026-27 under reg 60(1). On a merge of both heads, the change in comment 6594856385 fails that case. The new composition note gives whichever of #2845 and #2806 lands second a patch that keeps the reg 60(1) gate on migration-day status. With that patch, all 42 cases and the property file pass on the merge. My earlier reply overstated the |
juaristi22
left a comment
There was a problem hiding this comment.
Re-reviewed 5961424461ff5ca4b7f87582d37d55314c5411b6 and the description update caught by the publication guard. COMPLETE — A1 and A2 are resolved; no findings or material source gaps remain.
The committed test/docstring/reference fixes and case 42 passed 45 focused checks through final UC and household net income. The actual PR description now fixes the course-scope statement, historical causal wording and as-made NI rate link. I also checked the filed #514 addendum edit, rather than treating the proposed text as adopted prematurely.
No unchanged broad suite was rerun. The future #2845 composition patch remains outside this head. Approval is deferred because the PR is still a draft. The completed review can be retained when readiness and applicable required checks at the exact head are checked later.
Fixes #2346
What and why
Universal Credit has a basic condition that the claimant "is not receiving education" (Welfare Reform Act 2012 s.4(1)(d)). In the UC Regs 2013, the Secretary of State sets out who receives education (reg 12), when a person undertakes a course (reg 13) and who doesn't have to meet the condition (reg 14(1)).
is_uc_eligibleonmainread no education condition, so the model paid a full-time student who meets no exception. The issue's 19-year-old university student in Plymouth gets £6,348.91 onmainat 2da212a, the figure the issue reports.This PR adds:
is_in_full_time_education(Person, year), an input that stays false until a user or dataset sets it: whether the person takes a full-time course of study or training. The model reads the level fromcurrent_educationandin_HE, and no formula derives the input from them. Carer's Allowance is paid to carers in full-time education (SSCBA 1992 s. 70(3), SI 1976/409 reg. 5) #2311 needs the same fact for Carer's Allowance and can read it; this PR doesn't change Carer's Allowance.has_education_maintenance_funding(Person, year), a second input that also stays false until a user or dataset sets it: whether the person has a student loan or grant for their maintenance on the course, the test in reg 12(2)(b). The model doesn't readstudent_loansoreducation_grantsfor it, since it can't tell from either amount what the support pays for. A learner aged 19 or over on a further education course can get Learner Support for travel, accommodation, materials and equipment, a laptop or childcare, and only support for their maintenance meets reg 12(2)(b). In case 30, a student on a full-time course that doesn't lead to advanced education, with a £100 equipment grant, a £3,000 student loan and no maintenance funding, keeps an award of £5,098.80.is_qualifying_young_person_for_uc_education: the qualifying young person status the model reads for the condition. Under SI 2014/1230 reg 28, a person entitled to an existing benefit doesn't count as a qualifying young person for the UC Regs. The Secretary of State lists the existing benefits in reg 2(1): income-based JSA, income-related ESA, Income Support, Housing Benefit, Child Tax Credit and Working Tax Credit. The model reads entitlement from the year's reported receipt, the evidence it reads for legacy closure. A claimant or partner on a couple's tax credit award counts as entitled, since the couple claims jointly (Tax Credits Act 2002 s.3(3)(a)). A person outside the definition under reg 28 takes the ordinary route under reg 12(2). This PR leavesis_qualifying_young_person_for_universal_creditas it stands: it doesn't apply reg 28, and the model still reads it for the child element, the benefit cap and Council Tax Reduction. Apply SI 2014/1230 reg 28 in is_qualifying_young_person_for_universal_credit #2804 tracks the same rule inis_qualifying_young_person_for_universal_credit.uc_receiving_education(reg 12). A person on a full-time course receives education where:current_educationorin_HE;has_education_maintenance_funding;uc_is_excepted_from_education_condition(reg 14(1), and SI 2014/1230 reg 60), with one sourced condition for each limb the model can express:uc_limited_capability_for_work);uc_is_responsible_foster_parent_of_child_under_oneoruc_is_foster_parent_or_new_friend_or_family_carer;claims_uc_at_legacy_closure), which the model takes as the qualifying claim under reg 48. A person who receives education under reg 12(2) gets the protection; a qualifying young person under reg 12(1A) doesn't, while a person outside the definition under reg 28 takes reg 12(2) and gets it.meets_uc_education_condition(s.4(1)(d)): not receiving education, or having an exception.is_uc_eligibleneeds one claimant (is_uc_single_or_joint_claimant) to meet the age condition and the education condition together, on every route.The couple rule
Under reg 3(2), a couple can claim jointly where one member has reached the qualifying age for State Pension Credit "if the other member does meet that condition", or where one member receives education and has no exception "if the other member does meet that condition or is excepted from it". In ADM E2016, DWP accepts a joint claim where one member meets all the basic conditions. Under reg 14(1)(f), an older student can claim jointly with a younger partner who meets them. The model follows E2016, so one claimant must meet the age and education conditions together. A member of a couple who claims as a single person under reg 3(3) has no joint claimant beside them, so they must meet both conditions themselves.
For a couple aged 70 and 60 with no income in 2026-27, the model gives these awards:
Each positive award comes to 12 × £666.97 = £8,003.64, DWP's joint rate where one or both claimants are 25 or over, from Benefit and pension rates 2026 to 2027. Since 15 May 2019 the couple can't claim Pension Credit instead (SPCA 2002 s.4(1A)).
The model keeps the condition on every route into
is_uc_eligible:For a couple aged 30 where one member takes a full-time degree and the other doesn't, the pair claim jointly at the couple rate, £8,003.64 a year with no income. Under reg 68, DWP counts a student's loan and some grants as student income, after statutory exclusions and disregards. The model leaves
student_loansandeducation_grantsout of its Universal Credit unearned income list (gov.dwp.universal_credit.means_test.income_definitions.unearned), so it counts no student income and the couple gets the full joint amount. If both members study and neither has an exception, the couple gets nothing.Young people on legacy benefits
An 18-year-old on a full-time course that doesn't lead to advanced education, who reports Working Tax Credit or Housing Benefit, keeps the award they get on
main. Under reg 28 they don't count as qualifying young people, and with no maintenance funding they don't receive education under reg 12(2). One with maintenance funding receives education under reg 12(2)(b), and on a closure claim keeps the award under reg 60(1) (cases 34 and 42).mainLimited capability for work
Under reg 14(1)(b), for a claim on or after 15 December 2021, the decision maker must determine limited capability for work (LCW) before the course starts. The model reads
uc_limited_capability_for_workand holds no other LCW fact. The model can derive the LCW variable from disability status without an assessment, so it can keep students who have never had an LCW determination, as well as students with an assessment after their course began. A full-time student aged 30 on the standard daily living rate of PIP, withis_disabled_for_benefitstrue and no limited capability for work and work-related activity, gets £5,098.80 (12 × £424.90) in the model. The model doesn't infer LCW from an ESA award. A user who wants an ESA assessment to count setsuc_limited_capability_for_workoruc_limited_capability_for_WRA, and a user or dataset can setis_disabled_for_benefits, from which the model derives LCW.What the model can't express
The model can't express the parts of regs 12 to 14 and SI 2014/1230 regs 28 and 60 below.
Where the annual closure proxy departs from reg 60
The model applies the reg 60(1) protection to a claimant or partner who doesn't count as a qualifying young person, in a family that claims at closure (
claims_uc_at_legacy_closure), in every model year in which that flag holds. Unless a user or dataset sets the flag, the model derives it fromlegacy_benefits_closedandwould_claim_uc_at_legacy_closure, and unless a user or dataset setslegacy_benefits_closed, it takes that closure from theactiveparameters. Against reg 60, and SR 2016/226 reg 61 in Northern Ireland, the model departs in the ways below. Each gain or loss concerns a full-time student with no reg 14(1) exception whose family has no other claimant who meets both the age and the education conditions, such as the single claimant of cases 36 to 41 or the younger member of case 25's mixed-age couple.Migration day. Under reg 60(1), DWP lifts the condition for a student on the course on the migration day, the day before the Universal Credit award starts (reg 49). With the closure taken from the
activeparameters, the model protects a student only from the model year in which their reported benefit closes: 2025-26 for tax credits, 2026-27 for Income Support and income-based JSA. A tax credit family whose Universal Credit award started in 2024-25 has its migration day in that year, where, on the parameters alone, the model protects nobody (case 40). So for 2024-25 the model gives no Universal Credit to a student in that family whom DWP protects under reg 60(1). Likewise, on the parameters alone, the model gives no Universal Credit before 2026-27 to a student whom DWP protects under reg 60(1) after an earlier migration from Income Support or income-based JSA. The model also never applies the text in force before 27 January 2025, under which DWP lifted the condition for a notified person who was undertaking a full-time course "on the day on which all awards of any existing benefit are to terminate as a consequence of a claim for universal credit" (SI 2025/3 reg 6(9)). Where, for a year before 2025-26, a user or dataset setsclaims_uc_at_legacy_closuretrue, astest_education_condition_invariantsdoes in 2019, 2021 and 2024, or setslegacy_benefits_closedtrue for a family whosewould_claim_uc_at_legacy_closureholds, the model protects every claimant and partner in the family who doesn't count as a qualifying young person. DWP protected no one under reg 60 before 24 July 2019, when SI 2019/1152 inserted it, then only a notified person until 26 January 2025, and from 27 January 2025 only a person who had made a qualifying claim (reg 48); in Northern Ireland the Department for Communities could issue no migration notice before 25 July 2022 (SR 2016/226 reg 45(7)). So a partner whom DWP never notified gains, and so does everyone the model protects before 24 July 2019, or in Northern Ireland before 25 July 2022.The same course. Under reg 60(1), DWP lifts the condition only "while they are continuing to undertake that course", the course the student undertook on the migration day. The model reads
is_in_full_time_educationyear by year, keeps no record of which course a student takes, and keeps the exception whatever the course, or none, as case 38's comment notes andtest_reg_60_protection_follows_the_closure_dateschecks. A student who finishes that course and starts another full-time course keeps the award in the model, where DWP applies the condition again and pays nil. So does a claimant who wasn't on a full-time course on the migration day and starts one later, to whom DWP never applies reg 60(1).Reg 60(2) (NI reg 61(2)), which the model never applies. Under it, DWP (in Northern Ireland, the Department for Communities) stops the protection in any assessment period in which a transitional element would have stopped under reg 56 or reg 57 (NI regs 57 and 58), and under reg 56(1) in every later one:
The model keeps the student's exception through each, so the student keeps the award in the model where DWP applies the condition again. Case 37 works through these events for a claimant with no earnings, who stays single and keeps the award.
The claim. The model has no deadline day. It treats a claim at closure (
claims_uc_at_legacy_closure, which it derives fromlegacy_benefits_closedandwould_claim_uc_at_legacy_closureunless a user or dataset sets it) as the qualifying claim under reg 48, and any other claim (would_claim_ucalone) as a claim outside the migration, as cases 36 and 39 show. A family that claimed of its own accord by the final deadline after its migration notice made a qualifying claim, so where a user or dataset setswould_claim_uc_at_legacy_closurefalse for it, its student loses the award in the model, where DWP pays it. A family that claimed before DWP issued it a migration notice didn't claim as a notified person (reg 44(6)), so its claim doesn't count as a qualifying claim (reg 48). Yet if it still reports the closed benefit and keeps the defaultwould_claim_uc_at_legacy_closure, its student gains the award in the model, where DWP pays nil. Nor does the model check that DWP issued a migration notice to both joint claimants (reg 48), so a couple whose second member never got one gains the protection.Reported receipt. As for reg 28, the model reads the year's report in place of entitlement to an existing benefit on a given day (reg 44(1)), and it keeps reading a legacy benefit report after the award closed. With the flag derived, a student who reports a closed benefit for part of a year counts as migrated for the whole year, and gains the award for the months before their migration day (reg 49). A student whose report stops in a later year, as the report of an award that ended would, loses the protection in the model from that year, where DWP keeps it while they continue the course (reg 60(1)). A student who doesn't report the award DWP moved them off gets no protection in the model. Cases 37 and 38 keep the report in 2026 and 2027 to stand for the award that closed, and where a user or dataset sets
claims_uc_at_legacy_closure, the student keeps the protection without a report.Northern Ireland
Claimants in Northern Ireland meet the same condition under the Welfare Reform (Northern Ireland) Order 2015 art. 9(1)(d). The Universal Credit Regulations (Northern Ireland) 2016, which the Secretary of State made under Article 4(1)(a) of the 2015 Order, have GB reg 3(2) as their reg 3(1), and GB reg 13 and reg 14(1) under the same numbers. Under their reg 12(1), the decision maker treats every qualifying young person as receiving education, with no exception for a training scheme, which the model doesn't apply in Great Britain either. So the model's one rule covers Northern Ireland, and case 21 tests it there.
The Department for Communities applies the transitional provisions this PR follows under the Universal Credit (Transitional Provisions) Regulations (Northern Ireland) 2016 (SR 2016/226), with these dates from legislation.gov.uk:
NI regs 29, 49 and 61 match GB regs 28, 48 and 60, with NI cross-references: Article 9(1)(d) of the 2015 Order for section 4(1)(d) of the 2012 Act, reg 6 of the NI Universal Credit Regulations for reg 5, reg 47(4) for reg 46(4), and regs 57 and 58 for regs 56 and 57. So the model's GB routes carry over to Northern Ireland unchanged, and its departures from reg 60 above apply to reg 61 too. Case 41 tests reg 61(1): the student of case 36, in Northern Ireland, keeps 12 × £400.14 = £4,801.68 in 2025-26, at the monthly standard allowance of £400.14 that the Department for Communities set in the Social Security Benefits Up-rating Order (Northern Ireland) 2025 art 32(2) and Schedule 13, as made.
Which years
The change reaches every year the model calculates Universal Credit eligibility. No parameter or date limits it: regs 12 and 14 have applied since 29 April 2013, reg 28 since 16 June 2014 with no amendment, and the dated changes to reg 14(1)(b) concern its timing and the Scottish benefits, which the model doesn't read. In model year 2014 the model raises
ParameterNotFoundErrorforgov.dwp.pension_credit.mixed_age_couples.saving_latest_birth_date, onmaintoo. Case 22 tests 2016.Where the model takes the closure from the
activeparameters, it applies the reg 60(1) protection only from 2025-26 for tax credits and from 2026-27 for Income Support and income-based JSA, in Great Britain and Northern Ireland alike. So on the parameters alone, the model protects no one in 2024-25 or earlier, and never reaches a year in which only the text in force before 27 January 2025 applied. A user or dataset can also setclaims_uc_at_legacy_closuretrue for any year, orlegacy_benefits_closedtrue for a family whosewould_claim_uc_at_legacy_closureholds (its default), and the model then applies the protection in that year: cases 19, 25, 26, 34 and 35 set the first for 2026, andtest_education_condition_invariantsdraws it for 2019, 2021, 2024 and 2026. Cases 36 to 42 test 2024 to 2027 with the closure taken from the parameters, andtest_reg_60_protection_follows_the_closure_datesdraws its model year from 2020 to 2028, besides its grids for 2024 to 2026.Data
This repository holds no survey-backed record of full-time study or maintenance funding.
git grepfinds:current_education, the enrolment stage;in_HEandin_FE;employment_status, with aSTUDENTcategory;student_loansandeducation_grants, amounts that don't say what they pay for;childcare_grant_full_time_student, an input no dataset supplies.Both new inputs stay false on current data until a dataset supplies them, so aggregates don't move. With neither input set, the new variables still evaluate: they give everyone
meets_uc_education_conditiontrue, sois_uc_eligiblegivesmain's result. On two hand-built datasets with neither input, every variable both versions define givesmain's values.Overlapping work
git merge-treemerges this branch cleanly withmainat 2da212a, 52b3080 and 1d4d020, #1969 (b4e8722), #2279 (c5b9850), #2688 (0d760fc), #2845 (f379377),fix-2120-uc-carer-awards(260f6a7) andfix-2562-esa-abolition(#2813, dc69f3a).receives_benefits_in_own_right. Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279's reports of income-related ESA and income-based JSA overlap this branch's reg 28 list, and applying both gives the same result. The reg 5(1)(a) route needs the change in the reg 12(1A) bullet above.is_qualifying_young_person_for_universal_creditand reads a report only while the model pays that benefit. With both merged, cases 31, 33 and 34 fail on that variable, and whichever PR lands second makesis_qualifying_young_person_for_uc_educationreturn it. On its own, that change stops the reg 60 gate reading a closed report. An 18-year-old on a full-time non-advanced course with maintenance funding, whose closure the model derives from a Working Tax Credit report, would then count as a qualifying young person and lose the reg 60(1) protection, although on the migration day reg 28 still applied and the student received education under reg 12(2)(b). Case 42 tests that migrant, and fails under that change alone. So the author of whichever PR lands second keeps the gate on the migration-day status, which reads the report of the award that the claim ended, and keeps case 42 passing. With that gate, the model still gives £5,112.96 in the 2026-27 Working Tax Credit row of "Young people on legacy benefits", but through the reg 60(1) exception rather than reg 28. Whether DWP pays that young person once the tax credit award has ended turns on UC Regs reg 5(5), which this PR leaves open.fix-2562-esa-abolitionwidensclaims_uc_at_legacy_closureto families whose income-related ESA award the abolition reaches (esa_income_abolition_reached). This branch then gives reg 60 protection to every claimant and partner in those families who doesn't count as a qualifying young person. DWP gives reg 60(1) protection only to a person "who has made a qualifying claim", which the Secretary of State defines in reg 48 as "a claim for universal credit by a single claimant who is a notified person or by joint claimants, both of whom are notified persons, where the claim is made on or before the final deadline". Abolition alone does not make their claim qualifying: under regs 44(6) and 48, DWP must have issued a migration notice to each claimant, and they must claim by the final deadline. The Abolish income-related ESA from 1 July 2026 (NI 1 October 2026) #2562 author should distinguish that qualification from the abolition-triggered claim proxy before the model gives reg 60 protection. The author of whichever PR lands second carries that integration fix.Invariants
These hold for every input:
is_in_full_time_educationnever raises a benefit unit's Universal Credit. A family in a household of its own gets either itsmainamount or nil.main's.mainUniversal Credit.is_uc_eligibleequal a reference implementation, which reads the inputs and applies regs 3(2), 12 and 14 as DWP reads them in ADM E2016, and SI 2014/1230 regs 28 and 60. Without the education condition, the reference givesmain's eligibility.claims_uc_at_legacy_closure(no user or dataset sets it orlegacy_benefits_closed),uc_is_excepted_from_education_conditionholds if and only if, in that model year, they report a legacy benefit whoseactiveparameter reads false and their family claims at closure (would_claim_uc_at_legacy_closure). The model then finds a full-time degree student among them eligible if and only if the exception holds, and finds anyone among them who isn't on a full-time course eligible either way.We check the first eight with Hypothesis in
policyengine_uk/tests/test_uc_receiving_education_properties.py, for families in households of their own:main'sis_uc_eligibleformula, which the test copies with its comments removed; the formatter re-wrapped two lines.test_student_head_moves_non_dependant_to_joint_tenantpins the cross-family effect.test_reg_60_protection_follows_the_closure_dateschecks invariant 9:would_claim_uc_at_legacy_closuretrue or false (andwould_claim_ucthe opposite), on a full-time degree or not;activeparameters, not from constants, andtest_closure_grids_straddle_each_closurechecks that the grid years, which it reads from the constants the grids use, still fall either side of each closure.Test plan
test_student_head_moves_non_dependant_to_joint_tenanttakes the non-dependant contribution from SI 2026/148 art. 32(3). Case 2's housing costs element comes from the model's Universal Credit LHA rate for Plymouth (£400 a month), and its total matchesmain.make formatequivalent on the changed files:uvx ruff@0.17.2 format --check,uvx ruff@0.17.2 check,uvx ruff@0.17.2 check --select F811,F821 policyengine_ukandgit diff --check, all clean.changelog.d/2346.fixed.md.Results, each run alone without survey data (
HUGGING_FACE_TOKENandPOLICYENGINE_UK_DEFAULT_DATASETunset):main(1d4d020)tests/policy/baseline/gov/dwp/universal_credit/uc_receiving_education.yaml(new)tests/test_uc_receiving_education_properties.py::test_examples_reach_the_casestests/test_uc_receiving_education_properties.py(new, all five tests), withtest_uc_legacy_closure_properties.py(4) andtest_legacy_closure_scope.py(21),-n 4test_closure_grids_straddle_each_closure, which reads only parameters, passedtests/test_parameter_descriptions.pytests/policy/baseline/finance/benefit/family/universal_credit/(directory)tests/policy/baseline/gov/dwp/universal_credit/(directory, the 42 new cases and 16 existing ones)person_legacy_benefits_closed.yamland the threelegacy_closure_*.yamlfiles undertests/policy/baseline/tests/code_health(-n 4)We ran each mutant on a copy of the code, first against the YAML cases and then against the property file. For the first nine mutants, the YAML runs, against the 35 cases, and the ninth mutant's property run used db140e0; their other property runs used 13bbf82, and the two with shrinking off used 27e004a. At all three commits every formula matches this head, and the property file's code matches it apart from docstrings, documentation and the closure tests, which these runs predate. At db140e0 the YAML file differs from this head only in comments and cases 36 to 41. The last two rows ran at f06a9c6, on all 41 cases,
person_legacy_benefits_closed.yaml, the threelegacy_closure_*.yamlfiles and the property files; the legacy closure YAML files pass under both.-x; with shrinking off, fails at the reference differential after 1,055 sis_qualifying_young_person_for_universal_creditstudent_loansandeducation_grantsin place ofhas_education_maintenance_funding-x; with shrinking off, passes all 19 examples-xis_qualifying_young_person_for_universal_credit, before reg 28, for the reg 60 protectiontest_examples_reach_the_casesfails (64 s)migrated_student, the reg 60(1) limb, fromuc_is_excepted_from_education_conditiontest_reg_60_protection_follows_the_closure_dateson its 2025 and 2026 grids,test_education_condition_invariantson an explicit example, andtest_examples_reach_the_caseslegacy_benefits_closedclose any reported legacy benefit, ignoring theactiveparameterstest_reg_60_protection_follows_the_closure_dateson its 2024 and 2025 grids; in the legacy closure property files, 3 of 4 tests intest_uc_legacy_closure_properties.pyand 1 of 21 intest_legacy_closure_scope.pyfail (442 s)The YAML cases kill all eleven mutants. A property run that reaches its cap without a failure only shows that the cases it reached in time don't kill that mutant; several runs shared the host, so the times also vary with load.
Case 35 pins the reg 60A gate. With a closure claim, every claimant who isn't a qualifying young person meets the condition under reg 60(1), and a claimant or partner on the couple's tax credit award loses that status under reg 28. A claimant responsible for a child or qualifying young person in the benefit unit meets reg 14(1)(c). The gate changes the result only where the claimant is a qualifying young person and an adult in the benefit unit who is neither claimant, partner, child nor qualifying young person reports Working Tax Credit, as in case 35. The model keeps the gate because under reg 60A the Secretary of State waives the age condition and nothing else. The model's reg 60A route reads Working Tax Credit from every member of the benefit unit, where under reg 60A(1) the single claimant, or both joint claimants, must be "entitled to an award of working tax credit"; that predates this PR, and we set it out in #2805.
No existing test's expected value changes.
Reproducer
Reproducer:
policyengine_uk/tests/test_uc_receiving_education_properties.py::test_examples_reach_the_cases;policyengine_uk/tests/policy/baseline/gov/dwp/universal_credit/uc_receiving_education.yamlaxiom: TheAxiomFoundation/rulespec-uk#514 queued
🤖 Generated with Claude Code