Skip to content

Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday - #2279

Open
MaxGhenis wants to merge 16 commits into
mainfrom
uc-pc-qyp-sixteen-route
Open

MaxGhenis wants to merge 16 commits into
mainfrom
uc-pc-qyp-sixteen-route

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

UC Regs 2013 reg 5(1) makes a person who has reached 16 but not 20 a qualifying young person:

(a) up to, but not including, the 1st September following their 16th birthday; and
(b) up to, but not including, the 1st September following their 19th birthday, if they are enrolled on, or accepted for, approved training or a course of education [...]

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.

  • New date fact. 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 from date_of_birth, or from age and months_since_last_birthday, and a user can set it directly. It mirrors the Day-level input current_day_is_before_first_september_following_16th_birthday in rulespec-uk's uk/regulations/uksi/2013/376/5.yaml. A named constant holds the statutory age, 16, with both citations.
  • Both QYP variables now read: the age condition, AND (the new fact OR the education route), AND NOT an own award.
    • Reg 5(3)'s contract-of-employment exclusion applies to route (b) only, so a 16-year-old apprentice qualifies through route (a).
    • Reg 5(5) and reg 4A(5) exclude a person receiving UC, ESA or JSA from both routes. Without the education condition, route (a) exposed a gap: the exclusion read only receives_benefits_in_own_right, a bare input that defaults to false.
    • Each predicate now also reads a positive person-level report of the person's own UC, income-related or contributory ESA, or income-based or contribution-based JSA. A report names the recipient, so the predicates need not compute family awards, which themselves depend on QYP status. An explicit false flag no longer hides a reported award.
    • Reg 4A(5) also lists income support; reg 5(5) does not. The PC predicate reads 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.
    • Reading the calculated award adds no cycle to the baseline or to the shipped reforms run here. Income Support's family follows the Child Benefit test, not this one. A source graph of the baseline shows that, leaving out the labour supply measurement branches, is_on_income_support reaches 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.
    • A reform that makes Income Support read Pension Credit now raises 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.
    • A reform that replaces income_support's formula with zero ends the status of a claimant or partner. It leaves a non-claimant member's own report standing, as is_on_income_support documents. Neutralizing income_support ends both.
  • Docstring only. is_responsible_for_child_or_young_person_for_uc_or_housing_benefit said "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, and has_sixteen_year_old_dependant from #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:

  • Every annual age of 16. The 16th birthday falls between 7 October of the year before and 6 October, so the following 1 September falls in the year or the next, always after 6 April.
  • A 17-year-old born from 1 September to 6 October. Their route runs to 1 September of the year.
  • Nobody older. Below 16, the person counts as a child anyway.

The bias comes from calendar arithmetic over every birthday, using Python date arithmetic, 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:

Annual age True share of the year This PR (year starts before the date) Status on 6 October Status quo (route omitted)
16 0.464 1.000 0.099 0
17 0.040 0.099 0 0
Sum 0.504 1.099 (+0.595) 0.099 (−0.405) 0 (−0.504)

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.year becomes >, 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):

Case Facts Expected
1 16, outside education UC and PC QYP; child or QYP for both
2 16, apprentice QYP: reg 5(3) excludes employment contracts from route (b) only
3 16, outside education, route set false not a QYP
4 16, upper secondary, route set false QYP through route (b)
5 17, born 15 September 2009 in the route until 31 August 2026: QYP
6 17, born 1 September 2009 a 16th birthday on 1 September runs the route to the 17th birthday: QYP
7 17, born 31 August 2009 the route ended on 31 August 2025: not a QYP
8 16, own-right flag set not a QYP (reg 5(5), reg 4A(5))
9 16, looked after QYP, but not the family's child or QYP
10 15 and 18, outside education neither is a QYP; the 15-year-old is a child
11 lone parent 40 + 16-year-old outside education first-child higher amount £351.88 × 12 = £4,222.56; work allowance (reg 22(1)(b)) £710 × 12 = £8,520; maximum amount £424.90 × 12 + £4,222.56 = £9,321.36; not the cap's single rate
12 pensioner 70 + 16-year-old outside education first child addition (Sch IIA para 10, born before 6 April 2017) £81.07 × 52 = £4,215.64; minimum guarantee £238.00 × 52 + £4,215.64 = £16,591.64
13 2025-26: lone parent, 16-year-old outside education, children 7 and 5 the 16-year-old ranks first: £339 × 12 = £4,068; the 7-year-old £292.81 × 12 = £3,513.72; the 5-year-old (born after 6 April 2017) is the third child under reg 24A: £0, limit-affected; total £7,581.72
14 a 17-year-old with date_of_birth 15 September 2009 beside another household's 16-year-old with none both in the route and QYPs: a date of birth given for one person leaves the other's age-placed date unchanged

In 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 omits receives_benefits_in_own_right or sets it false.

  • Five awards (UC, income-related ESA, contributory ESA, income-based JSA, contribution-based JSA), each with a working-age parent (40) and a pension-age parent (70). The 16-year-old outside education who reports the award holds QYP status under neither scheme, and the family gets no UC child element, no work allowance and no PC child addition (10 cases).
  • An 18-year-old in upper secondary education reporting each award: the education route ends too.
  • An explicit false flag does not erase a reported ESA award.
  • A parent's own report leaves their 16-year-old in the route.
  • Zero reports leave the route available.
  • Contribution-based JSA ends UC and PC status but not Child Benefit's, whose reg 8 lists income-based JSA only.
  • In 2025, a pensioner's 16-year-old outside education reporting their own Income Support: no PC QYP status and no PC child addition, but still a UC QYP. A pensioner's 18-year-old in education reporting Income Support loses PC status only.
  • In 2026, after Income Support stops paying, a member's report of it leaves the PC route open.
  • A claimant with a calculated award (£72.90 × 52 = £3,790.80) loses PC status and keeps UC status. A claimant with a report and no calculated award keeps both.
  • A 17-year-old partner who reports nothing keeps PC status when the claimant reports the award: the status follows the report.
  • Five 16-year-olds each report one penny of one award other than Income Support: each loses both statuses. A member reporting one penny of Income Support loses PC status only.

test_pension_credit_young_person_income_support.py, 10 tests.

  • The 2025 pensioner case again, then under a reform that neutralizes income_support: the young person becomes a PC QYP and the child addition returns. UC status holds in both.
  • Cross-unit regression, at each way of ending the award. A 19-year-old carer claims Income Support in their own benefit unit and lives with a pensioner who claims PC with £1,000 of Attendance Allowance (2024). On the award of £3,728.40 the carer is no QYP, so they count as another resident and the pensioner gets no severe disability addition (PC £11,343.80). With the formula replaced by zero, or income_support neutralized, the carer is a QYP again and the pensioner gets the £4,238 addition (PC £15,581.80).
  • Calculation order. The status and Income Support, then Pension Credit and is_on_income_support, calculate to the same result in either order.
  • Labour supply responses. Pension Credit calculates without recursion with both elasticities on.
  • Invariants over a grid. The grid covers ages 16 to 19, in and outside education, reports of £0, £0.01 and £3,000, carer or not, in a pensioner's benefit unit or their own (96 cases, each a young person and a pensioner, so 192 people). Nobody on Income Support is a PC QYP, in 2025 or 2026. Zeroing or neutralizing the award gives everyone the model treats as a claimant or partner the same is_on_income_support and PC QYP status. The grid has no case built as a partner.

