Skip to content

UK SPI stage 1 keeps 4–17% of the tape's donors; replace the removed QRF tail gate with a donor-fidelity check #1276

Open

Description

@juaristi22

Status (16 October 2026)

The original write-up is in this issue's edit history. Weighted aggregates and shares only below; counts stay in local receipts.

Why the draws have little support

  1. Some components are rare on the tape itself. TAXTERM has a value on 0.024% of the tape's weighted records and OTHERINC on 0.12%. At the SPI channel's size, that prevalence gives a few dozen carriers at most. So no check that requires more than 100 independent carriers can be met honestly for these columns.

  2. The stage-1 resample discards most donors. uk_runtime/spi_income.py resamples the 2022-23 tape with replacement, in proportion to FACT, at DEFAULT_SPI_DONOR_SAMPLE_SIZE = 100_000, and then fits on uniform weights, as the enhanced-FRS pipeline does. The forest sees only part of each component's tape carriers (expected under the resample's design):

    stage-1 output share of the tape's carriers kept
    hmrc_spi_pay 11.1%
    savings_interest_income 11.7%
    private_pension_income 13.4%
    dividend_income 6.2%
    self_employment_income 6.4%
    hmrc_spi_state_pension_income 13.4%
    hmrc_spi_employment_expenses 11.0%
    property_income 5.6%
    hmrc_spi_employment_benefits 7.0%
    gift_aid 4.8%
    other_investment_income 4.4%
    hmrc_spi_miscellaneous_employment_income 5.5%
    hmrc_spi_incapacity_benefit_income 17.3%
    hmrc_spi_unemployment_benefit_income 14.8%
    hmrc_spi_other_social_security_income 11.1%
    hmrc_spi_other_income 16.0%
    hmrc_spi_taxable_termination_pay 4.5%
    charitable_investment_gifts 5.0%

    For common components the loss doesn't matter, since thousands of donors survive. For rare ones it sets the shape of the column. The QRF module protects rare sign classes from its own n-of-n bootstrap, but this pre-fit resample sits upstream of that protection, and every forest then runs its own bootstrap on top (QRF amount forests lose donor design weights after the n-of-n bootstrap: extreme-tail draws are a seed lottery #481).

  3. Copy rows inflate row counts and spread the mass. CGT clones copy their parent's values at split, small weights, and SPI income-band donor households carry small weights by design. Any tail check has to count independent draws, not rows.

  4. Termination pay sits low against the tape. At calibrated weights the build carries roughly half the tape's weighted total, before uprating; at design weights it sits much closer. These SPI concepts are structurally zero on FRS rows, so part of the gap may be coverage rather than the draw.

  5. Incapacity-benefit income dropped with Draw SPI-channel incomes at the FRS twin's conditional rank (#840) #1256. Its prevalence among SPI recipients fell to about a sixth of the tape's weighted share. Most of the tape's carriers have no pay, trade or pension, and Draw SPI-channel incomes at the FRS twin's conditional rank (#840) #1256 leaves those twins' copies undrawn.

Remaining work

Sequencing

  1. A runs on the merged base.
  2. B can land independently of A. If A changes the pool, arm B's tolerances after A.
  3. Neither is in the Budget release (UK local-area release: publish a K=15, 150,000-household dense dataset in at most two builds #1301).

Done when

  • A receipt under experiments/ gives the stage-1 donor-pool shares and the chosen sampling, with cost and twin measurements.
  • The replacement check blocks release on the material columns and meets its three acceptance cases.
  • The termination-pay gap is decomposed, and the incapacity-benefit draw is decided.
  • A national candidate built from main certifies with the replacement check in place.

Relates #481 (per-forest bootstrap), #462 and #464 (the incident and the old gate), #840 and #1256 (the stage-1 draw), #1095 and #1100 (spi_benefit_coherence), #1063, #1242, #1277, #1281, #1288.

Activity

  1. juaristi22 commented on Oct 14, 2026

    @juaristi22
    CollaboratorAuthor

    Request: design a gate that actually catches imputation failures

    #1281 removes the UK QRF tail-concentration gate (uk_qrf_tail_concentration). This comment records why, and asks for a replacement that measures what the gate was meant to.

    What the removed gate measured

    On the 14 October national build from main (121fd4dd6), the gate blocked release-cut certification twice in one day, neither time for a data-quality reason:

    Its statistic is the share of a column's weighted mass held by the 100 heaviest rows. It fails above 0.99947 and only checks columns with at least 104 non-zero rows. On that build:

    column rows independent draws top-100 share
    charitable_investment_gifts 135 61 1.0000
    hmrc_spi_taxable_termination_pay 121 52 0.9997
    hmrc_spi_incapacity_benefit_income 157 75 0.9996
    hmrc_spi_other_social_security_income 182 89 0.9991
    hmrc_spi_other_income 200 89 0.9984
    hmrc_spi_unemployment_benefit_income 293 145 0.9842
    other_investment_income 3,020 1,255 0.6988
    dividend_income 15,076 6,584 0.5490
    • It measured clone-copy arithmetic. Every column near the threshold has fewer than 100 independent draws, so its "top 100 rows" is every draw plus a few copies. Nearly all the remaining rows are CGT-clone copies with small weights. The share falls as the row count rises, and a pass or fail comes down to whether those copies hold about 0.04% or 0.05% of a small column's mass.
    • It could not catch its own incident. The Build M live default: non_sch_d_capital_gains lands at $74.6B vs its $10.2B SOI target (+635%); Schedule-D CGD route is empty #462 signature (100 of 2,295 rows holding 89% of a column) passes at 0.99947. The module docstring already recorded this in August: no single threshold was both incumbent-compatible and incident-catching.
    • The columns it tripped on carry little weight. Three of the failing columns (hmrc_spi_incapacity_benefit_income, hmrc_spi_other_social_security_income, hmrc_spi_other_income) are not policyengine-uk variables, and no national target reads them. hmrc_spi_taxable_termination_pay feeds employment_income at about 0.02% of its total.
    • It missed a real change except by accident. Draw SPI-channel incomes at the FRS twin's conditional rank (#840) #1256 cut incapacity-benefit income in the SPI channel from 284 independent draws to 75. Its prevalence among SPI recipients fell from 1.47% to 0.21%, against 1.26% of weighted records on the SPI tape. On the tape, 91% of these carriers by weight have no pay, trade or pension, and Draw SPI-channel incomes at the FRS twin's conditional rank (#840) #1256 leaves such twins' copies undrawn. The gate flagged this only because the drop pushed the column into the narrow band of row counts where the arithmetic trips. Had the draws landed on 300 rows, it would have passed while prevalence sat about six times too low.

    What the replacement should do

    1. Compare each imputed column with its donor. Stage-1 SPI outputs are compared with the SPI tape and stage-2 outputs with the FRS rows they train on. The check covers weighted prevalence and weighted total, with tolerances armed from a measured baseline plus recorded headroom. It must fail a Build M live default: non_sch_d_capital_gains lands at $74.6B vs its $10.2B SOI target (+635%); Schedule-D CGD route is empty #462-style inflation (a column's total several times its donor's) and the Draw SPI-channel incomes at the FRS twin's conditional rank (#840) #1256 incapacity-benefit drop.
    2. Compare the tail's shape with the donor's, on independent draws. Collapse CGT-clone copies onto their parent draw and treat each non-clone SPI row as a draw. Then compare how heavy the build's upper tail is with the donor's at a matched carrier count, for example the share held by the top 1%, or by the top k at the column's own count. Flag a build tail heavier than the donor's by more than a measured margin.
    3. Scope by materiality. Block on columns the engine reads or that feed an engine input, and on unbound columns where no calibration target would see a defect. Report the rest as diagnostics.
    4. Keep a concentration backstop for the large engine-read columns, re-measured on a current UK baseline with headroom, and low enough to fail the Build M live default: non_sch_d_capital_gains lands at $74.6B vs its $10.2B SOI target (+635%); Schedule-D CGD route is empty #462 signature.
    5. Acceptance cases: a replay of the Build M live default: non_sch_d_capital_gains lands at $74.6B vs its $10.2B SOI target (+635%); Schedule-D CGD route is empty #462 signature must fail; the Draw SPI-channel incomes at the FRS twin's conditional rank (#840) #1256 incapacity-benefit drop must fail; clone-copy arithmetic on the 14 October build must pass. The stage-1 donor-pool finding above (4–17% of tape carriers kept) should be visible in the gate's details for each column, even where it doesn't block.
    6. Receipts: measure on the latest national candidate and its spine. Commit receipts under experiments/, with weighted aggregates only and cells under 10 suppressed.

    Until a replacement lands, no release-blocking check covers imputed-column tails. tools/measure_uk_weighted_integrity_baselines.py and uk_qrf_tail_concentration_columns stay in place as the measurement surface for this work.

  2. juaristi22 commented on Oct 15, 2026

    @juaristi22
    CollaboratorAuthor

    One more requirement for the replacement, from Vahid's review of #1281: keep raw carrier and independent-draw counts out of committed gate notes and out of any report that ships with a release. The removed gate's spec notes and its report details both carried small per-column counts. Shares, ratios and pass or fail are enough for the record. Count-level evidence stays in local receipts, with cells under 10 suppressed.

    This issue also absorbs #1277's point: the replacement counts independent draws, not rows (item 2 of the request above). #1277 closes when #1281 merges.

  3. added a commit that references this issue on Oct 15, 2026
  4. changed the title [-]UK SPI stage 1 keeps 4–17% of the tape's donors, and the QRF tail gate counts rows rather than draws[/-] [+]UK SPI stage 1 keeps 4–17% of the tape's donors; replace the removed QRF tail gate with a donor-fidelity check[/+] on Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions