Repository navigation
Calculate Childcare Grant from eligible children’s costs - #2585
juaristi22 wants to merge 2 commits into
Conversation
Aggregate impact on certified data: passBase Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 0 (thresholds in Notes
Moves flagged for reviewNone. Spending by benefit and service
Caseloads
Standard reform costings (change in gov_balance)
Standard reforms: change in each reform's target
Household net income by decileNo household's net income changes in any year. No change in any year: receipts by tax head; government balance, each component counted once; model totals; household income totals; population; poverty rates; people in poverty; inequality; mean household net income in each side's own deciles. Checks failing on both base and head (1)
Counted-once checkEach of 84 components was raised by £1 per unit in 2026-27, and every head of its totals was read household by household: its own head must rise by exactly £1, no other tax head may move, no other benefit may rise, and the model's own total must move by what its heads moved. A head that breaks a rule is measured again twice: with every other head held at its baseline value, and with every other head held at its value after the first £1 while the component rises again. It fails where either still shows the response. 0 failed; 0 were skipped. Provenance
Dataset: Receipts by tax head: head levels
Spending by benefit and service: head levels
Caseloads: head levels
Government balance, each component counted once: head levels
Model totals: head levels
Household income totals: head levels
Population: head levels
Poverty rates: head levels
People in poverty: head levels
Inequality: head levels
Mean household net income in each side's own deciles: head levels
Standard reform costings (change in gov_balance): head levels
Standard reforms: change in each reform's target: head levels
|
vahid-ahmadi
left a comment
There was a problem hiding this comment.
@juaristi22 thanks, this is a clean fix for #1931. Moving the cost read onto eligible children matches Care to Learn and Tax-Free Childcare, the one-payment convention is sensible, and the new cases (adult costs, negative costs, member order, separate benefit units, final totals) are good. Two things on the amount side, both older than this PR, checked against the regulations and the SFE AY 2026/27 guidance.
Checked at head 6c19f4bd9
- 85% coverage and the £199.62 / £342.24 weekly maxima match SI 2011/1986 reg 45(5) (current text) and SFE Grants for Dependants AY 26/27 §5 (p.19). The AY 26/27 figures are unchanged, so the 2026 parameter values are right.
- Under-15 / under-17-with-SEN, the TFC / UC / WTC / NHS exclusions and the nanny exclusion match reg 45(2), (3), (3B) and (7A).
- Paying the family award to one parent matches SFE §5.15 (p.33): "where two students apply for CCG ... SLC request that all of the CCG is allocated to one parent", and the total equals two halves under reg 47(5).
- Local run in a fresh venv:
policyengine-core test .../dfe/childcare_grant51 passed;pytest tests/test_childcare_grant.py1 passed. CI is green at this head.Reproducer:,axiom: ... queuedandchangelog.d/1931.fixed.mdare present. No other open PR toucheschildcare_grant.
Findings
-
should: the one-child / two-plus split counts age-eligible children, not children in childcare.
childcare_grant.py:34-38picks the weekly cap fromchildcare_grant_eligible_children, which counts every child who meets the age rule, whether or not they have childcare costs. To be fair to the current code: reg 45(5) and reg 47(2)/(2C) are worded as "one dependent child" / "two or more dependent children", which on its own supports a plain count. SLC applies it to the children in the application, though. SFE AY 26/27 pp.49-51 reassesses to the one-child cap when a child stops needing childcare (the Travis and Miriam examples), and SLC sample-checks students "receiving up to £342.24 for only one child in childcare". GOV.UK also frames the income limits as "if you're applying for 1 child" / "2 or more children". Repro at this head: a student with a 3-year-old costing £20,000 and a 10-year-old with no childcare costs gets £17,000 (85% of £20,000 under the £342.24 cap). Under SLC practice it is £10,380.24 (52 x £199.62). The same count drives the income limit inchildcare_grant_eligible.py(£20,107.23 vs £28,914.47). This predates the PR, but it sits in the cap step this PR now applies after aggregation, and the fix is small: counteligible_child & (child_costs > 0)for both the cap and the income limit, and add one test with that household expecting 10,380.24. If you'd rather follow the literal dependent-child count, say so in the variable documentation and I'm fine with that. -
should: there is no income taper, and with this fix the grant now pays out at full value across the taper band. Reg 47(2A)/(2B) reduce the annual maximum pound for pound once dependants' income exceeds £9,727 (one child) or £11,118 (two or more), reaching nil above £20,107.23 / £28,914.47 (reg 47(2C)). SFE AY 26/27 pp.45-46 and 50-51 show the working (£15,000 income, one child: £10,380.24 - £5,273 = £5,107.24). The model only has the cliff at the upper limit. Repro at this head: one child, £12,000 costs,
childcare_grant_household_income£15,000 returns £10,200; the guidance gives £5,107.24. Two children at £10,000 each, same income: model £17,000, guidance £13,914.48. This predates the PR, but now that costs are read from the children, any household calculation between the two thresholds is overpaid, by up to about £10,380 for one child. The PR body says "Existing eligibility proxies, coverage rates, caps and annual 52-week treatment remain in place", so I'm happy for this to be a linked follow-up issue rather than part of this PR. Please open and link it before merge so it doesn't get lost.
Not for this PR (for the follow-up issue, no action here)
- The legal income measure is the residual income of the partner and any adult dependants plus the children's net income (reg 47(2A)-(2C)). The proxy,
maintenance_loan_household_income, is benefit-unit net income, which includes the student's own income. - Parameters change on 1 January, but the AY runs from 1 September (nanny exclusion from 1 August 2026, per GOV.UK). With a calendar-year period, 2026 applies the nanny exclusion to January to July 2026 too.
- Reg 47(2C) says nil where income "exceeds" £20,107.23; the model's
<gives nil at exactly that figure. The legal amount there is 1p, so this changes nothing in practice. - Aggregate impact shows Childcare Grant at £0 on both sides. That is expected, because
childcare_grant_full_time_studentdefaults to False and the FRS has no input for it, so this fix only shows up in household calculations for now.
Reviewed against SI 2011/1986 regs 45 and 47 (legislation.gov.uk, current text), GOV.UK Childcare Grant pages and SFE Grants for Dependants AY 2026/27 v3.0.
🤖 Generated with Claude Code
vahid-ahmadi
left a comment
There was a problem hiding this comment.
@juaristi22 thanks, second round at 6763caa6a. The delta since my review (git diff 6c19f4bd9..6763caa6a) is four lines of the childcare_grant documentation string, with no change to executable code, so my round-1 test run at 6c19f4bd9 (51 YAML cases and the pytest file) still holds.
Earlier findings
- should, cap and income limit count age-eligible children rather than children in childcare: resolved. I offered documenting the literal dependent-child count as an alternative, and
childcare_grant.py:13-16now does that, naming the SLC application-based practice. #2604 also records it as a separate practice question, including the guidance's temporary zero-charge exception, which is a fair point against a plain positive-cost test. - should, no income taper: resolved. #2604 is open and linked from the PR body. It covers the reg 47(2A) to (2C) £1-for-£1 taper with the £9,727 / £11,118 lower and £20,107.23 / £28,914.47 upper thresholds, the residual-income measure, the academic-year and nanny-exclusion dates and the upper-limit boundary, with the SFE p.51 controls.
No new findings. The test jobs at this head were cancelled by the runner rather than failing; I re-ran the workflow and it passed. Approving.
🤖 Generated with Claude Code
Fixes #1931
Calculate Childcare Grant from the nonnegative childcare expenses recorded on eligible dependent children in the benefit unit. Apply the existing coverage rate and one-/two-plus-child family cap after aggregation. For an eligible student with one child's annual costs of £5,000, the model now returns £4,250; the previous formula read zero costs on the student and returned £0.
Pay the family award once, allocating it to the eldest eligible student parent. This is a model allocation convention: actual eligible student partners can share the award under SI 2011/1986 regulation 47(5) and SFE guidance §5.15. Variable documentation and program metadata describe that convention. Existing eligibility proxies, coverage rates, caps and annual 52-week treatment remain in place.
The cap and income-limit child count retains every age-eligible dependent child in the benefit unit, including children with no childcare expenses. This preserves the existing dependent-child interpretation; SLC guidance describes an application-based count of children needing childcare, which the model does not distinguish.
The linked follow-up tracks the missing income taper, the residual-income measure, academic-year/effective-date treatment and the upper-limit boundary. It records the child-count practice question separately, including the guidance's temporary zero-charge exception. These are existing limitations outside the child-cost aggregation fix.
The new regressions cover adult/ineligible-child costs, negative child costs, two eligible parents, asymmetric parent eligibility, member ordering, separate benefit units and the final household benefit/spending totals. Four existing positive grant cases now put their costs on the children and retain their expected awards.
Reproducer: policyengine_uk/tests/policy/baseline/gov/dfe/childcare_grant/childcare_grant.yaml
Validation: the original regressions first reproduced the defect (12 failed, 10 passed); after the formula fix, one focused batch passed 55 items (51 YAML, 4 Python), with 0 failures or skips. Those results are retained for the subsequent documentation-only review follow-up: the module AST excluding the documentation attribute is unchanged, and no model tests were rerun for those four lines. One subsequent whole-repository
make formatpassed (1,943 files unchanged; Ruff checks pass). Full/population suites and documentation build were not run locally; upstream CI will run its applicable checks. No private dataset or encoder run was used.axiom: TheAxiomFoundation/rulespec-uk#365 queued
The existing parity queue was extended with exact reg45(5)/regulation 47(5) text, module/corpus targets and independently sourced companion cases. Corpus PR #765 remains unmerged and the provision endpoint unavailable; a signed encoder run also requires separate approval.