Fail on base.

  • Route cases (the first 12). The author restored main's two QYP formulas, removed the new variable and stripped its output lines, then ran the 10 cases that do not set the new input: 7 failed and 3 passed.
    • Cases 1, 2, 5, 6 and 9 fail on is_qualifying_young_person_for_universal_credit ([0, 0] for [0, 1]).
    • Case 11 fails on uc_individual_child_element (£0 for £4,222.56), and case 12 on child_minimum_guarantee_addition (£0 for £4,215.64).
    • Cases 7, 8 and 10, where the route does not apply, pass on both.
  • Own-award file. At 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.py gains invariant 5:
    1. Calendar differential, exhaustive. For every date of birth from 2008 to 2011 (1,461 people, aged 14 to 18 on 6 October 2026), the new variable equals date(2026, 4, 6) < first 1 September after the 16th birthday. Python date arithmetic computes the right side and shares no code with the model's month grid.
    2. Calendar differential, generated. Hypothesis draws dates of birth from 1990 to 2031 and years from 2013 to 2031.
    3. Route properties over a grid. The grid covers ages 14 to 21, every education value, own-right benefits, approved training, apprenticeship and looked-after status, with the route off and on (2,048 families). For UC and PC:
      • switching the route on never removes QYP status;
      • with it on, exactly ages 16 to 19 without own-right benefits qualify, whatever the education inputs;
      • own-right benefits exclude both routes;
      • UC and PC agree below 19.
    4. The existing oracle (qualifying_young_person) gains the route for UC and PC.
  • The benefit cap oracle (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.py gains ESA and JSA reader regressions, with no route or own-right override, for a young single and an own-award recipient (4b09a3601).
  • Three generators elsewhere build "a member outside every programme's family" from a 16-to-19-year-old outside education (test_means_test_income_properties.py, and other_members and other_members_with_awards in test_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:

  • reading the raw date_of_birth input instead of birth_day (killed by case 14);
  • >= 901 to > 901 (1 September birthdays);
  • >= to > on the year;
  • dropping the route from UC;
  • dropping it from PC;
  • applying the own-right exclusion to route (b) only;
  • dropping the reported-award exclusion from both predicates (13 failed and 2 passed at 4b09a360).

At b7e87482b, whose pushed successor c5b9850a3 changes 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:

  • dropping the Income Support exclusion from PC: 4 YAML cases and 5 Python tests fail;
  • reading the raw income_support_reported instead of is_on_income_support: 2 YAML cases and 3 Python tests fail;
  • > 0 to >= 1 on the ten report comparisons in the UC and PC predicates: the penny case fails;
  • > 0 to >= 1 on a member's own report in is_on_income_support: its penny case fails;
  • dropping the payee from the claimant's branch: the partner case fails;
  • applying the own-report branch to everyone, the shape of the review's finding: 1 YAML case and 2 Python tests fail;
  • applying the award branch to everyone: 3 YAML cases and 1 Python test fail;
  • dropping the active gate: the 2026 case fails;
  • dropping the neutralization gate: the reform test fails.

At b7e87482b. The UK hub ran, one file or directory at a time with no survey dataset and pytest at -n 0 with xfail strict:

  • YAML: own awards 23, no-education route 14, work allowance eligibility 13, means-test income members 7, the UC child element directory 41, the PC guarantee credit directory 81, child_qualifying_young_person.yaml 8, claimant_not_own_child_or_qualifying_young_person.yaml 11 and income_support_take_up_and_person_awards.yaml 11 passed (209);
  • Python: the Income Support file above 10, child and adult definitions 21, legacy award readers 20, benefit cap specified benefits 46, means-test income 2, legacy carer premium 4, legacy closure scope 21, entered-directly lifecycle 16, Income Support eligibility 4, Income Support gate 13, disabled simulated benefits 49, UC legacy closure 4, household subset 9, measurement branches 5 and winter heating payment 4 passed (228);
  • Ruff 0.17.2 format and check on the changed Python files, the F811/F821 check on the package, git diff --check and the changelog fragment check: passed.

At c5b9850a3.

  • CI: all 19 checks pass, including Test / YAML, the six Python shards, Fix evidence and Aggregate impact (code run 41075807321, impact run 41075807002).
  • The final review below reran 104 YAML cases and 192 Python tests at this head, and all passed. Two more mutations each fail tests: applying PC's Income Support exclusion only above age 16 (2 YAML cases fail), and adding that exclusion to UC (4 YAML cases fail).

At 4b09a360.

  • CI: all 19 checks pass, including Test / YAML, the six Python shards, Fix evidence and Aggregate impact.
  • The adoption build's local runs gave 193 YAML cases and 115 Python tests passing across the 17 scopes listed in its verification comment.
  • Ruff 0.17.2 format --check and check pass on the changed Python files.

Review

  • First round.
  • Vahid's review (14 October) found:
    • the own-award gap (must);
    • a conflict with main in the legacy readers' generator (must);
    • the literal 16 (nit);
    • the overlaps with the stacked drafts (note).
  • María's review (26 October, REQUEST_CHANGES at 560005cfa) found the same own-award gap as C1.
  • The UK hub's adoption build (Subfleet 20261029-155413-adopt-2279b) answered both reviews:
    • d6bcbf89f adds the reported-award exclusion;
    • 34291de5f merges main daec84d5b and keeps both sides of each conflict;
    • 781245da6 names the constant;
    • 4b09a3601 adds the reader regressions.
    • Its replies: to María, to Vahid.
  • The UK hub's review (GPT-6.1 Sol, Subfleet 20261029-222452-review-2279) of 4b09a3601 requested 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. 33168ccbb fixes all three. It confirmed the law, the readers, the merge of main and the 19 CI checks at 4b09a3601.
  • The UK hub's delta review (GPT-6.1 Sol, Subfleet 20261030-003530-review-2279-delta) of 33168ccbb requested 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. c5b9850a3 reads is_on_income_support instead, drops the helper and adds the regression. This body answers the P3 on its evidence and wording.
  • The UK hub's final review (GPT-6.1 Sol, Subfleet 20261030-105600-review-2279-r3) of c5b9850a3 APPROVED 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.
  • María's re-review (30 October) APPROVED c5b9850a3, which closes her changes request.

Aggregate impact on certified data (CI)

The figures below come from the run for c5b9850a3 (41075807002, merge 960b028ea9), which the report comment reports. It compares main against the head merged into main:

Base 648b112443 against head 960b028ea9, both on enhanced_frs_2024_25.h5 (policyengine-uk-data 1.58.1, sha256 056e80fd8e91), fiscal years 2025-26 to 2031-32. Each year is a full microsimulation run on each side.

Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 0 (thresholds in report_spec.yaml; a flag does not fail the job).

Household net income by decile

No 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.

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.

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Reduce Universal Credit taper rate to 20% -£1.6m -£1.6m -£1.7m -£1.8m -£2.1m -£2.1m -£2.2m

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, base 64cf5828ee), 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 for 4b09a3601 (40964661226) and 33168ccbb matched in all 224 rows.

Microsimulation impact: local runs before the own-award exclusion

These local runs measured the route alone. They predate d6bcbf89f and the later merges of main; the CI report above covers c5b9850a3.

They are real Microsimulation runs on the Enhanced FRS 2024-25 (uk-data 1.57.4, SHA-256 03fe15e4…8d4), run one at a time, each in its own venv, for 2025-26 to 2030-31:

  • base: 6d89338ab (main on 2026-10-11);
  • branch: 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_credit and universal_credit_pre_benefit_cap;
  • benefit_cap_reduction, housing_benefit and pension_credit;
  • council_tax_reduction, council_tax_benefit and council_tax_less_benefit;
  • two_child_limit_payment, household_net_income, in_poverty_bhc and in_poverty_ahc;
  • the cap's single rate, the LHA shared-rate test, and the CTR lone-parent test and applicable amount.

What changes:

Year New UC and PC QYPs (people) UC child element (£m) UC work allowance (£m) UC maximum amount (£m) PC child addition, intermediate (£m) Scottish CTR young persons (people)
2025-26 +240,663 +27.1 +19.7 +3.0 +27.1 +137
2026-27 +241,578 +28.3 +20.5 +3.1 +28.2 +138
2027-28 +242,471 +29.0 +21.1 +3.2 +29.2 +138
2028-29 +243,441 +29.9 +21.6 +3.3 +30.1 +139
2029-30 +244,512 +30.9 +22.1 +3.3 +31.2 +139
2030-31 +245,613 +33.3 +22.7 +4.0 +33.9 +140

Why the money doesn't move (2026-27):

  • Who the new QYPs are. 101 records hold all of them: 196,728 people aged 16 and 44,850 aged 17. The 17-year-olds have imputed birthdays from 1 September to 6 October.
  • Most head their own benefit unit. The FRS records 234,205 of them as the claimant of their own unit, apart from their parents. The UC child index, child element and responsibility test leave out claimants, and so do the PC child-or-QYP flag and Scottish CTR. So only the 7,372 dependants change the PC flag.
  • The dependants live in units that don't receive UC. The 7,372 sit in 22 benefit units. There the UC child element rises by £28.3m, but none of those units receives UC in either run.
  • The higher maximum amounts don't reach UC. Four of those units qualify for UC, so their maximum amount rises (+£3.1m). Their UC stays £0, because the take-up draw has them not claiming (would_claim_uc false for all four).
  • The PC child addition is an ungated intermediate. The model computes it for every benefit unit with a child or QYP (£62.0bn on main in 2026-27), and only units eligible for Pension Credit receive it. The +£28.2m sits in the same 22 units, none of which receives pension_credit.
  • Scottish working-age CTR reads the UC definition (SSI 2021/249 reg 6). Two records (138 people) become young persons for it, and no CTR amount changes.

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:

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:

The benefit cap and LHA family tests keep main's stand-in (has_sixteen_year_old_dependant, and the age-16 term in is_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_qualifies in uk/regulations/uksi/2013/376/5.test.yaml (TheAxiomFoundation/rulespec-uk#446, merged c65fd7dfb); 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

…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>
This was referenced Oct 10, 2026
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>
MaxGhenis added a commit that referenced this pull request Oct 11, 2026
…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>
MaxGhenis and others added 2 commits October 11, 2026 08:53
…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>
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

PE-UK main fix landed (UK hub, 2026-10-11 21:42Z): #2349 merged as ed7b42f9. It fixes test_dataframe_dataset.py::test_an_exported_frame_loads_back_and_exports_itself, which failed on every PR after #2251. Merge main into this branch and push once; don't re-run an old run, because re-runs use the original commit. If this PR's only Test failure was that test, the next run should be green.

MaxGhenis added a commit that referenced this pull request Oct 12, 2026
Brings in #2279's head 9d2fadb.

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

Brings in #2299's round 1 fixes, #2387 and #2279's head 9d2fadb.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 12, 2026
Brings in #2279's head 9d2fadb and main e01f2f5 with it.

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

Brings in #2301 and #2299's round 1 fixes, #2387 and #2279's head. The
one conflict, the CTC QYP docstring, keeps both changes: the ceased-
education wording and the Income Support own-right input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

UK hub takeover for the Budget fix sprint (Max, 21 Oct). The owner session local_6b7a5349 has been idle since mid-October. The hub woke it on 21 Oct at 23:58Z, and it did not start. To land this before the Budget model locks (the first pe-uk release cut by about 12:00Z Thu 22 Oct), the hub has taken it over as build 20261021-211253-sprint-2279, which merges main, re-tests, pushes as a fast-forward and marks it ready. The hub then reviews it and lands it through the merge queue.

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

@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

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

MaxGhenis and others added 2 commits October 21, 2026 21:18
# 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>
MaxGhenis added a commit that referenced this pull request Oct 22, 2026
…routes

Brings in #2387 at 78a8cca, which carries main. The one conflict, the
legacy readers property test, takes #2279's current version: this PR's
own commits do not touch it.

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

Brings in #2299 on main with #2279 and #2387.

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

Brings in #2301's apprentice fix and the restack on main with #2279,
apprentice-on-a-course wording and this PR's Income Support input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Aggregate impact on certified data: pass

Base 648b112443 against head 960b028ea9, both on enhanced_frs_2024_25.h5 (policyengine-uk-data 1.58.1, sha256 056e80fd8e91), fiscal years 2025-26 to 2031-32. Each year is a full microsimulation run on each side.

Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 0 (thresholds in report_spec.yaml; a flag does not fail the job).

Workflow run and full report

Notes

  • In the years a figure from fewer than 10 records is suppressed (spending.disabled_students_allowance), every total containing it, and the decile incomes, are rebuilt from their other components, so no published total includes it. Those totals are marked †. A change is withheld where the two sides leave out different figures and can't be lined up.

Moves flagged for review

None.

Spending by benefit and service

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed

Caseloads

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed

Standard reform costings (change in gov_balance)

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Reduce Universal Credit taper rate to 20% -£1.6m -£1.6m -£1.7m -£1.8m -£2.1m -£2.1m -£2.2m
Raise additional rate by 3pp 0 0 0 0 0 suppressed suppressed

Standard reforms: change in each reform's target

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Reduce Universal Credit taper rate to 20%: change in universal_credit +£1.6m +£1.6m +£1.7m +£1.8m +£2.1m +£2.1m +£2.2m
Raise additional rate by 3pp: change in income_tax 0 0 0 0 0 suppressed suppressed

Household net income by decile

No 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)
  • VAT excluding private school fees is never negative (sign:vat): 2025-26: 76 units below zero, a weighted £-53,888,684 in total; 2026-27: 76 units below zero, a weighted £-55,774,885 in total; 2027-28: 76 units below zero, a weighted £-57,687,092 in total
