Skip to content

UK spine follow-ups: SPI donors funded, WAS coherence, residential split, Child Benefit, release-cut blockers (#1063) - #1089

Open
juaristi22 wants to merge 21 commits into
mainfrom
uk-spine-followups-1063
Open

juaristi22 wants to merge 21 commits into
mainfrom
uk-spine-followups-1063

Conversation

@juaristi22

@juaristi22 juaristi22 commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #1063. On main (after #1084). Receipts: experiments/1063-uk-spine-followups.md.

The eight items and the fold-ins

  • Items 1, 2, 4 (c1): the SPI income band donors become a mass-conserving support channel. Each band seats max(120, ceil(published taxpayers / 250)) donors at equal weights, funded in proportion from the households of each donor's region; no created mass, graph policy declared, an identity-keyed seating (salt spi_income_band_donor_draw) the E8 receipt reruns from ids with the licensed tape.

  • Item 3 (c2): mass_increasing_support leaves the coverage validator.

  • Items 5, 6 (c3+c4): WAS wealth is coherent with tenure (no mortgage debt off mortgaged tenure, no main-residence value off owner tenures) and with its own totals (property and financial wealth derived from their components, identities asserted at cleaning); new gate uk_stage_was_wealth_coherence.

  • Items 7, 8 (c5, c6, c5b): the residential-property flag is carried as weight by a new expansion stage, cgt_residential_split, after the incidence anchor: each liable gainer's solved probability splits the household into arms at p·w and (1 − p)·w (2^k arms for k gainers), so the residential count and gains are identities on HMRC Table 8a in every gain band at design weights, with no draw and no seed; calibration decides between the arms as it does for the incidence clones. The asset-type stage types the arms and fits Table 7 once per claimant status with an error-diffusion walk (seed 554 retired). The per-band walk of c5 is replaced because its gains error in the open band was bounded by one stake several times the band's expectation.

  • Fold-ins: salary sacrifice predicted only for people who are paid (c7); Child Benefit claims and opt-outs redrawn after the SPI chain against HMRC's August 2025 statistics in a new child_benefit_take_up stage (c8).

  • Release cut (c9): the certifier rehearsal's five blockers cleared at their source (candidate label, export surface read without ids and with the weight, 43 declared outputs allow-listed, incapacity_benefit_reported dropped at the release boundary, FRS 2024-25's TRAIN2 read, the LCFS donor floored at 0 with a receipt, three signed register entries). The plan-2 measure exclusions are renewed to 2026-11-03.

  • Identity receipts after the pensions PR: since microcosm#1069 c9 the salary_sacrifice stage lowers a converted record's pay in place, after the CGT support split and the incidence anchor banded on it, so the E8 recomputes banded 2,797 clone carriers on post-sacrifice pay and failed on the final build's spine. The stage now keeps a converted record's pre-conversion pay on salary_sacrifice_pre_conversion_pay (an internal carrier, dropped at the release boundary) and the identity tool reverses the conversion for every receipt of an earlier stage; the residential split walks the liable gainers in person_id order so its arms are bitwise identical for any row order. The certifier's non-negative and QRF tail gates read export-dropped declared outputs from the spine frame.

  • Target registry: the SPI 2023-24 Table 3.6 employment, self-employment and private-pension amounts by total-income band are projected to 2025 with HMRC's band-specific Income Tax Liabilities growth, as the count, total-income and tax rows by band already were, instead of a flat national index. The flat average-earnings index raised the £15,000 to £20,000 band's employment amount 10.7% while HMRC projects the band's total income down 2.3%; the three lowest employment targets move from £18.7bn, £53.8bn and £190.4bn to £17.7bn, £47.5bn and £172.5bn, and the frame's design-weight gaps from 20 to 31% to 15 to 22%. The remaining gap is the frame's composition of the low bands, documented in the receipts note as a follow-up.

