Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
667fe53
Rebase SPI donor savings to SPI's 2023-24 bands, then the OBR path
juaristi22 Oct 30, 2026
f3cb3dd
Widen the UC take-up gate to 0.06 for the savings rebase
juaristi22 Oct 30, 2026
b441bb5
Hold the SPI £1m-and-over interest row on its measured distortion
juaristi22 Oct 30, 2026
4a6dc72
Rebase SPI donor savings on the tape's taxpayers
juaristi22 Oct 30, 2026
092de13
Book FRS savings income on SPI's definitions
juaristi22 Oct 30, 2026
b822474
Locate SPI copies' other-income draw at the twin's other income
juaristi22 Oct 30, 2026
9ee6fbd
Merge main into uk-savings-costing-anchor-1418
juaristi22 Oct 30, 2026
995969f
Leave the savings rebase off tapes whose weights don't gross
juaristi22 Oct 30, 2026
e5ac7a5
Regenerate the H2 spine fixture for the other-income analogue
juaristi22 Oct 30, 2026
53ad299
Zero a missing self-analogue output before the SPI transport
juaristi22 Oct 30, 2026
45c2888
Regenerate the H2 spine fixture on policyengine-uk 2.164.2
juaristi22 Oct 30, 2026
d563f4f
Bind SPI Table 3.10's employment-income counts by self-employment band
juaristi22 Oct 30, 2026
5c0137f
Anchor HMRC's tax on savings rows on the OBR savings path (option A)
juaristi22 Oct 31, 2026
6485af4
Join UC take-up pools that cannot meet DWP's count
juaristi22 Oct 31, 2026
f3540cb
Store the self-employed's gross receipts for the trading allowance
juaristi22 Oct 31, 2026
f9676fe
Revert the OBR-path anchor for HMRC's tax on savings rows (option A)
juaristi22 Oct 31, 2026
0c2f531
Declare the trading allowance receipts stage's source-household read
juaristi22 Oct 31, 2026
3702769
Pin the trading allowance receipts stage in the stage-order tests
juaristi22 Oct 31, 2026
b9bcd70
Drop the ISA subscriptions stage's empty fit-weight records
juaristi22 Oct 31, 2026
c5ef127
Anchor the SPI copies' gift leaves to the tape's weighted totals
juaristi22 Oct 31, 2026
a8e2587
Bind DWP's income-based Jobseeker's Allowance spending
juaristi22 Oct 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1418-spi-donor-savings-rebase.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The SPI income stage now rebases the 2022-23 tape's interest and other income to HMRC's published 2023-24 Table 3.7 amounts band by band (the tape banded by its own total income; the published rows are vendored as `uk/hmrc_spi_table_3_7_savings_rows.json`), then carries both to the frame's year on the OBR's savings income growth (October 2026 EFO, Table 4.5), the path the Table 3.7 band targets take. Before, each column moved by its engine index from 2022 to 2024: other income by GDP per head (x1.09) where SPI's own other income grew x1.65 in the single year to 2023-24, and interest by ONS household interest (x2.27) where SPI's grew x2.69 in that year. The frame then carried about 70 percent of the Table 3.7 savings rows at design weights, and the solve met them by stretching the weights of the few households that carry them. On the tape, the £1m-and-over band's other income outweighs its interest, as in the published table, so the rebase also undoes the split the two indices had flipped (#1418 savings follow-up).
1 change: 1 addition & 0 deletions changelog.d/1428-dwp-jsa-income-based.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The national calibration binds DWP's income-based Jobseeker's Allowance spending, `dwp.jsa.amount_income_based` (benefit expenditure and caseload tables, Spring Forecast 2026), at the calendar-2025 window of the fiscal years, as the pension-age Housing Benefit row is (#1428; María's ruling of 2026-10-31). Income-based JSA closes with the move to Universal Credit: £91.0m in FY2024-25 and under £1m from FY2025-26. Without the split, the calibration met OBR's total JSA by scaling income-based reporters together with contributory claimants. The release-cut input-mass check then found `jsa_income_reported` beyond its band.
1 change: 1 addition & 0 deletions changelog.d/1428-frs-other-income-definitions.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FRS rows now book savings income on SPI's definitions (#1428). NS&I Direct Saver and the other NS&I and bank products go to `savings_interest_income`, because SPI has counted all interest paid gross or net as bank interest since 2016-17 (Table 3.7 note 4); gilts stay in `other_investment_income`. Income from an annuity not bought with pension funds or from a trust or covenant (FRS PENTYPE 4 and 5) moves from `private_pension_income` to `other_investment_income`, the "other income" Table 3.7 counts (note 6). The FRS records the whole annuity payment, of which only the income element is taxable.
1 change: 1 addition & 0 deletions changelog.d/1428-isa-fit-weight-records.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The ISA subscriptions stage no longer declares an empty list of fit-weight records (#1428). It fits no model, but the release-cut weights audit read its empty list as a fitting stage that recorded nothing, so every national certified after #1421 failed `uk_weights_audit`.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The national calibration binds SPI 2023-24 Table 3.10's individuals with employment income by self-employment band, `hmrc.spi.employment_income.count_by_self_employment_income_band`, over the eight bands from £1 to £10,000 that the self-employment counts and amounts already bind (#1418; María's choice of 2026-10-30). With the self-employment counts alone, the solve could meet each band with traders who have no pay, while in Table 3.10 more than half of the traders below £3,000 also have employment income; an employee's side profit is taxed at their marginal rate, so the mix of small traders decides what the Budget's trading allowance measures cost. Each person with pay over the year (`employment_income_annual`, the variable Table 3.6's employment rows read) counts in their own self-employment band, taxpayer or not. Table 3.10's column is wider, income assessed under ITEPA 2003 plus other miscellaneous income, so like Table 3.6's employment rows the binding leaves out miscellaneous income, benefits in kind and the contributory benefits. Counts grow to 2025 with HMRC's projected growth of all taxpayers (Income Tax liabilities statistics Table 2.5), as the self-employment counts do, and the zero band and the bands from £10,000 are signed out row by row.
1 change: 1 addition & 0 deletions changelog.d/1428-spi-gift-anchor.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The SPI income stage anchors the drawn copies' Gift Aid and charitable investment gifts to the SPI tape's own weighted totals (#1428; María's ruling of 2026-10-31). Both are sparse leaves that the stage-1 chain draws after, and conditioned on, the income leaves. Rebasing the donor savings on the tape's taxpayers therefore changed which few donors' gifts the copies drew, and the release-cut input-mass check found `charitable_investment_gifts` beyond its band. Each column's total over the drawn copies, at the stage's household weights, is now scaled to the tape's FACT-weighted total. A column that the tape or the copies carry none of is left as drawn. The stage evidence records each column's factor, never a total or a count.
1 change: 1 addition & 0 deletions changelog.d/1428-spi-other-income-analogue.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The SPI stage-1 transport now locates each copy's other-income draw at its FRS twin's own other income (gilts, trust and purchased-annuity income), as it does for the other investment leaves, instead of an identity uniform that ignored the twin's holdings (#1428).
1 change: 1 addition & 0 deletions changelog.d/1428-spi-rebase-taxpayers.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The SPI donor savings rebase now computes each band's factor over the tape's taxpayers (TAXPAYER 1), since SPI Table 3.7 counts taxpayers only, and gives non-taxpayers the all-bands factor. Over all records, non-taxpayers' savings income in the two lowest bands left the rebased taxpayers short of the published amounts there (#1428).
1 change: 1 addition & 0 deletions changelog.d/1428-trading-allowance-receipts.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A new last spine stage, `trading_allowance_receipts`, stores the self-employed's gross receipts (`self_employment_gross_receipts`), so policyengine-uk applies the trading allowance against receipts as the law does (#1428; María's choice of 2026-10-31). Neither the FRS nor the SPI tape records turnover. Without receipts the engine relieved profits up to the allowance in full and nothing above it, so a rise in the allowance missed its main base: traders who already claim the £1,000 allowance against larger receipts. Under current law a claimant's receipts are their profit plus £1,000 exactly. Each trader with a positive profit is flagged as a claimant with probability 0.64 for profits up to £4,500, 0.32 from £4,500 to £9,000 and none above, drawn on their own `person_id`. A claimant's receipts are their profit plus £1,000; any other trader's are their profit plus £3,000, expenses at the raised allowance, since the costing books traders who switch to the allowance as behaviour. The rates are calibrated to HMRC's policy paper (about 400,000 taxpayers with a lower bill, by about £380 on average) until HMRC publishes claimant counts. Every trader with a positive profit is treated as filing, as the Table 3.10 counts the calibration binds are of filers, so a profit of £1,000 or less is now taxed where the engine used to relieve it in full. `self_employment_gross_receipts` leaves the register of engine inputs left at their default and joins the export surface's allowed columns. The stage spec and schema, the graph and kernel wiring, the spine build, the coverage manifest, the H2 fixture and the data contract's gate digests follow.
1 change: 1 addition & 0 deletions changelog.d/1428-uc-take-up-joins-unmet-pools.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The Universal Credit take-up stage now joins a pool that cannot meet DWP's count to a neighbouring pool, so each family type's caseload lands on DWP's wherever its entitled units allow (#1428; María's ruling of 2026-10-31). The stage pools adjacent award bands within a family type until they rest on enough sources. A pool can't meet DWP's count when its reporters' and other undrawn units' expected claims already exceed it, or when it exceeds every entitled unit. Such a pool used to keep its clipped rate, which left the family type's caseload off DWP's by the gap. Which bands fell short also moved with small upstream changes: in this pull request single people's caseload ended 0.0568, then 0.0674, of DWP's count further from it than before the redraw. Now, lowest first, such a pool joins the adjacent pool with more room for its gap, the lower on a tie, until every pool can meet its count or the family type is one pool. The room is the claims the neighbour's drawn units can add beyond its own count when the pool falls short, or the drawn claims it can give up when the pool overshoots. The `uk_stage_uc_take_up` gate returns to main's 0.055, so the branch's widening to 0.06 is undone. The stage spec, the coverage manifest, the H2 fixture and the data contract's gate digests follow.
11 changes: 11 additions & 0 deletions docs/uk-income-anchors-280.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@ membership carries the uprated value with its receipt.
by `tools/pin_uk_uprating_engine_values.py`, whose `--check` refuses drift), so
it runs and reproduces without policyengine-uk; a `requires_uk` test holds the
pins in lockstep with the installed engine, and an engine bump re-runs the tool.