Counted-once check

Each 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.

  • widowed_parents_allowance: pension_credit responds only through other heads in fewer than 10 households (other heads that moved there: carers_allowance; gov_spending; not a failure)
  • widowed_parents_allowance: pension_credit responds only through other heads in fewer than 10 households (other heads that moved there: carers_allowance; household_benefits; not a failure)
Provenance
Side Commit Runtime Environment hash Partition hash Time
base main 648b112443 uk 2.165.1 (files b1e23f085205), core 3.33.12, py 3.13.18 aarch64 d4ab1b95973c 22e0ebaf0971 2039.5s
head #2279 merged into main 960b028ea9 uk 2.165.1 (files 4be25b597504), core 3.33.12, py 3.13.18 aarch64 d4ab1b95973c 22e0ebaf0971 2058.36s

Dataset: hf://policyengine/policyengine-uk-data-private/enhanced_frs_2024_25.h5@2526edbbb687a3b54dbb11272e5389e4b62d2ac2, sha256 056e80fd8e9151d78f432a25cde863340baee81304c6216a37fb575ce8563784 (verified on both sides), policyengine-uk-data 1.58.1.

Receipts by tax head: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Final charge on cash interest in non-cash ISAs £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Lifetime ISA withdrawal charge £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Income tax £294.46bn £315.23bn £334.00bn £349.45bn £365.10bn £383.16bn £397.71bn
National Insurance (employee and self-employed) £51.70bn £54.14bn £56.13bn £57.73bn £59.76bn £61.64bn £63.51bn
National Insurance (employer) £148.71bn £155.37bn £160.88bn £165.09bn £172.06bn £177.36bn £182.57bn
Employer NI change borne by capital £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Employer NI change borne by consumers £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Capital gains tax £12.60bn £13.15bn £13.60bn £14.11bn £14.67bn £15.24bn £15.80bn
Student loan repayments £7.82bn £8.13bn £8.39bn £8.58bn £8.79bn £9.04bn £9.29bn
VAT excluding private school fees £249.37bn £256.88bn £266.66bn £272.70bn £278.97bn £285.24bn £291.59bn
Change in VAT from its baseline £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
VAT on private school fees £1.95bn £2.01bn £2.09bn £2.14bn £2.21bn £2.27bn £2.31bn
Change in private school VAT from its baseline £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Fuel duties £24.71bn £24.59bn £26.54bn £26.58bn £26.26bn £25.53bn £24.40bn
Alcohol duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Tobacco duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Vehicle excise duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Betting and gaming duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
TV licence fee £4.26bn £4.40bn £4.41bn £4.42bn £4.43bn £4.44bn £4.45bn
Council tax £49.21bn £52.16bn £54.89bn £57.95bn £61.16bn £64.47bn £67.91bn
High value council tax surcharge £0.00bn £0.00bn £0.00bn £0.48bn £0.49bn £0.50bn £0.51bn
Northern Ireland domestic rates £0.55bn £0.58bn £0.60bn £0.63bn £0.66bn £0.69bn £0.72bn
Business rates £34.68bn £36.20bn £38.00bn £39.46bn £42.27bn £43.25bn £44.29bn
Stamp duty land tax on the household's own purchases £6.88bn £7.32bn £7.66bn £8.06bn £8.47bn £8.91bn £9.35bn
Stamp duty land tax paid by companies £4.72bn £4.57bn £5.29bn £6.03bn £6.37bn £6.92bn £7.45bn
Land transaction tax (Wales) £0.07bn £0.08bn £0.08bn £0.09bn £0.10bn £0.10bn £0.11bn
Land and buildings transaction tax (Scotland) £0.31bn £0.32bn £0.33bn £0.35bn £0.36bn £0.38bn £0.40bn
Unspecified tax change borne by capital £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Unspecified tax change borne by consumers £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Wealth tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Wealth tax excluding the main residence £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Land value tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Carbon tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Spending by benefit and service: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Lifetime ISA bonus £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
State Pension £121.68bn £125.92bn £126.73bn £129.50bn £132.99bn £136.66bn £140.43bn
Pension Credit £6.66bn £7.03bn £7.30bn £7.51bn £7.65bn £7.84bn £8.03bn
Winter Fuel Payment £1.86bn £1.85bn £1.79bn £1.77bn £1.78bn £1.78bn £1.78bn
Pension Age Winter Heating Payment (Scotland) £0.19bn £0.20bn £0.20bn £0.21bn £0.21bn £0.22bn £0.22bn
Universal Credit £75.48bn £80.52bn £83.04bn £85.11bn £86.31bn £87.90bn £88.58bn
Housing Benefit £8.56bn £8.89bn £9.24bn £9.48bn £9.72bn £9.99bn £10.27bn
Council Tax Reduction £2.07bn £2.20bn £2.31bn £2.45bn £2.58bn £2.72bn £2.86bn
Employment and Support Allowance (income-related) £0.79bn £0.82bn £0.84bn £0.86bn £0.88bn £0.90bn £0.92bn
Employment and Support Allowance (contribution-based) £5.20bn £5.37bn £5.56bn £5.69bn £5.82bn £5.95bn £6.09bn
Jobseeker's Allowance (income-based) £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Jobseeker's Allowance (contribution-based) £0.29bn £0.30bn £0.31bn £0.31bn £0.32bn £0.33bn £0.34bn
Income Support £0.11bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Working Tax Credit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Child Tax Credit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Incapacity Benefit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Personal Independence Payment £26.95bn £28.03bn £29.05bn £30.09bn £30.75bn £31.51bn £32.21bn
Disability Living Allowance £7.49bn £7.79bn £8.07bn £8.36bn £8.55bn £8.76bn £8.95bn
Attendance Allowance £8.68bn £9.03bn £9.36bn £9.69bn £9.90bn £10.15bn £10.38bn
Carer's Allowance £4.48bn £4.66bn £4.83bn £5.00bn £5.11bn £5.23bn £5.35bn
Carer Support Payment (Scotland) £0.31bn £0.32bn £0.33bn £0.34bn £0.35bn £0.36bn £0.37bn
Carer's Allowance Supplement (Scotland) £0.00bn £0.04bn £0.04bn £0.05bn £0.05bn £0.05bn £0.05bn
Severe Disablement Allowance £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn
Industrial Injuries Disablement Benefit £0.50bn £0.52bn £0.54bn £0.55bn £0.56bn £0.58bn £0.59bn
Armed Forces Compensation Scheme £0.61bn £0.63bn £0.65bn £0.66bn £0.68bn £0.69bn £0.71bn
War Widow's or Widower's Pension £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Child Benefit £16.19bn £16.82bn £17.39bn £18.00bn £18.39bn £18.84bn £19.25bn
Scottish Child Payment £0.49bn £0.52bn £0.53bn £0.56bn £0.57bn £0.57bn £0.57bn
Two Child Limit Payment (Scotland, counterfactual) £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Healthy Start vouchers £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn
Maternity Allowance £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Statutory Maternity Pay £3.30bn £3.41bn £3.54bn £3.62bn £3.70bn £3.78bn £3.87bn
Statutory Paternity Pay £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Statutory Sick Pay £1.12bn £1.16bn £1.20bn £1.23bn £1.26bn £1.29bn £1.32bn
Sure Start Maternity Grant £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn
Bereavement Support Payment £0.67bn £0.69bn £0.71bn £0.73bn £0.75bn £0.76bn £0.78bn
Widowed Parent's Allowance £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Tax-Free Childcare £0.56bn £0.57bn £0.58bn £0.59bn £0.61bn £0.62bn £0.59bn
Extended childcare entitlement £2.99bn £3.50bn £3.62bn £3.75bn £3.83bn £3.92bn £4.01bn
Universal childcare entitlement £2.06bn £2.22bn £2.29bn £2.38bn £2.44bn £2.51bn £2.56bn
Targeted childcare entitlement £0.50bn £0.51bn £0.52bn £0.54bn £0.55bn £0.57bn £0.56bn
Childcare Offer for Wales £0.26bn £0.28bn £0.28bn £0.29bn £0.30bn £0.31bn £0.32bn
Care to Learn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Childcare Grant £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Parents' Learning Allowance £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn
Adult Dependants' Grant £0.06bn £0.06bn £0.04bn £0.04bn £0.04bn £0.04bn £0.04bn
Travel Grant £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed
16 to 19 Bursary Fund £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
NHS spending £203.89bn £204.30bn £204.56bn £204.91bn £205.34bn £205.85bn £206.35bn
State education spending £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn
Transport subsidy spending £25.05bn £25.10bn £25.14bn £25.18bn £25.23bn £25.30bn £25.36bn
Cost-of-living support payment £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Energy Bills Rebate £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Energy Price Guarantee subsidy £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Basic income £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Disability basic income £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Other public spending budget change £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Caseloads: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Lifetime ISA bonus 0k 0k 0k 0k 0k 0k 0k
State Pension 11,264k 11,145k 10,819k 10,700k 10,723k 10,750k 10,776k
Pension Credit 1,486k 1,480k 1,474k 1,455k 1,453k 1,455k 1,464k
Winter Fuel Payment 7,844k 7,783k 7,473k 7,375k 7,391k 7,409k 7,427k
Pension Age Winter Heating Payment (Scotland) 803k 802k 800k 797k 799k 801k 802k
Universal Credit 6,342k 6,347k 6,350k 6,316k 6,298k 6,291k 6,246k
Housing Benefit 1,249k 1,245k 1,246k 1,229k 1,229k 1,229k 1,232k
Council Tax Reduction 3,176k 3,195k 3,200k 3,215k 3,224k 3,235k 3,242k
Employment and Support Allowance (income-related) 71k 71k 71k 71k 71k 71k 71k
Employment and Support Allowance (contribution-based) 719k 720k 721k 722k 724k 726k 728k
Jobseeker's Allowance (income-based) 0k 0k 0k 0k 0k 0k 0k
Jobseeker's Allowance (contribution-based) 67k 67k 67k 67k 67k 68k 68k
Income Support 25k 0k 0k 0k 0k 0k 0k
Working Tax Credit 0k 0k 0k 0k 0k 0k 0k
Child Tax Credit 0k 0k 0k 0k 0k 0k 0k
Incapacity Benefit 0k 0k 0k 0k 0k 0k 0k
Personal Independence Payment 3,567k 3,575k 3,579k 3,585k 3,593k 3,602k 3,611k
Disability Living Allowance 1,291k 1,294k 1,296k 1,298k 1,300k 1,304k 1,307k
Attendance Allowance 1,695k 1,698k 1,700k 1,703k 1,707k 1,711k 1,715k
Carer's Allowance 1,034k 1,037k 1,038k 1,040k 1,042k 1,044k 1,046k
Carer Support Payment (Scotland) 71k 71k 71k 72k 72k 72k 72k
Carer's Allowance Supplement (Scotland) 0k 71k 71k 72k 72k 72k 72k
Severe Disablement Allowance 4k 4k 4k 4k 4k 4k 4k
Industrial Injuries Disablement Benefit 137k 137k 137k 138k 138k 138k 139k
Armed Forces Compensation Scheme 93k 93k 94k 94k 94k 94k 94k
War Widow's or Widower's Pension 0k 0k 0k 0k 0k 0k 0k
Child Benefit 8,123k 8,120k 8,120k 8,130k 8,145k 8,161k 8,178k
Scottish Child Payment 351k 352k 352k 355k 353k 351k 340k
Two Child Limit Payment (Scotland, counterfactual) 0k 0k 0k 0k 0k 0k 0k
Healthy Start vouchers 200k 200k 200k 201k 201k 201k 202k
Maternity Allowance 0k 0k 0k 0k 0k 0k 0k
Statutory Maternity Pay 333k 333k 334k 334k 335k 336k 337k
Statutory Paternity Pay 0k 0k 0k 0k 0k 0k 0k
Statutory Sick Pay 181k 182k 182k 182k 182k 183k 183k
Sure Start Maternity Grant 26k 26k 26k 26k 26k 26k 26k
Bereavement Support Payment 98k 98k 98k 98k 98k 99k 99k
Widowed Parent's Allowance 0k 0k 0k 0k 0k 0k 0k
Tax-Free Childcare 1,043k 1,027k 1,021k 1,024k 1,029k 1,026k 1,011k
Extended childcare entitlement 795k 800k 804k 806k 809k 814k 814k
Universal childcare entitlement 1,071k 1,072k 1,074k 1,076k 1,079k 1,089k 1,090k
Targeted childcare entitlement 165k 156k 154k 154k 155k 155k 150k
Childcare Offer for Wales 39k 39k 39k 39k 39k 39k 39k
Care to Learn 0k 0k 0k 0k 0k 0k 0k
Childcare Grant 0k 0k 0k 0k 0k 0k 0k
Parents' Learning Allowance 98k 98k 98k 99k 99k 99k 99k
Adult Dependants' Grant 18k 18k 12k 12k 12k 12k 12k
Travel Grant 0k 0k 0k 0k 0k 0k 0k
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed
16 to 19 Bursary Fund 0k 0k 0k 0k 0k 0k 0k
Cost-of-living support payment 0k 0k 0k 0k 0k 0k 0k
Energy Bills Rebate 0k 0k 0k 0k 0k 0k 0k
Energy Price Guarantee subsidy 0k 0k 0k 0k 0k 0k 0k
Basic income 0k 0k 0k 0k 0k 0k 0k
Disability basic income 0k 0k 0k 0k 0k 0k 0k
Government balance, each component counted once: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Government receipts, each component once £892.00bn £935.14bn £979.55bn £1,013.85bn £1,052.14bn £1,089.13bn £1,122.37bn
Government spending £529.38bn † £543.55bn † £550.93bn † £559.77bn † £567.54bn † £576.30bn † £584.05bn †
Government balance, each receipt once £362.62bn † £391.59bn † £428.62bn † £454.08bn † £484.59bn † £512.83bn † £538.32bn †
Benefits in household_benefits but not in gov_spending £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Model totals: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
gov_tax £892.00bn £935.14bn £979.55bn £1,013.85bn £1,052.14bn £1,089.13bn £1,122.37bn
household_tax £491.97bn £520.87bn £549.93bn £573.92bn £598.90bn £624.26bn £645.90bn
pre_budget_change_household_tax £484.15bn £512.74bn £541.54bn £564.86bn £589.61bn £614.72bn £636.09bn
gov_spending £529.38bn † £543.55bn † £550.93bn † £559.77bn † £567.54bn † £576.30bn † £584.05bn †
household_benefits £529.38bn † £543.55bn † £550.93bn † £559.77bn † £567.54bn † £576.30bn † £584.05bn †
gov_balance £362.62bn † £391.59bn † £428.62bn † £454.08bn † £484.59bn † £512.83bn † £538.32bn †
Household income totals: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Household market income £1,637.96bn £1,702.77bn £1,757.35bn £1,806.41bn £1,855.97bn £1,912.43bn £1,970.44bn
Household net income £1,636.69bn † £1,685.25bn † £1,716.89bn † £1,749.83bn † £1,781.14bn † £1,819.80bn † £1,862.66bn †
Household net income (HBAI definition) £1,449.57bn £1,497.73bn £1,532.81bn £1,567.22bn £1,600.03bn £1,638.06bn £1,679.23bn
Population: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
People 69,245k 69,383k 69,473k 69,592k 69,738k 69,912k 70,080k
Benefit units 35,110k 35,181k 35,226k 35,286k 35,360k 35,449k 35,534k
Households 31,046k 31,108k 31,149k 31,201k 31,267k 31,345k 31,420k
Poverty rates: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Absolute poverty, before housing costs: everyone 16.3% 15.6% 16.4% 16.3% 16.5% 16.5% 16.6%
Absolute poverty, before housing costs: children 24.7% 22.3% 23.3% 22.9% 23.1% 23.2% 23.4%
Absolute poverty, before housing costs: working-age adults 14.1% 13.9% 14.7% 14.7% 14.8% 14.8% 14.9%
Absolute poverty, before housing costs: pensioners 13.7% 13.5% 14.0% 13.9% 14.0% 13.9% 13.9%
Absolute poverty, after housing costs: everyone 21.6% 21.1% 22.0% 22.5% 22.7% 23.0% 23.3%
Absolute poverty, after housing costs: children 33.3% 31.3% 32.2% 32.4% 32.5% 33.0% 33.9%
Absolute poverty, after housing costs: working-age adults 19.9% 19.9% 20.8% 21.5% 21.8% 22.0% 22.2%
Absolute poverty, after housing costs: pensioners 12.7% 12.6% 13.1% 13.2% 13.3% 13.4% 13.5%
Relative poverty, before housing costs: everyone 17.0% 16.3% 16.4% 16.3% 16.3% 16.3% 16.7%
Relative poverty, before housing costs: children 25.9% 23.4% 23.3% 22.9% 22.8% 22.9% 23.7%
Relative poverty, before housing costs: working-age adults 14.5% 14.3% 14.6% 14.7% 14.7% 14.7% 15.0%
Relative poverty, before housing costs: pensioners 14.5% 14.7% 13.9% 13.9% 13.8% 13.9% 14.0%
Relative poverty, after housing costs: everyone 21.4% 21.0% 21.2% 21.3% 21.3% 21.5% 21.7%
Relative poverty, after housing costs: children 33.1% 31.1% 31.1% 31.0% 31.1% 31.2% 31.7%
Relative poverty, after housing costs: working-age adults 19.9% 19.8% 20.2% 20.4% 20.5% 20.6% 20.8%
Relative poverty, after housing costs: pensioners 12.5% 12.4% 12.2% 11.7% 11.7% 11.8% 12.0%
People in poverty: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Absolute poverty, before housing costs: everyone 11,278k 10,837k 11,403k 11,358k 11,478k 11,506k 11,643k
Absolute poverty, before housing costs: children 3,688k 3,332k 3,496k 3,437k 3,477k 3,499k 3,545k
Absolute poverty, before housing costs: working-age adults 5,965k 5,921k 6,308k 6,354k 6,417k 6,426k 6,517k
Absolute poverty, before housing costs: pensioners 1,624k 1,584k 1,599k 1,568k 1,583k 1,580k 1,581k
Absolute poverty, after housing costs: everyone 14,944k 14,662k 15,296k 15,679k 15,862k 16,047k 16,331k
Absolute poverty, after housing costs: children 4,975k 4,689k 4,832k 4,863k 4,898k 4,986k 5,125k
Absolute poverty, after housing costs: working-age adults 8,463k 8,491k 8,965k 9,325k 9,464k 9,549k 9,669k
Absolute poverty, after housing costs: pensioners 1,506k 1,482k 1,500k 1,491k 1,500k 1,513k 1,537k
Relative poverty, before housing costs: everyone 11,750k 11,332k 11,389k 11,348k 11,358k 11,410k 11,702k
Relative poverty, before housing costs: children 3,872k 3,504k 3,496k 3,434k 3,431k 3,451k 3,577k
Relative poverty, before housing costs: working-age adults 6,159k 6,103k 6,301k 6,349k 6,363k 6,388k 6,540k
Relative poverty, before housing costs: pensioners 1,720k 1,724k 1,592k 1,565k 1,563k 1,571k 1,585k
Relative poverty, after housing costs: everyone 14,849k 14,583k 14,728k 14,809k 14,885k 15,005k 15,219k
Relative poverty, after housing costs: children 4,942k 4,664k 4,660k 4,663k 4,681k 4,702k 4,798k
Relative poverty, after housing costs: working-age adults 8,426k 8,460k 8,679k 8,821k 8,877k 8,964k 9,056k
Relative poverty, after housing costs: pensioners 1,482k 1,459k 1,389k 1,325k 1,327k 1,340k 1,366k
Inequality: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Gini coefficient, equivalised HBAI income before housing costs 0.3080 0.3057 0.3063 0.3062 0.3068 0.3073 0.3089
Mean household net income in each side's own deciles: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Decile 1: mean household_net_income per household £17,681 † £17,895 † £17,904 † £18,146 † £18,308 † £18,581 † £18,739 †
Decile 2: mean household_net_income per household £27,939 † £28,874 † £28,683 † £29,083 † £29,354 † £29,679 † £30,165 †
Decile 3: mean household_net_income per household £34,320 † £35,455 † £36,108 † £36,626 † £37,218 † £37,722 † £38,220 †
Decile 4: mean household_net_income per household £38,933 † £40,603 † £41,873 † £42,783 † £43,423 † £43,928 † £44,548 †
Decile 5: mean household_net_income per household £43,239 † £45,442 † £45,787 † £46,995 † £47,729 † £48,921 † £50,229 †
Decile 6: mean household_net_income per household £47,927 † £49,126 † £50,887 † £51,728 † £52,601 † £54,202 † £55,188 †
Decile 7: mean household_net_income per household £55,662 † £56,509 † £57,442 † £57,965 † £59,083 † £59,211 † £60,675 †
Decile 8: mean household_net_income per household £68,789 † £70,704 † £71,779 † £73,356 † £73,952 † £75,362 † £77,248 †
Decile 9: mean household_net_income per household £78,890 † £80,992 † £82,907 † £84,246 † £85,584 † £87,169 † £89,597 †
Decile 10: mean household_net_income per household £117,005 † £120,262 † £122,221 † £124,681 † £126,775 † £129,108 † £131,486 †
Standard reform costings (change in gov_balance): head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise basic rate by 1pp £6.41bn £6.66bn £6.64bn £6.86bn £7.04bn £7.26bn £7.48bn
Raise higher rate by 1pp £3.61bn £3.87bn £4.00bn £4.21bn £4.43bn £4.71bn £4.83bn
Raise personal allowance by ~800GBP/year £-3.80bn £-3.92bn £-3.98bn £-4.09bn £-4.17bn £-4.21bn £-1.94bn
Raise child benefit by 25GBP/week per additional child £-1.99bn £-1.75bn £-1.55bn £-1.39bn £-1.30bn £-1.17bn £-1.06bn
Reduce Universal Credit taper rate to 20% £-20.77bn £-21.32bn £-21.79bn £-22.02bn £-22.36bn £-22.77bn £-22.96bn
Raise Class 1 main employee NICs rate to 10% £10.89bn £11.30bn £11.71bn £12.01bn £12.31bn £12.62bn £13.01bn
Raise VAT standard rate by 2pp £24.89bn £25.71bn £26.63bn £27.24bn £27.87bn £28.50bn £29.14bn
Raise additional rate by 3pp £2.90bn £3.15bn £3.22bn £3.42bn £3.61bn suppressed suppressed
Raise the Universal Credit standard allowance by 10% £-3.06bn £-3.18bn £-3.27bn £-3.35bn £-3.44bn £-3.48bn £-3.50bn
Raise the State Pension by 10% £-7.78bn £-8.01bn £-7.99bn £-8.14bn £-8.42bn £-8.55bn £-8.89bn
Standard reforms: change in each reform's target: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise basic rate by 1pp: change in income_tax £6.55bn £6.86bn £6.80bn £7.01bn £7.21bn £7.44bn £7.66bn
Raise higher rate by 1pp: change in income_tax £3.61bn £3.92bn £4.01bn £4.23bn £4.45bn £4.72bn £4.84bn
Raise personal allowance by ~800GBP/year: change in income_tax £-3.93bn £-4.07bn £-4.16bn £-4.25bn £-4.32bn £-4.38bn £-1.75bn
Raise child benefit by 25GBP/week per additional child: change in child_benefit £2.33bn £2.14bn £1.94bn £1.76bn £1.65bn £1.52bn £1.40bn
Reduce Universal Credit taper rate to 20%: change in universal_credit £20.74bn £21.31bn £21.77bn £22.03bn £22.34bn £22.74bn £22.96bn
Raise Class 1 main employee NICs rate to 10%: change in national_insurance £11.15bn £11.63bn £12.00bn £12.29bn £12.61bn £12.94bn £13.31bn
Raise VAT standard rate by 2pp: change in vat £24.69bn £25.51bn £26.42bn £27.03bn £27.65bn £28.28bn £28.91bn
Raise additional rate by 3pp: change in income_tax £2.90bn £3.15bn £3.22bn £3.42bn £3.62bn suppressed suppressed
Raise the Universal Credit standard allowance by 10%: change in universal_credit £3.06bn £3.19bn £3.29bn £3.37bn £3.44bn £3.50bn £3.53bn
Raise the State Pension by 10%: change in state_pension £10.68bn £11.17bn £11.28bn £11.60bn £12.04bn £12.47bn £12.97bn

