Repository navigation
SPI donor savings rebased to SPI's 2023-24 bands (savings follow-up to #1418) - #1428
Draft
juaristi22 wants to merge 17 commits into
Draft
juaristi22 wants to merge 17 commits into
juaristi22 wants to merge 17 commits into
Conversation
The SPI income stage now moves the 2022-23 tape's interest and other income to HMRC's published 2023-24 Table 3.7 amounts band by band, with the tape banded by its own total income, before any draw. The OBR's savings income growth then carries both columns to the frame's year, 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), interest by ONS household interest (x2.27). SPI's own amounts grew x1.65 and x2.69 in the single year to 2023-24. The frame carried about 70 percent of the savings rows at design weights, and the solve met them by stretching 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; the two indices had flipped that split. - uk/hmrc_spi_table_3_7_savings_rows.json vendors the 26 published rows from the feed pin, declared in the vendoring register and the country package. - spi_income.rebase_spi_donor_savings scales the prepared tape. A band the tape carries none of is left as it is and named in the receipt. The forest and the band-donor resample both read the rebased tape. - _spi_income_uprating_factors grows the two columns from 2023-24 on hmrc_uprating.uk_obr_savings_income_level. Both columns now run on the projection's GDP per head from the frame's year on (SPI_HISTORY_INDEX_DIFFERS_FROM_PROJECTION). - Tests cover the rebase, the published rows, the OBR factor and the stage wiring, and the engine lockstep tests are updated. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The first spine built from 667fe53 stopped at uk_stage_uc_take_up. Single people's caseload ended 0.0568 of DWP's count further from it than before the redraw, against 0.0523 on PR C's spine and a tolerance of 0.055. The rebased SPI savings feed the UC reporter redraw's investment-income predictor. The few SPI benefit units whose report changes move the take-up stage's pool merging, which joins adjacent award bands until each pool rests on enough sources. Single people's £1,000 to £1,100 award band moved from a reachable pool into one whose DWP target exceeds the entitled units. The reachable pools still realize DWP's counts within 0.005. The tolerance goes to 0.06 (María's ruling of 2026-10-30), as PR A took it from 0.05 to 0.055 for the gilt reshuffle. Pool boundaries that move with small upstream changes are a follow-up on #1334. The gate's note, the committed-parameter test and the data contract's mirrored digests are updated. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The national built from f3cb3dd fitted every SPI Table 3.7 interest and other-income band but one, the £1m-and-over other-income row included. It missed the £1m-and-over interest amount row outside the uk_target_fit band. By María's ruling of 2026-10-30, the row is held out of the objective as a measured distortion, as the ten HMRC £1m-and-over cells are, and measured on every evaluation. The register entry carries no figures for the band. How the rebase sits in the top bands at design weights is a follow-up. Co-Authored-By: Claude Opus 5.5 <[email protected]>
SPI Table 3.7 counts taxpayers only, but the rebase divided its amounts by the tape's amount over all records in each band. In the two lowest bands non-taxpayers hold much of the tape's savings income, so the rebased taxpayers reached only part of the published amounts there. The prepared tape now carries the tape's TAXPAYER flag. Each band's factor is computed over its taxpayers and applied to them; non-taxpayers take the all-bands factor, as donors below the first band already do. A tape without the flag counts every record, and the receipt says which basis it used. Option F of the #1428 other-income investigation. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Two FRS-row mappings disagreed with SPI Table 3.7, so the frame's other income below £100k carried the wrong concept. - NS&I Direct Saver and the other NS&I and bank products move from other_investment_income to savings_interest_income. SPI has counted all interest paid gross or net as bank interest since 2016-17 (note 4). Gilts stay in other income, as interest on securities (note 6). - Income from an annuity not bought with pension funds or from a trust or covenant (FRS PENSION.PENTYPE 4 and 5, the ADULT ANYPEN4 and ANYPEN5 sources) 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. Both columns are taxable savings income in policyengine-uk, so the savings tax on these rows does not move. The FRS spine test covers both routes. Options A1 and A2 of the #1428 other-income investigation. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Other income was the one investment leaf of the SPI stage-1 transport with no FRS analogue. Its quantile came from an identity uniform keyed on the twin, which ignored the twin's holdings. Copies drew large amounts on modest incomes, and the draw was out of line with the rest of each copy's income. The analogue map now names the twin's own other_investment_income: gilts, trust income and purchased-annuity income, the FRS sources of SPI's other income after the previous commit. A copy's draw now ranks with its twin's holdings, as interest, dividends, pensions and property do. The stage spec's analogue_columns (sources.yaml and source_stages.json) and the release input coverage manifest that pins the spec follow, and the spine test's synthetic frame carries the column, as the FRS spine always writes it. Option C of the #1428 other-income investigation. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Brings #1428 up to date with main, including PR B (#1421, the ISA subscriptions and policyengine-uk 2.164.2). Conflicts resolved: - frs_spine: main's ISA interest column, with this branch's NS&I-to- deposit routing. - The FRS spine test: ISA interest apart, plus NS&I as deposit interest (5.0 a week of non-ISA interest). - The SPI engine test: the savings columns on the OBR path. - The vendoring register: both new resources. - The coverage manifest: regenerated. - The data contract: gate digests recomputed for the merged gates.json (main's, plus the UC take-up tolerance of 0.06). Co-Authored-By: Claude Opus 5.5 <[email protected]>
The band factors divide published amounts by the tape's FACT-weighted
amounts, so they assume the tape grosses to the taxpayer population.
The H2 spine fixture's synthetic tape doesn't: its factors ran to
millions, lifted its band donors' income out of their bands, and the
fixture's regeneration refused ("no record whose uprated total income
lies in the band from 200000"). CI's acceptance and staging smoke tests
would meet the same refusal.
Now a column whose banded tape amount, over the bands it carries, is
under a tenth of the published amount is left as it is, and the receipt
says so. The production tape holds about 0.37 of the published interest
and 0.6 of the other income. A test covers the skip.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
CI's engine-uk graph test and integration-uk smoke test refused the SPI income stage: the fixture's stage-spec snapshot still named no analogue for other_investment_income, against the reviewed map (C in b822474). tools/graph_uk_spine_fixture.py regenerates it on the merged tree: - fixture.json carries the new analogue entry; - the fixture's derived CGT and property fact sources follow the synthetic frame they're built from; - uk_spine.json's pin follows. Co-Authored-By: Claude Opus 5.5 <[email protected]>
CI's engine test test_spi_stage2_does_not_require_frs_other_investment_income runs the SPI income stage on a frame whose FRS rows carry no other_investment_income. The stage fills that output with the stage-time zero, but only after the stage-1 draw. Since b822474 the transport reads the column as the twin's own analogue before the draw, so the stage refused the frame. Now a stage-1 output that is its own analogue and that the frame doesn't carry gets the same zero before the transport. Every twin locates as a non-carrier, and the final values are unchanged. Production FRS frames always write the column. Co-Authored-By: Claude Opus 5.5 <[email protected]>
e5ac7a5 built the fixture in a local environment still on policyengine-uk 2.162.14. The lock has pinned 2.164.2 since the merge of main (9ee6fbd), so CI's engine-uk graph test found the stamped engine version differ from the graph it builds. Rebuilt with the environment synced to the lock: the graph's version stamp moves, and so do the fixture's synthetic property rental facts, which the engine uprates. Co-Authored-By: Claude Opus 5.5 <[email protected]>
María's choice of 2026-10-30 (microcosm#1418, the trading allowance measures). With Table 3.10's self-employment counts alone, the solve can meet each GBP 1 to GBP 10,000 band with traders who have no pay. In the table, more than half of the traders below GBP 3,000 also have employment income, and an employee's side profit is taxed at their marginal rate. - hmrc.spi.employment_income.count_by_self_employment_income_band counts each person with pay over the year (employment_income_annual, which Table 3.6's employment rows read) in their own self-employment band, taxpayer or not, over the same eight bands as the self-employment counts. Table 3.10's column also holds miscellaneous income, benefits in kind and the contributory benefits, which the binding leaves out, as Table 3.6's does. The counts grow to 2025 with HMRC's projected growth of all taxpayers, as the self-employment counts do. - The zero band and the bands from GBP 10,000 are signed out row by row. The target is declared signature-incomplete beside the other Table 3.10 rows and is unmapped in the registry parity (no uk-data metric). - The national references and membership and the national compile-parity receipt are regenerated on the pinned feed (53815b2): eight new rows. The membership's 13 SPI dividend rows and 8 Table 3.10 self-employment amount rows also take the October EFO engine pins that main's policyengine-uk 2.164.2 lock brought (b89d3aa). The merge left those rows on the 2.162.14 pins. The parity receipt doesn't move for them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
María's choice of 2026-10-31 (microcosm#1428). The three Income Tax liabilities statistics Table 2.6 rows bound HMRC's own 2024-25 and 2025-26 projections as a calendar-2025 window: GBP 8.40bn of tax on savings, x1.52 on the 2023-24 outturn, against x1.36 for savings income on the OBR's path. With SPI Table 3.7's savings income already on the OBR path, those rows pinned the tax on savings, and so the savings rates' static yield, about 12% above the base the Budget's costing grows. - The rows now read the 2023-24 outturn alone. They are observed facts, so hmrc_itl_savings_rate leaves the source-projection families. - They grow by the OBR's savings income growth (October 2026 EFO Table 4.5), x1.3562 to calendar 2025, the index Table 3.7's savings rows take: GBP 1.61bn, 2.24bn and 3.66bn, GBP 7.51bn in all. - Growing the tax on the income's path leaves out the leverage that HMRC's projection carries from the frozen Personal Savings Allowance and thresholds. - The national references, membership and compile-parity receipt are regenerated on the pinned feed, and only the three rows move. The engine test's comments now name calendar 2025's two tax years. Co-Authored-By: Claude Opus 5.5 <[email protected]>
María's ruling of 2026-10-31 (microcosm#1428): fix the take-up pools instead of widening the gate again. The spine from d563f4f stopped at uk_stage_uc_take_up. Single people's caseload ended 0.0674 of DWP's count further from it than before the redraw: 0.0568 on f3cb3dd's spine, against this branch's 0.06. The reachable pools met DWP's counts. The gap was the pools whose DWP count exceeds every entitled unit, which kept a clipped rate, and which bands land there moves with small upstream changes. - After the source-support pooling, the stage joins a pool that cannot meet DWP's count to an adjacent pool (_join_unmet_pools). Such a pool's undrawn units' expected claims exceed the count, or the count exceeds them plus every drawn unit. Lowest first, it joins the neighbour with more room for its gap, the lower on a tie. Joining stops when every pool can meet its count or the family type is one pool. A family type whose count exceeds all its entitled units stays one unreachable pool. - The stage spec restates the pooling rule (sources.yaml, source_stages.json, UC_TAKE_UP_POOLING), and the coverage manifest and the H2 fixture follow. - uk_stage_uc_take_up returns to main's 0.055, undoing this branch's 0.06. The gate note, the committed-parameter test and the data contract's mirrored digests follow. - New tests: an unmet pool joins the neighbour with more room, or the lower one on a tie; an overshooting pool joins one with drawn claims; a family type that cannot meet its count is one unreachable pool, flagged by the side it misses on. Co-Authored-By: Claude Opus 5.5 <[email protected]>
María's choice of 2026-10-31 (microcosm#1428). policyengine-uk applies
the trading allowance against self_employment_gross_receipts, which
neither the FRS nor the SPI tape records. Without receipts it relieved
profits up to the allowance in full and nothing above. So the Budget's
raise to GBP 3,000 missed its main base, the traders who already claim
the GBP 1,000 allowance against larger receipts.
- A new last spine stage, trading_allowance_receipts, flags each trader
with a positive profit as a claimant, drawn on their own person_id:
0.64 for profits up to GBP 4,500, 0.32 from GBP 4,500 to GBP 9,000,
none above.
- It stores receipts of profit plus GBP 1,000 for a claimant, exact
under current law, and profit plus GBP 3,000 for any other trader
(expenses at the raised allowance; the costing books switchers as
behaviour). A person with no positive profit keeps zero.
- The rates are calibrated to HMRC's policy paper (about 400,000
taxpayers with a lower bill, about GBP 380 on average) until HMRC
publishes claimant counts.
- Every trader with a positive profit is treated as filing, as the
bound Table 3.10 counts are of filers. So a profit of GBP 1,000 or
less is now taxed.
- Wiring:
- the operation kind in the stage schema and the allowed kinds;
- the stage in sources.yaml and source_stages.json, with its mass
receipt;
- the graph reads and cell, the kernel modules, the spine build and
fixture implementations, and the coverage manifest family.
- self_employment_gross_receipts leaves UK_ENGINE_DEFAULT_INPUTS and
joins the export surface's allowed columns (terminal_gates and
gates.json), so the data contract's gate digests follow.
- Tests: the stage's own, and the rosters (51 stages). The H2 fixture
follows.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This was referenced Oct 31, 2026
This reverts commit 5c0137f (María's ruling of 2026-10-31, microcosm#1428). The national built from 6485af4 stopped at uk_target_fit. Under the engine's rules, tax on savings grows faster than savings income: the Personal Savings Allowance and the thresholds are frozen. So the tax rows grown on the income's path asked for less tax than SPI Table 3.7's savings income gives. To meet them, the solve cut the interest rows in the basic-rate bands: the GBP 30,000 band ended 30.7% under target and the GBP 20,000 band 12.0% under. HMRC's own 2024-25 and 2025-26 projections carry that leverage, and the national from b441bb5 met them beside the interest rows. So the savings rates' yield stays pinned to the projection. Its distance from the costing is a difference of basis (the projection's leverage) and of behaviour (ISA shifting), not a fault in the base. The three rows, their references, membership and parity receipt, and the two tests are back as at d563f4f. The changelog fragment goes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The spine from f9676fe stopped in trading_allowance_receipts, and so did CI's graph driver test and the staging smoke test. The stage counts its receipt's support by household_source_id, but its graph declaration read only self_employment_income, so the executor didn't hand it that cell. The stage now declares the read, as regional_home_values and property_purchases do, and the H2 fixture's graph follows. The fixture tool's legacy oracle runs every stage on the whole frame, so it didn't catch this. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The savings follow-up to #1418, part 1: fix the low savings base at its source instead of making the solve stretch weights. This branch starts from PR C's last commit (
0b276e571, merged as #1420).Draft. The national built from
b441bb5a0(onf3cb3ddd3's spine) is certified and shippable, staged locally. The other-income fixes below (4a6dc728ftob82247443), the small traders' pay (d563f4fbc), the tax on savings rows (5c0137fb4) and the take-up pools (6485af4af) need a new spine and national. A spine and national are building from0c2f5314f, which carries all of them except the tax on savings rows, reverted (below). The spine fromf9676fed9stopped in the new receipts stage, which hadn't declared its source-household read;0c2f5314fdeclares it. Staging reaches its repository, so the cut can publish. The spine fromd563f4fbcstopped at the UC take-up gate. The spine from6485af4afpassed every gate, but its national stopped at the fit gate on the tax on savings rows. The measurements will come in a comment before review.The problem
PR C binds SPI Table 3.7's interest and other income by total-income band. Interest moves to the OBR's savings path from 2023-24.
The PR C national built from
5aba83cd2stopped at certification on that band. Its figures stay out of this thread, because the band rests on the SPI tape.The change
spi_income.rebase_spi_donor_savingsruns before any draw. It scales each donor's interest and other income to HMRC's published 2023-24 Table 3.7 amounts:The stage-1 forest and the band-donor resample both read the rebased tape. The receipt records factors only, never counts.
_spi_income_uprating_factorsthen grows the two columns from 2023-24 to the frame's year on the OBR's savings-income growth (October 2026 EFO, Table 4.5), through the newhmrc_uprating.uk_obr_savings_income_level. This is the path the Table 3.7 band targets already take. From the frame's year on, the engine projects both at GDP per head, so both columns are now listed inSPI_HISTORY_INDEX_DIFFERS_FROM_PROJECTION.uk/hmrc_spi_table_3_7_savings_rows.jsonvendors the 26 published rows (13 bands × 2 columns) from the feed pin. It is declared in the vendoring register and the country package.Docs and changelog:
docs/uk-income-anchors-280.mdandchangelog.d/1418-spi-donor-savings-rebase.changed.md.The UC take-up gate: 0.055 to 0.06 (second commit)
The first spine built from
667fe532astopped atuk_stage_uc_take_up. After the take-up redraw, single people's caseload ended 0.0568 of DWP's count further from it than before. PR C's spine was at 0.0523, and the tolerance is 0.055. The cause:The reachable pools still realize DWP's counts within 0.005. The tolerance goes to 0.06 (María's ruling of 2026-10-30), as PR A took it from 0.05 to 0.055 for the gilt reshuffle. Pool boundaries that jump with small upstream changes are a follow-up on #1334. The gate's note, the committed-parameter test and the data contract's mirrored digests are updated.
Superseded by
6485af4af, which fixes the pools and returns the gate to 0.055 (below).The national from
f3cb3ddd3: what movedThe spine passed every gate. The national's fit gate blocked on one row. Below £1m, every Table 3.7 interest and other-income band fits within 1% after the solve, and the £1m-and-over other-income row that blocked PR C's national now passes the fit gate.
The share of each group's solved mass sitting on households stretched beyond 3× their design weight, PR C's blocked national against this one:
The last row is the FRS under-reporting part 2 would address.
At design weights, the rebased frame carries more savings than SPI in the £200k–£1m bands, where its top earners are already over-represented, so the solve now cuts those savers' weights instead. The figures for the £1m-and-over band stay out of this thread, because it rests on the SPI tape.
The £1m-and-over interest hold (third commit)
The one row the fit gate blocked on, Table 3.7's £1m-and-over interest amount, is held as a measured distortion, as the ten HMRC £1m-and-over cells are (María's ruling of 2026-10-30), and is still measured on every evaluation. How the rebase sits in the top bands at design weights is a follow-up.
Why other income is short below £100k, and the fixes (fourth to sixth commits)
A read-only investigation of the national from
f3cb3ddd3found that below £100k the frame carries 0.40 of Table 3.7's other income at design weights. It ranked three causes:By María's choice (2026-10-30), three changes address the first two, plus a fix to part 1's rebase:
4a6dc728f, the rebase on taxpayers (F). Table 3.7 counts taxpayers only, so each band's factor is now computed over the tape's taxpayers (TAXPAYER 1), and non-taxpayers take the all-bands factor. Over all records, the two lowest bands' taxpayers fell short of the published amounts.092de13b9, FRS savings income on SPI's definitions (A1, A2).savings_interest_income, since SPI has counted gross-paid interest as bank interest since 2016-17 (note 4). Gilts stay in other income.b82247443, a coherent other-income draw on SPI copies (C). The stage-1 transport locates each copy's draw at its twin's own other income (gilts, trust and annuity income) rather than at an identity uniform that ignored it. The stage spec's analogue map and the coverage manifest follow.The investigation's frame arithmetic puts other income below £100k at design weights at about 0.66 of its target with these changes, against 0.40 before. The next build measures it.
FRS rows keep their own reported incomes: the investigation's larger lever, an SPI fill of other income on FRS-row taxpayers, is not taken.
The small traders' pay: SPI Table 3.10's employment-income counts (
d563f4fbc)The national from
b441bb5a0costs the Budget's trading allowance measures (Table 6.1 line 15) below PR A's, and both sit below the costing. A read-only investigation found two things:By María's choice (2026-10-30),
hmrc.spi.employment_income.count_by_self_employment_income_bandbinds Table 3.10's individuals with employment income over the same eight bands from £1 to £10,000 as the self-employment counts:employment_income_annual, the variable Table 3.6's employment rows read) counts in their own self-employment band, taxpayer or not.target_reference_signed_exclusions.json.b89d3aab8). The merge had left them on the 2.162.14 pins. The parity receipt doesn't move for them, and builds compile live from the pins.The tax on savings rows: the OBR path (
5c0137fb4, reverted inf9676fed9)The three ITL Table 2.6 rows bound HMRC's own 2024-25 and 2025-26 projections as a calendar-2025 window: £8.40bn of tax on savings, ×1.52 on the 2023-24 outturn. Table 3.7's savings income grows only ×1.36 on the OBR's path. Bound together, the rows pinned the tax on savings, and so the savings rates' static yield, about 12% above the base the Budget's costing grows.
By María's choice (2026-10-31, option A):
hmrc_itl_savings_rateleaves the source-projection families. Only the three rows move in the references, the membership and the parity receipt.Reverted (
f9676fed9, María's ruling of 2026-10-31). The national from6485af4afstopped atuk_target_fit, as the leverage caveat predicted:HMRC's own projection carries that leverage, and
b441bb5a0's national met it beside the interest rows. So the savings rates' yield stays pinned to the projection. Its distance from the costing is a difference of basis and of behaviour (ISA shifting), not a fault in the base.UC take-up pools that cannot meet DWP's count (
6485af4af)The spine from
d563f4fbcstopped atuk_stage_uc_take_up. Single people's caseload ended 0.0674 of DWP's count further from it than before the redraw (0.0568 onf3cb3ddd3's spine).By María's ruling (2026-10-31), the stage is fixed instead of the gate widened again:
The trading allowance's base: the self-employed's gross receipts (
f3540cbc6)policyengine-uk applies the trading allowance against
self_employment_gross_receipts, and neither the FRS nor the SPI tape records turnover. Without receipts, the engine relieved profits up to the allowance in full and nothing above. So the Budget's raise to £3,000 missed its main base: traders who already claim the £1,000 allowance against larger receipts and gain the larger deduction. No published source gives turnover beside profit. But under current law a claimant's receipts are their profit plus £1,000 exactly, so only who claims is missing.By María's choice (2026-10-31), a new last spine stage,
trading_allowance_receipts, sets each trader's receipts:person_id: 0.64 for profits up to £4,500, 0.32 from £4,500 to £9,000, none above.self_employment_gross_receiptsalso leaves the register of engine inputs left at their default and joins the export surface's allowed columns, so the data contract's gate digests follow.Up to date with main, and CI (30 Oct)
9ee6fbde0merges main into the branch. The main change it brings is PR B (Budget 2026 ISA data, PR B of #1418: ISA subscriptions per adult, the Lifetime ISA anchor, additive ISA income, policyengine-uk 2.164.2 #1421: ISA subscriptions, policyengine-uk 2.164.2). Conflicts resolved:gates.json;995969f02leaves the savings rebase off any tape whose weights don't gross to the population.e5ac7a5d8regenerates the H2 spine fixture for the other-income analogue (C). CI's engine-uk graph test and the integration-uk smoke test refused its stale stage-spec snapshot.53ad29934starts a missing self-analogue output at zero before the SPI transport. Since C, the transport readsother_investment_incomeas each twin's own analogue before the stage-1 draw, so CI's engine test of a frame whose FRS rows carry no other income was refused. That column now gets the stage's zero before the transport, as it did after the draw, and the final values are unchanged. Production FRS frames always write it.45c2888faregenerates the H2 spine fixture on policyengine-uk 2.164.2, the lock's pin since the merge of main.e5ac7a5d8had built it in a local environment still on 2.162.14, which CI's engine-uk graph test caught.Not in this PR
Checks run locally
All pass. Engine-free tests:
test_uk_spi_savings_rebase.py;test_uk_trading_allowance_receipts.py, the stage rosters, the country spec, the graph and the terminal gates.The updated engine tests (
engine/uk/test_uk_spi_income.py,engine/uk/test_uk_hmrc_uprating.py,engine/uk/test_uk_itl_savings_tax_rates.py,engine/uk/test_uk_release_input_coverage.py) and the H2 acceptance test run in CI.🤖 Generated with Claude Code