Skip to content

Port the post-freeze childcare / Tax-Free Childcare inputs and targets, re-pin the Chronicle feed, and activate the DfT bus targets (#834, #789) - #874

Merged
juaristi22 merged 11 commits into
mainfrom
uk-childcare-tfc-834
Sep 7, 2026
Merged

Port the post-freeze childcare / Tax-Free Childcare inputs and targets, re-pin the Chronicle feed, and activate the DfT bus targets (#834, #789)#874
juaristi22 merged 11 commits into
mainfrom
uk-childcare-tfc-834

Conversation

@juaristi22

@juaristi22 juaristi22 commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Closes #834 and closes #789 (WS-E of #145, epic #665). Plan: repos/uk-834-childcare-tfc-port-plan.md (approved 2026-09-04, re-based on main 96faa5d5); Codex implemented increments I0–I2 under the approved-plan protocol (deviations listed below), followed by the licensed lane (L0–L5) whose receipts are in experiments/834-childcare-tfc-receipts.md. Rebased onto main after #849 merged (2026-09-07): the branch is this PR's own commits on 396df96f, the diff is #834's own. Vahid's review pass at 8de6bf44 is addressed below. Draft — María reviews everything at the end; never merge without her go.

What the issue asked for, re-measured against main

Item State on main This PR
tax_free_childcare_spend_routed_share (uk-data #472) Absent; policyengine-uk ≥2.93 reads it and defaults to 1.0 (the un-routed spend the June cut shipped). Generic assign_period_constant op in frs_person_draws, reading a new stochastic entry of take_up_contract.json at the build year (2024-04-01 → 0.593; series 2017–2025 from HMRC's account-activity ODS, Chronicle package pinned by sha). Engine floor ≥2.93, lock 2.94.0.
Five childcare targets (uk-data #473/#474, pe-uk #1830/#1834) None on the surface. hmrc.tfc.government_top_up, hmrc.tfc.children_with_used_accounts, dfe.funded_childcare.working_parent_children_2_to_4, dfe.funded_childcare.early_learning_2_year_olds, dfe.funded_childcare.universal_only_children — declared once, resolved by period (Part E): the issue's five numbers at 2024, the FY2025-26 / Jan-2025 counterparts at the 2025 calibration year.
Take-up rates re-fit (uk-data #473) June rates. L5: re-fit at the 2024 base on the L2 column twin with the repaired fitter (A6). Fitted on the L2 column twin with an expected-count objective (see Deviations): extended 0.812 → 0.6054, universal 0.563 → 0.4539 landed; TFC (0.997, at its ceiling: with spend routed at 0.593 the two HMRC rows conflict) and targeted (1.0, a ~157-child eligible base) held at 0.88 / 0.597 for María's ruling (plan A22/A23).
DfT bus targets (#789, Chronicle #202/#231) Facts absent from the pinned feed. dft.bus_fare_receipts.england and dft.bus_net_support.england active at household grain; London / outside-London / UK rows declared with model-side filters and signed-excluded until the regional measure lands (target_reference_signed_exclusions.json). DfT labels its reporting year by the March end year, so its fiscal-year facts are compared on their own coverage start year: the label-2025 fact is FY2024-25 and binds at 2025 as an uprating hold from 2024, like every other lagged fact on the surface (review finding 1).
Chronicle feed pin 226358e7 feed (v1 schema); 12 references broken on the current feed. Re-pinned to chronicle main 6fb700e (128 717 rows, facts sha 6ae49d7d…, manifest sha dcda51d6…, v2 schema via #849); contract repaired by declaration only (Part A / plan §5c).

Contract repairs carried by the re-pin (I0)

The previous feed pin (226358e7, schema v1) broke 12 references on the current Chronicle main and moved 101 values. The repair is declaration-only: UC household rows re-bound to the dwp.uc_households family-type/children/youngest-child series with dimensions (exact key set) + dimension_values (subset) selectors and 2025 calendar-year averages (April–December, the months the feed carries); the caseload row keeps DWP deductions Table 1 total_units with a tightened selector (A16; chronicle#247 filed for the Stat-Xplore family-type Total row); the two CGT totals bound to Table 1's Total line (Table 5 duplicates excluded by source_measure_id); the stale PIPR deferral retired and the available local rent rows activated on the same temporal basis (A18); the runner now requires --ledger-manifest-sha256 and refuses a feed that is not the committed pin unless --allow-unpinned-feed is recorded. One I0 defect surfaced by the L4-pre calibration is fixed in this PR's second commit: the Scotland youngest-child row keeps its household-grain binding (the measure provider has no categorical household-to-benefit-unit broadcast). Receipts Part A; docs/uk-chronicle-feed-repin.md.

Surfaces moved

Lane A: assign_period_constant operation kind (schema branch, allow-list, sources.yaml + byte-lockstep source_stages.json); frs_person_draws artifact pin of the take-up contract (its sha folds into the node key) and build-year lockstep check; take_up_contract.json (new stochastic entry; hours entry frozen_by_adjudication; four programs entries with corrected citations, two re-fitted with fitting_receipt); loader validation of entity; H2 parity fixture regenerated (twice: the column, then the contract sha); coverage manifest; degenerate_reviewed_exclusions.json (+1); spine_swap_signed_differences.json (net-new column; re-fit mechanism_change with L5 magnitudes); contract digests; roster pins. Lane B: uk_population_targets.json (+7 active rows, +6 signed-held bus rows, Scotland row grain); difference value operation with value_operands; expected_member_count cardinality guard; observation_basis; entity_name/period_type/period_value/layout_groupby_value_id selector keys; target_reference_signed_exclusions.json (new register, declared in the country package); regenerated national/local references, membership and compile-parity receipts (415 active / 7 no fact / 8 signed on feed 6ae49d7d…); test fixtures from Chronicle rows only (uk_bus05i_fy2025.json, feed rows appended). Fitter: tools/fit_uk_childcare_takeup.py rewritten (tool version 3). Engine: policyengine-uk>=2.93, lock 2.94.0.

Deviations from the plan

  • Fitter objective (plan D6 said identity-keyed draws). The build keys each take-up flag on the spine's int64 benunit id at the frs_take_up stage and later stages clone and re-key rows, so no re-drawn realisation can sit on the persisted one (66% flag agreement re-hashing final ids; chance 51%), and the draw objective is a step function whose 1e-2 finite-difference step flipped no unit on the targeted row's 157-child base (first fit left it untouched). The fitter now takes two engine runs with the flags forced on and fits each row's expectation (linear in its own rate, bilinear with the extended rate through the engine's mutual exclusions): smooth, exact for the build, ceilings reported. Plan A24; receipts Part C L5.
  • Two rates held rather than landed (plan A6 said the four land). TFC and targeted come out at their ceilings (0.997 / 1.0); landing 1.0 would make the persisted flag constant (degenerate-gate refusal) and assert a 100% take-up the evidence does not support. Held at 0.88 / 0.597 with the full fitted vector in the receipts; María's rulings A22/A23.
  • A21 (CGT vintage after the re-pin): ruled 2026-09-05 — facts bound as published, the OBR miss signed on the stack register under UK CGT: translate the 2024-25 gains outturn to the 2025 calibration year (forestalling year; OBR CGT +44.2% signed on the stack register) #875 (window to 2026-10-05); the with-children deferral clock (2026-09-30) is unchanged.
  • Codex implemented I0–I2 with zero deviations from the plan text; the two I0 row defects above were found by the licensed runs and fixed by hand.

Verification

  • Hermetic funnel on the I1+I2 tree (María's UK + machinery set, test_contract.py, the graph package incl. test_h2_uk_spine_parity), JUnit-counted from this branch's venv with policyengine-uk 2.94.0: 2 893 tests, 2 failures, 27 skipped — the two failures are the documented licensed-cache regeneration tests (test_cached_reference_regeneration…, test_cached_candidate_regeneration…: committed evidence minted under 2.89.0 vs the installed engine, Make the UK eFRS parity re-pin one reviewed command; hold every identity mirror in lockstep (companion to #747) #749's lane), failing on main the same way. Post-port targeted set 468 passed / 1 skipped. After the L5 landing: graph package + digest-sensitive set 463 tests, the same single licensed-cache failure. ruff check clean on every touched file; tools/ci_test_groups.py --verify ok; tools/build_uk_release_input_coverage_manifest.py --check ok. CI is authoritative. Rebased onto main 396df96f (after Accept chronicle-era Chronicle artifact schema ids and fact-key domains; CHRONICLE_* env names (chronicle#143 consumer side) #849 merged, 2026-09-07): the branch is this PR's own eleven commits, cherry-picked clean. The primary-QRF worker identity's approved uv.lock digest is the rebased tree's (this branch's engine floor bump changed the lock; Portable authenticated worker identity: semantic identity plus audit-only aliases for the primary-QRF worker binding (fixes #863) #871 on main attests it) with no derived identity re-pin needed. Rebased-tree set: 1 295 tests on the identity, pin, UK contract, graph, spec-engine and data-contract files, green apart from one regeneration race with the review fixes that a re-run cleared; the review-fix set 485 green. Full-tree funnel on the L5 tree (every shard's tests, 8 233 tests): 19 failures — 13 US tests that need policyengine-us (this venv carries the uk extra only; the same files fail on a clean origin/main checkout in the same venv), the same two licensed-cache tests, and four of mine, fixed in the last commit: the live-tree guard found the incumbent package name in the new signed-exclusions register (now uk-data#230), and the monetary-binding receipt contract needed the two new reference fields (value_operands, expected_member_count) — test_monetary_targets and the calibrate shard's monetary tests green after the fix.
  • Composition branch uk-publication-stack-834 (1b85e5ff, 0fc7f470, 5a61e9b9, 1c732f78 — register port, required artifact, seam clock, signed CGT deferral — on this branch's head): the register port's 406-test set green (pins, spec bundles, source stages, terminal gates, battery bindings, gate battery, release certification, test_contract.py).

Licensed lane (receipts Parts B–D; raw JSON licensed-side)

  • L0 / L1 (before-receipts): spine-n read at 2.94.0 and an engine-only twin at the floor bump: benunit and time_period byte-equal, the engine bump re-draws 37 imputed household columns through its predictors; the five childcare rows read 1.07 / 1.02 / 1.28 / 0.60 / 1.10 at 2024 with the routed share absent.
  • L2 (column twin) / L3 (round trip): 28 stages, 15/15 spine gates; the only between-twin difference is the new column (0.593 for every person, read back by the engine) and its consequences — 1 414 TFC families' net income moves, so the WAS/LCFS/ETB imputations re-draw for 654 / 1 196 / 1 027 households (≈80% of them TFC families), the regional property rescale and the energy IPF ripple to every owner / household at median relative 3e-4 – 1e-3; no cash, weight or benefit-unit column moves; column totals within ±0.9%. The routed share moves only the TFC spend row (1.07 → 0.70); the four count rows are identical to three decimals.
  • L5 (re-fit): three fits (draw objective at eps 1e-2 and 0.05, then the expected-count objective, converged: loss 0.080); ceilings at a take-up of one — spend 0.81, children 1.14, extended 1.63, targeted 0.79, universal 1.58. Landed extended 0.6054 / universal 0.4539; rebuilt twin realises 0.999 / 1.087 (expectations 0.986 / 1.000); the two moved flags signed (max |Δshare| 0.207 / 0.110), everything else as L2.
  • L4 (composition, uk-publication-stack-834): on the rebuilt twin, family_equal as L4-pre: loss 0.352 → 0.0235, 90.0% of 371 targets within 10%, ESS 5 868. The seven new rows all fit within 0.5% (targeted from 0.77× at design weights to 0.997). The pre-existing surface is where L4-pre left it to the second decimal: the same seven uk_target_fit breaches (OBR CGT +44.2% — A21; the four UC with-children cells −29…−39% signed on the stack's register to 2026-09-30; the two state-pension SPI bands +35% / +25%), UC caseload −13.4%. The port neither helps nor harms the Port UK compact source stages and 2024-25 refresh from uk-data #145 residuals. Terminal battery with the ported register (run 5, after María's A21 ruling): the four UC with-children cells and OBR CGT (+44.2%, signed 2026-09-05 → 2026-10-05 under UK CGT: translate the 2024-25 gains outturn to the 2025 calibration year (forestalling year; OBR CGT +44.2% signed on the stack register) #875, the 2024-25 → 2025 translation of the forestalling-year gains) defer under their in-force entries; uk_target_fit still fails on the two state-pension SPI bands (+34.6% / +25.3%) and reports the private-pension 100–150k deferral stale (+24.7%, back inside the bound). Those are the stack's open register questions, not Port post-freeze UK childcare/TFC spine inputs and calibration contract #834 rows.
  • Bus (UK bus fare and subsidy consumer targets from Chronicle facts #789, Part D): on the L2 twin at design weights England fares 0.569× and net support 0.712× the FY2024-25 facts (DfT's label-2025 rows, year ending March 2025, held 2024 → 2025 as decided in the review; E9 measured 0.56× / 0.71×); London 0.276×, outside London 0.760× / 0.969× — the Adjudicate the incumbent's four post-calibration aggregate scalers #790 gradient, which is why those rows are declared with filters but signed-held.

Vahid's review at 8de6bf44 (2026-09-07)

  • 1 (DfT fiscal-year label): fixed generically. A fiscal-year fact that carries period_coverage.start_date is compared on that start year; the publisher's label is recorded as ledger_fact_period_label when it differs. On the feed only DfT labels by the closing year (24 facts, its own coverage notes say so); HMRC's labels already equal their coverage start; every other publisher carries no coverage and is untouched. The two bus rows now bind FY2024-25 (label 2025) at 2025 with a 2024 → 2025 uprating hold, value unchanged; test_ledger_targets pins the rule. Adjudicate the incumbent's four post-calibration aggregate scalers #790's scalers inherit it.
  • 2 (CGT gate on this tree): declared, not carried. On this branch's tree the release-blocking uk_target_fit gate fails on obr.capital_gains_tax (+44.2%) in addition to main's pre-existing UC with-children failures; the signing (UK CGT: translate the 2024-25 gains outturn to the 2025 calibration year (forestalling year; OBR CGT +44.2% signed on the stack register) #875, to 2026-10-05) lives on uk-publication-stack-834 until the stack merges its register. Receipts Part C L4 say so.
  • 3 (fitter provenance): the receipts were wrong, the contract right. The tool-v3 fit ran on l5-refit-twin/l5-refit.h5 (c2804c18…, the spine rebuilt on the second run's landing) with seed 0, which tool v3 records and does not use; the basis forces the flags on, so that twin's persisted draws do not enter it. Receipts corrected.
  • Q1 (prefix geography pin): fixed. DfT rows take their geography from the declared bus area (england, london → E12000007, england_outside_london → DfT's own id, uk), never a prefix; an undeclared area refuses.
  • Q2 (per-family rules in the tools): not the intended home. The UC family's calendar-year average and the PIPR basis note are generator rules today; the contract's allowed_value_operations does not yet carry calendar_year_average. Moving both into declarations (a family default in resolution_defaults, or per row) is filed as a follow-up in the plan's §7 rather than widened into this PR.
  • Q3 (hermetic receipt): the loader now refuses a landed fitting_receipt whose engine_version is present and null (the no-engine receipt the hermetic tests mint); receipts without the field (the incumbent parity records) are unaffected. Test added.
  • Q4 (seed 42 vs 0): see 3 — the first two fits used 42, the landed fit 0; both recorded.
  • Nits: source_stages.json key order is the regenerator's output (the lockstep test holds); the routed share's 2024-04-01 and the re-fitted rates' 2024-04-06 keys are both selected by rate_at's year granularity; national_staging_build_record.json is a dated record of the incumbent's 0.812 / 0.563 and stays as written.

UK-specificity note

The routed share, the DfE/HMRC taxonomies and the DfT geographies are UK; assign_period_constant, difference, expected_member_count and observation_basis are generic surface machinery.

Not in this PR

The publication-stack target-fit register itself, including the new CGT entry (lives on uk-publication-stack-834, the composition branch, not for merge on its own); the #875 translation; the expected-count objective's extension to the four would_claim_* build draws (they stay identity-keyed by design); pointer comments on #789/#790 and closing #153 (each on María's go); the London / outside-London bus measure (#790's gradient); chronicle#247 (Stat-Xplore Total row); the CGT year (A21).

🤖 Generated with Claude Code

@vahid-ahmadi

Copy link
Copy Markdown
Contributor

Review pass at 8de6bf44 (Claude Code, high effort; worktree checkout, scoped to this PR's own eleven commits after the #849 merge at 632da3c3 — 75 files, of which exactly one, test_ledger_targets.py, is also touched by #849 and only in separate hunks, so nothing of #849's leaks into what I read; #849's loader, Logbook and 22 commits were not reviewed. PR-owned suites plus source-stage and ledger suites: 925 passed, 10 skipped (3 feed-absent, 7 requires_uk); ruff check and ruff format --check clean on all 40 owned Python files; both census tools, the coverage manifest and spec_engine_coverage report current; ci_test_groups --verify ok).

Lane A holds

assign_period_constant is closed-world at every layer: the schema branch (sources.schema.json:5762-5789, additionalProperties: false, period_source const build_year, dtype const float64), the op allow-list, the pinned op sequence, and the runtime. rate_at (take_up_contract.py:123-136) selects the latest date key at or before the build year — 2024 → 2024-04-01 → 0.593 — and refuses a build year before the series; a missing entry raises at entry(); the build-year/frame lockstep is asserted in frs_person_draws.py:57-62; the entity is closed-world in both the contract loader and the stage. The engine claims are exactly right: at tag 2.94.0 tax_free_childcare_spend_routed_share is a Person variable with default_value = 1, tax_free_childcare.py:67-72 clips it to [0, 1] and multiplies it into the eligible expense, and the file 404s at 2.92.0 and exists at 2.93.0, so the floor is correct and assigning to every person matches the engine's grain. Export allow-list, gates.json, graph.py cells and the degenerate register entry are all in place.

The fitter deviation is the right fix for the right reason: the persisted flags are keyed on stage-time benunit ids that later stages re-key, so no re-drawn realisation can sit on the persisted one and the draw objective is a step function; the expected-count objective (two engine runs with flags forced on, own rate linear, extended rate bilinear through the engine's exclusions) is smooth and exact for the build, and test_childcare_expected_counts_are_bilinear_in_the_extended_rate pins the form. Holding TFC and targeted at 0.88 / 0.597 rather than landing a 1.0 that would trip the degenerate gate is the honest disposition. The feed-pin guard records --allow-unpinned-feed in the run manifest instead of hiding it, and APPROVED_UV_LOCK_SHA256 is a tripwire that raises on a lock change rather than a bypass (the constant equals the sha256 of this branch's uv.lock; main's equals main's).

Findings

1. Should-fix — the bus rows bind a closing-year fiscal label against a surface that reads every other FY row as opening-year, on a value the engine CPI-uprates. The six DfT facts are {"type": "fiscal_year", "value": 2025} and the contract note says so explicitly: "DfT closing-year label: period 2025 means YE March 2025" (uk_population_targets.json:11065, :11108). On the same feed and the same fiscal_year type, obr.capital_gains_tax at 2025 is FY2025-26 (£21.8bn, receipts Part C) and hmrc.tfc.government_top_up at 2025 is FY2025-26 — opening-year. Meanwhile bus_fare_spending carries uprating = gov.economic_assumptions.indices.obr.consumer_price_index in 2.94.0 and the measure is simulated at the calibration year, so dft.bus_fare_receipts.england (£3.417bn, YE March 2025) is compared with the LCFS 2024 base uprated to 2025. The mislabel is roughly one year's CPI, small next to the 0.57× gap the receipts already show, but the row is off by one fiscal year relative to everything beside it. This needs a declared convention — either a Chronicle-side re-stamp of BUS05i to the opening year, or a contract field that says which year a fiscal_year label denotes — before it becomes the precedent for #790's scalers.

2. Should-fix — the re-pin makes the release-blocking uk_target_fit gate fail on a second family, and the signing that absorbs it is on another branch. My compiled-value diff of the membership file between main and this head: 112 active candidates moved (the body says 101), all dwp.uc.* except hmrc.cgt.gains_total £65.9bn → £127.3bn (+93.1%) and hmrc.cgt.taxpayers_total 378k → 584k (+54.5%); one candidate removed, fourteen added, no status flips. Receipts Part C L4 lands obr.capital_gains_tax at +44.2% against the gate's 0.25 bound (gates.json:708-710, release-blocking) and lists it as "failing, unsigned". The #875 deferral that signs it lives on uk-publication-stack-834; there is no target-fit reviewed-exclusions register on this branch at all (build/uk/ carries only the degenerate, input-mass and QRF-tail registers). The UC with-children cells already fail on main the same way, so this is pre-existing in kind, but the PR adds a second failing family whose fix is out of tree. Either carry the #875 entry here or say in the body that main's terminal gate fails on CGT until the stack merges.

3. Should-fix — fitter provenance names the wrong input. take_up_contract.json's fitting_receipt.input_h5_sha256 is c2804c18… for both landed rates, while the receipts (line 197) say the L5 fit ran on l2-column.h5 at 2c69544b…, and the tool-v3 run's input is not named. The expected-count basis does not depend on persisted flags, so the numbers are probably unaffected, but the receipt should name the H5 it hashed.

Questions

  • tools/generate_uk_target_references.py:215-216 adds prefix geography pins ("dfe.", "england") and ("dft.", "england"), which would stamp dft.bus_fare_receipts.uk and .london as England if they are ever activated. Inert while signed-excluded; latent.
  • Repair generality: the dimensions handling in _sum_target_ids is general, but family == "dwp_universal_credit" → calendar_year_average (:269) and the ons.rent.private_rent block in generate_uk_local_target_references.py:55-63 are per-family rules living in a tool rather than declarations in the contract. Is that the intended home?
  • take_up_contract.py:216 requires a fitting_receipt only for continuous fitted_offline entries, and nothing rejects a landed receipt with engine_version: null. A hermetic (no-engine) receipt is distinguishable by three nulls and a sentinel, but no field says so.
  • Receipts say the L5 fit used seed 42; the contract's fitting_receipt.seed is 0. Tool v3 discards the seed, so harmless, but the two documents disagree.

Nits

source_stages.json carries key-order churn in unrelated stages (age, uc_reporter_redraw, gift_aid: 0 → 0.0); the lockstep test passes so it is regenerator output, but worth a line so it is not read as hand edits. Date keys differ (2024-04-01 for the routed share, 2024-04-06 for the re-fitted rates); rate_at is year-granular so harmless. national_staging_build_record.json:2053, 2083 still records 0.812 / 0.563, as a dated record rather than a reader.

Needs the licensed engine or data

The two requires_uk routed-share tests, the three childcare-target tests, the engine-version test, the pinned-feed regeneration tests, every L0–L5 receipt, the fit reproduction, and the 0.593 series derivation from the HMRC ODS.


Nothing blocks on code, and this stays draft on your side until #849 lands and the diff becomes #834's own. The two things I would settle before that: the fiscal-year convention for the DfT rows (1), because it decides how #790 binds, and whether the CGT gate failure is carried or declared here (2). 3 is a one-line receipt fix.

juaristi22 and others added 11 commits September 7, 2026 14:13
policyengine-uk 2.93.0 adds the person-level tax_free_childcare_spend_routed_share
input and 2.92.2 applies the Tax-Free Childcare rate to the gross payment; the
port of uk-data #473/#474 needs both. 2.94.0 is the release the free-childcare
dashboard was measured on. The frame and data shards' uk extras had drifted
back to >=2.88 after #811; all three shards now agree on >=2.93.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tract they compile against (#834, #789 I0)

The committed national surface was generated on a Chronicle feed (226358e7, 107,550
rows) the local census no longer pinned (33ca98a, 108,112 rows); on the pinned feed
19 references failed to compile and the calibration runner refused to run. Both
surfaces now pin one feed built from Chronicle main 6fb700e (128,717 rows, facts
6ae49d7d…, manifest dcda51d6…, artifact schema v2), and the runner refuses any other
feed unless --allow-unpinned-feed is passed and recorded.

What the re-pin surfaced and how the contract answers it:
- The #239 UC crosses share source_concept and groupby_dimension with the base
  family-type and number-of-children tables; the composition rows now pin their
  exact dimension key set, and the generator no longer reads a `dimensions` list as
  a sum trigger.
- The caseload row keeps binding DWP UC deductions statistics Table 1 (measure
  total_units, month groupby): its April–December 2025 calendar-year average is
  6,758,889, the value #735 recorded; the Stat-Xplore family-type sum runs 1.45%
  above it (chronicle#247 asks for the cube's Total row).
- The CGT totals pin Table 1; Table 5's UK rows carry the same concepts.
- Every dwp_universal_credit row is a calendar-year average (one temporal basis):
  the 100 payment-band rows move from the May-2025 snapshot to the 2025 mean, and
  the Scotland youngest-child row is re-bound at benefit-unit grain.
- ons.population.scotland_households_3plus_children is signed out (its selector
  reaches person-level mid-year population rows; #736).
- The local private-rent deferral private_rent_pipr_after_target_period is stale
  since #231 extended PIPR to 2023–2026: the 314 rows bind the 2025 calendar-year
  average (12 members each). Local active references 19,618 -> 19,932.
- The nine scotgov council-tax bands restore themselves (#231's measure-id fix).

Regenerated with the repository tools: national and local references and
membership, the local census and validation levels, both compile-parity receipts,
the feed-row fixture. A hermetic test regenerates the surfaces from the pinned
identity and skips only when the feed is absent. docs/uk-chronicle-feed-repin.md
records the procedure.

Codex implemented increment I0 of the #834 plan under the approved-plan protocol;
reviewed section by section, source_repo restored to the canonical
PolicyEngine/chronicle, census regenerated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…us targets (#834, #789)

Lane A (D1–D6): a generic `assign_period_constant` source operation gives
`frs_person_draws` the HMRC-derived `tax_free_childcare_spend_routed_share`
from a new `stochastic` entry of the take-up contract at the build year
(2024-04-01 → 0.593; 2017–2025 series from the March-2026 account-activity
ODS, Chronicle package and sha pinned), with the stage's node key folding the
contract sha, a build-year lockstep check against the installed engine, the
degenerate-column and swap-register entries, and the H2 fixture regenerated.
The contract's TFC citation is corrected, the targeted rationale added, and
the extended-hours distribution moved to `frozen_by_adjudication`. The
childcare fitter is rewritten: targets compiled from the contract and the
pinned feed, four rates, hours frozen, identity-keyed draws, `--eps`
recorded in the receipt.

Lane B (D8–D13): five childcare rows declared once and resolved by period
(`hmrc.tfc.*` at fiscal-year government/person grain; the three DfE rows with
the `sum` cardinality guard, the `difference` operation and the observation
basis), the two England bus rows active at household grain and the London,
outside-London and UK rows declared with model-side filters but signed on the
new `target_reference_signed_exclusions.json`; national references,
membership, local references and the compile-parity receipts regenerated on
feed `6ae49d7d…` (415 active / 7 no fact / 8 signed). Publisher fixtures for
the tests are Chronicle rows (`uk_bus05i_fy2025.json`), never hand-copied.

Two I0 defects fixed here: `dwp.uc.scotland_households_child_under_1` keeps
its household-grain binding (the measure provider has no categorical
household-to-benefit-unit broadcast, so the benefit-unit re-bind refused at
calibration), and the held London / outside-London bus rows carry their
region filters.

Receipts: `experiments/834-childcare-tfc-receipts.md` Parts C–E (L2 column
twin, L3 round trip, L5 fit diagnosis, bus re-measurement, both-basis table).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nd land the extended and universal rates (#834 L5)

The fitter no longer draws. The build keys each take-up flag on the spine's
int64 benunit id at the frs_take_up stage and later stages clone and re-key
rows, so no re-drawn realisation can sit on the persisted one (re-hashing
the final ids agrees with the persisted universal flag on 66% of rows,
chance being 51%), and the draw objective was a step function whose
finite-difference step flipped no unit on the targeted row's ~157-child
base. Two engine runs with the flags forced on (extended on, then off) now
give each row's weighted per-row contributions; each target's expectation is
linear in its own rate and bilinear with the extended rate through the
engine's mutual exclusions (targeted and universal need a family that is not
extended-eligible). The objective is smooth, exact for the build, and the
receipt reports each row's ceiling at a take-up of one. Unit test pins the
bilinear form; `--eps` is recorded in the receipt.

Fitted on the L2 column twin at the 2024 base year (Jan-2025 vintage override
on the extended row, routed share 0.593 from the contract, hours frozen):
extended 0.812 → 0.6054 and universal 0.563 → 0.4539 land as 2024-04-06
entries with a `fitting_receipt`. `tax_free_childcare` (0.997, at its
ceiling: with spend routed the two HMRC rows conflict) and
`targeted_childcare` (1.0, the eligible base reaches 0.85 of the count) are
held at 0.88 / 0.597 for adjudication (plan A22/A23); a rate of 1.0 would
also make the persisted flag constant. Receipts Part C L5 carry the three
fits, the probes and the rebuilt twin's magnitudes; the swap register signs
the two moved flags as `childcare-take-up-refit-834`; the H2 graph parity
fixture is regenerated (the take-up stages' node keys fold the contract sha)
and the contract test's pinned rates follow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Run 1 is the weighting-sensitivity receipt (per-target uniform weights);
run 2 the family-equal composition, equal to L4-pre to the second decimal
with the seven new rows binding within 0.5%; run 4 reproduces it on a
payload-identical rebuild with the seam supplying the exclusion clock, so
the ported target-fit register renders its verdict: the four UC
with-children cells defer, OBR CGT and the two state-pension bands fail
unsigned, the private-pension band entry reads stale. The runner's
--build-record-json is the seam's own output; the note records how run 2
overwrote the twin's spine sidecar and why the twin was rebuilt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…w reference fields in monetary-binding receipts (#834)

The live-tree guard refuses the incumbent package name: the signed
target-reference exclusions register now cites uk-data#230 and the uk-data
PR as every other live file does. The monetary binding's activated-reference
receipt is an exact key set; LedgerTargetReference gained value_operands and
expected_member_count for the multi-member childcare rows, so the receipt
contract and its hermetic fixture carry them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
María ruled A21 as option 1 on 2026-09-05: the facts stay bound as published
and obr.capital_gains_tax@2025 is signed on the publication stack's register
under microcosm#875 (window to 2026-10-05). Run 5 reproduces run 4 and the
battery now defers five cells; the two state-pension bands and the stale
private-pension band entry remain the stack's open questions.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…fix (#834)

The membership report embeds the signed-exclusion rationale, so the
citation fix in the register moved its digest; the references themselves
are byte-identical. The packaged-report test carries the uk-data wording.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…dentity (#834)

Main's #871 attests the workspace lock; this branch's policyengine-uk floor
bump (>=2.93, lock 2.94.0) changed uv.lock, so the merged tree's digest is
approved. No derived identity moved: the spec-engine identity, legacy
adapter, re-emission, field-usage and inventory-coverage suites pass on the
approved digest without a re-pin.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…834)

The provenance receipt read policyengine-uk's version unconditionally, so
the hermetic determinism test with an injected runner failed in CI's
no-engine lanes (fast spine-uk, wheels) while every engine lane passed.
The version is recorded as absent when the distribution is missing; a
real fit still needs the engine for its basis runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…verage start year, pin DfT geographies from the declared area, refuse hermetic fitting receipts

Finding 1: DfT labels its reporting year by the March end year (label 2025
is FY2024-25) where every other fiscal-year label on the surface is the
opening year, and it says so in the fact's own coverage dates. A fiscal-year
fact that carries period_coverage.start_date is now compared on that start
year, with the publisher's label recorded as ledger_fact_period_label when
it differs; facts without coverage keep their label. On the pinned feed only
DfT's 24 facts translate (HMRC's labels already equal their coverage start;
no other publisher carries coverage). The two active bus rows bind
FY2024-25 at 2025 as a 2024 -> 2025 uprating hold, values unchanged; the
references, membership and parity receipts are regenerated and two ledger
tests pin the rule.

Q1: DfT rows take their geography from the declared bus area (england,
london, england_outside_london under DfT's own id, uk) instead of a prefix
pin, so a held London or UK row can never be stamped England if activated;
an undeclared area refuses.

Q3: the take-up contract loader refuses a landed fitting_receipt whose
engine_version is present and null (the receipt the hermetic fitter tests
mint); receipts without the field stay valid.

Finding 3 / Q4 / finding 2: the receipts name the tool-v3 fit's input
(l5-refit.h5, c2804c18..., seed 0, unused) and declare that this tree's
release-blocking uk_target_fit gate fails on OBR CGT until the publication
stack merges the register that signs it (#875).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@juaristi22

Copy link
Copy Markdown
Collaborator Author

Thanks — addressed at the rebased head (the branch is now this PR's own commits on main 396df96f after #849 merged; the chronicle-dual-accept commits are gone from the diff).

1 (DfT fiscal-year label) — fixed generically, in the compile. A fiscal-year fact that carries period_coverage.start_date is compared on that start year, and the publisher's label is recorded as ledger_fact_period_label when it differs. Measured on the pinned feed: DfT is the only publisher whose fiscal-year label is the closing year (24 facts; its own coverage notes say "labels the reporting year by its March end year"); HMRC's 18 covered facts already label by the opening year; no other publisher carries coverage, so nothing else moves. The two active bus rows now bind the label-2025 fact as FY2024-25 with a 2024 → 2025 uprating hold, exactly like the other lagged facts on the surface (SPI, age); values unchanged. The 2023 production parity row moves to the FY2023-24 fact by the same rule. Two tests in test_ledger_targets.py pin it (with coverage → start year and label recorded; without → label). #790's scalers inherit the convention, so it is settled before they bind.

2 (CGT gate on this tree) — declared, not carried. The register lives on the stack; carrying one entry here would mean porting the register machinery, which is the stack's PR. The receipts (Part C L4) and the PR body now say that on this branch's tree the release-blocking uk_target_fit gate fails on obr.capital_gains_tax (+44.2%) in addition to main's pre-existing UC with-children failures, and that the signing (#875, to 2026-10-05, María's ruling of 2026-09-05) lives on uk-publication-stack-834 until the stack merges.

3 (fitter provenance) — the receipts were wrong, the contract right. The tool-v3 fit ran on l5-refit-twin/l5-refit.h5 (c2804c18…, the spine rebuilt on the second run's landing), seed 0 — tool v3 records the seed and does not use it. The basis forces the flags on, so that twin's persisted draws (which differ from l2-column.h5 only in the two re-fitted flags and the imputed surfaces downstream of them) do not enter it. Receipts corrected; that also answers Q4.

Q1 — fixed. DfT rows take their geography from the declared bus area (england, london → E12000007, england_outside_london → DfT's own id, uk → K02000001), never a prefix; an undeclared area refuses. The membership now shows the held rows under their own geographies.

Q2 — not the intended home. The UC family's calendar-year average and the PIPR basis note are generator rules today, and allowed_value_operations does not yet carry calendar_year_average. Moving both into declarations (a family default in resolution_defaults, or per row) is filed as a follow-up in the plan's §7 rather than widened into this PR.

Q3 — fixed. The loader refuses a landed fitting_receipt whose engine_version is present and null (the receipt the hermetic fitter tests mint); receipts without the field — the incumbent parity records — stay valid. Test added.

Nits: source_stages.json key order is the regenerator's output (the lockstep test holds); 2024-04-01 vs 2024-04-06 are both selected by rate_at's year granularity; national_staging_build_record.json is a dated record of the incumbent's rates and stays as written.

@vahid-ahmadi

Copy link
Copy Markdown
Contributor

Second pass at 659adb1a (Claude Code, high effort; worktree checkout, the fix commit read against the dispositions; the branch is now eleven commits of its own on main with #849 merged, 75 files; 928 passed, 10 skipped, 0 failed across the PR-owned suites plus the ledger suite; ruff clean; both census tools, the coverage manifest and ci_test_groups --verify current).

All three should-fixes and the two fixable questions closed as described; one note back on item 1, and two stale sentences.

1. Verified — and the rule is generic. _comparable_period_value (ledger_targets.py:1556-1577) keys a fiscal_year fact on period_coverage.start_date when it carries one and on the label otherwise, with no publisher check; the new tests use an unrelated source and the with-coverage case fails on the old head. ledger_fact_period now carries the comparable value and ledger_fact_period_label is stamped only when it differs. The two active bus rows bind the label-2025 fact at resolved_fact_period 2024 with a 2024 → 2025 hold and unchanged values (£3,417,388,656 and £3,024,904,321); the 2023 parity row moves to the FY2023-24 fact and its value moves with it, which is right for a ledger_only row. I could not check the "24 DfT facts, only closing-year publisher" claim without the feed. One thing worth saying plainly about the hold: it is a declared lag, not an applied index — 370 references on the surface carry uprating_from_period and none carries uprating_index, and no UK code applies one — so the bus target stays FY2024-25 nominal at period 2025, exactly like the SPI, age and ISC holds beside it. That is the surface's convention and not a regression here; whether the engine's CPI uprating of bus_fare_spending puts the measure a year ahead of that target is the same question those holds already carry, and the licensed comparison is where it gets answered.

2. Verified. Receipts Part C L4 (:416-419) and the PR body both state that on this branch's tree the release-blocking uk_target_fit gate fails on obr.capital_gains_tax at +44.2% in addition to main's UC with-children failures, and that the #875 signing lives on the stack until its register merges. Declared, not carried, is the honest shape.

3. Verified. Receipts and take_up_contract.json now agree: l5-refit-twin/l5-refit.h5, sha c2804c18…, seed 0 recorded and unused, engine 2.94.0.

Q1 verified. DFT_BUS_AREA_GEOGRAPHY_IDS (generate_uk_target_references.py:216-239) maps the declared area to its geography and refuses an undeclared one; the six held rows now sit under their own geographies in the membership. Two notes: the ("dfe.", "england") prefix pin remains (defensible, DfE is England-only, but it is the same shape), and the held rows still carry geography_level: "country" under region and DfT ids.

Q3 verified. The loader refuses a fitting_receipt whose engine_version is present and not a non-empty string, and admits one without the field (take_up_contract.py:199-222); the test covers null, valid and absent and fails on the old head.

Q2 and Q4. Accepted as deferred to the plan's §7 and as answered by item 3 respectively; the plan file is not in-tree so I take the §7 filing on trust.

Two stale sentences

  • PR body, the bus row of the table, still says "the FY2025 facts".
  • uk_population_targets.json:11065 and :11108 still say "DfT closing-year label: period 2025 means YE March 2025", which under the new rule reads inverted — period 2025 is now the FY2025-26 comparable, and the bound fact is YE March 2025 held from 2024.

Nothing outstanding from me. Once the two sentences are corrected this is ready for María's end-of-stack review; the fiscal-year rule is settled generically, which is what #790 needed.

@vahid-ahmadi vahid-ahmadi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving on the closing round above (#874 (comment)): all findings closed at 659adb1a; two stale sentences noted there, nothing outstanding from me. Still María's end-of-stack review to merge.

@juaristi22
juaristi22 merged commit 5ab1b05 into main Sep 7, 2026
23 checks passed
juaristi22 added a commit that referenced this pull request Sep 7, 2026
Rebased onto main 5ab1b05 (#874 childcare/TFC and #849 landed). The
three generated locks that conflicted are regenerated on the combined
tree: the coverage manifest's source-manifest sha, the uk spec digest
pin, and the graph parity fixture. The stage-level receipt was repeated
on the rebased tree with policyengine-uk 2.94.0 and reproduced every
figure; the receipt records both engine versions and the rebased
implementation commit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
juaristi22 added a commit that referenced this pull request Sep 8, 2026
Rebased onto main 5ab1b05 (#874 childcare/TFC and #849 landed). The
three generated locks that conflicted are regenerated on the combined
tree: the coverage manifest's source-manifest sha, the uk spec digest
pin, and the graph parity fixture. The stage-level receipt was repeated
on the rebased tree with policyengine-uk 2.94.0 and reproduced every
figure; the receipt records both engine versions and the rebased
implementation commit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
juaristi22 added a commit that referenced this pull request Sep 9, 2026
…onthly rent x A17-uprated private-renter households (#355, ruling 2026-09-08)

ons.rent.private_rent (314 local-authority rows, ONS PIPR, active since
#874's re-pin) bound a mean monthly rent to rent/private_rent, a weighted
annual total, so every row sat at a relative error of 1e5-1e6 and at the
loss cap on the rowwise pool. The calibration is linear in the weights, so
the mean's linear form is a total: uk_private_rent_mean_to_total, applied
in uk_local_target_surface after the A15/A17 uprating, composes each row's
value as 12 x mean x the same authority's bound tenure/private_rent count,
keeps the mean and the count in the row's metadata, refuses by area when
the count is missing or non-positive, and records every cell under
cross_grain.private_rent_mean_to_total. The metric is unchanged. The
census rationale for the PIPR source no longer claims the feed carries
only 2026-06 (it carries the 2025 months the reference averages); the
committed census artifact is regenerated. Implemented by Codex from
.codex-work/PLAN-rent.md; verified here: ruff clean, 137 tests across the
ledger-target, local rowwise, rowwise candidate and census files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
juaristi22 added a commit that referenced this pull request Sep 9, 2026
…onthly rent x A17-uprated private-renter households (#355, ruling 2026-09-08)

ons.rent.private_rent (314 local-authority rows, ONS PIPR, active since
#874's re-pin) bound a mean monthly rent to rent/private_rent, a weighted
annual total, so every row sat at a relative error of 1e5-1e6 and at the
loss cap on the rowwise pool. The calibration is linear in the weights, so
the mean's linear form is a total: uk_private_rent_mean_to_total, applied
in uk_local_target_surface after the A15/A17 uprating, composes each row's
value as 12 x mean x the same authority's bound tenure/private_rent count,
keeps the mean and the count in the row's metadata, refuses by area when
the count is missing or non-positive, and records every cell under
cross_grain.private_rent_mean_to_total. The metric is unchanged. The
census rationale for the PIPR source no longer claims the feed carries
only 2026-06 (it carries the 2025 months the reference averages); the
committed census artifact is regenerated. Implemented by Codex from
.codex-work/PLAN-rent.md; verified here: ruff clean, 137 tests across the
ledger-target, local rowwise, rowwise candidate and census files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Port post-freeze UK childcare/TFC spine inputs and calibration contract UK bus fare and subsidy consumer targets from Chronicle facts

2 participants