#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 juaristi22 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

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.

@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Response to María's review, C1.

I confirmed the reported-award gap and fixed it in d6bcbf89f. Both QYP predicates now exclude positive raw person-level reports of UC, income-related/contributory ESA and income-based/contribution-based JSA, alongside the explicit own-right flag. This follows UC regulation 5(5) and SPC regulation 4A(5), avoids calculating family awards and preserves the shared input's existing semantics for other programmes.

The corrected YAML at cc4d3bb8a76d41510f8f455eec1ef424b89cf344 gave 13 failed, 2 passed before the fix. At final head 4b09a360185feff341747aa1b9f34e48f7d89dfb, it gives 15 passed, including the requested ESA/JSA situations and downstream UC child elements/work allowances and PC child additions. It also covers UC and contributory reports, the education route, false flags and parent-only/zero-report controls. The omitted-override ESA/JSA reader regressions both pass within the full reader run (20 passed at 4b09a360185feff341747aa1b9f34e48f7d89dfb). The scratch mutation that drops the exclusions is killed by 13 failing cases, with 2 controls passing at 4b09a360185feff341747aa1b9f34e48f7d89dfb.

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.

@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Response to Vahid's review.

  • Own awards: d6bcbf89f supplements both UC/PC QYP exclusions with raw person-level UC/ESA/JSA reports. At final head 4b09a360185feff341747aa1b9f34e48f7d89dfb, the award YAML gives 15 passed. The ESA/JSA reader regressions use a young Scottish single and an own-award recipient with no route or own-right override; both pass within the full reader run (20 passed at 4b09a360185feff341747aa1b9f34e48f7d89dfb). The general non-family generators retain their route condition because award-zero and Income-Support-only members can otherwise qualify through UC regulation 5(1)(a). The zero-report YAML control demonstrates that distinction.
  • Merge: 34291de5f merges main daec84d5b69c1a5f8beb47a623046de2287d1718. The legacy generator retains both the route line and main's work inputs. Both conflicted property files retain the new test scopes from each side; the PR body explains main's legitimate replacement of the obsolete FRS-equivalence assertion.
  • Statutory-age literal: 781245da6 introduces a named constant with citations to UC regulation 5(1)(a) and SPC regulation 4A(1)(a).
  • Overlaps: the PR body records that downstream open drafts need to reconcile this head before landing. I changed none of those branches.

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.

