Repository navigation
Description
What the model leaves out
is_qualifying_young_person_for_universal_credit (policyengine_uk/variables/gov/dwp/universal_credit/child_element/is_qualifying_young_person_for_universal_credit.py, main 2da212a) applies the age, education or training, entry and terminal date conditions of UC Regs 2013 reg 5, and reg 5(5) through the input receives_benefits_in_own_right (Universal Credit, ESA or JSA in the person's own right, false unless a user sets it).
Reg 5(5) reads: "A person who is receiving universal credit, an employment and support allowance or a jobseeker's allowance is not a qualifying young person." In reg 2 the Secretary of State defines "employment and support allowance" and "jobseeker's allowance" without their income-related and income-based forms, so under reg 5(5) DWP doesn't count a young person receiving Universal Credit, contributory ESA or contribution-based JSA as a qualifying young person. #2279 (open, c5b9850a3) excludes a young person who reports Universal Credit, or ESA or JSA of either kind, reading the person's raw reports (universal_credit_reported, esa_income_reported, esa_contrib_reported, jsa_income_reported and jsa_contrib_reported). Those reports identify the person's own awards without calculating family benefits that depend on qualifying young person status and could recurse.
The predicate doesn't apply the Universal Credit (Transitional Provisions) Regulations 2014 reg 28:
Where a person who would (apart from the provision made by this regulation) be a “qualifying young person” within the meaning of regulation 5 of the Universal Credit Regulations is entitled to an existing benefit— (a) that person is not a qualifying young person for the purposes of the Universal Credit Regulations; and (b) regulation 5(5) of those Regulations applies as if, after “a person who is receiving” there were inserted “an existing benefit (within the meaning of the Universal Credit (Transitional Provisions) Regulations 2014),”.
The Secretary of State lists the existing benefits in reg 2(1): income-based jobseeker's allowance, income-related employment and support allowance, income support, housing benefit, child tax credit and working tax credit. Under reg 28, DWP also excludes people entitled to any of them: income-based JSA and income-related ESA, which #2279 reads, and Income Support, Housing Benefit, Child Tax Credit and Working Tax Credit. We fetched reg 28 from legislation.gov.uk at seven dates from 16 June 2014 to 6 April 2026 and on 1 November 2026, and its text reads the same in every copy, so it applies in every model year from 2014-15. Sources: https://www.legislation.gov.uk/uksi/2014/1230/regulation/28/2026-11-01 (CLML SHA-256 8b95cf93d9ab150a82ff1d6fa8108991292d59b79d32dcc454f6ab83dc38af92) and https://www.legislation.gov.uk/uksi/2014/1230/regulation/2/2026-11-01 (32580f8665764351c4d681faaa523170a2248cb9ac6a4a9e38b4458878b38a9a).
So a young person who reports Income Support or Housing Benefit in their own right, or who holds a tax credit award, still counts as a qualifying young person in the model, unless a user sets receives_benefits_in_own_right. A parent then gets the child element for them, where DWP pays none under reg 28.
Reproduction
Run the script below with policyengine-uk, using one simulation per case: python qyp_routes.py
"""Reg 28 and the Universal Credit qualifying young person predicate.
One simulation per case, 2026, North East, owned outright, no income. Every
person has is_claimant_or_partner set.
"""
from policyengine_uk import CountryTaxBenefitSystem, Simulation
SYSTEM = CountryTaxBenefitSystem()
Y = 2026
PARENT = dict(age=45, is_parent=True, is_claimant_or_partner=True)
YOUNG = dict(age=17, current_education="POST_SECONDARY", is_claimant_or_partner=False)
CASES = {
"parent 45 + 17 reporting IS 3,000": [PARENT, dict(YOUNG, income_support_reported=3_000)],
"parent 45 + 17 reporting HB 3,000": [PARENT, dict(YOUNG, housing_benefit_reported=3_000)],
"parent 45 + 17 reporting ESA(IR) 3,000": [PARENT, dict(YOUNG, esa_income_reported=3_000)],
"parent 45 + 17 reporting JSA(IB) 3,000": [PARENT, dict(YOUNG, jsa_income_reported=3_000)],
"parent 45 + 17 reporting nothing": [PARENT, dict(YOUNG)],
"parent 45 + 17 with receives_benefits_in_own_right": [
PARENT,
dict(YOUNG, receives_benefits_in_own_right=True),
],
"single 18 on a non-advanced course (no full-time flag) reporting HB 6,000": [
dict(age=18, current_education="POST_SECONDARY", is_claimant_or_partner=True, housing_benefit_reported=6_000)
],
}
VARS = [
"is_qualifying_young_person_for_universal_credit",
"is_qualifying_young_person_for_uc_education",
"uc_receiving_education",
]
for name, members in CASES.items():
ids = [f"p{j}" for j in range(len(members))]
people = {
pid: {k: {Y: v} for k, v in m.items() if k in SYSTEM.variables}
for pid, m in zip(ids, members)
}
sim = Simulation(
situation=dict(
people=people,
benunits={"b": {"members": ids}},
households={
"h": {
"members": ids,
"region": {Y: "NORTH_EAST"},
"tenure_type": {Y: "OWNED_OUTRIGHT"},
}
},
)
)
flags = " ".join(
f"{v}={[bool(x) for x in sim.calculate(v, Y)]}" for v in VARS if v in SYSTEM.variables
)
print(
f"{name}: eligible={bool(sim.calculate('is_uc_eligible', Y)[0])} "
f"UC={sim.calculate('universal_credit', Y)[0]:.2f} "
f"child_element={sim.calculate('uc_child_element', Y)[0]:.2f} {flags}",
flush=True,
)We ran it on #2346's branch, which leaves this predicate as it stands on main, and, for the columns with #2279, on that branch merged with #2279 (c5b9850a3).
| Benefit unit | Young person's own report | Predicate | Parent's Universal Credit | Predicate with #2279 | Parent's Universal Credit with #2279 |
|---|---|---|---|---|---|
| Parent 45, dependant 17 on a non-advanced course | Income Support £3,000 | true | £9,321.36 | true | £9,321.36 |
| The same | Housing Benefit £3,000 | true | £9,321.36 | true | £9,321.36 |
| The same | income-related ESA £3,000 | true | £9,321.36 | false (reg 28) | £5,098.80 |
| The same | income-based JSA £3,000 | true | £9,321.36 | false (reg 28) | £5,098.80 |
| The same | none | true | £9,321.36 | true | £9,321.36 |
The same, with receives_benefits_in_own_right set |
none | false | £5,098.80 | false | £5,098.80 |
| An 18-year-old heading their own benefit unit on a non-advanced course | Housing Benefit £6,000 | true | £4,062.96, no child element | true | £4,062.96, no child element |
Under reg 28 the young people on Income Support and Housing Benefit don't count as qualifying young people, so the parent's award should match the row with receives_benefits_in_own_right, 12 × £424.90 = £5,098.80. The model pays 12 × (£424.90 + £351.88) = £9,321.36, £4,222.56 more, with or without #2279. In the last row the predicate, and is_child_or_qualifying_young_person_for_universal_credit, are true for the claimant themselves; the child element stays nil, and we haven't traced what the other readers do with that. For a couple on a tax credit award, #2346's case 33 in uc_receiving_education.yaml asserts the predicate true for both 18-year-olds on the couple's Working Tax Credit award.
Every reader
Readers among the variables (grep -rlE at main 2da212a for the predicate and its child-or-young-person wrapper):
variables/gov/dwp/universal_credit/child_element/is_child_or_qualifying_young_person_for_universal_credit.py, the wrapper;variables/gov/dwp/universal_credit/benefit_cap/is_uc_benefit_cap_exempt_specified_benefit.py(line 111, the young person limb of reg 83(1)), which reads both;variables/gov/dwp/universal_credit/is_qualifying_young_person_for_uc_education.py(added by Universal Credit is paid to full-time students (WRA 2012 s. 4(1)(d); UC Regs 2013 regs. 12-14) #2346; applies reg 28 itself for regs 12(1A) and 60).
Readers of the wrapper, which inherit the gap:
variables/gov/dwp/lha_renter_has_non_dependant.pyvariables/gov/dwp/universal_credit/child_element/disability/severe_disability/uc_individual_severely_disabled_child_element.pyvariables/gov/dwp/universal_credit/child_element/disability/uc_individual_disabled_child_element.pyvariables/gov/dwp/universal_credit/child_element/uc_child_index.pyvariables/gov/dwp/universal_credit/child_element/uc_child_limit_disregarded.pyvariables/gov/dwp/universal_credit/child_element/uc_child_limit_position.pyvariables/gov/dwp/universal_credit/child_element/uc_is_child_born_before_child_limit.pyvariables/gov/dwp/universal_credit/child_element/uc_is_child_limit_affected.pyvariables/gov/dwp/universal_credit/is_responsible_for_child_or_qualifying_young_person_for_universal_credit.pyvariables/gov/local_authorities/council_tax_reduction/is_child_or_young_person_for_council_tax_reduction.py
variables/household/demographic/is_child.py names the wrapper only in its documentation.
Tests that read the predicate or the wrapper:
tests/policy/baseline/finance/benefit/child_qualifying_young_person.yamltests/policy/baseline/finance/benefit/family/universal_credit/child_element/disability/uc_individual_disabled_child_element.yamltests/policy/baseline/finance/benefit/family/universal_credit/claimant_child_element.yamltests/policy/baseline/finance/benefit/family/universal_credit/is_uc_assessed_claimant.yamltests/policy/baseline/finance/benefit/family/universal_credit/is_uc_claimant.yamltests/policy/baseline/finance/benefit/family/universal_credit/work_allowance/is_uc_work_allowance_eligible.yamltests/policy/baseline/gov/dwp/benefit_cap.yamltests/policy/baseline/gov/dwp/means_test_income_members.yamltests/policy/baseline/gov/local_authorities/council_tax_reduction/council_tax_reduction_young_person.yamltests/test_benefit_cap_specified_benefits_properties.pytests/test_council_tax_reduction_severe_disability_family.pytests/test_council_tax_reduction_young_person_properties.pytests/test_means_test_income_properties.pytests/test_uc_claimant_elements_properties.pytests/test_uc_claimant_income_properties.pytests/test_uc_receiving_education_properties.py(Universal Credit is paid to full-time students (WRA 2012 s. 4(1)(d); UC Regs 2013 regs. 12-14) #2346)
Proposed fix
- Apply reg 28 in the predicate itself for Income Support, Housing Benefit, Child Tax Credit and Working Tax Credit: the person's own report of any of them, or, for a claimant or partner, the couple's tax credit award (Tax Credits Act 2002 s.3(3)(a)). Leave income-related ESA and income-based JSA to Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279, which reads them with the other ESA and JSA reports.
is_qualifying_young_person_for_uc_education(Universal Credit is paid to full-time students (WRA 2012 s. 4(1)(d); UC Regs 2013 regs. 12-14) #2346) could then read the predicate directly. - Read reported receipt in place of entitlement, as the model already does for legacy closure (
legacy_benefits_closed), and read the raw reports, as Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279 does, so the predicate doesn't recurse through family benefits. - Land it after Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279, or fold both lists into one exclusion.
- Rerun the readers and tests above.
- Check whether the Enhanced FRS puts a 16 to 19-year-old who reports their own Income Support or Housing Benefit in their parent's benefit unit. If it never does, the fix moves household calculations only.
Related
#2292 proposes excluding a young person who receives Income Support in their own right from the qualifying young person tests for Pension Credit, Child Benefit and Child Tax Credit, and leaving Universal Credit's test alone, since UC Regs reg 5(5) doesn't list Income Support. For Universal Credit, DWP excludes a person entitled to Income Support, Housing Benefit or either tax credit under SI 2014/1230 reg 28.
🤖 Generated with Claude Code
Activity
MaxGhenis commentedon Nov 1, 2026
Picking this up for the UK hub. Reply here if you're mid-change and I'll stop.
Generated by Claude Code
MaxGhenis commentedon Nov 1, 2026
Ready for hub review
Head:
dbaf71872870f26988c802cab622919c450d95f1on branchclaude/reg-28-qualifying-young-person-xorqye. The harness pins pushes to this name. The branch starts at #2279's headc5b9850a3592769ba35f4d5b5c72c7ba8d2e6f22and adds two commits. I opened no PR, so no CI has run.Items
policyengine_uk/variables/gov/dwp/universal_credit/child_element/is_qualifying_young_person_for_universal_credit.py:49-78. I folded both lists into Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279's singlereceives_own_benefitsexpression. It now also readsincome_support_reportedandhousing_benefit_reported, plusreceives_tax_credits, which is true for a person's own Child Tax Credit or Working Tax Credit report, and for a claimant or partner when either of the couple reports one (lines 56-67). All four reports arePersonvariables (policyengine_uk/variables/gov/dwp/income_support_reported.py,housing_benefit/housing_benefit_reported.py,child_tax_credit_reported.py,working_tax_credit_reported.py). The couple sum masks withis_claimant_or_partnerbeforebenunit.sum, the same waylegacy_benefits_closeddoes. Documentation and references now cite TP reg 28, reg 2 and TCA 2002 s.3 (lines 8-25). Reg 5(5) already excludes income-related ESA and income-based JSA through Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279's reports, so I added nothing for them.claimant_or_partner(line 66). This is tested by the YAML case "A parent's own reports of every existing benefit leave their young person a qualifying young person." and by a property.OWNED_OUTRIGHTandRENT_PRIVATELY(rent £6,000), I calculated each reader first in a fresh simulation and thenuniversal_credit. The readers were the predicate, its wrapper,is_uc_benefit_cap_exempt_specified_benefit,lha_renter_has_non_dependant, both disabled child elements,uc_child_index,uc_child_limit_disregarded,uc_child_limit_position,uc_is_child_born_before_child_limit,uc_is_child_limit_affected,is_responsible_for_child_or_qualifying_young_person_for_universal_credit,is_child_or_young_person_for_council_tax_reduction,housing_benefit,council_tax_reductionandhousehold_net_income. That gave 238 runs with 0 errors.is_qualifying_young_person_for_uc_education(Apply the Universal Credit education condition #2806) isn't in this base.Issue script output
Before (at
c5b9850a3):After (this branch):
The script prints only the predicate column because
is_qualifying_young_person_for_uc_educationanduc_receiving_educationdon't exist at this base.Tests
policyengine_uk/tests/policy/baseline/finance/benefit/family/universal_credit/uc_qualifying_young_person_existing_benefits.yamlhas 11 cases, all passing. Cases 1-7 are the issue's table rows in 2026 (North East, owned outright), asserting the predicate, the wrapper,uc_child_elementanduniversal_credit: £5,098.80 for the Income Support, Housing Benefit, ESA, JSA and own-right rows, £9,321.36 with no report, and £4,062.96 for the 18-year-old claimant. The other 4 cases cover own CTC/WTC reports (2024), a parent's reports, a couple's Working Tax Credit award excluding both 18-year-olds, and a couple's Child Tax Credit award leaving a dependant in place.policyengine_uk/tests/test_uc_qualifying_young_person_existing_benefits_properties.pyhas 4 Hypothesis properties (25 derandomised examples each, about 30 s for the file with-n 4):universal_creditnever rises when their young person's report of any existing benefit turns positive (2024 and 2026).Each example sets inputs on one cached simulation per shape and year, because building a simulation took about 3.7 s here. A check over 6 examples found that cached and fresh simulations give the same predicate and
universal_credit.qualifying_young_person_reported_own_awards.yamlasserted that a young person reporting Income Support stays a UC qualifying young person. They now assert false under reg 28, and I renamed 4 of them (YAML case names aren't inshard_durations.toml).test_pension_credit_young_person_income_support.py::test_own_income_support_ends_the_young_person_until_a_reform_removes_itnow expects[False, False]for the UC predicate, with and without the reform. Its name is unchanged.test_benefit_cap_lha_scheme_split_properties.py,test_pension_credit_young_person_income_support.py,test_child_and_adult_definitions_properties.pyandtest_legacy_award_readers_properties.py: 136 passed;policyengine_uk/tests/code_health: 3007 passed;ruff@0.17.2 format --check,check,check --select F811,F821andgit diff --check: clean.Revert and mutation checks
c5b9850a3: 6 of the 11 new YAML cases fail (Income Support, Housing Benefit, the 18-year-old's Housing Benefit, own tax credits, and both couple tax credit cases), and so do the 6 updated Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279 cases. The 5 that pass were already correct under Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279. The propertytest_any_own_existing_benefit_report_excludes_the_personfails, and so does the updated PC test. My first draft of the properties drew reg 5(5) reports so often that this property passed without the change. I biased the generators (second commit), and it now fails as it should.False & ...): the first property fails, and 2 YAML cases fail.test_a_claimants_reports_never_change_a_dependants_predicatefails, and 2 YAML cases fail.Skipped
uc_receiving_education.yaml,test_uc_receiving_education_properties.py) aren't in this base, so I didn't run them. After this change, Apply the Universal Credit education condition #2806's case 33 ("a couple of 18-year-olds on a joint Working Tax Credit award…", 2024) needsis_qualifying_young_person_for_universal_credit: [false, false]in place of[true, true]. I ran the same household here withoutis_in_full_time_education, which isn't in this base:is_claimant_or_partnercomes out[True, True]and the predicate[False, False]. I couldn't run its other two assertions, which read Apply the Universal Credit education condition #2806's variables, so they're pending. Once both land,is_qualifying_young_person_for_uc_educationcould read the predicate directly, as the issue suggests.Judgment calls
jsa_income_reportedin 2026, the predicate excludes on any positive report, whatever the year. In 2026 the model treats Income Support as no longer paid (gov.dwp.income_support.active), and tax credits from 2025-26 (gov.dwp.tax_credits.active). Even so, a 2026 Income Support report or a 2025+ tax credit report still triggers reg 28 here. Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279's Pension Credit test, by contrast, readsis_on_income_support, which gates onactiveand on neutralisation. So the same 2026 young person can be a PC young person but not a UC one (Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279 case "…leaves the Pension Credit route available once Income Support stops paying."). And a reform that neutralises Income Support doesn't restore UC status. The alternative is to gate the Income Support and tax credit reports on theiractiveparameters. That would turn the issue's 2026 Income Support row back into £9,321.36, against the brief's expected £5,098.80, so I didn't do it. Whether this matters depends on whether the dataset carries legacy reports after closure, which I can't check.PR text for the hub
Generated by Claude Code
MaxGhenis commentedon Nov 2, 2026
Picking this up for the UK hub. Reply here if you're mid-change and I'll stop.
Generated by Claude Code
MaxGhenis commentedon Nov 2, 2026
PR_BODY.mdGenerated by Claude Code
MaxGhenis commentedon Nov 2, 2026
Ready for hub review
Head:
4b4d30df264e3951def2e597aed0aeca15f943aeon branchclaude/reg-28-qualifying-young-person-5v7i6p(the harness pins pushes to this name). It starts atdbaf71872870f26988c802cab622919c450d95f1and adds two commits. I opened no PR and pushed to no other branch, so no CI has run.Findings
P1, closed benefits (ruling 1).
is_qualifying_young_person_for_universal_credit.py:34-77reads a report of a tax credit only whilegov.dwp.tax_credits.activeholds (:50-53), income-based JSA only whilegov.dwp.JSA.income.activeholds (:66), and Income Support only whilegov.dwp.income_support.activeholds (:67). Each is also skipped when a reform neutralises the benefit:income_support,jsa_incomeandtax_credits. Housing Benefit and IR-ESA stay ungated (:68,:65). The gate follows the reviewer'sgated-reading.diff. Households A to G give the reviewer's gated column exactly: A true / £9,321.36, B false / £4,801.68, C true / £8,869.68, D false / £4,721.40, E true / £8,869.68, F true / £9,321.36, G false / £5,098.80. Against #2279's head they give the base column, and againstdbaf71872the head column. Knock-on edits:c5b9850a3and passes now.qualifying_young_person_reported_own_awards.yaml:48,:224,:365), and the 2026 IS case asserts UC[false, true]with a matching name (:498).[False, True](test_pension_credit_young_person_income_support.py:195-198).PAYING,reg_28_exclusions).P2-1, the reg 5(5) mislabel. Fixed in the variable docstrings, the code comments, the property file (
REG_5_5_REPORTSnow holds UC, contributory ESA and contribution-based JSA;EXISTING_BENEFITSadds IB-JSA and IR-ESA), #2279's YAML header and the body.git grepfinds no remaining place that puts IR-ESA or IB-JSA under reg 5(5).P2-2, Scottish CTR (ruling 2). New
is_qualifying_young_person_under_universal_credit_regulation_5.pyapplies reg 5 with its own 5(5) exclusions. The UC predicate is that variable less reg 28.is_child_or_young_person_for_council_tax_reduction.py:62-72reads the reg 5 variable for Scotland's working-age branch, withis_child_for_universal_creditand the looked-after and claimant-or-partner masks. That also takes #2279's IR-ESA and IB-JSA exclusions out of Scottish CTR.probe_ctr.py(Scotland, parent 45 and a 17-year-old, CTR claimed, council tax £1,500), flag / CTR at basec5b9850a3→dbaf71872→ now:The flag moves back, but the CTR award in these households doesn't. The award follows the parent's UC, which reg 28 does reduce: £8,869.68 at base and £4,801.68 at
dbaf71872and now, for the IS row. Withwould_claim_ucfalse, CTR is £1,500 (£1,374.96 for the CTC row) under all three trees. The newcouncil_tax_reduction_young_person.yamlCase 11 shows where the flag moves money: a 19-year-old with their own HB keeps the 22-year-old applicant a lone parent, with a £4,968.60 applicable amount. Case 12 covers IS, IB-JSA, IR-ESA and contribution-based JSA. Both fail atc5b9850a3and atdbaf71872. ARULINGS.mdentry is the hub's to log.P2-3, #2806. I didn't edit #2806's files. I merged this head with
a5d166266in a scratch tree. With the four replacements below,uc_receiving_education.yamlpasses 35/35 andtest_examples_reach_the_casespasses. This branch's three YAML files pass 52/52 on that tree. Ruling 1 doesn't move any of the four, because cases 31 and 34 and the examples rest on Housing Benefit in 2026, and case 33 on WTC in 2024.uc_receiving_education.yamlcase 31 (line 824):is_qualifying_young_person_for_universal_credit: [false][false, false][false]test_uc_receiving_education_properties.pyline 701:[False, False]Docstring: replace #2806's last sentence ("The model's other rules read is_qualifying_young_person_for_universal_credit, which leaves reg. 28 out.") with: "is_qualifying_young_person_for_universal_credit applies reg. 28 too, but reads a report of Income Support, income-based JSA or a tax credit only in a year the model pays that benefit; this variable reads those reports in every year." If #2806 instead reads the predicate directly, as the reviewer recommends, the sentence becomes "It equals is_qualifying_young_person_for_universal_credit, which applies reg. 28." The "reads entitlement from the year's reported receipt" sentence then goes too. I haven't run that variant. Its property 8 counts raw reports in 2026 among its
YEARS, so it would need the same paying-year filter.P2-4, mutants M1 and M2. I added two YAML cases: "A partner's own Housing Benefit report leaves an 18-year-old claimant a qualifying young person." and "A dependant's own Child Tax Credit award leaves an 18-year-old claimant a qualifying young person." I also added
test_the_predicate_is_reg_5_less_reg_28withqualifying_families, which draws claimants, partners or dependants, and dependants who meet reg 5 two times in three. I put it in the existing property file rather than a newtest_reg28_exact_property.py, so it shares the cache and fixture without importing one test module from another.P3-1.
_CACHEholds the cloned system and the simulations. A module-scoped autouse fixture clears it afteryield.P3-2. The UC predicate cites SR 2016/226 regs 2 and 29, and its docstring names them. The reg 5 variable cites UC Regs (NI) 2016 reg 6 (
nisr/2016/216/regulation/6), which I took from the review's note that reg 29 modifies that regulation's para (5).P3-3. PC test docstring and assertions now match its name. #2279's YAML: "…excludes them from the Pension Credit and UC statuses." (
:526); "…leaves the Pension Credit and UC routes available once Income Support stops paying." (:498); "…counts as neither a Pension Credit nor a UC young person." (:439).P3-4 and P3-5. I applied every voice row: title, body ¶1 and ¶2, the Law, Invariants, Limitation and Changelog rows, docstring :13-20, comment :49-55, new YAML :1-8, #2279 YAML :6-8, :433 and :434, PC test :6-9, and property file :12, :65-69 and :166. Some rows needed adjusting:
cycle2.py), 0 errors, UC matching a fresh calculation in each.axiom: hub fills this line.Axiom companion cases for
uk/regulations/uksi/2014/1230/28.yamlCompanion cases for
uk/regulations/uksi/2014/1230/28.yaml(#2346's packet). Each case gives the inputs, the outcome the law gives (worked by hand), and the year. Money is the annual Universal Credit award: 12 times the monthly amounts the model holds for 2024-25 to 2026-27 (single claimant aged 25 or over £393.45, £400.14 and £424.90 a month; the child element for this young person £339.00 in 2025-26 and £351.88 in 2026-27). The model computed the same figures.Tests run (this head)
uc_qualifying_young_person_existing_benefits.yaml: 17/17.qualifying_young_person_reported_own_awards.yaml: 23/23.council_tax_reduction_young_person.yaml: 12/12.child_qualifying_young_person.yaml,family/universal_credit/,qualifying_young_person_no_education_route.yaml,benefit_cap.yaml,benefit_cap/,means_test_income_members.yaml,council_tax_reduction/): 1,093 passed.tests/policy, was killed (out of memory) afterbaseline/(489 files) andintegration/(7 files) had passed, 5,179 cases. I then ran the 15reforms/files one by one: 114 passed.test_own_family_status_contract_by_applicant_scheme[SCOTLAND-2026-45-True], which fedis_qualifying_young_person_for_universal_creditas an input to Scottish CTR. The second commit supplies the reg 5 variable; the file now passes 8/8.code_health: 3,008 passed.ruff 0.17.2 format --check,check,check --select F811,F821, andgit diff --check: clean.test_uc_receiving_education_properties.pyis Apply the Universal Credit education condition #2806's and isn't in this base.Revert checks. I ran this branch's tests against the formula at each base.
c5b9850a3: 9/17 new YAML cases fail, 5 of Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279's file fail, CTR Cases 11 and 12 fail, and the PC reform test and three of five properties fail.dbaf71872: the three closed-benefit cases fail, Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279's 2026 IS case fails, CTR Cases 11 and 12 fail, the PC reform test and the exact property fail, and the zero-report property fails because the reg 5 variable is missing.dbaf71872by design; only the mutants fail them.Skipped
Judgment calls
jsa_incomeandtax_credits, as the reviewer's diff does, not onlyincome_support.is_qualifying_young_person_under_universal_credit_regulation_5.test_council_tax_reduction_young_person_properties.pyandtest_council_tax_reduction_severe_disability_family.pynow read or supply the reg 5 variable for Scotland, since that's the status SSI 2021/249 reg 6 imports.Generated by Claude Code
MaxGhenis commentedon Nov 2, 2026
Ready for hub review
Verdict: REQUEST_CHANGES at
4b4d30df264e3951def2e597aed0aeca15f943aeThis is the delta review of
dbaf71872870f26988c802cab622919c450d95f1..4b4d30df264e3951def2e597aed0aeca15f943aeonclaude/reg-28-qualifying-young-person-5v7i6p.The code follows both rulings, and every number I could rerun matches the hand-off. Ruling 1 holds: households A to G give the gated column exactly. Ruling 2 holds: the Scottish probe rows match the hand-off's figures on all three trees. M1, M2 and the four gate mutants die. Merges with
main, #2279 and #2806 are clean, and #2806's tests pass with the four replacements.I'm requesting changes for three P2 items, all text or test comments; the formula needs no change:
git grepfinds none.There are no P1s.
Scripts named below (
scripts/*.py) ran in this review's cloud scratch space against detached worktrees, and they aren't in the repository. Each table gives the command's output as run.Findings
P2-a – P2-1 is only partly closed: two #2279 test files still put IR-ESA and IB-JSA under reg. 5(5)
The hand-off says "
git grepfinds no remaining place that puts IR-ESA or IB-JSA under reg 5(5)". Runninggit grep -n -i "5(5)" -- policyengine_ukat the head finds two such places. Both came in with #2279 (d6bcbf89and4b09a360, ancestors ofc5b9850a3, not ofmain), so this branch carries them under the brief.policyengine_uk/tests/test_legacy_award_readers_properties.py:687-690. The test is parametrised over["esa_income_reported", "jsa_income_reported"], and its comment reads "UC reg 5(5) and SPC reg 4A(5) still exclude the member's own award." Replace the comment with:policyengine_uk/tests/test_benefit_cap_specified_benefits_properties.py.OWN_RIGHT_REPORTSholdsesa_income_reportedandjsa_income_reported.own_benefitslabels the whole tuple "UC reg 5(5): a personal report identifies an award in the person's own right."The oracle still agrees with the model only because
YEAR = 2025(line 91), when income-based JSA pays. Move the year to 2026 and the oracle goes wrong, because the predicate then reads no income-based JSA report.Replace line 281 with:
Replace "(reg 5(5))" at line 34 with "(reg 5(5); SI 2014/1230 reg. 28)".
The other
5(5)hits are sound:uc_unearned_income.yaml:344,qualifying_young_person_no_education_route.yaml:152,qualifying_young_person_reported_own_awards.yaml:1-11and:428, andcouncil_tax_reduction_young_person.yaml:405concern contributory awards or the own-right flag.benefit_cap.yaml:158,benefit_cap_specified_benefits.yaml:1235andis_uc_benefit_cap_exempt_specified_benefit.py:113read thereceives_benefits_in_own_rightflag.P2-b – The body's Case 11 sentence is false: Case 11 shows the applicable amount, not the award
The PR body (Scotland section, last paragraph) says: "Where the flag does change the award, the new Case 11 shows it". The hand-off says the same: "Case 11 shows where the flag moves money".
Case 11 (
council_tax_reduction_young_person.yaml:355-389) asserts the flags,is_council_tax_reduction_lone_parentandcouncil_tax_reduction_applicable_amount: 4_968.60. It asserts no award. Its household's award is £0 at bothdbaf71872and the head, because the household does not take up CTR. I ran the case's inputs through aSimulation(scripts/case11.py):would_claim_council_tax_reductioncouncil_tax_reductiondbaf71872The flag moves the award only once applicable income exceeds the applicable amount. I took the same household with
would_claim_council_tax_reduction: true, council tax £1,800 and the applicant's employment income set (scripts/case11c.py). The young person reports either income-related ESA or Housing Benefit, £3,000, in 2026:c5b9850a3dbaf71872Against #2279, the base this lands on, Scottish CTR moves only for income-related ESA and income-based JSA reports. The IS, HB and tax credit rows return to #2279's values.
Replace the body's last two sentences of that paragraph ("Where the flag does change the award … applicable amount.") with:
Make the same correction in the hand-off. If the hub wants the award pinned, add
would_claim_council_tax_reduction: true,employment_income: 5_000and acouncil_tax_reductionassertion as a separate case.P2-c – #2806 merge note: the proposed docstring sentence is accurate, but it describes a merged tree that contradicts ruling 1
On the tree merged with #2806's head
a5d166266(merge-tree29c87347),is_qualifying_young_person_for_uc_educationreturnsis_qualifying_young_person_for_universal_credit & ~(own_award | joint_tax_credits). It reads the rawEXISTING_BENEFITSreports in every year. So in a closed year it treats a report as entitlement under reg. 28, which ruling 1 rejects.I took a single claimant aged 18 on an upper-secondary course, North East, owned outright, reporting £3,000 (
scripts/probe2806.py, merged tree). The columns are the UC predicate, #2806's variable,uc_receiving_educationand the UC award:The award doesn't move in this household, but the two reg. 28 readings disagree in every closed year. The hand-off's first proposed sentence matches the code, but it writes that disagreement into a docstring. It also omits the neutralisation check.
Recommendation for the landing PR: take the hand-off's alternative and make #2806's variable return
is_qualifying_young_person_for_universal_credit. With that change on the merged tree:truefor Apply the Universal Credit education condition #2806's variable;uc_receiving_education.yamlpasses 35/35 with the four replacements;test_uc_receiving_education_properties.pyfails 1 of 3 (test_education_condition_invariants, 381 s). Hypothesis falsifies it in 2026 with a member reportingworking_tax_credit_reported: 1000: the variable gives True and the oracle False, because the oracle still counts a closed-year tax credit report. The hand-off predicted this. The oracle needs the same paying-year filter as this branch'sPAYING; carry that edit with the variant.Then use the hand-off's second sentence ("It equals is_qualifying_young_person_for_universal_credit, which applies reg. 28."). Drop "The model reads entitlement from the year's reported receipt…". If the hub keeps #2806's own reading instead, the sentence needs the neutralisation clause:
P3-a – Neutralisation covers three aggregate variables only, and only Income Support's is tested
is_qualifying_young_person_for_universal_credit.py:40-50checksis_neutralizedonincome_support,jsa_incomeandtax_credits. Ruling 1 names onlyincome_support, so this follows the ruling, but two gaps remain (scripts/cycle.py, head):child_tax_creditalone leaves household D (2024, own CTC £1,000) excluded:[False, False].housing_benefitleaves household G (2026, own HB) excluded:[False, False]. Ruling 1 reads HB "in every year", so this follows the ruling. The hub may want to say whether it also meant "under every reform", since it is household E's problem for HB.jsa_incomeneutralisation works: a 2025 IB-JSA report under that reform gives[False, True].jsa_incomeortax_credits. The only test that does is the PC test, and it neutralisesincome_support. That is why M8 fails only the PC test.There is no text change for the hub's ruling here, only a note. If the gate stays, consider one YAML-free Python case that neutralises
tax_creditsin 2024.P3-b – The exact property's generator meets reg. 5 less often than its docstring says
test_uc_qualifying_young_person_existing_benefits_properties.py:142-146(thequalifying_familiesdocstring) says each slot "would meet reg. 5 two times in three". The PR body's Tests bullet says the same. Butqualifyingis built frommember(), which draws a reg. 5(5) report three times in seven (sampled_from((None,) * 4 + REG_5_5_REPORTS), line 85). So aqualifyingdraw meets reg. 5 at most four times in seven, and a slot at most about 8 in 21.The property still kills M1, M2, M7, M9 and M10, so this is wording. Two fixes:
member()an argument that turns off the reg. 5(5) report draw, and pass it forqualifying.P3-c – The PR body describes how the work was produced
dbaf71872, a state that never lands. Drop the column, and give "CTR before (Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279) / now".dbaf71872), … because it reads the reg. 5 variable this change adds." Keep the Count 16-year-olds outside education as UC and Pension Credit qualifying young persons until the 1 September after their 16th birthday #2279 sentence before it.P3-d – The Northern Ireland UC Regs citation has no quoted source
The reg. 5 variable cites
https://www.legislation.gov.uk/nisr/2016/216/regulation/6, and its docstring says "Northern Ireland's twin is UC Regs (NI) 2016 reg. 6". No input quotes the SR number 2016/216 or the text of that regulation. The previous review said only that SR 2016/226 reg 29 "modifies UC Regs (NI) 2016 reg 6(5)". legislation.gov.uk doesn't resolve from here, so the hub should confirm the number before landing.P3-e – The Axiom companion cases: case 6 rests on a provision nobody quoted
Cases 1 to 5 rest on law quoted in the brief, issue or body:
Case 6 says "Only the person who claims Housing Benefit is entitled to it". No input quotes a Housing Benefit provision for that; the previous review stated it without a citation too. The outcome doesn't depend on it, because the 30-year-old partner fails reg. 5's age condition. Either cite the HB entitlement provision once the hub has it, or replace the sentence with:
P3-f – Writing rules in the delta's text
A regulation, scheme or document acts in several new lines, and one comment is passive.
is_qualifying_young_person_under_universal_credit_regulation_5.py:14-17:19-21:50is_qualifying_young_person_for_universal_credit.py:34:46-47council_tax_reduction_young_person.yamlCase 11 comment:405)uc_qualifying_young_person_existing_benefits.yaml, partner HB case comment#2279 YAML:1-3(qualifying_young_person_reported_own_awards.yaml)2804.fixed.md, sentence 2The body Law bullet and the property-file comment come from the previous round's own voice rows. I'm correcting them here, not faulting the author.
Previous-round findings and rulings
_CACHEand a module-scoped autouse fixture clear it afteryield.removedUC[False, True]and its docstring matches. #2279's YAML names now read "…Pension Credit and UC statuses" (:526), "…Pension Credit and UC routes…" (:498) and "counts as neither…" (:439).axiom: hub fills this lineis kept. The cycle invariant now states what was run.Ruling 1 code. I read
is_qualifying_young_person_for_universal_credit.py:34-77.paying(active, name)returnsbool(active) and not system.get_variable(name).is_neutralized.tax_credits_payingbefore the couple sum, so the couple branch is gated too.jsa_income_payingandincome_support_payinggate lines 66-67.esa_income_reported(:65) andhousing_benefit_reported(:68) are ungated.system.parameters(y)givestax_credits.activeTrue/False/False andincome_support.activeTrue/True/False for 2024/2025/2026, so the gate reads 2025 as tax credits closed and IS paying.Households A to G (
scripts/probe.py; parent 45 and 17-year-old onPOST_SECONDARY, North East, owned outright, no income). Each cell gives the young person's predicate and the parent's annual UC.c5b9850a3dbaf71872income_supportThe head matches the previous review's gated column and the hand-off in all seven, and the PR body's first table in all seven rows.
Ruling 2 code.
is_qualifying_young_person_under_universal_credit_regulation_5.pyapplies the age condition, reg. 5(1)(a) or (b), and a reg. 5(5) exclusion ofreceives_benefits_in_own_right,universal_credit_reported,esa_contrib_reportedandjsa_contrib_reported. It has no reg. 28 term.& ~entitled_to_existing_benefit.is_child_or_young_person_for_council_tax_reduction.py:60-72reads(is_child_for_universal_credit | reg-5 variable) & ~looked_after & ~claimant_or_partner. That mirrorsis_child_or_qualifying_young_person_for_universal_creditexactly, with reg. 5 in place of the UC predicate.Scottish probe (
scripts/probe_ctr.py; Scotland, parent 45 and 17-year-old, CTR claimed, council tax £1,500). Each cell gives the flag / CTR; the last column gives CTR withwould_claim_ucfalse.dbaf71872Every figure matches the hand-off and the body. The body's statement that the award doesn't move in these households holds: the parent's UC is the same at
dbaf71872and the head (£4,801.68 for IS 2025, £4,721.40 for HB and CTC 2024), and so is CTR. Cases 11 and 12 pass at the head and fail atc5b9850a3and atdbaf71872(2 failed each). Mutant M11, which points the Scottish branch back at the UC predicate, fails exactly those two cases.Ruling 2 against the quoted law. The brief quotes SSI 2021/249 reg. 6(1) as defining a young person "by reference to the definition of 'qualifying young person' in regulation 5 of the 2013 Regulations". It quotes reg. 28 as applying "for the purposes of the Universal Credit Regulations" and WRA 2012 Part 1. Nothing quoted contradicts the ruling.
Mutants (scratch copy, restored after each;
scripts/mutate.py,scripts/runmut.sh)Each mutant ran against:
test_council_tax_reduction_severe_disability_family.py;council_tax_reduction/andtest_council_tax_reduction_young_person_properties.py.test_the_predicate_is_reg_5_less_reg_28test_own_income_support_ends_the_young_person_until_a_reform_removes_it(see P3-a)jsa_contrib_reportedJSA.income.activecouncil_tax_reduction/YAML directorytest_council_tax_reduction_young_person_properties.py:test_scheme_selection_over_every_combination,test_each_scheme_reads_its_own_young_person,test_schemes_differ_only_where_the_two_definitions_differincome_support.activeAxiom companion cases (head,
Simulation;scripts/axiom.py,scripts/probe.py)[false, false]; partner reports[false, false]; no report[true, true][true, false][true, false](but see P3-e)I checked the arithmetic by hand:
Case 6 cites a provision nobody quoted (P3-e).
Checked and sound
uv sync --frozen --extra dev,HUGGING_FACE_TOKENandPOLICYENGINE_UK_DEFAULT_DATASETunset):uc_qualifying_young_person_existing_benefits.yaml17/17;qualifying_young_person_reported_own_awards.yaml23/23;council_tax_reduction_young_person.yaml12/12.child_qualifying_young_person.yaml8,family/universal_credit/574,qualifying_young_person_no_education_route.yaml14,benefit_cap.yaml18,benefit_cap/116,means_test_income_members.yaml7 andcouncil_tax_reduction/333. The hand-off's 1,093 equals this plus the 23-case reported-awards file.test_council_tax_reduction_young_person_properties.py,test_council_tax_reduction_severe_disability_family.py,test_legacy_award_readers_properties.pyandtest_benefit_cap_specified_benefits_properties.py.c5b9850a3and ofdbaf71872.c5b9850a3: 9/17, 5/23 and 2/12 fail. The 9 include the Reproducer "A parent's young person who reports their own Income Support attracts no child element."dbaf71872: 3/17, 1/23 and 2/12 fail.code_health: 3,008 passed in 242 s, 0 failed.ruff 0.17.2 format --check(2,098 files already formatted) andruff check(all checks passed) are clean, and so isgit diff --check dbaf71872 4b4d30df.git merge-tree --write-tree, clean, no conflicts):main47bda0a7a→f1abdf23;c5b9850a3→b82ab8ad;a5d166266→29c87347. Apply the Universal Credit education condition #2806 is still open at that head.uc_receiving_education.yamlfails 3/35 before the replacements. With the four replacements (lines 824, 879, 908 and property line 701), it passes 35/35, andtest_uc_receiving_education_properties.pypasses 3/3 in 139 s.universal_credit. That gave 0 errors, and UC matched a fresh calculation in every order.uc_child_index, the four child-limit variables, both disabled-child elements, the responsibility flag,lha_renter_has_non_dependant, the LHA shared-rate renter, the UC cap exemption, the CTR young person flag, the reg. 5 variable andcouncil_tax_reduction.benefit_cap, which doesn't exist.is_claimant_or_partner, the reg. 5 inputs and parameters.What I couldn't check
cycle2.py) and its privately rented tenure. I ran my own 210 orders, owned outright only.Generated by Claude Code
MaxGhenis commentedon Nov 3, 2026
Picking this up for the UK hub. Reply here if you're mid-change and I'll stop.
Generated by Claude Code
MaxGhenis commentedon Nov 3, 2026
PR_BODY.mdGenerated by Claude Code
MaxGhenis commentedon Nov 3, 2026
MERGE_2806.mdGenerated by Claude Code
MaxGhenis commentedon Nov 3, 2026
AXIOM_ADDENDUM.mdGenerated by Claude Code
MaxGhenis commentedon Nov 3, 2026
Ready for hub review
Head:
d5d32074d569331db3cda217701cdee5ed152d10on branchclaude/review-2804-gate-benefits-px4evr(the harness pins pushes to this name). It starts at4b4d30df264e3951def2e597aed0aeca15f943aeand adds three commits. I opened no PR and pushed to no other branch, so no CI has run. The hub documents are the three comments above:PR_BODY.md,MERGE_2806.mdandAXIOM_ADDENDUM.md.Before starting. No push to this branch or #2279's in the last 6 hours, and no comment said anyone was mid-change. #2806's head moved from
a5d16626to4d363920at 20:15Z on 2 Nov (4.6 h before I started), but only by mergingmain; its three files this job touches are byte-identical at both heads. I treated #2806 as a read-only input and went ahead.mainhas moved to52b30809since the brief.Findings
P2-a (item 1).
test_legacy_award_readers_properties.py:690-692andtest_benefit_cap_specified_benefits_properties.py:34and:283-286carry the review's replacement comments.git grep -n -i "5(5)" -- policyengine_ukat the head. The UC reg. 5(5) hits, and what each concerns:uc_unearned_income.yaml:344: contributory ESA and JSA.qualifying_young_person_no_education_route.yaml:152andqualifying_young_person_reported_own_awards.yaml:2and:430: contributory awards, contribution-based JSA and the PC reg. 4A(5) twin.council_tax_reduction_young_person.yaml:407: contribution-based JSA.benefit_cap.yaml:158,benefit_cap_specified_benefits.yaml:1235,is_uc_benefit_cap_exempt_specified_benefit.py:113andtest_benefit_cap_specified_benefits_properties.py:421: the own-right flag.test_benefit_cap_specified_benefits_properties.py:34and:283: now name reg. 28 for income-related ESA and income-based JSA.is_qualifying_young_person_for_pension_credit.py:37: says reg. 5(5) doesn't list income support, which is right.:49,:88,:90and:120, and both UC variables: UC, contributory ESA and contribution-based JSA only.test_benefit_cap_specified_benefits_properties.pywithYEAR = 2026in a scratch copy fails 1 of 46:test_an_element_a_reform_gives_is_decided_by_its_value[uc_LCWRA_element]. That failure isn't the oracle's. 2026 is a rebalancing year, and the modifier sets the LCWRA element over the test's reform. The oracle-based properties passed at 2026, because no derandomised draw gives a young person an income-based JSA report where it decides the cap. The oracle is still wrong in principle at 2026, so I gaveown_benefits(:282) the paying-year filter: it readsjsa_income_reportedonly whilegov.dwp.JSA.income.activeholds. With the filter, the file passes 46/46 atYEAR = 2025and 45/46 at 2026, with the same unrelated LCWRA failure. I keptYEAR = 2025: 2026 adds the rebalancing failure and gains nothing.P2-b (item 2).
council_tax_reduction_young_person.yaml:411: the Case 11 household withwould_claim_council_tax_reduction: true, council tax £1,800, the applicant'semployment_income: 5_000, and the 19-year-old reporting £3,000 of income-related ESA in 2026.council_tax_reduction_applicable_incomecounts: £6,406.60.scotland.council_tax_reduction.means_test.withdrawal_rate): 0.2 × £1,438.00 = £287.60. Maximum support is 100% of £1,800, and there is no non-dependant deduction.c5b9850a3the household gives £1,305.44: the flag is false, so the applicable amount is the single under-25 75.65 × 52 = £3,933.80, and £1,800 − 0.2 × (£6,406.60 − £3,933.80) = £1,305.44. Case 13 fails there and passes at4b4d30df.P2-c (item 3). See
MERGE_2806.md.4d363920, Apply the Universal Credit education condition #2806's variable returnsis_qualifying_young_person_for_universal_credit, and its docstring carries the sentence the brief gives. I also dropped two clauses the change makes false, its opening "and have no entitlement…" and its last sentence ("…which leaves reg. 28 out."), and the two unused constants.paying_reports: benefits the model pays that year whose variables no reform neutralises. The file runs underwith_main_eligibility, so the neutralisation check reads the simulation's own system.uc_receiving_education.yaml: 3/35 fail before the change, 35/35 pass after.test_education_condition_invariantsfalsified in 2026 by a claimant reporting WTC £1,000.P3-a (item 4).
is_qualifying_young_person_for_universal_credit.py:47-81:paying(active, *names)is false when the benefit is closed or any of its variables is neutralised. The checks are:income_support.jsa_income.esa_income(new).housing_benefit(new).child_tax_creditortax_credits.working_tax_creditortax_credits.The year gates are unchanged. The couple sum reads
tax_credits_reported, which gates each credit by its own check (:64).New
test_uc_qualifying_young_person_neutralised_benefits.py(19 Python tests, no YAML)::87: 2024, one young person per benefit, under no reform and under each of the 7 neutralisations. The expected vector is exact, so it also checks that each reform leaves every other report's exclusion in place.:99: the same for a couple of 18-year-olds whose partner reports the joint CTC or WTC award.:129:child_tax_creditalone gives[True, False](CTC lifted, WTC not), andtax_creditsgives[True, True].:139: household G (own HB, 2026) goes from false / £5,098.80 to true / £9,321.36.:153: household D (own CTC £1,000, 2024) goes from false / £4,721.40 to true / £8,721.36 underchild_tax_creditand undertax_credits.Row mutants (one check dropped each; the run covers the new file and the PC test):
income_support[income_support], PC reform testjsa_income[jsa_income]esa_income[esa_income]housing_benefit[housing_benefit], household Gchild_tax_creditonlytax_creditsonlyworking_tax_creditonlytax_creditsonlyEvery mutant is killed.
Updated to say "only in a year the model pays that benefit, and only if no reform neutralises it":
AXIOM_ADDENDUM.mdleaves reforms out, as the brief says.P3-b (item 5).
member()takesreg_5_5_reports(:86), andqualifyingpassesFalse(:138).Two further changes, both judgment calls:
qualifyingdraws ages 16 to 18, not 16 to 19. A 19-year-old never met reg. 5 in the draws, because they set no entry or terminal-date input: 0 of 4,375.qualifyingreports at most one existing benefit, none one time in three (one_existing_benefit,:117). With only the first fix, M1 survived the exact property and died only on its YAML case. Six reports, each positive one time in three, left a qualifying person outside reg. 28 about 9% of the time. With at most one report, the predicate is true for 32–44% of slots, up from 11–19%.Over 10,000 derandomised draws for 2025, reg. 5 is met by the claimant in 59% of draws, the partner or dependant in 62%, and the dependants in 65% and 69%. Hypothesis's
one_ofdoesn't pick branches uniformly, so this isn't two in three. The docstring (:176-179) and the body's Tests bullet give these figures.Mutants after the change, each also run against the PC and neutralisation tests:
P3-c (item 6). The Scotland table reads "CTR before (#2279) / now" with no
dbaf71872column. Thedbaf71872Tests sentence is gone.P3-d (item 7). The docstring of
is_qualifying_young_person_under_universal_credit_regulation_5.py:18-19reads "reg. 6 of the Universal Credit Regulations (Northern Ireland) 2016 (SR 2016/216)", and the reference URL stays. The body's Northern Ireland line quotes reg. 6(5).P3-e (item 8). Case 6 in
AXIOM_ADDENDUM.mduses the review's sentence.P3-f (item 9). I applied each voice row:
:14-17,:19-21and:51;:35;:48-49;Merges (item 10).
git merge-tree --write-tree --name-onlyfrom the head is clean againstmainf4a31701(and the currentmain52b30809), #2279c5b9850a3and #28064d363920. I didn't mergemain.Tests (from
uv sync --frozen --extra dev)uc_qualifying_young_person_existing_benefits.yamlqualifying_young_person_reported_own_awards.yamlcouncil_tax_reduction_young_person.yamlcouncil_tax_reduction/)code_healthruff 0.17.2 format --check .(2,099 files already formatted),check .,check --select F811,F821 policyengine_ukandgit diff --check 4b4d30df HEAD: clean..github/check_fix_pr.pyon the final title and body, with changes from themainmerge base: PASS, "Changed executable tests: 15".4b4d30df): 9 of the 19 new tests fail: the[esa_income],[housing_benefit],[child_tax_credit]and[working_tax_credit]rows of:87, the[child_tax_credit]and[working_tax_credit]rows of:99, and:129,:139and:153. All three YAML files pass.c5b9850a3(with Cases 11 and 12: 3/13) and passes at4b4d30df.c5b9850a3the rest is:test_any_own…,test_zero_…andtest_the_predicate_is_reg_5_less_reg_28);income_supportreform, × North East and Scotland × 15 readers, each beforeuniversal_credit) gave 0 errors and 0 UC mismatches. The readers were the UC child/QYP flag,uc_child_index, four child-limit variables, both individual disabled-child elements,is_responsible_for_child_or_young_person_for_uc_or_housing_benefit,lha_renter_has_non_dependant, the LHA shared-rate renter,is_uc_benefit_cap_exempt, the CTR young person flag, the reg. 5 variable andcouncil_tax_reduction.[F,F],[F,F],[T,T],[T,F],[T,F].Skipped
pgrepwait) hit its background time limit and was stopped. It only waited for a probe whose output I had already read, so nothing was lost.Judgment calls
mainand Apply the Universal Credit education condition #2806 is read-only here.YEAR = 2025.qualifying. Without the second, the exact property stopped killing M1.MERGE_2806.md, besides the brief's sentence, I dropped two clauses of Apply the Universal Credit education condition #2806's docstring and two constants that the change makes false or unused.Generated by Claude Code