Measured so far (licensed local arms, nothing released)

  • Arm C (the walk): all spine gates pass; residential gains gap 0.3% of target; calibration loss 0.00853 against main's 0.00836, ESS 5,217 (5,198), max-to-median weight 1,115 (1,079; bound 1,151), projection entrants 6,715 (6,711).
  • Arm B (WAS): all spine gates pass including the coherence gate; the rule drops £127bn of £1,266bn off-tenure mortgage mass; renters with property wealth fall to 16.5% private and 4.6% social.
  • Child Benefit trial: 86.7% of eligible children claimed for (HMRC 86.6%), 9.0% of claiming families opted out, 11.1m children in payment against 13.5m before.
  • Final national build (two runs, 2fe7d8b and 64c460b; the second differs only by the carrier column): all spine gates, 7/7 seam gates, calibration loss 0.00713 over 1,090 targets (main's 2026-09-30 build 0.00836); evaluation against the enhanced-FRS incumbent passed (loss 0.00817 vs 0.327, 1,020 target wins vs 30); E7 and E8 pass with the licensed tape. The release-cut certifier reaches all 20 gates: 19 pass; uk_input_mass_parity fails on three columns against the uk-data 1.56.16 enhanced FRS (access_fund +535%, jsa_income_reported +1,058%, working_tax_credit_reported +747%; see the open calls). The compile-parity receipt was re-signed for the band-aware Table 3.6 amounts and the stale adult_ema exclusion retired.

Tests

Stacked PRs get no CI here. At the stack head: 983 targeted tests, the engine-free lane (6,294 passed; the two failures were the extras-free-venv and importlib-mode environment checks), the engine-UK lane (137), the integration smoke build and the parity fixture tests pass; the c9 commit was checked on its touched files plus the engine-UK lane and the smoke build.

Open calls

  • Child Benefit opt-outs are drawn from fully charged families first, then the taper; or one pool over every charged family.
  • other_residential_property_value (WAS DVHseVal) excludes buy-to-let, which sits in the drawn remainder.
  • The Table 8a rows stay fenced; promoting them to bound targets is the UK CGT: sub-exempt gains capped at exactly the annual exempt amount become 10.8m taxpayers under uprating; interpolate the remainder below the threshold and anchor incidence #970 leverage question after the final build.
  • The certifier's input-mass findings (not new to this stack: the 2026-09-30 main candidate sat at +421% and +428% on the same JSA and tax-credit columns): access_fund is one FRS household whose 19-year-old reports an access fund the FRS weeklyises to £1,035.62 (sernum 14443, ACCSSAMT), annualised to £54,037 and taken to the calibration's 10x weight bound; jsa_income_reported and working_tax_credit_reported sit in the SPI synthetic channel, where the stage-2 QRF imputes these legacy-benefit leaves onto taxpayer rows. An FRS outlier fence, the SPI channel's treatment of the two leaves, or reviewed exclusions: her call.

🤖 Generated with Claude Code

@vahid-ahmadi

Copy link
Copy Markdown
Contributor

Automated review pass (Claude Code, high effort) — round 1 at ebff8412

Verdict: one blocker and one urgent split-out. The rest is design-sound. c9 allow-lists the five internal disability carriers on the export surface, and a test that was green on #1084 now fails. Separately, the plan-2 exclusion renewal sits behind unmerged #1084, so main's exclusions lapse on 3 October. The spine changes (c1, c3+c4, c5b, c8) read correctly. C2, the final build and the certifier run are still owed, so this stays draft.

All diffs are against #1084's head 004b5c9c, so they cover only this PR. I ran 13 targeted engine-free files: 356 passed, 1 failed (item 1). These are the new-stage files, plus stage health, terminal gates, battery bindings, diagnostics, source stages, release coverage, salary sacrifice and LCFS.


1. Blocking: c9 puts the internal disability carriers on the export allow-list.

  • 48eb73bf adds person.attendance_allowance_reported, dla_sc_reported, dla_m_reported, pip_m_reported and pip_dl_reported to uk_export_surface's allowed_extra_columns (gates.json:926 and the four lines around it).
  • Those are UK_INTERNAL_DISABILITY_REPORTED_COLUMNS (frs_disability.py:25). test_uk_source_stages.py::TestE3ManifestLockstep::test_internal_disability_carriers_stay_out_of_export_registers exists to keep them off the release.
  • That test fails at this head. On 004b5c9c none of the five is listed, so the failure is new.
  • R2's "every declared stage output the incumbent lacks is allow-listed" caught the internal carriers. Drop them from the allow-list, or drop them at the release boundary as incapacity_benefit_reported is. Stacked PRs get no CI here, so this would only have shown up after the retarget.

2. Urgent, outside the stack: ship the plan-2 renewal to main on its own.

  • On main 5187fce2, slc.borrowers.plan_2_liable and plan_2_above_threshold expire on 2026-10-03 (calibration_measure_exclusions.json:362,371).
  • c5368635 renews them to 2026-11-03, but only on this branch, which is stacked on UK pensions: resident State Pension, Pension Credit, contributions and salary-sacrifice relief (#1069) #1084, itself still a draft.
  • Until a one-line PR to main lands, every UK build on main fails on those two exclusions from tomorrow. I'd cherry-pick c5368635 onto main today. It is self-contained, apart from the gate-digest re-pin it needs.

3. Should-fix: the WAS tenure rule drops £127bn of mortgages but keeps the property they're secured on.

  • mortgage_debt is HMortGR8, all of the household's mortgages (was_wealth.py:251; the comment at :712 says so).
  • The rule zeroes it off a mortgaged tenure, so outright owners' and renters' buy-to-let and second-home mortgages go: £127bn of £1,266bn, all of it OthMortR8, per the receipts.
  • The property those loans are secured on stays, in other_residential_property_value and the drawn remainder. So property and net wealth rise by £127bn on exactly the landlord households the WAS undercount already hits.
  • Mortgaged-tenure households also keep their other-property mortgages inside mortgage_debt. So the column means "all mortgages" on one tenure and "main-residence mortgage only" on the others.
  • Suggested fix: apply the tenure rule to the main-residence mortgage (TotMortR8), which you already read for diagnostics. Carry the other-property mortgage (OthMortR8_sum) unrestricted, as its own column or added back to mortgage_debt. Then the coherence gate tests the thing that is actually structural.

4. Should-fix: the residential split's band identities hold at design weights only, and nothing reports them after calibration.

  • cgt_residential_split makes Table 8a's count and gains identities in every gain band before calibration.
  • Calibration binds only the two national totals: hmrc.cgt.residential_property_taxpayers and _gains in uk_population_targets.json. The by-band rows stay fenced, as the receipts note says.
  • The arms differ only in capital_gains_residential_property, and main and residential rates are both 18/24 from 2025-26. So no other target distinguishes the arms, and the post-calibration split within each household is set by the solver's update rule, not by data.
  • That's fine as a design, and it's the incidence-clone precedent. But C2 should report the by-band residential count and gains on the calibrated weights next to the design-weight identities, so the "identity in every band" claim isn't read as a release property.
  • Also worth recording in C2: the row-level minimum weight and the positive-weight median with the arms in place. A 0.04-household arm next to a 20.76 one is fine on the fold, but it is new for any row-level reader.

5. Question: Child Benefit children in payment are about 5% below HMRC.

  • The vendored August 2025 statistics give 12.73m children registered and 1.00m opted out (all ages), which implies 11.73m in payment.
  • The trial reads 11.1m in payment, while the claim rate (86.7% against 86.6%) and the opted-out family share (9.0% against 684,635 / 7,552,330 = 9.07%) both match.
  • So the gap is in the eligible-child base or in the age mix of opt-outs, not in the rates. HMRC's opt-outs are concentrated at ages 10–16 (63,975 to 94,905 a year against 9,770 at age 0).
  • Is the gap in the frame's eligible children, or in opt-out families having more children than the frame's charged families? One line in the receipts would settle it.

6. Nit: the stage notes overstate what calibration binds. cgt_residential_split.py:23 and the source_stages.json / sources.yaml notes say calibration "binds" Table 8a. It binds the two national totals only (see item 4). Reword it to "binds the national residential taxpayers and gains".


Your open calls

  • Child Benefit opt-out pool: I'd use one pool over every charged family, with selection probability proportional to the charge as a share of the benefit (1 for fully charged, tapering below). That keeps fully charged families most likely, as now, without making taper families structurally ineligible. Opting out has a real cost for them (a Self Assessment return) and HMRC doesn't split opt-outs by income. Either way, check the age profile of opt-outs against the vendored by-age rows, since that's where item 5's gap may sit.
  • other_residential_property_value excludes buy-to-let (DVBltVal): fine as long as the buy-to-let value lands somewhere in property_wealth. Item 3's fix matters more here, because buy-to-let is exactly where the dropped mortgages sit.
  • Table 8a rows fenced: agree. Leave promotion to UK CGT: sub-exempt gains capped at exactly the annual exempt amount become 10.8m taxpayers under uprating; interpolate the remainder below the threshold and anchor incidence #970 after the final build, with item 4's post-calibration readings as the evidence for it.

Checked and correct

  • c1, mass and seating:
    • Donors are funded from each donor's region stratum. Every stratum's and the total household mass are conserved, and the stage refuses if funding would empty an incumbent row (spi_band_donors.py:694-702).
    • Seating is an Efraimidis–Spirakis draw without replacement on identity-keyed uniforms, salted per band, and households already taken are excluded across bands. So no source household seats twice.
  • c1, identity keys: donors carry their own support clone index (2), so geography identity keys stay unique.
  • c5b, identity and the fold:
    • Arms take id + j × multiplier and carry cgt_residential_clone_index. The geography identity kernel reads both and enforces an int64 index (graph_population.py:84-85, 402).
    • The family fold now folds residential arms onto their household as well as support copies (diagnostics.py:492), so the weight-ratio and ESS gates read the folded weights.
    • k > 3 liable gainers is refused rather than grown.
  • c8, rates: the claim rate and opted-out family share match the vendored HMRC rows (item 5 aside).
  • c3+c4: the coherence gate and the derived-total identities are asserted at cleaning.
  • Tests: all the new-stage test files pass.

juaristi22 and others added 12 commits October 2, 2026 14:34
 items 1, 2, 4)

spi_income_band_donors copied 120 FRS households per HMRC Table 2.5 band
from GBP 200,000 and added the band's published taxpayers on top of the
incoming weights: about 450,000 households of created mass on rows
starting at up to 2,992 each, recorded with no declared factor under
graph policy `free`, with no coverage family and an RNG draw.

Channel design:
- Each band seats max(120, ceil(published taxpayers / 250)) donors at
  equal weights of the published taxpayers over the count (1,436 / 244 /
  120 / 120 on the 2024-25 counts; none above 250).
- The donors' mass leaves the incumbent households of each donor's
  region in proportion (María's ruling: region), so the total and every
  region's mass are conserved; the total is made bit-exact on one
  incumbent row and the record carries declared_factor 1.0 under its own
  reason. Graph policy `declared`.
- Seating is identity-keyed: Efraimidis-Spirakis keys log(u) / (w * p)
  on stable_identity_uniforms(person_id, seed 3, salt
  spi_income_band_donor_draw:<band>), one carrier per household, highest
  band first. The carriers' leaf draw in hmrc_spi_income_spine is keyed
  on the FRS person each carrier copies (salt
  spi_income_band_donor_leaf_draw) instead of the row order.
- The tape is prepared as the income stage prepares it (its stage-1 seed
  and the ONS age draw), so composites and State Pension recipients sit
  in the same age bands in the propensity table and the band pools.
- Small frames: seats scale to at most 15% of the eligible households
  and the weights so no region gives up more than half its mass; both
  scales are recorded and the published band masses are asserted only
  when both are one. A full-sample build must be at full scale.

Declarations: manifests rendered from the stage's own payload, the
closed schema branches, `_declared_seeds` keyed by salt, a
`spi_income_band_donors` family with mass_conserving semantics in the
release-input coverage manifest, and the gate
uk_stage_spi_income_band_donors_support rewritten (conserved total,
maximum weight, minimum count, funding factors, band masses at full
scale).

Identity tool: E8 reconstructs the pre-donor frame from the artifact
(CGT layers folded, each region's incumbents scaled back) and checks
the stored layer; with the licensed tape (--spi-tab) it reruns the
seating from ids in original and permuted person order against the
stored donors, carriers and band weights. The tape-derived propensity
table is not written into the receipt.

Coverage manifest, charter-H2 fixture and gate digests regenerated.

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

The SPI income band donors were the last stage to add support mass; they
are now a funded channel, so every stage family declares mass_conserving.
The family parser refuses any other semantics, the terminal family gate
accepts only a household record that conserves the total and declares
factor one, and the manifest generator reads the one constant.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…HMRC's August 2025 statistics (#1063 fold-in)

frs_take_up drew would_claim_child_benefit at a flat 89% (HMRC, 2022) over
every benefit unit with the FRS reporters on top, and
child_benefit_opts_out at a flat 23% over every benefit unit whatever its
income. The engine pays on the first and never reads the second, so 96%
of eligible children were claimed for and paid and obr.child_benefit sat
about 20% over.

New stage child_benefit_take_up, after pension_credit_take_up and before
CGT cloning:
- one temporary engine materialization of
  is_child_or_qualifying_young_person_for_child_benefit and
  adjusted_net_income (consumed only);
- claims by family, keyed on the age of the eldest eligible child: a
  reporter claims, the other families claim at rates solved from the
  eldest age down so the claimed share of the eligible children of each
  single year of age is HMRC's Table 15 rate (May 2025, opted-out
  families included), weighted at the stage's household weights; clipped
  rates are receipted;
- opt-outs: the published opted-out share of registered families
  (684,635 of 7,552,330, August 2025) of the claiming families, drawn
  among claiming non-reporting families with the highest adjusted net
  income at or above full withdrawal of the charge, then inside the
  taper for any remainder (thresholds read from the engine);
- would_claim_child_benefit = claims and not opted out (the flag the
  engine pays on); child_benefit_opts_out = opted out; a unit without an
  eligible child keeps its early draw.

Trial on the 1 October spine at design weights: 86.7% of eligible
children claimed for (target 87.1% at the frame's age mix, HMRC 86.6%),
9.0% of claiming families opted out, all from fully charged families,
11.1m children in payment against 13.5m before.

The published rows are vendored from the pinned Chronicle feed
(hmrc_child_benefit_statistics.json). Gate
uk_stage_child_benefit_take_up holds the claimed share overall and by
age and the opted-out share; the terminal take-up signal gate no longer
measures the two flags. New op kinds assign_family_claims_by_child_age
and assign_opt_out_by_charge_income with closed schema branches; graph
cells, kernels, fixture tool, roster 36 -> 37, microcosm-data gate maps,
coverage manifest, charter-H2 fixture and digests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The salary_sacrifice stage fitted its QRF on the FRS respondents asked
the question and predicted for everyone else in the frame, children and
people without a job included: on the 30 September national build 1.94m
of the 7.6m people with pension_contributions_via_salary_sacrifice had
no employment income (0.53m under 16) and carried GBP 7.1bn of the GBP
22.5bn, and the stage's headcount receipt counted them before its
conversion top-up.

The prediction population is now the people not asked who have
employment income; a person not asked with none is set to zero and the
receipt records the rows and mass. An asked person keeps what they
reported. The conversion tops the headcount up from the true starting
count (rate 0.15 of an 18.0m donor pool on the 1 October spine, cap 0.5).

Manifests, coverage manifest and charter-H2 fixture regenerated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tems 5, 6 and the identities)

On the national build from main, 22 percent of mortgage_debt (GBP 381bn) sat
on households off a mortgaged tenure, housing-association renters held GBP
300bn of main_residence_value, 8 percent of owners had none, net financial
wealth exceeded gross on 17 percent of households and property_wealth
departed from its components on a third. Tenure entered the chain as one
soft predictor, the recipient's is_renting came from an engine formula that
omits RENT_FROM_HA, and every total was drawn independently of its parts.

- One four-way tenure category on both sides (uk_runtime.tenure_constants,
  also read by the local targets): the donor's from Ten1R8 and DVPriRntR8,
  the recipient's from tenure_type. Its flags replace is_renting and
  has_mortgage_tenure; the stage reads no engine tenure formula.
- main_residence_value is fitted and drawn on the owner tenures only and
  mortgage_debt on the mortgaged tenure only, zero by rule elsewhere. The
  receipt records the donor mortgage mass the rule leaves out (mortgages on
  other property held off a mortgaged tenure), read against TotMortR8 and
  OthMortR8_sum.
- Totals are derived from drawn components: property_wealth (with a drawn
  remainder for buy-to-let, overseas and other property),
  gross_financial_wealth (with a drawn remainder) and net_financial_wealth
  as gross less consumer debt and the student loan balance. The pinned tab
  satisfies each identity on every row; cleaning refuses a donor that does
  not. A sum that leaves the donor's range gives up the excess from the
  remainder first, and the receipt counts the rows.
- Seven chain segments with their own child seeds (the first four roots are
  unchanged).
- New release-blocking gate uk_stage_was_wealth_coherence; the support gate
  also covers private_pension_wealth.
- regional_property_uprating scales the property components with the total,
  so the identity survives the uprating.

Manifests, schema, coverage manifest, H2 fixture (its WAS donor now
satisfies the identities) and gate digests regenerated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
On the 30 September spine (main c5a1cba) the identity tool failed E7 and E8:

- E7 read every SPI-synthetic household as support clone index 1; the income
  band donors carry the same flag at index 2. It now reads the donors' index.
- E8 compared the stored and recomputed student_loan_plan with Series.equals,
  which calls equal columns of different string dtypes unequal. It now
  compares values.
- E8's reversed-order recompute of the incidence anchor differed from the
  forward one by 1e-13: the sums the anchor's factors depend on were pairwise
  float sums, which move in their last bits under a row permutation. The
  anchor takes correctly rounded sums, so its weights do not depend on the
  row order.

With these, E7 and E8 both pass on that spine.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…assignment (#1063 items 7, 8)

The residential-property flag was realised by one weighted systematic walk
over every liable gainer in ascending gain order. The balance a heavy row
left behind was paid by the next row whatever its weight, so a light row at
the top of the gains order was flagged far more often than its probability,
and one row could move the realised residential gains by most of the Table 8
total (the cap-80 arm of #1045 failed that way).

- Each HMRC gain band has its own walk, offset and balance. The receipt
  reports per band the pool, expected and realised count and gains, and the
  walk's own bounds, with the share of realised residential gains on the ten
  largest flagged stakes and the largest pool stake against the target.
- uk_stage_hmrc_cgt_asset_type_spine_summary holds every band to those
  bounds and the totals to the wider of 5 percent and the summed band
  bounds. The Bernoulli sigma and maximum_gains_sigma are retired: the sigma
  assumed independent draws and shrank when rows were split.
- A zero-weight row can no longer be flagged.
- The main type is fitted once per claimant status: claimants to Table 7's
  shares among the three eligible types, non-claimants to the shares that
  restore the published composition. It is realised by an error-diffusion
  walk in descending gain order, so realised gains by type track the fit.
  The assignment is deterministic and seed 554 is retired.
- The receipt reports the composition by claimant status against Table 7.
  The fact fence and diagnostic-only status are unchanged.

Manifests re-rendered from the code, schema, coverage manifest, H2 fixture
and gate digests regenerated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
slc.borrowers.plan_2_liable and slc.borrowers.plan_2_above_threshold
expired on 2026-10-03; an expired entry raises in the national build and
in the release-cut certifier. Renewed one month on María's ruling of
2026-10-02; the plan-2 borrower coverage gap stays with microcosm#868.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…#1063 item 7)

The banded walk of e0b41a59b turned residential probabilities into whole
flags, so its gains error in each HMRC band was bounded by one row's stake,
and in the open band one stake (£10.9bn at a 0.2% probability) was 7.5
times the band's whole expectation: the band was either short by its
lottery expectation (−£172m) or over by a giant stake (+£10.3bn). Closing
rows, sub-bands and weight-keyed eligibility were considered and rejected
(ruling 2026-10-02).

- New expansion stage cgt_residential_split after cgt_incidence_anchor;
  hmrc_cgt_asset_type_spine moves after it (roster 35 → 36). The stage
  solves the residential logistic as the asset-type stage did and splits
  every household carrying liable gainers into arms: p·w and (1 − p)·w
  for one gainer, 2^k arms at product weights for k gainers (k > 3
  refused). The residential arm's gainer carries the whole gain in
  capital_gains_residential_property; lineage columns
  household_is_cgt_residential_clone, cgt_residential_clone_index (the
  kernel's id + j × multiplier rule) and cgt_residential_probability.
  Mass and every household's composition are conserved (graph policy
  conserve, declared factor 1.0, exact-total correction on an unsplit
  incumbent, mass_conserving coverage family). No draw, no seed.
- At design weights the residential count and gains are identities on
  Table 8a in every gain band. Gate uk_stage_cgt_residential_split_mass
  holds the conserved mass, the arm weights as the declared products,
  the arm count implied by the households by liable gainers, the solve
  and the identities by band; the asset-type summary gate holds the
  identities its arms restate and drops the walk bounds.
- hmrc_cgt_asset_type_spine types the residential arms and keeps the
  BADR claims and the Table 7 fit by claimant status; seed 553,
  _banded_systematic_flags and assign_residential_property_flag go.
- The weight-ratio and ESS family folds, the geography identity kernel
  (a cgt_residential_split path step), the export allow-list, the
  national sampling raw mask, the student-loans receipt and the E8
  identity receipt (arms reconstructed first, clone pairs and support
  copies checked on the arm-free frame) carry the new layer; the parity
  fixture, coverage manifest and gate digests are regenerated.

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

The certifier rehearsal on the 2026-09-30 national build failed 5 of 20
gates. Each is addressed at its source:

- uk_export_surface: the candidate label is microcosm_uk_2024_25; the
  certifier and the full-build evidence read the surface through
  uk_export_candidate_columns (structural ids stripped, the frame's weights
  presented as household.household_weight); the 43 declared stage outputs
  the incumbent never carried are allow-listed (ruling 2026-10-02); the one
  reviewed exclusion, person.incapacity_benefit_reported, leaves at the
  release boundary (uk_release_export_frame), with the graph readback
  expecting the same frame and the spine keeping the column for the SPI
  income stage.
- uk_degenerate_release_surface: FRS 2024-25 renames TRAIN to TRAIN2 and
  recodes it (9 = none of these, 10 = Kickstart); the education stage reads
  it, so is_in_approved_training carries signal; incapacity is dropped.
- uk_nonnegative_columns: the LCFS donor's diary consumption is floored at
  the declared floor (0) before the imputation, the clip ranges and the
  rake, with a receipt; the support gate declares the floor and holds it.
- uk_input_mass_parity: adult_ema and dfe_education_spending enter the
  per-reference register (the enhanced FRS barely carries either input).
- uk_qrf_tail_concentration: the sparse charity-gifts leaf re-enters the
  register (168 carriers; the 2026-09-21 retirement is recorded).

Smoke builds: uk_stage_child_benefit_take_up declares population_fact_check
as the Pension Credit gate does, and the anchor composition check allows
the absolute rounding tolerance the other mass identities use. The gates
manifest, coverage manifest, parity fixture and digests are regenerated;
the regression battery's clock moves to the entries' approval date.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The arms B and C, the certifier rehearsal R5, the Child Benefit trial, the
residential split and the rulings of 2026-10-02, with arm C2 and the final
build still to land.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ure, the pins and the readouts (#1063)

- Blocker: the five internal disability carriers leave the export
  allow-list and are dropped at the release boundary beside
  incapacity_benefit_reported; the input-mass evidence pin and its test
  mirror follow the c9 register entries.
- WAS mortgages (item 3): only the main-residence mortgage (TotMortR8) is
  tenure-stratified; the mortgages on other property (HMortGR8 less
  TotMortR8) are drawn on every tenure without a stratum and mortgage_debt
  is derived as their sum, so a renter's or an outright owner's buy-to-let
  keeps its mortgage beside the property. Eight chain segments (the first
  seven roots unchanged); the coherence gate holds the main-residence
  mortgage off the mortgaged tenure at zero and the mortgage identity on
  every row; the donor-range cap pins a float overshoot to the maximum.
- Item 4: scripts/residential_readout.py reports the by-band residential
  count and gains on calibrated weights beside the design identities, and
  the row-level weight shape with the arms; C2's readings are in the note.
- Item 5: the children-in-payment gap is the design-weight child base.
- Item 6: the split's docstring names what calibration binds.
- The stack arm's blocked cell, dwp/uc_payment_dist/SINGLE_annual_payment
  _28_800_to_30_000, has one supporting household whose award moved out of
  the band when the larger donor block reshuffled every later draw; it
  joins the measure exclusions beside its sibling of the same class.

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

Copy link
Copy Markdown
Collaborator Author

Thanks, all six addressed in the latest commit; the branch is now on main (#1084 merged) with the same twelve commits plus this one.

  1. Carriers (blocker). The five internal disability carriers are off the allow-list and leave at the release boundary with incapacity_benefit_reported (UK_RELEASE_EXPORT_DROPPED_COLUMNS); test_internal_disability_carriers_stay_out_of_export_registers passes, and the terminal-gates test now asserts the boundary drops exactly the reviewed exclusions plus the carriers.
  2. Plan-2 renewal. María's call: it stays on this PR, which she expects to merge today or tomorrow, and no dataset is built without it.
  3. Mortgages. Taken as suggested, with one difference: the other-property mortgage is HMortGR8 less TotMortR8 on the donor rather than OthMortR8_sum, so the identity mortgage_debt = main_residence_mortgage + other_mortgage holds on every donor row and no mass leaves (the £15bn of HMortGR8 that is neither TotMortR8 nor OthMortR8_sum stays). Only the main-residence mortgage is tenure-stratified; the other mortgages are drawn on every tenure, conditioned on the property the chain already placed, and mortgage_debt is the derived sum. Eight segments (the first seven roots unchanged), the coherence gate holds main_residence_mortgage_off_mortgaged_tenure_rows at zero and mortgage_debt_violation_rows at zero, and the receipt reports the other-mortgage mass held off the mortgaged tenure on each side instead of a dropped mass.
  4. By-band identities after calibration. scripts/residential_readout.py on C2's calibrated weights: the two bound national totals hold (count 202,628 vs 202,630; gains £12.246bn vs £12.242bn) and within them the solver moves mass between bands (£250–500k 3,680 → 4,256 taxpayers, £500k–1m 2,004 → 2,173, £1–2m 730 → 552, £2–5m 427 → 274, open band 134 → 139; under £250k within 3%). Row level with the arms in place: smallest positive calibrated weight 0.012 (an arm, 0.050; smallest design arm 0.144), positive-weight median 26.6 (arms 11.8), no row at zero. Both are in the receipts note, with the by-band identities now described as a design-weight property.
  5. Children in payment. The 11.1m is at design weights on the 1 October spine, where the eligible-child base is 14.04m; the rates match HMRC and opted-out families carry 1.82 children each, so claimed children are 12.17m and 1.09m of them opted out. On the 30 September build's calibrated weights the base is 14.79m, which at the same rates gives 11.7m against HMRC's 11.73m. The gap is the design-weight child base, not the rates or the opt-out ages.
  6. Wording. Done in the docstring; the manifest notes already named the two rows.

On the open calls: the opt-out pool and the Table 8a promotion are María's; the buy-to-let value sits in the drawn remainder of property_wealth, and with item 3 its mortgage now sits beside it.

One more thing the stack arm found: dwp/uc_payment_dist/SINGLE_annual_payment_28_800_to_30_000 has a single supporting household on every main-based spine (a disabled single council tenant, design weight 760 against 1,654). On the stack its award moved £678 lower, out of the band, when the larger donor block reshuffled the rows every later non-identity-keyed draw reads; the cell joins the measure exclusions beside its 27,600–28,800 sibling of the same uk-data#452 class, for María's signature.

🤖 Generated with Claude Code

@juaristi22
juaristi22 marked this pull request as ready for review October 2, 2026 14:05
juaristi22 and others added 6 commits October 2, 2026 15:30
…sehold UC band (#1063)

The stack arm on 5583250 calibrates to loss 0.00769 and blocks on two
cells. dwp/uc_payment_dist/COUPLE_NO_CHILDREN_annual_payment_22_800_to_24_000
has two supporting households on every main-based spine; both awards moved
out of the band when the stages drawn after the WAS wealth chain read their
rows in a new order, so it joins the uk-data#452 measure exclusions beside
its siblings. hmrc.spi_region.income_tax_by_region_12570_15000@E12000008
sits on its target at design weights (69.6m against 69.5m; 62.4m on main,
where the solver pulled it to +22.6%) and the same pull now lands it at
+32.0%; the regional low-band cells move 10-30% at design weights between
spines because the salary-sacrifice and pension draws are positional. It is
deferred four weeks in the target-fit register (it was deferred on
2026-09-18 and retired on #1012 at +24.5%), for the draw-order fix at its
source and the pull's diagnosis.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…l employment-income bands (#1063)

The deferral's reason and the receipts note said the draw order; the stack 2
solution says the solver fills the 20-31% short national employment-income
bands with the same low-paid South East households that carry this cell.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…t a flat index (#1063)

The three lowest employment-income rows were 20-31% short at design weights
on every spine. They are SPI 2023-24 Table 3.6 amounts, the employment
income of taxpayers whose total income falls in a fixed nominal band, and
the registry projected them to 2025 with the flat OBR average-earnings index
(1.107), while the count, total-income and tax rows by band already use
HMRC's own band-specific growth from Income Tax Liabilities Table 2.5. An
amount held in a fixed band moves with the band's membership: HMRC projects
the 12,570-15,000 band's total income +5.0%, the 15,000-20,000 band's -2.3%
and the 20,000-30,000 band's +0.3%. The SPI tape re-banded under the
engine's own indices agrees with the direction in every band.

The employment, self-employment and private-pension amount references (39)
now use hmrc.itl_2026.total_income_growth_by_total_income_band. The three
lowest employment targets move from GBP 18.7bn, 53.8bn and 190.4bn to
17.7bn, 47.5bn and 172.5bn, and the frame's design-weight gaps from -19.8%,
-31.2% and -25.1% to -15.5%, -22.1% and -17.4%. Dividend and savings-interest
amounts keep their own indices, since those move with rates rather than band
membership. The remaining gap is the frame's composition of the low bands
(the receipts note names where), a follow-up in the spine.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ers just above the allowance (#1063)

Arm stack 3 on the band-aware targets: 7/7 seam gates, loss 0.00713, the
employment rows within 4-12%, the South East cell still +30.3%. The frame
has 0.278m South East taxpayers in the 12,570-15,000 band against HMRC's
0.364m (2.31m against 2.97m nationally) and they pay GBP 250 each against
GBP 215, so the bound count rows scale them by a third and the tax cell
overshoots. The deferral's reason and the note say so; the fix is the
lowest band's support in the frame.

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

The incumbent-2025 compile-parity receipt pinned the 39 SPI Table 3.6
amount references at their flat-index values. With the band-aware ITL
growth projection (2fe7d8b) the compiled values moved and the release-cut
certifier's first part, uk_ledger_compile_parity_incumbent_2025, refused the
final build's candidate on the stale signatures. Regenerated against the
pinned ledger (facts 28b7105…, national surface): exactly those 39 rows
change, 243 parity, target-reference and country-spec tests pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…plit order-free (#1063)

The E8 identity receipt on the final build's spine failed three ways.
Two share a root on main: since microcosm#1069 c9 the salary_sacrifice
stage lowers a converted record's employment_income and zeroes its employee
contribution in place, after the CGT support split and the incidence anchor
banded every carrier on that pay. The receipt's recomputes therefore banded
2,797 clone carriers on post-sacrifice pay, moved 82 of them down a Table 3
band, derived different anchor targets (55,622 households off by up to
286) and selected a different support family in two bands. The stage now
keeps a converted record's pre-conversion pay on
salary_sacrifice_pre_conversion_pay (zero elsewhere; an internal carrier
dropped at the release boundary), and every receipt of a stage before
salary_sacrifice reverses the conversion from it.

The third was the split's own: two person orders of the same frame gave
arm weights 1e-13 apart because the logistic solve's bisection sums are
pairwise float sums. The split walks the liable gainers in person_id order
(the identity on the spine's sorted tables) and breaks a tie for the
exact-total correction row by household id; the order test is bitwise.

Manifests, coverage manifest, the UK graph's cell inventory and the
charter-H2 fixture regenerated; receipts for the 2fe7d8b final build in
the experiment log.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
juaristi22 and others added 2 commits October 3, 2026 00:54
…ema input-mass exclusion (#1063)

The release-cut certifier on the second final build reached all 20
gates. Two failed on columns the release boundary drops: the five
internal disability carriers and the salary-sacrifice pre-conversion pay
carrier leave the candidate (UK_RELEASE_EXPORT_DROPPED_COLUMNS), so
uk_nonnegative_columns and uk_qrf_tail_concentration, which check every
declared stage output, reported them absent. Both bindings now read an
export-dropped declared column from the spine frame the certifier
supplies, at the spine's weights; without that frame the absence still
fails, as before.

uk_input_mass_parity failed the adult_ema exclusion as stale: the build
carries it at +74% against the enhanced-FRS reference, inside the
tolerance, so the entry is retired and the sealed evidence digest moves.
The gate's three live findings (access_fund, jsa_income_reported,
working_tax_credit_reported) are diagnosed in the experiment log and left
to the ruling: one FRS outlier at the calibration's weight bound and the
SPI channel's legacy-benefit leaves, neither new to this stack.

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

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

This branch has not been deployed

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

Labels

None yet

Projects

None yet

2 participants