@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Pushed final head 4b09a360185feff341747aa1b9f34e48f7d89dfb to uc-pc-qyp-sixteen-route with a fast-forward update. Merge commit 34291de5f647f3821339ddbdafac971d01919805 incorporates main daec84d5b69c1a5f8beb47a623046de2287d1718.

At 4b09a360185feff341747aa1b9f34e48f7d89dfb: 193 YAML cases and 115 Python tests passed. I ran each file or directory sequentially in the foreground through the requested capped runner, with pytest -n 0 and both specified environment variables unset. All model situations were synthetic.

YAML command: policyengine-core test -c policyengine_uk <scope>. Python command: pytest -n 0 -vv <file>; the child/adult file used -q.

Scope Passed
policyengine_uk/tests/policy/baseline/finance/benefit/qualifying_young_person_reported_own_awards.yaml 15
policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/child_element 41
policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/guarantee_credit 81
policyengine_uk/tests/policy/baseline/finance/benefit/child_qualifying_young_person.yaml 8
policyengine_uk/tests/policy/baseline/finance/benefit/claimant_not_own_child_or_qualifying_young_person.yaml 11
policyengine_uk/tests/policy/baseline/finance/benefit/qualifying_young_person_no_education_route.yaml 14
policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/work_allowance/is_uc_work_allowance_eligible.yaml 13
policyengine_uk/tests/policy/baseline/gov/dwp/means_test_income_members.yaml 7
policyengine_uk/tests/policy/baseline/gov/dwp/pension_credit/claimant_partner_rates.yaml 3
policyengine_uk/tests/test_child_and_adult_definitions_properties.py 21
policyengine_uk/tests/test_means_test_income_properties.py 2
policyengine_uk/tests/test_legacy_award_readers_properties.py 20
policyengine_uk/tests/test_benefit_cap_specified_benefits_properties.py 46
policyengine_uk/tests/test_claimant_not_own_child_properties.py 5
policyengine_uk/tests/test_council_tax_reduction_young_person_properties.py 6
policyengine_uk/tests/test_council_tax_reduction_severe_disability_family.py 8
policyengine_uk/tests/test_benefit_cap_lha_scheme_split_properties.py 7

