Repository navigation
UK: childcare take-up at the spring term, under-2 take-up and single-age targets behind the £100k childcare costing (policyengine-uk 2.147.2) - #1338
Conversation
|
@juaristi22 thanks, this is my review at Verdict. The six fixes are well aimed and the nesting fix (item 1) is clearly right. But this head should not be built as the release yet. The contract still carries the tool version 4 rates while the code now reads the universal rate as a conditional rate (finding 1). The pin pass is also still open (finding 2). Separately, the take-up that is supposed to fix the £100k cell is not conditioned on income anywhere in this diff. DfE and CenTax both point to take-up for newly eligible families being higher than our contract rate, not lower (finding 3). So I would not expect this PR to close the gap to CenTax through take-up. The gap more likely sits in who is eligible and what each child costs. Findings 1 and 2 must be fixed before the build. Everything else can follow. What I ran (and what I didn't)
Findings
Expected effect on the 30-hours static costThis can only be read as a direction, not a size:
On balance I'd expect a fall of the order of £0.1-0.2bn, partly offset by the refit. I would not expect the result to reach CenTax's £0.80bn unless reception children are also removed from the reform cost. What looks right
🤖 Generated with Claude Code |
|
@juaristi22 thanks, this is my second review, at Verdict. Both musts from round 1 are fixed: the four rates are refitted at tool version 5, and the pin pass is done (tests and CI green). I have no must this round. The branch merges cleanly with main, and on the a858c100 build it does what it set out to do: on CenTax's basis the dashboard now lands at £0.87bn / £0.96bn against CenTax's £0.80bn / £0.98bn. Two things should be settled before this becomes the promoted release, though neither blocks the merge. First, the universal rate now sits at its lower bound of 0, so universal take-up is exactly the extended draw. The fit is pushed there by a row that has the same 31 December age problem you used to sign out the age-4 cells (finding A). Second, a858c100 was built without #1332, which has since merged, and #1344 changes who is on UC. So the v5 rates and the receipts will need one more refit once the merge order is fixed (finding B). Round 1 findings at this head
What I got wrong in round 1. I expected the fixes to cut the 30-hours cost by about £0.1-0.2bn. As shipped it went from £1.01bn to £0.96bn, a fall of only £0.05bn, though the engine also moved from 2.132.6 to 2.134.4. The refit raised the extended rate from 0.66 to 0.76, and that offset more of the nesting gain than I allowed. My point that the remaining gap was reception, not take-up, did hold. New findingsA. should: the universal fit is at a corner, driven by a row with the age-4 reference-date problem.
B. should: refit once the merge order is fixed.
C. should: Tax-Free Childcare take-up near 1. The v5 rate is 0.9912. Weighted, it is 0.998 in the over-£100k cell. The two HMRC rows still cannot both be met: children with used accounts come out 11% high, top-up spend 14% low. So the rate is set by the spend row, not by an observed take-up. I've raised the effect on the reform's Tax-Free Childcare leg on the dashboard PR. Here I'd only ask that the receipt say plainly that the rate is not a take-up estimate for newly eligible families. D. nit: the DfE single-age values. The four rows read the pinned feed, which I could not open, so I have not checked their values against DfE's January 2025 tables. The definitions and filters look right: reception left out from age 2, no primary filter at age 1, universal age 3 counting working-parent children. What I ran (and what I didn't)
What looks right
🤖 Generated with Claude Code |
Item 6 of the £100k childcare costing fixes (#1278, #1095). With ages 0, 1 and 2 bound, calibration still moved the single years inside the 0-4 and 5-9 sex bands: on the pre-Budget combined build the UK held too many 5-year-olds and too few 4-year-olds, and England's 5-year-olds sat about 15% above ONS. England's funded childcare is costed on England's children, so the contract now binds eleven more all-persons rows: - ons.population.age_3 .. age_6, pinned to K02000001 like ages 0-2; - ons.population.england_age_0 .. england_age_6, pinned to E92000001 by the generator's nation substring rule, counting persons in households with country == ENGLAND. Each reads the ONS mid-2025 estimate by single year of age (MYE2 persons), the series the sex bands and the region 0-9 cells sum, and counts age >= n and age < n + 1. The nested rows are consistent: the UK rows for ages 0-4 sum to the two 0-4 sex bands, and England's rows sit inside its nine region 0-9 cells. Every band stays bound. Wales, Scotland and Northern Ireland get no single-year rows, because each nation's single year rests on too few FRS households; the UK rows bind their sum, and Scotland's age 0 keeps scotland_babies_under_1. Registry accounting: eleven contract-only declarations. The contract has 432 targets (421), the registry scope 373 (362) and the unmapped declarations 187 (176); the mapped targets stay at 186. The compile-parity tool gains the rows' ledger-only rationale. The references and membership are regenerated in the next commit, which takes the active references from 1,322 to 1,333. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frs_take_up drew would_claim_universal_childcare and would_claim_extended_childcare independently. policyengine-uk allocates a working-parent child's first 15 hours to the universal entitlement when the unit's own universal flag is false, so a family over the £100k limit drawn extended-yes and universal-no gained universal hours only once the limit was removed. The extended draw is unchanged; the universal flag is now extended OR u < r, where r, the contract's universal_childcare rate, is the rate among units not claiming the extended hours. The operation declares nested_under (added to the assign_binary_from_rate schema) and the stage refuses a manifest that drops it. The contract entry carries nested_under and says the rate is conditional; its value is unchanged until the fitter refits it. The take-up signal gate targets the nested marginal and fails on any unit claiming the extended hours without the universal ones. The flags were drawn before any support or CGT copy existed, so every copy inherited its host's draws, and a cell with few hosts fixed the take-up of all its copies. A new last spine stage, childcare_copy_take_up, re-draws would_claim_tfc, the extended, universal (nested) and targeted flags and maximum_extended_childcare_hours_usage for copy benefit units only (a non-empty household clone path or a positive benunit support clone index), through frs_take_up's derive_childcare_take_up at the same rates, seed and salts, keyed on the copy's own benunit_id (source id plus a multiple of an offset above every id, so unique and distinct from every native id). Native units keep their draws; the anchored flags and would_claim_uc_childcare are untouched. The graph declares an open surface and an isolation boundary for it, as for the other take-up redraws; the kernel tables, spine registration, fixture tool, roster pins (46 to 47 stages) and docs move with it. The coverage manifest, compile-parity receipts and the H2 fixture are left for the pin pass to regenerate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… rows The childcare fitter (tool version 5) fits universal_childcare on the nested basis frs_take_up now draws: the universal-only row's expectation is r_e * sum(with extended) + (1 - r_e) * r_u * sum(without extended), from the existing forced-flag sides, with no allocated term; the targeted row keeps its allocation side. The diagnostic draw helper nests the universal flag the same way. DfE's January registrations leave out children in reception, but the working-parent 2-to-4 and universal-only rows counted them. The fitter's basis and both calibration bindings (concept and policyengine filters) now leave out a child whose current_education is PRIMARY, with the existing != predicate operator. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
childcare_copy_take_up ran after private_school_attendance, which broke test_the_stage_is_the_last_of_the_spine and left the stored attendance imputed from the copies' inherited childcare flags: policyengine-uk's attends_private_school ranks households on net income, and household_benefits counts Tax-Free Childcare and the extended, universal and targeted entitlements. The re-draw now runs after student_loans, still after every copy stage, and private_school_attendance is the last spine stage again. The stage-order tests, the acceptance-receipt roster helper, the stage notes and the changelog follow. The fitter's basis docstring no longer describes a universal allocated term, which tool version 5 dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
María's ruling (2026-10-21): bind every DfE funded-childcare count the model can carry, even on few records, and sign out only those with a real reason. The contract now binds seven England rows from the January 2025 census (Funded early education and childcare, headline figures): - dfe.funded_childcare.working_parent_children_age_1 .. age_4: working parent registrations, is_child_receiving_extended_childcare at whole-year age n, outside primary education from age 2 (DfE leaves reception out); - universal_nursery_children_age_3 and age_4: universal registrations in nursery, is_child_receiving_universal_childcare at age n outside primary education, working parent children included as in DfE's counts; - universal_reception_children_age_4: every 4-year-old in primary education, no take-up flag, so the share of 4-year-olds in school is pinned. The working parent 2 to 4 row stays bound beside the singles: it is the take-up fitter's anchor, and its fact and measure are the sum of ages 2 to 4, so the rows are nested and consistent like the ONS single years inside the sex bands. universal_only_children stays too: on the facts it is the nursery ages 3 and 4 less the working parent ages 3 and 4. The accounting note dfe_funded_childcare_single_ages signs out the rest of the table with its reason: 9 to 11 months (policyengine-uk gives whole-year age 0 no working parent hours), reception at age 3 (no FRS 3-year-old is in primary education), the totals and multi-age cells that sum or copy bound cells, the universal all-provision cells (nursery plus reception), DfE's eligible-children and all-children estimates (built from population and survey-based inputs) and the registered percentages. Registry accounting: seven contract-only declarations. The contract has 439 targets (432), the registry scope 380 (373) and the unmapped declarations 194 (187); the mapped targets stay at 186. A new signed in_year_snapshot hold covers the seven rows, and the compile-parity tool gains their ledger-only rationale. The references and membership are regenerated in the next commit (1,333 to 1,340 active references). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on 5) The fitter ran on a local spine built at 13307a6 (nested universal take-up, reception-age children out of the DfE rows, copies re-drawing their childcare draws; items 3 and 4 reverted), feed abf0921, model period 2024, the DfE 2-4 anchor at its 2025 vintage. Extended hours 0.7594 (0.6579 before), targeted 0.8478, Tax-Free Childcare 0.9912. The conditional universal rate stops at its lower bound of 0, so universal take-up is fully nested: the nested floor already exceeds DfE's universal-only count, because the model's interview-date ages hold more 4-year-olds outside reception than DfE's 31 December ages. Per the U2c ruling the bound is kept and the receipt records the residuals (universal-only 1.036, working parent 2-4 0.961); the national calibration binds the DfE rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…del's interview ages María's ruling (2026-10-21): the three age-4 rows added with the DfE single ages are signed out, not bound: dfe.funded_childcare.working_parent_children_age_4, universal_nursery_children_age_4 and universal_reception_children_age_4. DfE's January census reports ages as at 31 December, when about two-thirds of funded 4-year-olds are already in reception, while the model's whole-year age is the age at interview spread over the survey year, so fewer of its 4-year-olds are in primary education. Binding the single age-4 cells would move weight between 4-year-olds in school and in nursery because of the reference date, not the data. The working parent 2 to 4 and universal-only rows stay bound and are unaffected. Working parent ages 1, 2 and 3 and universal nursery age 3 stay bound. The three rows leave the targets, scope_target_ids and unmapped declarations; the accounting note dfe_funded_childcare_single_ages lists them as signed-out cells with the reason above, and the kept rows' declarations and binding notes no longer describe age-4 rows. The signed in_year_snapshot hold María confirmed (approved_by juaristi22, approved_on 2026-10-21) now covers the four kept rows, and the compile-parity tool's ledger-only rationale names the four, without the claim that they nest in the 2 to 4 and universal-only rows. Registry accounting: the contract has 436 targets (439), the registry scope 377 (380) and the unmapped declarations 191 (194); the mapped targets stay at 186. The references and membership were regenerated once with tools/generate_uk_target_references.py against the same national copy of the pinned feed abf0921 (source_fact_feed chronicle-uk-abf0921-consumer-facts) as the previous regeneration: the three references and their membership, geography-pin and uprating-hold entries are gone, every other reference is byte-identical, and active references go from 1,340 to 1,337. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fixture tool pinned 46 stages; childcare_copy_take_up makes 47. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A nested entry's rate is conditional on not holding the flag it is nested under. The childcare fitter fits that rate from tool version 5; an earlier receipt fitted the marginal rate. The loader now refuses a nested entry whose receipt is older (Vahid's round-2 item 1 on #1338). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UK lock moves from 2.145.0 to 2.147.2 (policyengine-core stays at 3.33.3). 2.147.2 stops the early years hours at school start by date of birth and starts working-parent hours the term after the minimum age (PolicyEngine/policyengine-uk#2570). The uprating pins' stamp, the concept coverage (311 to 314 inputs), the coverage manifest, the cited versions and APPROVED_UV_LOCK_SHA256 re-derive. The liquid-asset reason follows 2.147.0's share_like_capital; the three new inputs stay at their defaults. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DfE counts the children registered for each funded entitlement in its January census, by age on 31 December. policyengine-uk's entitlements are annual, and since 2.147.2 they stop at school start by date of birth and start working-parent hours the term after the minimum age. Four person measures (dfe_spring_term.py) classify each child at the spring term of the model year: the DfE age, and spring-term receipt of working-parent hours, universal hours and early learning for 2-year-olds. The universal and 2-year-old measures keep the engine's family and claim conditions but count every child of the DfE age, as the term after the birthday does; the engine still gates them on the age at 6 October. The resolver routes the measures, refuses them as persisted columns, and an engine test checks them on hand-built households (Vahid's round-2 finding A on #1338). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The engine allocates the universal and targeted hours from a child's gross working-parent hours (extended_childcare_entitlement_total_per_child), not from the net flag, so the claim condition now reads the gross hours. DfE records each child under one entitlement: at DfE age 2 a child counted under early learning for 2-year-olds is not counted again under working parents, and at 3 and 4 a working-parent child is one that uses more than the universal 15 hours, as the engine nets them. The engine tests add families that claim only working-parent hours, the DfE-age-2 corners, the date boundaries, a child outside England and one placed by age alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…re (tool version 6) The fitter measures the DfE rows through dfe_spring_term_status at model year 2025, whose spring term is the one DfE's January census counts, and no longer reads current_education. It fits a fifth rate, the share of children under 2 who use the hours their family's working-parent claim gives them, against the working parent rows at 9 to 11 months and age 1: each side's contributions from children under 2 are scaled by it, so every row stays linear in the rates. Working parent ages 2 and 3 and universal nursery age 3 are reported beside the fit, not fitted. The receipt records the expectation basis, what each rate estimates (the Tax-Free Childcare rate is a fit to HMRC's two rows, not a take-up estimate for newly eligible families: Vahid's round-2 finding C), and which rates sit at a bound. The fitter refuses a spine without stored birth months. The contract refuses a version-6 receipt without its estimand, or one at its own bound without a reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…onths Every DfE row now reads the spring-term measures (dfe_spring_term.py) instead of whole-year age and current_education (María's ruling of 2026-10-22 on Vahid's round-2 finding A). The working parent 9 to 11 months row is bound, with its own in_year_snapshot hold approved by María on 2026-10-22; the age-4 cells stay signed out, as the bound 2 to 4 and universal-only rows fix them. The accounting note states the basis, the cohort offset of the January 2025 census, the under-2 take-up and each sign-out's reason; the ONS single-year note names the mid-2025 release date (Vahid's item 8). The target references and membership regenerate against the pinned abf0921 feed: 1,338 active references, the 16 rows this PR adds and no other change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two more tests pin the active reference count, which the 9 to 11 months row takes from 1,337 to 1,338. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DfE registers far fewer of the youngest eligible children than of those aged 2 to 4, so one family rate fitted at 2 to 4 funded too many of them (María's ruling of 2026-10-22: "bind them with their own take up"). frs_take_up now draws, for each child of whole-year age 0 or 1 in a unit claiming the working-parent hours, whether it uses them, at the contract's extended_childcare_under_2_usage rate (provisional 0.75 until the v6 refit); a non-user gets max_free_entitlement_hours_used 0 and every other person the engine's default of 30. childcare_copy_take_up re-draws the copies' children on their own person ids against their own unit's flag. The stage manifests, graph cells, export allow-lists, signal gate (share over claiming children under 2), identity receipt and coverage manifest follow. The fitter scales the under-2 share only on the claiming-world sides. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ce changes The take-up signal gate's note now names the under-2 usage population, and the export surface allows person.max_free_entitlement_hours_used, so the gate policy, gates manifest, spec fingerprint and the release_cut part's digests move; contract.py and its test mirror follow (test_gate_battery_contract_pins recomputes them). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Calibration moves weight toward the units the DfE and HMRC rows need, so the take-up a costing uses can differ from the contract rate the spine drew at (Vahid's round-2 review of #1338, item 3). The new engine-free tool reads a built H5's frame and final weights and writes, for each childcare flag, the contract rate beside the realised unweighted and weighted shares over units with a child under 5 (all, natives and copies, and a pay-over-£100k cell), and the under-2 usage share over claiming units' children under 2. Cells under 10 source households read "<10". The receipt belongs in the run tree, never in the take-up contract. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first v6 refit, with one under-2 usage share, left the working parent rows at 9 to 11 months 16% over DfE and at age 1 24% under: DfE registers 44% of eligible 9 to 11 month olds against 69% at age 1, so one share cannot carry both. The draw now uses the rate of the child's whole-year age (extended_childcare_age_0_usage, extended_childcare_age_1_usage; provisional 0.51 and 0.81 until the refit), declared as one operation per age; the fitter fits one share per age, each scaling its own age's contributions. The take-up gate targets the children's weighted rate; its note, the gate digests, the coverage manifest, the realised-take-up report and the tests follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion 6) The fitter ran at model year 2025 on a local spine built from this branch (6aea1ec; the fit overrides the take-up columns, the only ones the later commits change), policyengine-uk 2.147.2, feed abf0921. Tax-Free Childcare 0.9697, working parent 0.8361, targeted 0.6547, universal 0 (at its lower bound: the claiming families alone exceed DfE's universal-only count), age-0 usage 0.698 and age-1 usage 1 (at its upper bound: the frame's 1-year-olds fall short of DfE's count at the 2-to-4 rate). The fitted rows are within 2% of DfE apart from universal-only (1.065), age 1 (0.894) and HMRC's two TFC rows (1.064 and 0.927), which calibration binds. Each receipt records its estimand and, at a bound, its reason; every row it carries rests on at least 10 FRS source households. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
policyengine-uk 2.146.1 applies Carer Support Payment's earnings limit (PolicyEngine/policyengine-uk#2473), so the Carer's Allowance take-up stage's reason for leaving Scotland closed (the engine did not apply the limit) no longer holds. The stage still leaves Scotland closed, as before; its text says the engine applies the limit and that opening Scotland is a follow-up (microcosm#1319). The engine test now pins the limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stage The spine driver tests build a synthetic frs_take_up stage; it now declares the two per-age max_free_entitlement_hours_used operations the stage requires, and the person table is checked for the column. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e fixture (pin pass) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3bb28f8 to
9427392
Compare
|
@vahid-ahmadi Thanks for round 2. Everything is addressed on the rebuilt branch. It's on main 8b0224e and re-locked to policyengine-uk 2.147.2, which carries PolicyEngine/policyengine-uk#2570 (the issue you raised on #8 about reception and 9-11-month-olds). Could you review once more? The body has the full list; point by point: A. The universal fit at a corner (the 31 December basis). The DfE rows are now measured at the spring term.
B. Merge order. María ruled that #1338 lands first. #1344 then rebases onto it and re-pins to 2.147.2, and its session has agreed. The refit and pin pass are done on this head, so #1332 is in the input and #1344 refits after. C. Tax-Free Childcare. The fitter's receipt now records an estimand for every rate. For Tax-Free Childcare it says the rate reproduces HMRC's two rows, trading one against the other, and is not a take-up estimate for newly eligible families. Round-1 items:
D. DfE single-age values. All bound values match DfE's published January 2025 figures exactly. The nursery cells come from the 2026 release's 2025 row; the 2025 release has no nursery column, and the four-child difference is hospital-school children with no year group. New since round 2:
The signed national build from this head is next. I'll post the gates, the fit and the costing here and on #8 when it lands. 🤖 Generated with Claude Code |
The FRS spine has written War Widow's Pension to its own input since #1222, but the enhanced FRS still adds FRS benefit code 9 into bsp_reported, so the column is outside the reference surface. Without an allow-list entry, release-cut certification blocked on it (uk_export_surface). The entry goes in the allow-list tuple and its gates.json mirror; the battery and release-cut gate digests are re-pinned (the spine and calibration-seam digests do not move). A new engine-free test runs the export-surface gate on the spine's declared columns through the production export path, so the next such column fails CI rather than certification. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@juaristi22 thanks, this is my third review, at Verdict. Every round-2 finding is dealt with, and I have no must. Moving the DfE rows to the spring term and the engine's date-of-birth school start was the right fix, and the rows now match DfE's January 2025 figures. CI is green at this head. One thing should change before the signed build, because it is cheap now and costs a refit later. The new under-2 usage draw is keyed on the child's age on 6 October, but the rows it is fitted to use DfE's age on 31 December. So the two rates mix age groups, and that is most of why the age-1 rate sits at its bound (finding E). The other points are for the receipt and for the dashboard rerun. Earlier findings at this headRound 1:
Round 2:
New findingsE. should, before the signed build: key the under-2 usage draw on DfE age, not on age at 6 October.
F. should: binding January 2025 sets where the dashboard lands, so the receipt should say so.
G. nit: why the universal rate is at 0. Universal is drawn as extended OR u < r, and the extended flag is drawn for every benefit unit, including families who can never get working-parent hours. So universal take-up cannot fall below 0.836 for any family. The universal-only row is then fixed only through weights.
H. should: put the eligible pool by DfE age in the receipt. The question from my round 1 is still open, and now there is a number for it. At a family rate of 0.836, with age-1 usage at 1, the frame's working-parent children at DfE age 1 (174k before calibration) imply roughly 225k eligible children. DfE's 195,100 at 70% implies about 279k. DfE says its eligible estimates need caution. But a pool about a fifth smaller, which calibration then makes up through weights, is worth seeing in the receipt: the eligible count by DfE age, unweighted source households and weighted. I. nit: DfE's under-2 take-up percentages. The PR body says DfE registers 44% of eligible 9-11-month-olds and 69% at age 1. The January 2025 release gives 42% and 70%. If the body's figures come from the January 2026 release, please say so, since the rows are January 2025. J. should, until pe-uk #2588 lands: the universal and 2-year-old measures count children the engine does not fund. The measures count every child of the DfE age in the spring term. The engine still gates these entitlements on age at 6 October, so it pays nothing for children born from 7 October to 31 December. Calibration will hit DfE's count partly with children who cost nothing in the model, so universal and targeted spend will come out a little low until the engine follows. Please name this in the receipt beside the rows it affects. How this interacts with policyengine-uk #2570
ReleaseThere is no new release since a858c100. The dataset repo has two staged calibration attempts from 23 October, but nothing has been published, so I have no new aggregates. What I ran (and what I didn't)
What looks right
🤖 Generated with Claude Code |
…ersion 7) The DfE rows count children by their age on 31 December, but the under-2 usage draw picked each child's rate by its whole age on 6 October, so the age-0 rate blended 9-to-11-month-olds with 12-to-14-month-olds and some 2-year-olds took the age-1 rate (Vahid's round-3 finding E). dfe_spring_term.dfe_age_at_31_december restates policyengine-uk's date of birth (birth_instant_from_age, grid_months_to_yyyymmdd) so the engine-free stages key on the same age; an engine test holds it to the engine at 2024-2026. frs_take_up (and so the copies' re-draw, the take-up signal gate and the realised-take-up report) draws each child of DfE age 0 or 1 at its age's rate, and frs_take_up now reads months_since_last_birthday. The fitter (tool version 7) splits the under-2 contributions by the same age. Refit on the 6aea1ec fit spine: age-0 usage 0.6962, age 1 still at its bound, working parent 0.8262, early learning for 2-year-olds 0.6551; the 9-to-11-months row now fits exactly and the age-1 row's shortfall falls from 0.894 to 0.943. The coverage manifest and H2 fixture are regenerated; the gate pins do not move. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@vahid-ahmadi Thanks for round 3. E is fixed on this branch (a12885f), and the build results below answer F–J. The national file from 829c1bc is certified and published to the inspect lane: cut tag E. The under-2 draw is keyed on DfE age.
F. Census year. Agreed: binding January 2025 sets where the file lands. The table under G shows the gap to January 2026 for every row, from the January 2026 facts already in the pinned feed. Moving the binding rows to January 2026 is a follow-up; until then this table is the comparison. G. Realised rows by DfE age after calibration (certified file, England, final weights, spring term of 2025-26; nearest 100):
H. Working-parent eligible pool by DfE age (every family claiming, before school in the spring term, England, final weights; nearest 1,000):
I. Fixed in the body. The January 2025 release gives 42% at 9 to 11 months and 70% at age 1. The 44% and 69% I quoted are the 2026 release's revision of January 2025. J. Named in the body, beside the universal and 2-year-old rows. Until PolicyEngine/policyengine-uk#2588 lands, those measures count children born from 7 October to 31 December, whom the engine does not fund that year. So calibration partly meets DfE's counts with children who cost nothing in the model, and universal and targeted spend come out a little low. The build (829c1bc):
Other points.
Could you take one more look at the E commit? The dashboard rerun on the published file goes on #8. 🤖 Generated with Claude Code |
Main's 47 stages (with #1338's childcare_copy_take_up) plus uc_take_up and frs_annual_income_spells. The gate-battery pins are regenerated by the pin pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main gained childcare_copy_take_up with #1338, so uc_take_up makes 48 stages and frs_annual_income_spells 49. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gates.json moves with the uc_take_up and frs_annual_income_spells stage gates, UC leaving the take-up signal gate, and the signal gate's note (under-2 usage now over children of DfE age 0 or 1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in after #1338 (pin pass) The national and local compile-parity receipts were regenerated on the #1344 head rebased onto main 65e5eae after #1338, on policyengine-uk 2.147.2, and do not move; the release input coverage manifest and the charter-H2 spine fixture do. The feed stays abf0921. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fragment's figures came from the fixes-2-and-3 arm on 2.134.4. They now compare #1338's certified build with this branch's arm, both on policyengine-uk 2.147.2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The changelog fragment's 2025 income tax and costing figures come from the e399d21 arm, signed with a throwaway key, against #1338's certified build. Say so, and say that current income lands below OBR's receipts on this build rather than by construction (Vahid's items 25 and 26 on #1344). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Microcosm fixes from reconciling PolicyEngine's static cost of removing the £100,000 limit on England's working-parent hours against CenTax's (Removing the childcare cliff edge, September 2026), and from Vahid's two review rounds. The dashboard is PolicyEngine/uk-childcare-100k-threshold#8. Part of #1339.
Rebuilt on main 8b0224e (after #1333) and re-locked to policyengine-uk 2.147.2. This lands before #1344, which rebases onto it (María's ruling of 2026-10-22 on Vahid's finding B). The earlier history is replaced: the reverted SPI items 3 and 4 and their reverts are dropped, and the pin passes are regenerated once at the end.
What changes
frs_take_up).would_claim_universal_childcare= extended OR u < r, where r is the rate among units not claiming the extended hours.childcare_copy_take_up, a new stage after every copy stage). SPI, high-value-home and CGT copies re-draw the childcare flags and hours on their own benefit units instead of inheriting their host's.uk_runtime/dfe_spring_term.py, routed through the measure resolver).current_education.max_free_entitlement_hours_used0; everyone else keeps the engine's 30.dfe_spring_term.dfe_age_at_31_decemberrestates the engine's date of birth so the build stages, which run without the engine, key on it; an engine test holds it to policyengine-uk.tools/report_uk_childcare_realised_take_up.py. It gives each flag's contract rate beside its realised unweighted and weighted share at a built file's final weights. Cells under 10 source households read "<10".The refit (tool version 7, local spine at 6aea1ec)
The national calibration binds every row; the receipt records each residual.
Build
Built at 829c1bc (before the DfE-age keying of item 7, which the next build from main carries) on the signed 9427392 spine, certified on 23 October and published to the inspect lane as
microcosm-uk-2024-25-national-20261023T075039Z-a133f37a; not promoted to latest.uk_export_surface: main'swar_widows_pension_reported(Split War Widow's Pension (FRS code 9) out of bsp_reported #1222) had no allow-list entry. 829c1bc adds it, and a new engine-free test runs that gate on the spine's declared columns.For review
Not in this PR
uk-cc-spiprior) and the SPI pay floor (uk-cc-spipay), each for its own PR.Tests
tools/ci_test_plan.py verifyis ok.Disclosure control: the figures here are weighted national aggregates and public DfE, ONS and HMRC figures. Every fitted or diagnostic row rests on at least 10 FRS source households, and nothing rests on unit records.
🤖 Generated with Claude Code