The SPI donors reach the frame's year apart from these indices for interest
and other income (microcosm#1418 savings follow-up). The income stage first
scales each donor's two columns to HMRC's published 2023-24 Table 3.7 amounts
in its band of total income (`spi_income.rebase_spi_donor_savings`, the rows
vendored as `uk/hmrc_spi_table_3_7_savings_rows.json`), then grows them on the
OBR's savings income path the Table 3.7 targets take
(`hmrc_uprating.uk_obr_savings_income_level`). Each band's factor is the
published amount over the tape's FACT-weighted one; a band the tape carries
none of is left as it is. From the frame's year on, the engine projects both
columns at GDP per head.
- Count rows: `hmrc.itl_2026.taxpayer_count_growth_by_total_income_band`, HMRC's
own projected growth in Income Tax payers for the Table 2.5 band that contains
the SPI band (Income Tax liabilities statistics, July 2026), as the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
"assign_student_loan_plan_cohorts",
"assign_main_asset_type",
"assign_trips_from_band_means",
# microcosm#1428: the self-employed's gross receipts.
"assign_trading_allowance_receipts",
"assign_uniform_draw",
"aggregate_person_to_benunit",
"allocate_per_capita_from_cell_table",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6612,6 +6612,75 @@
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": [
"kind",
"output",
"profit",
"draw",
"seed",
"claimant_bands",
"claimant_expenses",
"other_expenses",
"calibration",
"rule"
],
"properties": {
"kind": {
"const": "assign_trading_allowance_receipts"
},
"output": {
"type": "string"
},
"profit": {
"type": "string"
},
"draw": {
"type": "string"
},
"seed": {
"type": "integer"
},
"claimant_bands": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"rate"
],
"properties": {
"upper_inclusive": {
"type": "number",
"exclusiveMinimum": 0
},
"rate": {
"type": "number",
"minimum": 0,
"maximum": 1
}
}
}
},
"claimant_expenses": {
"type": "number",
"minimum": 0
},
"other_expenses": {
"type": "number",
"minimum": 0
},
"calibration": {
"type": "string"
},
"rule": {
"type": "string"
}
}
},
{
"type": "object",
"additionalProperties": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,15 @@
"approved_on": "2026-10-23",
"expires_on": "2027-01-23"
},
{
"name": "hmrc.spi.savings_interest_income.amount_by_total_income_band.band_1000000",
"reason": "Measured distortion, not thin support, as for the ten HMRC £1m-and-over cells (María's rule of 2026-10-21). With the SPI donors' interest and other income rebased to SPI's 2023-24 bands (microcosm#1428), the first national built from f3cb3ddd3 fitted every other interest and other-income band, the £1m-and-over other-income row included, and missed this row outside the uk_target_fit band. Held out of the objective and measured on every evaluation. The row rests on the SPI Public Use Tape, so its figures and its neighbours' stay out of this register.",
"tracking": "microcosm#1428",
"approved_by": "juaristi22",
"adjudication": "María's ruling of 2026-10-30 on the savings follow-up (microcosm#1428): hold the £1m-and-over savings interest amount row as a measured distortion, rebuild the national on the same spine and score it; how the rebase sits in the top bands at design weights is a follow-up.",
"approved_on": "2026-10-30",
"expires_on": "2027-01-30"
},
{
"name": "slc.repayments.england_plan_5",
"reason": "Structural under-support: the student-loans stage tops Plan 5 up to SLC's liable stock, which binds, and fewer than ten source households carry Plan 5 repayments on #1333's 2.145.0 spine, so no weights reach SLC's repayment figure; the row's model figures are withheld.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@
"kind": "legacy_json",
"schema_id": "legacy_json"
},
{
"path": "hmrc_spi_table_3_7_savings_rows.json",
"kind": "legacy_json",
"schema_id": "legacy_json"
},
{
"path": "slc_liable_stocks.json",
"kind": "legacy_json",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1074,10 +1074,11 @@
},
{
"targets": [
"hmrc.spi.employment_income.count_by_self_employment_income_band",
"hmrc.spi.self_employment_income.amount_by_self_employment_income_band",
"hmrc.spi.self_employment_income.count_by_self_employment_income_band"
],
"reason": "Banded by groupby on the person's own self-employment income after loss relief, SPI Table 3.10's range; the band is the fan-out dimension, not a measurement filter, and Table 3.10 counts taxpayers and non-taxpayers alike. Bound from GBP 1 to GBP 10,000 only, the bands above stay with Table 3.6's self-employment rows by total income, so the two tables do not pull against each other (microcosm#1418)."
"reason": "Banded by groupby on the person's own self-employment income after loss relief, SPI Table 3.10's range; the band is the fan-out dimension, not a measurement filter, and Table 3.10 counts taxpayers and non-taxpayers alike; its employment-income count adds the year's pay as a filter over the same bands. Bound from GBP 1 to GBP 10,000 only, the bands above stay with Table 3.6's self-employment rows by total income, so the two tables do not pull against each other (microcosm#1418)."
}
]
}
3 changes: 2 additions & 1 deletion packages/microcosm-build/src/microcosm/build/uk/gates.json
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@
"check": "uc_take_up",
"maximum_caseload_deviation": 0.055
},
"notes": "Stage-time gate over the Universal Credit take-up receipt (microcosm#1334): every reporter claims and no unit outside the draw changes; in each DWP family type the pools whose target is reachable realize DWP's calendar-2025 caseload within 0.055 of it at the stage's weights, Great Britain only, counting units the engine moves off their legacy benefits at their expected claim at the closure; and the family type's caseload is no further from DWP's than before the redraw, give or take 0.055 of DWP's count. The tolerance was 0.05 until microcosm#1418: routing gilt interest out of dividend_income reshuffles the SPI channel's draws. On the spine of 2026-10-28 (da944f12c), single people's caseload ends 0.0523 of DWP's count further from it than before the redraw (0.0486 on the spine before, 3c0d11b6's), so 0.055 holds it with about 8,000 to spare; couples without children, lone parents and couples with children end closer than before by 0.53, 0.054 and 0.42 of their counts, and every family type's reachable pools realize DWP's count within 0.0015. The terminal take-up signal gate no longer measures would_claim_uc, whose rate is now set by award band and family type, not as one share of the age population."
"notes": "Stage-time gate over the Universal Credit take-up receipt (microcosm#1334): every reporter claims and no unit outside the draw changes; in each DWP family type the pools whose target is reachable realize DWP's calendar-2025 caseload within 0.055 of it at the stage's weights, Great Britain only, counting units the engine moves off their legacy benefits at their expected claim at the closure; and the family type's caseload is no further from DWP's than before the redraw, give or take 0.055 of DWP's count. The tolerance was 0.05 until microcosm#1418: routing gilt interest out of dividend_income reshuffles the SPI channel's draws. In the savings follow-up (microcosm#1428), small upstream changes moved single people's GBP 1,000 to 1,100 band into a pool whose DWP count exceeds its entitled units, and the family type's caseload ended 0.0568, then 0.0674, of DWP's count further from it than before the redraw. The stage now joins a pool that cannot meet its count to the adjacent pool with more room for its gap, so a family type's caseload lands on DWP's wherever its entitled units allow, and the tolerance stays at 0.055 (María's ruling of 2026-10-31). The terminal take-up signal gate no longer measures would_claim_uc, whose rate is now set by award band and family type, not as one share of the age population."
},
{
"id": "uk_stage_pension_credit_take_up",
Expand Down Expand Up @@ -1251,6 +1251,7 @@
"person.private_pension_income_annual_share",
"person.self_employment_income_annual_share",
"person.self_employment_income_other_spells",
"person.self_employment_gross_receipts",
"person.esa_health_condition_proxy",
"person.esa_includes_support_component",
"person.esa_support_group_proxy",
Expand Down
Loading
Loading