Ruff 0.17.2 format --check and check pass on all changed Python files. uvx ruff@0.17.2 check --select F811,F821 policyengine_uk, git diff --check and git diff --check origin/main...HEAD pass.

The scratch mutation drops the reported-award exclusion from both QYP predicates and runs under the lower mutation cap: 13 failed, 2 passed at 4b09a360185feff341747aa1b9f34e48f7d89dfb. The new ESA/JSA/UC and contributory-report cases kill it; the parent-only-report and zero-report controls pass. The corrected pre-fix run at cc4d3bb8a76d41510f8f455eec1ef424b89cf344 also gave 13 failed, 2 passed.

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.

@MaxGhenis

MaxGhenis commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator Author

Hand-off to the UK hub (owner session local_6b7a5349, 30 Oct 02:2xZ).

Head: 4b09a360185feff341747aa1b9f34e48f7d89dfb. That's the hub's adoption build (Subfleet 20261029-155413-adopt-2279b). It contains my last push, 560005cfa, and I added no commits. The branch is MERGEABLE (CLEAN).

Passed at this head:

  • CI, all 19 checks: Test / YAML, the six Python shards, Fix evidence, Aggregate impact, Lint, the docs build and the install smoke tests.
  • The adoption build's local runs: 193 YAML cases and 115 Python tests (verification comment).

