Repository navigation
Conversation
…ducation condition UC Regs 2013 reg. 5(1)(a) and SPC Regs 2002 reg. 4A(1)(a) make a person who has reached 16 a qualifying young person up to the 1 September following their 16th birthday, whatever their education. The model encoded only the education route, so a 16-year-old outside education was not a UC or PC qualifying young person. is_before_first_september_following_16th_birthday places that date from the date of birth and counts the route for a year that starts (6 April) before it: every annual age of 16, and a 17-year-old born from 1 September to 6 October. Both QYP variables OR it with the education route, under the same age band and own-right benefits exclusion. Tests: YAML cases hand-computed from the regulations (flags, date edges, UC child element and work allowance, PC child addition), the property-test oracle, a calendar differential over every birth date in 2008-2011 and across years, and a grid test of sufficiency, monotonicity, exclusion and UC/PC agreement. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two property tests build a member who is outside every programme's family as a 16 to 19 year old not in education. At 16 that member is now a UC qualifying young person under reg 5(1)(a), so the family's UC gains a child element and the tests' premise no longer holds. Their generators now set is_before_first_september_following_16th_birthday false for that member. The test functions are unchanged, so Hypothesis draws the same examples. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nefit reg. 4 period test_excluded_member_never_changes_is_eligibility adds a member aged 16 or over who is not in education, on the premise that they are not a qualifying young person. With reg. 4 a 16-year-old who has left education is one until the 31 August after their 16th birthday, so the generator now puts the member past that date, as #2279 does for the same reason in two other property tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…date-of-birth tests - The route variable's docs now warn that setting it for some people gives everyone else false, and point to date_of_birth for placing one person. They also state that the rule departs from the 6 October reading that is_SP_age uses. - YAML: a 2025-26 case where the 16-year-old ranks first under the two-child limit and the youngest child loses the element (GBP 7,581.72), and a case mixing a person with date_of_birth and one without (both in the route). The second kills a mutant that reads the raw date_of_birth input instead of birth_day. - means_test_income_members.yaml: the 17-year-old case's name says why they are outside the route. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
PE-UK main fix landed (UK hub, 2026-10-11 21:42Z): #2349 merged as |
|
UK hub takeover for the Budget fix sprint (Max, 21 Oct). The owner session If the owner session wakes, please don't push to this branch; comment here instead, so there's only one writer. 🤖 Generated with Claude Code |
|
UK hub: takeover withdrawn. The owner session has been woken (22 Oct 01:25Z) and is finishing this PR itself, so the hub's build is cancelled. One writer: the owner session. 🤖 Generated with Claude Code |
# Conflicts: # policyengine_uk/tests/test_legacy_award_readers_properties.py
#2089 replaced the shared is_benefit_cap_single_claimant_rate with is_uc_benefit_cap_single_claimant_rate and its Housing Benefit counterpart. The lone-parent case asserts the UC rate, which is still false: the 16-year-old outside education is a qualifying young person the claimant is responsible for. 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)
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 91 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
|
#2549 added other_members_with_awards ("As other_members") for the young singles and working parents properties. It draws a 16-60-year-old outside education, but unlike other_members it did not set is_before_first_september_following_16th_birthday false. A drawn 16-year-old was therefore a UC qualifying young person, so the family changed. In the shrunk case, a single 19-year-old in Wales with GBP 12,000 earnings, housing_benefit_applicable_income went from GBP 11,740 to 0, and the two properties failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22
left a comment
There was a problem hiding this comment.
Program review
Base repository: PolicyEngine/policyengine-uk
PR number: 2279
Reviewed head SHA: 560005c
Merge base SHA: 7918fc2
Mode: full
Scope: 11 changed paths: UC/Pension Credit no-education QYP route, date boundary, own-right exclusion, affected child additions and tests; read-only static review.
Source manifest: /private/tmp/policyengine-command-runs/881387a1b745/pr2279-external-review-20261026-review-sources.json
Review status: PARTIAL
Source Documents
- authorized-1.original — cached original.
- authorized-2.original — cached original.
- authorized-3.original — cached original.
- authorized-4.original — cached original.
- authorized-5.original — cached original.
Critical
C1 — Critical/P2: exclude reported own ESA/JSA awards from the new route (OPEN)
Locations: policyengine_uk/variables/gov/dwp/universal_credit/child_element/is_qualifying_young_person_for_universal_credit.py:48 and policyengine_uk/variables/gov/dwp/pension_credit/guarantee_credit/minimum_guarantee/additional/is_qualifying_young_person_for_pension_credit.py:43.
The new unconditional route still excludes own-right benefit recipients only through receives_benefits_in_own_right. The current owning source variables/household/income/receives_benefits_in_own_right.py:4–16 is a Person/YEAR bare input, defaultFalse, with no formula or reported-award dependencies. esa_income_reported and jsa_income_reported are separately supported Person/YEAR award inputs. The existing has_own_esa_award.py:25–26 explicitly treats a positive esa_income_reported as an own award, so identifying that person does not require guessing the payee of a household-level award.
A complete synthetic input uses fiscal2026, two people in one benefit unit and household:
- parent: age40, is_parentTrue, is_benunit_headTrue;
- young person: age16, is_benunit_headFalse, current_educationNOT_IN_EDUCATION, esa_income_reported5000 (or separately jsa_income_reported5000), with receives_benefits_in_own_right omitted;
- benefit unit members[parent,young]; household members[parent,young], countryENGLAND, rent0, savings0.
No explicit route override is required. For a whole age, the situation's default months_since_last_birthday6 places the16th birthday on6April2026. The new route fact isTrue; the16≤age<20 gate isTrue; the education route isFalse; the own-right input isFalse by default. Both changed QYP formulas consequently returnTrue, despite the person's positive reported own ESA/JSA award. POLICY independently qualified primary UCreg5(5) and PCreg4A(5), which apply the respective exclusions to both routes. Those source facts corroborate this code trace; CODE performed no external acquisition.
This is diff-exposed: the merge-base education-only QYP formulas rejected this16-year-old outside education, whereas the new route admits them without fixing the known own-award gate. It is the same must-fix issue raised in Vahid's old review5514434475, independently rechecked against the captured current source rather than accepted from that old review.
The result reaches financial intermediates. UC's child-or-QYP flag leaves out looked-after people, not this ordinary dependant; is_uc_claimant reads claimant/partner status, so the non-head dependant is included by uc_child_index. uc_individual_child_element applies the first-child amount to that rank. UC responsibility and therefore work-allowance eligibility also becomeTrue through this QYP. In the analogous pensioner70+young16 family, PC's child-or-QYP gate excludes the claimant/partner but retains this dependant and child_minimum_guarantee_addition counts them. Therefore this is a material eligibility/child-addition error under the review rubric. No exact paid UC, Pension Credit, tax or household-net-income amount has been executed or independently certified here; older review/private impact figures are not transferred.
Derive or supplement the common exclusion from unambiguous person-level own-award facts, preserving explicit input semantics and avoiding benefit-recursion or household-payee guesses. Add a focused test with reported ESA and JSA and the own-right flag omitted, covering both QYP predicates and a downstream child-element/addition result. The new YAML case sets the flagTrue directly; it does not cover the failed input route. The property oracle likewise reads the same bare flag. The modified non-dependant generators force the routeFalse and therefore cannot establish the missing award exclusion.
Coordinator assessment: POLICY independently corroborated the exclusion across both routes in UC regulation 5(5) and Pension Credit regulation 4A(5). Count this finding once. It is independently reconfirmed on the current head and remains the same issue raised in the earlier Vahid review; older executed amounts are not transferred.
Should Address
None.
Suggestions
None.
Evidence Gaps
- No local model/import, collection, pytest/YAML execution or independently executed exact-head regression was authorized or performed. The C1 eligibility/child-addition trace is static; no paid-benefit/tax/net-income numerical result is certified.
- The full review exceeded its original 20/25-minute timing envelope and closed through partial recovery. The long wall-time gap, late closure and incomplete full-workflow certification remain explicit; no clock reset or timing PASS.
- Role checks incomplete: pr2279-external-review-20261026-review-code.md; see recovered report.
- Role checks incomplete: pr2279-external-review-20261026-review-policy.md; see recovered report.
Notes
- Five fresh official legal originals and full linked text derivatives qualified; no material primary-source gap for the changed legal rule. Source acquisition closed at22:16:13.219370Z after7.194/180s; no subsequent source fetch.
- The disclosed any-part-of-fiscal-year annual approximation was checked and is not treated as a defect merely because a different annual convention is possible. Existing PC Income Support/age19 limitations remain pre-existing disclosed scope.
- The current16 raw observed hosted checks have matching head/CheckSuite joins and SUCCESS conclusions. Author-reported tests, historical private aggregate output, numerical impacts and prior internal approvals remain reports, not independent validation here.
- Scope brief was sealed105.080s after capture completion, beyond60s aim. Exact first-dispatch marker was not saved; earliest CODE skill seal and after-both-dispatch marker are retained, with conservative parallel timing below.
- No code changes, foreign branch effects, merge/queue actions or issue creation occurred. The exact clean snapshot was normally removed after both roles DONE; head/tree/index were unchanged, all5 cleanup children closed, no force or source edits.
- Final canonical timing includes the entire actual wall-time gap and normal cleanup. Original20/25-minute targets failed; completion-to-bookkeeping120s aim is measured below with no excluded time. Inert publisher preparation2922.007/180s also failed its timing cap; its original controls and recovery receipt remain preserved.
Validation Summary
Read-only exact-head source and test-oracle inspection by independent CODE and POLICY roles; one confirmed Critical/P2 eligibility defect,0 Should Address,0 Suggestions. Five official originals validated with integrity checker0;16 matching-head hosted checks SUCCESS. Local model/import/collection/tests/environment/docs/private-data execution NOT RUN. Full review timing target missed; PARTIAL / REQUEST_CHANGES.
Timing
setup seconds: 201.88s; scope seconds: 105.08s; parallel seconds: 3198.35s; policy seconds: 2981.97s; code seconds: 3198.35s; adjudication seconds: 51.64s; consolidation cleanup seconds: 86.53s; elapsed seconds: 3513.42s
Review Severity
REQUEST_CHANGES. Open findings: 1 critical, 0 should address, 0 suggestions.
|
Response to María's review, C1. I confirmed the reported-award gap and fixed it in The corrected YAML at The PR body now gives final-head verification and states that decision d1345 and the repository-wide hold still block merging. I changed no dataset handling or benefit-unit construction relative to main. |
|
Response to Vahid's review.
Final-head test and mutation results appear in the verification comment and PR body. Decision d1345 and the repository-wide merge hold remain in force. |
|
Pushed final head At YAML command:
Ruff 0.17.2 The scratch mutation drops the reported-award exclusion from both QYP predicates and runs under the lower mutation cap: 13 failed, 2 passed at Decision d1345 remains parked with Max, and the repository-wide merge hold remains in force. This update makes no dataset-handling or benefit-unit-construction change relative to main. I did not merge, enqueue, re-request review or dismiss a review. |
|
Hand-off to the UK hub (owner session Head: Passed at this head:
Impact: the Aggregate impact report (run 40964661226, uk-data 1.58.1) finds:
The only move is in the standard 20% UC taper reform costing, which falls by £1.6m in 2025-26 and by up to £2.2m a year by 2031-32. The body now quotes the report. My read of the fix (
Body: I restored the full description, which the adoption build had replaced with review replies. It now has the summary, the approximation and its bias table, both test files, the mutation list, the review history, the CI impact, the earlier local microsimulation (labelled as before the own-award fix) and the follow-ups. Pending before landing:
Downstream: the drafts #2300, #2301 and #2302 carry this PR's commits.
Owed by no one in this PR: the cap and LHA age-16 stand-in still counts a 16-year-old with their own award or in local authority care. The body's follow-ups list it. 🤖 Generated with Claude Code |
SPC Regs 2002 reg. 4A(5) ends qualifying young person status for a person receiving income support in their own right; UC Regs 2013 reg. 5(5) does not list it. Without this, a 70-year-old's 16-year-old reporting their own Income Support in 2025 counted under the new no-education route and drew a Pension Credit child addition of £4,061.20 (review of 4b09a36, P2). The Pension Credit predicate now reads receives_income_support_in_own_right, taken from draft #2302: the person's own reported Income Support while it is in payment and no reform removes it, as is_on_income_support reads another member's own claim. Its documentation here names Pension Credit only, the one reader at this head. Tests: the 2025 case, the education route, a report after Income Support stops paying, a reform that removes it, and a one-penny report of each listed award (the review's P3: '> 0' changed to '>= 1' survived before). The YAML header now says the reports count whether the own-right flag is omitted or false, and the changelog fragment drops its list markers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Pushed
The changelog fragment also drops its list markers, which the fragment check warned about. The body's |
The raw-report helper from 33168cc applied the rule for a non-claimant member to everyone. A claimant or partner needs a calculated award, as is_on_income_support already requires. So a reform replacing the income_support formula with zero left a young carer excluded, which kept them a counted resident and removed a co-resident pensioner's severe disability addition: £4,238 of payable Pension Credit in 2024. The predicate now reads is_on_income_support, and the helper goes. That variable keeps the non-claimant report, the positive-award test and the payee. Income Support's family follows the Child Benefit test and not this one, so the award does not depend on this status. A source graph of the model shows that outside the labour supply branches, is_on_income_support reaches 232 variables and none is this status or Pension Credit. The tests add the cross-unit regression at both reform mechanisms, both calculation orders, labour supply responses, a claimant with a report and no award, a partner who is not the payee, and an exhaustive grid for two invariants: nobody on Income Support is a Pension Credit young person, and zeroing or neutralizing the award gives claimants the same status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Pushed
The body now has this evidence and the run citations the review asked for. CI and the aggregate impact at 🤖 Generated with Claude Code |
juaristi22
left a comment
There was a problem hiding this comment.
Re-review at c5b9850a3, against main 648b11244. Approve.
C1 from my 26 October review is closed in both predicates. is_qualifying_young_person_for_universal_credit and is_qualifying_young_person_for_pension_credit now exclude a person with the explicit own-right flag or a positive person-level report of their own Universal Credit, income-related or contributory ESA, or income-based or contribution-based JSA. An explicit false flag no longer hides a reported award, and a parent's report is not attributed to the child. The Pension Credit predicate also reads is_on_income_support, which SPC reg. 4A(5) lists and UC reg. 5(5) does not.
What I checked:
- The regulation text on legislation.gov.uk: UC reg. 5(1)(a), (1)(b) and (5) (universal credit, ESA, JSA); SPC reg. 4A(1)(a), (1)(b) and (5) (the six benefits, income support included). The code's exclusion lists match.
- Cycle safety of the Income Support read:
income_support_eligiblecounts children through Child Benefit's test, not this one, so the baseline has no loop, and a reform that makes Income Support read Pension Credit raisesCycleError, as the body says. - The date fact:
birth_daygives YYYYMMDD; a birthday on or after 1 September moves the following 1 September to the next year; the year counts when it starts before that date. The calendar-oracle tests cover every date of birth from 2008 to 2011, and the route grid covers 2,048 synthetic families. - Tests: the own-award YAML pins the exclusions with family outcomes (no child element, no work allowance, no Pension Credit child addition), the education route, penny reports, Income Support in 2025 and after it stops paying in 2026, a claimant with a calculated award, and a partner who reports nothing. Locally at this head, 57 YAML cases and 99 Python tests pass: the own-award and no-education-route files, the work allowance and means-test members files, and the Income Support, child and adult definitions, legacy award readers, benefit cap and means-test income property files. CI is green at the head.
- Merge:
git merge-treeis clean against main and against a local merge of the PRs now in the merge queue.
Nit, no change needed before merge:
changelog.d/uc-pc-qyp-sixteen-route.fixed.md: the last clause ("and those receiving Income Support from Pension Credit's") reads oddly; "from the Pension Credit status" or similar.
Outside this approval: the body says the merge waits on decision d1345 (PolicyEngine/policyengine-uk-data#562), which is Max's call. I did not run the microsimulation; the aggregate-impact report's nil result is explained by the FRS benefit-unit placement the body describes.
Summary
UC Regs 2013 reg 5(1) makes a person who has reached 16 but not 20 a qualifying young person:
SPC Regs 2002 reg 4A(1) has the same two routes. Route (a) has no education condition. The model encoded only route (b), so a 16-year-old outside education was not a UC or Pension Credit qualifying young person (QYP). Nothing that reads QYP status counted them: the UC child element, the two-child limit order, the work allowance, UC responsibility and the Pension Credit child addition. On main, the benefit cap and LHA family rules already counted every 16-year-old non-claimant through a separate stand-in. There, the new route reaches only 17-year-olds born from 1 September to 6 October. The own-award reports below also end QYP status for older young people in education who report their own award, which the cap's specified-benefit rule reads.
is_before_first_september_following_16th_birthday(Person, year) is true when the fiscal year starts (6 April) before the 1 September following the person's 16th birthday. Like the model's other date-of-birth rules, it places the date fromdate_of_birth, or fromageandmonths_since_last_birthday, and a user can set it directly. It mirrors the Day-level inputcurrent_day_is_before_first_september_following_16th_birthdayin rulespec-uk'suk/regulations/uksi/2013/376/5.yaml. A named constant holds the statutory age, 16, with both citations.receives_benefits_in_own_right, a bare input that defaults to false.is_on_income_support. A claimant or partner is on it when the calculated award is positive and paid to them; any other member is on it when they report their own award while Income Support is in payment (gov.dwp.income_support.active, true until April 2026) and no reform neutralizes it. Without this, a 70-year-old's 16-year-old outside education who reported their own Income Support in 2025 drew a PC child addition of £4,061.20.is_on_income_supportreaches 232 of 1,357 variables, and neither this status nor Pension Credit is among them. The graph misses names that code builds at run time and edges that reforms add, so runs cover those: tests calculate both in either order and with labour supply responses on, and the marriage-neutral income tax reform and the basic income means-test settings calculate with Income Support in payment.CycleError, where the parent commit calculates it:income_support→pension_credit→ the severe disability addition → this status →is_on_income_support→income_support. No shipped reform replaces Income Support with a formula that reads Pension Credit. A reform that adds that edge has to break the loop itself.income_support's formula with zero ends the status of a claimant or partner. It leaves a non-claimant member's own report standing, asis_on_income_supportdocuments. Neutralizingincome_supportends both.is_responsible_for_child_or_young_person_for_uc_or_housing_benefitsaid "the UC child element does not yet model reg 5(1)(a)". It now says the UC test includes it. Its separate count of any 16-year-old non-claimant stays for Housing Benefit. HB's young person follows Child Benefit's school-leaver rules, which the model doesn't encode: Child Benefit (General) Regulations 2006 regs 4, 5 and 7.Merge hold. Two things hold the merge:
The annual approximation
The route ends on a fixed 1 September. The model's year runs from 6 April, and its age is the age on 6 October. A year counts in the route if any of it falls in the route, which happens exactly when the year starts before that 1 September.
Main already applies this rule to the route for the benefit cap and LHA family rates (
is_responsible_for_child_or_young_person_for_uc_or_housing_benefit, andhas_sixteen_year_old_dependantfrom #2089). Following it keeps UC's child element, work allowance, cap and LHA tests in agreement on who is a QYP. It is also the approximation the task suggested ("treat age 16 as within the route for the part-year").Who it covers:
The bias comes from calendar arithmetic over every birthday, using Python
datearithmetic, ages on 6 October, fiscal year 2026-27 and equally likely birthdays. The table compares the share of the year a person outside education is a child or in the route with what each rule counts:No annual boolean avoids bias. A status-on-6-October rule has less aggregate bias, but it would count only about one 16-year-old outside education in ten. It would also disagree with the cap and LHA family rules, which count all of them.
Switching to it takes one character in the new variable:
year_of_first_september >= period.start.yearbecomes>, since 6 October falls before the 1 September of a later year only. With no date of birth, a household calculation places the birthday on 6 April, so a 16-year-old outside education counts as a QYP for the year and a 17-year-old does not.Tests
qualifying_young_person_no_education_route.yaml, 14 cases hand-computed from the regulations (2026-27 unless stated):date_of_birth15 September 2009 beside another household's 16-year-old with noneIn
is_uc_work_allowance_eligible.yaml, Case 6 expected no work allowance for "a nonqualifying 16-year-old" outside education, which reg 5(1)(a) contradicts. It now sets the route false, and a new Case 6a asserts the default.qualifying_young_person_reported_own_awards.yaml, 23 cases. The reports identify the recipient whether a case omitsreceives_benefits_in_own_rightor sets it false.test_pension_credit_young_person_income_support.py, 10 tests.income_support: the young person becomes a PC QYP and the child addition returns. UC status holds in both.income_supportneutralized, the carer is a QYP again and the pensioner gets the £4,238 addition (PC £15,581.80).is_on_income_support, calculate to the same result in either order.is_on_income_supportand PC QYP status. The grid has no case built as a partner.Fail on base.
is_qualifying_young_person_for_universal_credit([0, 0]for[0, 1]).uc_individual_child_element(£0 for £4,222.56), and case 12 onchild_minimum_guarantee_addition(£0 for £4,215.64).cc4d3bb8a, before the fix, it gave 13 failed and 2 passed; the two passes are the parent-only-report and zero-report controls.Properties.
test_child_and_adult_definitions_properties.pygains invariant 5:date(2026, 4, 6) < first 1 September after the 16th birthday. Pythondatearithmetic computes the right side and shares no code with the model's month grid.qualifying_young_person) gains the route for UC and PC.test_benefit_cap_specified_benefits_properties.py) drops UC young persons older than 16 who report their own award. Its age-16 stand-in and its Housing Benefit test stay as on main.test_legacy_award_readers_properties.pygains ESA and JSA reader regressions, with no route or own-right override, for a young single and an own-award recipient (4b09a3601).test_means_test_income_properties.py, andother_membersandother_members_with_awardsintest_legacy_award_readers_properties.py). At 16, a member with no award or with Income Support only would now qualify through route (a). Those generators set the route false (2ef1d8dbf,560005cfa). The test functions stay as on main.Mutation checks. Each mutation ran alone, and in each case a new test failed:
date_of_birthinput instead ofbirth_day(killed by case 14);>= 901to> 901(1 September birthdays);>=to>on the year;4b09a360).At
b7e87482b, whose pushed successorc5b9850a3changes only the changelog fragment's wording, the UK hub ran nine more, each in its own scratch copy under a 2 GB RSS cap, with the worktree unchanged. Every one fails at least one test:income_support_reportedinstead ofis_on_income_support: 2 YAML cases and 3 Python tests fail;> 0to>= 1on the ten report comparisons in the UC and PC predicates: the penny case fails;> 0to>= 1on a member's own report inis_on_income_support: its penny case fails;activegate: the 2026 case fails;At
b7e87482b. The UK hub ran, one file or directory at a time with no survey dataset and pytest at-n 0with xfail strict:child_qualifying_young_person.yaml8,claimant_not_own_child_or_qualifying_young_person.yaml11 andincome_support_take_up_and_person_awards.yaml11 passed (209);git diff --checkand the changelog fragment check: passed.At
c5b9850a3.At
4b09a360.format --checkandcheckpass on the changed Python files.Review
20261010-204535-qyp16-review-r1c) APPROVED2ef1d8dbfwith seven P2 findings, andb171ef789addressed all of them.20261011-085419-qyp16-review-delta) APPROVEDb171ef789with no new defects.c65fd7dfb), as r1c asked.560005cfa) found the same own-award gap as C1.20261029-155413-adopt-2279b) answered both reviews:d6bcbf89fadds the reported-award exclusion;34291de5fmerges maindaec84d5band keeps both sides of each conflict;781245da6names the constant;4b09a3601adds the reader regressions.20261029-222452-review-2279) of4b09a3601requested changes: one P2, PC's missing Income Support exclusion (£4,061.20 in 2025), and two P3s, a penny-report control and the wording of the own-award test summary and the cap/LHA reach.33168ccbbfixes all three. It confirmed the law, the readers, the merge of main and the 19 CI checks at4b09a3601.20261030-003530-review-2279-delta) of33168ccbbrequested changes: one P2 and one P3. The helper applied the report rule to claimants, so a reform that replaced the Income Support formula with zero removed £4,238 of a co-resident pensioner's payable Pension Credit in 2024.c5b9850a3readsis_on_income_supportinstead, drops the helper and adds the regression. This body answers the P3 on its evidence and wording.20261030-105600-review-2279-r3) ofc5b9850a3APPROVED with one optional P3 on this body's wording, which this version answers. It confirmed the £4,238 fix under both ways of ending the award, and its probes found the reform limit stated in the summary.c5b9850a3, which closes her changes request.Aggregate impact on certified data (CI)
The figures below come from the run for
c5b9850a3(41075807002, merge960b028ea9), which the report comment reports. It compares main against the head merged into main:The only figure that moves is a standard reform costing. The table gives the change in the 20% taper reform's effect on the government balance: with this PR the reform costs £1.6m more in 2025-26, rising to £2.2m more in 2031-32, because it raises UC spending by those amounts more.
The check failing on both sides,
sign:vat(VAT excluding private school fees is never negative), predates this PR. The report also suppresses Disabled Students' Allowance, a figure from fewer than 10 records, on both sides, and leaves the additional rate reform blank in 2030-31 and 2031-32.Main moved between this run and the run for
33168ccbb(41003663431, base64cf5828ee), so 30 of the 224 metric rows differ in level between the two reports. This PR's own effect barely differs: only the two UC taper rows change, by at most £0.59, and Pension Credit spending matches in every year. The runs for4b09a3601(40964661226) and33168ccbbmatched in all 224 rows.Microsimulation impact: local runs before the own-award exclusion
These local runs measured the route alone. They predate
d6bcbf89fand the later merges of main; the CI report above coversc5b9850a3.They are real
Microsimulationruns on the Enhanced FRS 2024-25 (uk-data 1.57.4, SHA-25603fe15e4…8d4), run one at a time, each in its own venv, for 2025-26 to 2030-31:6d89338ab(main on 2026-10-11);addd1270d(this PR merged with that main).Each run saved 38 arrays a year (
microsim_dump.py) for people, benefit units and households. The comparison checked that ids and weights matched, then compared the arrays exactly, with no tolerance.What people receive: no change in any year. These differ in 0 records in every year from 2025 to 2030:
universal_creditanduniversal_credit_pre_benefit_cap;benefit_cap_reduction,housing_benefitandpension_credit;council_tax_reduction,council_tax_benefitandcouncil_tax_less_benefit;two_child_limit_payment,household_net_income,in_poverty_bhcandin_poverty_ahc;What changes:
Why the money doesn't move (2026-27):
would_claim_ucfalse for all four).pension_credit.The route would reach the parents' claims only if the data put these 16- and 17-year-olds in their parents' benefit units. The uk-data investigation measured that on this branch against
3173baa89, with uk-data 1.58.0:traincolumn that no FRS release has policyengine-uk-data#560): UC £0;Whether to re-form those units is the open question in PolicyEngine/policyengine-uk-data#562 (d1345).
Follow-ups (not in this PR)
Open drafts stacked on this PR carry the next pieces. Each needs to reconcile with this head's QYP predicates before it lands:
is_on_income_supportfor PC and does not add End the Pension Credit, Child Benefit and CTC qualifying young person status for Income Support in own right #2302'sreceives_income_support_in_own_right. Child Benefit's predicate cannot readis_on_income_support, because Income Support's family reads Child Benefit's status:is_on_income_support→income_support→income_support_entitlement→income_support_applicable_amount→is_responsible_for_child_or_young_person_for_legacy_benefits→is_child_or_young_person_for_legacy_benefits→is_child_or_qualifying_young_person_for_child_benefit→is_qualifying_young_person_for_child_benefit. So End the Pension Credit, Child Benefit and CTC qualifying young person status for Income Support in own right #2302 needs its own route there. Its branch also carries the Child Benefit school-leaver commits from the closed Add the Child Benefit qualifying young person routes for young people who have left education #2299.The benefit cap and LHA family tests keep main's stand-in (
has_sixteen_year_old_dependant, and the age-16 term inis_responsible_for_child_or_young_person_for_uc_or_housing_benefit). It counts any 16-year-old non-claimant for the year, including one with their own award or looked after, whom UC would not count. For UC, the stand-in now duplicates UC responsibility except in those two cases.axiom: uk:regulations/uksi/2013/376/5#qualifying_young_person encoded-correct, companion case
aged_16_before_first_september_following_16th_birthday_without_education_or_training_qualifiesinuk/regulations/uksi/2013/376/5.test.yaml(TheAxiomFoundation/rulespec-uk#446, mergedc65fd7dfb); uk/regulation/uksi/2002/1792/4A TheAxiomFoundation/rulespec-uk#360 queued (corpus scope in TheAxiomFoundation/axiom-corpus#765 not yet released)Reproducer: policyengine_uk/tests/policy/baseline/finance/benefit/qualifying_young_person_no_education_route.yaml::A 16-year-old outside education is a qualifying young person for UC and Pension Credit.
Reproducer: policyengine_uk/tests/policy/baseline/finance/benefit/qualifying_young_person_reported_own_awards.yaml
🤖 Generated with Claude Code