Impact: the Aggregate impact report (run 40964661226, uk-data 1.58.1) finds:

  • 0 checks broken;
  • 0 moves flagged;
  • no change in any year to household net income, poverty, inequality, receipts or the government balance.

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 (d6bcbf89f), against the reg 5 and reg 4A text on legislation.gov.uk:

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:

  1. The hub's independent review at this head.
  2. María's re-review: her REQUEST_CHANGES from 26 Oct at 560005cfa still stands, and the reply answers C1.
  3. d1345, parked with Max (uk-data#562).
  4. The repository-wide hold on agent merges, d2161.

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>
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Pushed 33168ccbb for the UK hub's review of 4b09a3601 (GPT-6.1 Sol, REQUEST_CHANGES).

  • P2. SPC reg 4A(5) lists income support, and the Pension Credit predicate left it out. In 2025, a 70-year-old's 16-year-old outside education who reported their own Income Support became a PC qualifying young person under the new route and drew a child addition of £4,061.20. The predicate now reads receives_income_support_in_own_right, taken from End the Pension Credit, Child Benefit and CTC qualifying young person status for Income Support in own right #2302: the person's own reported Income Support while it is in payment and no reform removes it. UC is unchanged, since reg 5(5) does not list income support.
  • P3. A new case has six 16-year-olds each report one penny of one listed award; changing > 0 to >= 1 now fails it.
  • P3. The own-award YAML header and the body now say the reports count whether the own-right flag is omitted or false, and the body says the reports also reach older young people in education through the cap's specified-benefit rule.

The changelog fragment also drops its list markers, which the fragment check warned about. The body's 33168ccbb section lists the runs: 194 YAML cases and 201 Python tests pass, and three mutations of the new exclusion each fail a new test. A delta review comes next; María's review stays open until she re-reviews, and d1345 and the repository-wide hold still apply.

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>
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Pushed c5b9850a3 for the P2 in the Sol delta review of 33168ccbb. The helper applied the rule for a non-claimant's own report to everyone. So a reform that replaced the income_support formula with zero left a young carer excluded, which kept them a counted resident and removed £4,238 of a co-resident pensioner's Pension Credit in 2024.

  • Fix: the Pension Credit predicate now reads is_on_income_support and receives_income_support_in_own_right goes. That variable keeps the member's own report, the positive-award test and the payee.
  • Cycle check: Income Support's family follows the Child Benefit test, not this one. Tests calculate the status and the award in either order and with labour supply responses on.
  • Regression: the review's reproduction exits 0 at this code. With the formula zeroed or the variable neutralized, the pensioner gets the £4,238 addition (Pension Credit £15,581.80, against £11,343.80 while the carer receives the award).
  • Local runs at b7e87482b, which differs from the head only in the changelog fragment's wording: 209 YAML cases and 228 Python tests pass, and all nine new mutations fail at least one test.

The body now has this evidence and the run citations the review asked for. CI and the aggregate impact at c5b9850a3 are running, and a Sol review of this head is next. The merge still waits on d1345, the repository-wide hold (d2161) and María's re-review.

🤖 Generated with Claude Code

@MaxGhenis
MaxGhenis requested a review from juaristi22 October 30, 2026 16:25

@juaristi22 juaristi22 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_eligible counts 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 raises CycleError, as the body says.
  • The date fact: birth_day gives 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-tree is 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.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants