Skip to content

UK: port the #1095 fixes that need no new policyengine-uk release - #1200

Draft
juaristi22 wants to merge 22 commits into
mainfrom
uk-data-ports-1095-3
Draft

juaristi22 wants to merge 22 commits into
mainfrom
uk-data-ports-1095-3

Conversation

@juaristi22

@juaristi22 juaristi22 commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

This is the third #1095 ports PR. It covers every actionable item on #1095 that waits for no policyengine-uk release, on the locked policyengine-uk 2.122.2.

A calibration of this head passes 6 of the 7 calibrated-seam gates. uk_weight_ratio fails at 1,155 against the fence of 1,151. Main, this branch's base, fails the same gate at 1,213, so the gate was already failing before this PR (below). Main's evidence reader refuses real run manifests (#1187), so both calibrations ran with #1188's fix on top, for measurement only.

Before any national build: the renewals of the measure exclusions that expire from 15 October, and a resolution of main's weight-ratio breach. After merge, as for #1100 and #1121: the signed-difference entries for the new and changed columns (D1, D5, D3, D4), whose drafts stay outside the repository until signed, then the national build and the certifier.

Summary

Root columns (D1, D5, D6)

Take-up and capital (D2, D3, D4)

  • D2. The UC take-up population is benefit units with a UC claimant or partner aged 18 or over and under Pension Credit qualifying age, the age half of the engine's is_uc_eligible (Supply is_hbai_dependent_child from the FRS and move targets and imputations off the deprecated is_child/is_adult policyengine-uk-data#486, Supply is_claimant_or_partner from the FRS adult table policyengine-uk-data#524). An engine lockstep test names the two cases it doesn't mirror: reg 8 claimants aged 16–17, and mixed-age couples on the Pension Credit route. The contract's population text is re-signed with the rate unchanged.
  • D3. Property in recorded capital. UC and Pension Credit recorded capital add the unit's share of its household's countable property to the financial carrier, mirroring each engine proxy. The engine replaces every capital source with the recorded figure, and the FRS capital question holds no property.
    • A unit that reports Universal Credit keeps its receipt and records no UC share. DWP assessed its capital to pay it, and the property is imputed without that receipt.
    • The reporter redraw screens an FRS unit that reports UC on its carrier alone, so that unit stays in the training set.
    • The uk_uc_capital_coherence gate re-derives both capitals, with the reporter rule, and now checks Pension Credit capital.
    • Open for María's ruling: the share follows the engine's proxy, which is gross value shared pro rata. The review measured what that leaves out (below).
  • D4. Pension Credit take-up (Solve Pension Credit take-up over entitled benefit units policyengine-uk-data#510) is solved over Great Britain, where DWP's rates apply, and drawn UK-wide. Units not entitled are drawn at the 37% rate, so a reform that entitles them pays them. Entitlement is read at the calibration year, on the frame policyengine-uk projects there from its survey year.

Targets (T1–T6)

What the measurement changed

The first head bound the top UC band and fitted the two PIP rows. It failed two gates, and calibrate-only runs on its spine attributed each failure to one change.

  • The PIP rows raise the weight ratio. They fit within 0.1%, but on that spine they pushed the heaviest support family, a single FRS household, by 180 times the positive median. So T6b holds them as diagnostics.
  • The top UC band costs the UC total. Unbound, the calibration holds 2.57 times DWP's count of lone parents in it and 1.99 times for couples with children, £13.1bn of UC, and reaches OBR's £76.1bn. Bound to DWP's counts, obr.universal_credit falls to −5.6%. Couples without children also have no household in the band. So the band stays unbound, as on main. The awards below it are too low for DWP's band counts and OBR's total to fit together, which needs its own issue.
  • D3 for every unit put UC reporters over the capital limit. On the Arm 2 spine, 90 FRS-channel rows that report UC went above £16,000 through property alone. 70 were in households with no property income, and 45 in households with two or more benefit units. Hence the reporter rule.
  • D1 from the donor's status flagged SPI rows with no trade. 1.67m weighted SPI-channel people were flagged with no self-employment income. Hence the earnings route for SPI rows.

María ruled on all four on 2026-10-10. The sparse-band support floor the plan added moves to its own PR.

Review round 1 (11 October)

An adversarial review of cf42d8a7a is in the comments. Five commits answer it.

  • D4 read 2024 values against 2025 rules, because the engine adapter stamped the frame with the entitlement year. The frame is now stamped with its survey year and projected. Units entitled to Guarantee Credit fall from 1,354k to 1,247k.
  • T5 rebased the 2022-23 SPI donors to 2024 by GDP per head, and so dropped the rise in savings interest of those years (ONS household interest income is ×1.99 from 2022 to 2023). The donor rebase is main's again, and only the projection leg of the band targets uses GDP per head. This was the cause of the pull on the top income cells that the first write-up assigned to T5 as a side effect.
  • D3's training screen dropped FRS units that report UC and hold imputed property. It now screens them on their carrier.
  • D6 let a redrawn SPI row keep its FRS twin's landlord flag, which turned the CGT residential signal on for 695k weighted SPI-channel people. Redrawn rows now drop it.
  • T2 now checks its classes against the engine's earned_income_tax.
  • W is reverted. With these fixes the West Midlands £12,570–15,000 income-tax cell is at +26.9%, above the bound, so María's deferral is needed. The retirement relied on the T5 error.

Open for María's ruling, from the review:

  • D3, who holds the property and at what value. Fewer than 10 benefit units are on the −1 sentinel, so the recorded figure decides alone.
  • The signed differences. The committed uc-reported-capital-net-new-column text predates D3.

Where this departs from the plan

  • D5 reads the foster relationship code alone. The carer's fostering allowance covers fewer than 10 of the 13 looked-after children the FRS records.
  • D2 doesn't use Max's test scaffolding, which isn't on main. His uk-take-up-per-person-spa branch solves the State Pension age by date of birth and will need a rebase onto D2.
  • D3 uses one share helper for both proxies, because the Pension Credit share needs a different owner set.
  • T3 adds an exact-id geography pin: the substring rule would read "scotland" in the row's concept.
  • T3 and T4 give their compile-parity rows their own rationales.
  • T5 changes the projection leg of the targets only (review round 1).
  • T6b lets a diagnostic reference carry a fact dated by month.
  • T1 leaves the top band unbound, and the support floor moves out (above).

Measurement

Each arm was built and calibrated locally on Chronicle 825406f. The control is this branch's base, main 7639ef8b0. The control and the head both carry #1188's commit on top, which changes the evidence reader and its tests only.

  • Control (main 7639ef8b0). Loss 0.00702, 98.5% of 1,167 targets within 10%, ESS 4,840.
  • This head. Loss 0.00691, 98.5% of 1,168 targets within 10%, ESS 4,849. Six of the 7 gates and the input-mass gate pass.
    • uk_weight_ratio fails: 1,155 against 1,151, on the same FRS household.
    • obr.universal_credit and obr.pip calibrate to 0.0%, obr.council_tax to −7.2% (control −9.5%), obr.vat to +23.5% (control +24.1%), and the three salary-sacrifice bands to within 0.3%. 108 of 111 UC cells are within 10% (control 106).
    • The £500,000–1,000,000 employment-income band is at −1.9% (control −3.4%).
    • 51 FRS-channel benefit units that report UC keep their receipt; 44 of them would have gone above £16,000.
    • On the head's weights, uk_uc_capital_coherence and uk_take_up_signal pass. The arm pipeline leaves both to the certifier, so they were run with their own evaluators.
  • Earlier arms, on the older base d50220d1f, are in the receipts. They attributed the first head's failures, and their deltas also carry Weight UK BRMA assignment by census private-rented households #1087's effect.

Full numbers are in experiments/1095-uk-data-ports-3-receipts.md.

Testing

  • Each commit: touched-surface tests.
  • This head:
    • the engine-free lane's 534 test files, with policyengine-uk blocked as in CI: 16,152 passed and 140 skipped. The three failures are environmental: the live Hugging Face pointer test fails on main too, the telemetry token exchange needs the hosted service, and main's US head-to-head scorer test needs Python 3.14.7;
    • the engine-uk lane's 41 test files, run in full: 179 passed;
    • against the pinned feed: the national compile-parity receipts regenerate identically, the committed references regenerate, and the NICs, PIP and salary-sacrifice diagnostics resolve their exact facts (45 passed).
  • Lint and CI plan: ruff check and ruff format --check pass on the 66 changed Python files, and so do tools/ci_test_plan.py verify and tools/graph_acceptance_burndown.py --verify.

Overlaps and what follows

Receipts: experiments/1095-uk-data-ports-3-receipts.md.

🤖 Generated with Claude Code

juaristi22 and others added 16 commits October 10, 2026 23:52
The calibration of #1121's head ran on 2026-10-08 (arm head-r3, tree
c7fe76e8a, the tree of the merged head): loss 0.00712, 98.3% of 1,167
targets within 10%, 7 of 7 seam gates and the certifier's input-mass gate
passing with the West Midlands deferral and the two UC band exclusions in
force. The results were posted on #1121 rather than pushed, to avoid
another CI run; this records them in the receipts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
)

Nothing set policyengine-uk's uc_is_in_gainful_self_employment, so the
engine's rule applied: any nonzero self_employment_income was gainful.
The spine floors losses at zero, so a trader at a loss or breaking even
never met the Universal Credit minimum income floor (uc_mif_applies, the
input's only reader), while an employee with a small side trade did.

Port uk-data#525's rule (UC Regs 2013 reg 64(a); ADM H4031, H4034): a
self-employed main job (EMPSTATI 3 or 4) whatever its profit, or a side
trade whose SEINCAM2 profit is above zero and above INEARNS pay. The root
stage sets it. SPI-channel rows keep their donor's status but draw new
incomes that do not read it, so after the redraw they take the earnings
route alone, until the SPI draw follows employment status (uk-data#529,
microcosm#840). A net-new export column; its signed difference waits for
María's signature.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1095)

The FRS 2024-25 question instructions put foster children under 16 in
the carer's benefit unit, and nothing set policyengine-uk's
is_looked_after_by_local_authority, so the engine counted them as the
carer's own children in its child tests (Universal Credit, Child Tax
Credit, Pension Credit, Tax-Free Childcare, extended childcare, the WTC
childcare element and the legacy benefits).

frs_relationships now sets the flag for a child-table member under 16
whose household-grid code toward an adult in their own benefit unit is
foster child (5), or toward whom that adult records foster parent (9).
The rule is declared in the stage (looked_after_child) and the spec
schema. It follows the relationship code alone: few such children are
recorded, and the carer's local-authority fostering allowance (ALLOW3)
covers fewer still. A net-new export column; its signed difference waits
for María's signature.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CGT residential split read property_income > 0 as one of its two signs of
residential property, so loss-making landlords lost the sign and its
log-odds shift.

The spine now carries an internal landlord flag,
reports_rent_from_other_property (a positive ROYYR1, at a profit or a
loss), which the CGT asset-type and residential-split stages read beside
household residential property value and property income. The declared
stock signal text says so. The flag is no engine input: the release
boundary drops it, beside the other internal carriers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Universal Credit take-up population becomes benefit units with a UC
claimant or partner (is_uc_claimant) aged 18 or over and under Pension
Credit qualifying age, the age half of policyengine-uk 2.122.2's
is_uc_eligible. The draw, the SPI-channel redraw and the take-up signal
gate share the helper; both stage manifests declare the role column and
the code refuses one that does not. The contract's UC population text is
re-signed with the rate unchanged.

The engine lockstep test now compares the population with is_uc_eligible
and names the two cases it does not mirror: reg 8 claimants aged 16-17
and mixed-age couples on the Pension Credit route.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
policyengine-uk replaces every capital source with a recorded figure of 0
or more, but TOTCAPB4 holds no property, so no unit was assessed on its
household's second homes, buy-to-let, buildings or land. The coherence
stage now records each capital as the financial carrier plus the unit's
share of the household's countable property, mirroring each engine
proxy: UC shares other residential and non-residential property by
claimants and partners; Pension Credit adds owned land and shares by
members at or over its qualifying age. The -1 sentinel passes through.

A unit that reports Universal Credit keeps its receipt and records the
carrier alone for UC (María's ruling of 2026-10-10). DWP assessed its
capital to pay it, and the property is imputed without that receipt and
shared over every claimant or partner in the household.

The reporter redraw screens SPI reporters on the carrier plus the share.
The capital gate re-derives both capitals from the carrier, the declared
shares and the reporter rule, and now checks Pension Credit capital. An
engine lockstep test shows the shares equal the engine's proxies,
recorded or mixed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed units (#1095)

DWP's take-up rates cover Great Britain, so each component band's
residual is now solved there and drawn for every entitled non-reporter,
Northern Ireland's included. A unit with no entitlement that does not
report claims at the Savings Credit-only take-up (37%, new contract key
pension_credit_newly_entitled), so a reform or a later year that entitles
it pays it. Entitlement is read at the release's calibration year, as
uk-data reads it, with the mixed-age saving still stored at the survey
year. The stage gate requires Great Britain receipts, and the receipt
records Pension Credit capital and deemed income by channel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
policyengine-uk projects a loaded dataset's savings_interest_income by
GDP per head (data/uprating_indices.yaml); the household-interest index
its Variable.uprating attribute names only carries a situation input
forward (uk-data#541). The SPI donor rebase and the 13 HMRC SPI
savings-interest band targets now follow the projection: donors fall
about 52%, the band targets 8.4%. Engine lockstep tests hold every
indexed SPI component and every HMRC SPI amount family to the
projection table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DWP reports PIP for England and Wales only since devolution, as obr.pip's
binding records. The two daily-living caseload rows take obr.pip's
country filter and household condition, and a dwp.pip. prefix pin
selects K04000001, the geography Chronicle stamps DWP's January 2025
caseload with. Under the Great Britain default they matched no fact; they
now resolve and add two calibration targets (1,233 active references).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OBR table 4.1 row 15 sums the England, Scotland and Wales council tax
receipts; row 19, bound before, adds Northern Ireland domestic rates,
the accruals adjustment and the high-value surcharge, which no
household's council tax carries (GBP 1.34bn, 2.7%). The row now binds
row 15 over Great Britain's households, pinned to the UK stamp Chronicle
gives OBR's table through a new exact-id pin, since the substring rule
would read Scotland in the concept.

Its compile-parity row gets its own rationale, since the series and the
period basis both differ from the incumbent's row 19.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OBR's table 3.8 total is a cash figure against the accrued class rows,
and it carries statutory-payment recoveries, Class 1A, 1B and 3 and
settlements no household carries; at the calendar-2025 window it sits
about 1.8% above the class rows. It leaves the fit as a diagnostic on
obr.ni_employee with its exact FY2025-26 fact (María's ruling of
2026-10-09; uk-data#537 drops it). The contract holds 391 targets and
1,232 active references; obr/ni moves to the excluded incumbent rows.

Its fixture-only compile-parity row carries the ruling, as
obr.state_pension's did (microcosm#1069 R4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The three HMRC Table 6.1 relief bands were held out since #1069 because
their band test compared adjusted net income with after-allowance
thresholds and ignored the Scottish bands. The measure resolver gains a
declared rate-class allocation: one cached counterfactual returns the
sacrifice to pay, and each person's relief is the rise in tax on earned
taxable income, bracket by bracket, under their rest-of-UK or Scottish
schedule from the simulation's own fiscal-year parameters (below 30%
basic, the top rate additional, the rest higher), as uk-data#533 does.
uk-data's worked examples reproduce exactly. The bands bind, the Table
6.2 total becomes a diagnostic on the higher-rate band, and the three
exclusions retire.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DWP publishes the UC monthly award bands in whole pence, each starting a
penny above its predecessor's top. The four payment-distribution bindings
opt in to band_semantics whole_pence_upper_closed: a band is (label lower
- GBP 0.01, next label lower - GBP 0.01] x 12 compared in whole pence, so
a penny-a-month award lands in the bottom band and GBP 100.01 a month no
longer rounds into the band below. The reading is opt-in and refuses an
unknown value; the other 28 banded bindings keep half-open bands.

The GBP 2,500.01 or over band stays unbound (María's ruling of
2026-10-10), and band_upper_bound closes the last bound band at GBP
2,500.00 a month. Bound to DWP's counts, it left OBR's Universal Credit
total 5.6% short on the measured head; unbound, the calibration holds 2.6
times DWP's count of lone parents in it. The generator counts the fan-out
facts it skips instead of declaring 12 (the feed leaves eight). 1,231
active references.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
)

Scoped to England and Wales, the two DWP caseload rows resolve, and a
calibration fits both within 0.1%. But fitting them pushes the heaviest
support family's weight from 1,061 to 1,241 times the positive median,
over the uk_weight_ratio fence of 1,151, which holds at the June
measurement. So the rows leave the fit (María's ruling of 2026-10-10):
each is a diagnostic reference on obr.pip, pinned to its exact January
2025 fact, and the dwp.pip prefix pin goes with the targets. Diagnostic
references now accept a fact dated by month. The contract holds 388
targets and 1,229 active references.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On this head the calibration brings the West Midlands GBP 12,570-15,000
income-tax cell to +23.9%, back inside the 25% bound, and uk_target_fit
fails the deferral as stale. The cell sat at +28.9% on main and +26.8% on
this branch's first head; without the PIP caseload rows and the top UC
band in the fit, and with UC reporters keeping their receipt, it comes
inside. The target-fit register is empty again.

The defect stands upstream (pe-uk#2174): the cell's design value is
still 6.2 times its target, so a later change can push it back over the
bound, and it would then need a new deferral.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The arms before and after María's rulings of 2026-10-10, the attribution
of the first head's two gate failures, and the final head's calibration,
gates and test runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@juaristi22

Copy link
Copy Markdown
Collaborator Author

Independent adversarial review (Claude Code) at cf42d8a7a

Static review only. I read the code, the committed registers and experiments/1095-uk-data-ports-3-receipts.md against main 7639ef8b0, and the installed policyengine-uk 2.122.2 source for engine semantics. I ran nothing. CI is green.

Verdict. Most of the ports are sound:

  • D1 and D2: the rules are as stated.
  • D2 direction: mixed-age couples are correctly inside the UC population.
  • Determinism: no new RNG streams.
  • D6: the flag is dropped at export.
  • T2: the rate-class arithmetic reproduces uk-data's examples by hand.
  • T1, T3, T4 and T6: they do what the body says.
  • Fixture: synthetic.

Three things should change before this leaves draft: D4 reads survey-year values against calibration-year rules, D3 adds gross property to recorded capital, and T5 changes the donor rebase in a way the targets can't absorb. One cross-PR collision with #1198 breaks the national compile whichever PR merges second.

Should fix

  1. D4 reads 2024 values against 2025 rules.

    • Where: pension_credit_take_up.py:232 calls engine.materialize(with_saving, requested, entitlement_year). with_saving is the survey-year frame (time_period=period), and the adapter builds UKSingleYearDataset(fiscal_year=int(period)) from the requested period (microcosm-frame/.../policyengine_uk.py:172).
    • Effect: the frame's 2024 incomes, capital and State Pension are relabelled as 2025 inputs and never projected. On main, both the frame and the call were at the survey year.
    • Example: a single pensioner on the full new State Pension and nothing else is read at about £221.20 a week against the 2025 guarantee of £221.85, so is entitled. On the released file at 2025 the pension is £230.25 and the unit is not.
    • Consequences: the entitled set E is inflated. The residual rates, and the stage-gate receipt, describe a population the released file doesn't reproduce.
    • Fix: materialize at the survey year with the engine computing period 2025, so policyengine-uk projects the frame forward. If that's what uk-data does, cite it.
  2. D3 adds gross property, not countable capital. It does so by mirroring the engine proxy, but the engine treats a recorded figure as already countable.

    • Gross value: UC_PROPERTY_CAPITAL_SOURCES sums other_residential_property_value and non_residential_property_value (Pension Credit adds owned_land) at market value (uc_capital_coherence.py:89-93).
      • UC Regs 2013 reg 49 and SPC Regs 2002 reg 19 value capital at market value less 10% for sale costs and less any debt secured on it.
      • A £120k buy-to-let with a £110k mortgage counts as about £0; this records £120k, so the unit fails the UC limit and Pension Credit deems tariff income on it.
      • was_wealth already draws other_mortgage, but only mortgage_debt reaches the frame.
    • Owners: _household_property_share splits a household's property over every claimant and partner in it (UC), or every member at or over qualifying age (Pension Credit). So an adult child's unit takes a third of the parents' buy-to-let; tests/engine/uk/test_uk_uc_capital_coherence.py:136 asserts the £66,667/£33,333 split. UC counts only the claimants' own capital (reg 18); Pension Credit counts the claimant's and partner's (SPCA 2002 s.5).
    • Size (receipts:108-112): £1,247bn of UC capital over 5.18m units. With the reporter rule, UC falls £0.80bn and 179k non-reporters lose UC.
    • What the reporter rule tells us: about 157k weighted UC reporters would have crossed £16,000 on imputed property, although DWP assessed their capital and paid them. That is evidence the imputed figure overstates countable capital. The rule protects reporters, but the same overstatement still applies to non-reporters.
    • Fix: net secured debt and the 10%, and assign property to its owning unit (the household reference person's unit, or WAS ownership where available) rather than pro rata.
  3. T5's donor rebase drops a known surge that the targets already contain.

    • Where: SPI_INCOME_UPRATING_INDEX_OVERRIDES (spi_income.py:93) rebases the 2022-23 SPI donors' savings interest to 2024 by GDP per head (×1.09) instead of household interest income (×2.27). The commit says donors fall about 52% and the band targets 8.4%.
    • The problem: the engine-alignment argument (uk-data#541, uprating_indices.yaml) is about how policyengine-uk projects a dataset forward from its own year. The 2022 → 2024 donor rebase is history, and savings interest roughly doubled over it as rates rose. The 2023-24 SPI target base contains that rise; the rebased donors don't. So the solve has to make up about half of SPI savings interest through the weights, concentrated where savings sit.
    • What it explains: T5 alone moves the £500k–1m employment band from −2.5% to −13.3%, which an 8.4% cut to savings targets can't do directly. The head still has top income cells at −11.5% and −15.2%.
    • Fix: keep the household-interest index for the historical rebase to the base year and use GDP per head only for the projection beyond it. Or record why not.
    • Index pairing: the amount rows also stay on a flat index while their count rows grow by band. The implied mean per saver falls 4–13% above £30k, against about 1.00 for employment under the UK spine follow-ups after #1045: SPI band donors as a mass-conserving support channel, WAS tenure coherence, the residential draw's bound and Table 7 composition, then a national build #1063 rule. Dividends and property (UK landlords: bind the SPI net property amounts, leave property_wealth to the engine, add receipts and finance costs #1145) have the same pattern.
  4. Cross-PR: One consistent UK target surface: enforced cross-grain reconciliation, declared uprating holds, nation household controls and the 942a1fa re-pin (#1123 PR A) #1198's holds register refuses this PR's T2 in either merge order.

  5. The after-merge sequence needs the exclusion renewals first.

    • dwp.uc.households_housing_element expires 2026-10-15, ons.savings_interest_income 2026-10-22, and the two slc.borrowers.plan_2_* entries 2026-11-03.
    • apply_uk_calibration_measure_exclusions raises outside the window (measure_simulation.py:919-931), so any national build after 15 October fails until they're renewed.
    • This PR's own measurement relied on both near-term entries. T5 moves the model further from the D.41 row that ons.savings_interest_income excludes, so that entry needs renewing, not dropping.
    • Treat the renewal PR as a prerequisite, not a follow-up.

Minor

  1. The measurement isn't of this tree, and the control is confounded.
  2. W's 1.1-point margin across the open PRs.
  3. The signed-difference step is not enforced, and one committed signature is now false.
    • Not enforced: spine_swap_signed_differences.json is read only by manual tools (verify_uk_spine_parity.py, compare_uk_h5_payload.py). The release cut already lists uc_is_in_gainful_self_employment and is_looked_after_by_local_authority as reviewed candidate-only fields.
    • False signature: the uc-reported-capital-net-new-column entry (:525) says uc_reported_capital comes "from the same frs_benunit_capital carrier". After D3 it doesn't, and its only check (that the column exists) still passes.
    • Question: why aren't D4 (would_claim_pc now drawn for 37% of non-entitled units) and T5 (SPI donor savings −52%) on the D1/D5/D3 signature list?
  4. D3 reporter rule.
    • The share doesn't stay put: a reporter records the carrier alone, but policyengine-uk's household residual sums only recorded figures. So a co-resident unit with the −1 sentinel absorbs the reporter's withheld share through the proxy, which contradicts "the other units of its household keep their own shares".
    • Training screen: uc_reporter_redraw.py:280/381 applies carrier plus property to every training unit, including the reporters the stage protects, so real UC reporters drop out of the QRF training set.
  5. D4's 37% draw has an Income Support side effect. policyengine-uk's income_support_eligible bars Income Support where meets_pension_credit_age_conditions & would_claim_pc, with no means test. So mixed-age couples on the Pension Credit route who report Income Support and aren't entitled to Pension Credit now lose Income Support 37% of the time. The population is small, but it's undeclared.
  6. D5's under-16 cutoff (frs_relationships.py:193, below_age: 16) can only drop flags. It misses 16–19-year-old foster children who are qualifying young persons in the carer's unit, who are still looked after (Children Act 1989 ss.22, 105; UC Regs reg 4(6)(a)). test_uk_frs_relationships.py:437 pins a 16-year-old as not looked after.
  7. D6 on SPI rows: the landlord flag is written from the FRS twin (frs_spine.py:661), and no SPI refresh rewrites it. So SPI rows whose drawn property income is zero keep the twin's flag and turn on the residential CGT signal. That's the "donor status says nothing about drawn incomes" problem D1 rules against.
  8. T2.
    • Wrong comparison: the receipt compares the class sum with Δincome_tax, which differs legitimately through savings, dividends and HICBC. The engine variable the allocation re-implements is earned_income_tax, and comparing against that would catch schedule drift.
    • Untested diagnostic: no test checks that the it_relief_total diagnostic resolves. _diagnostic_reference_metadata turns a mismatch into unavailable without failing.
    • Question: does HMRC Table 6.1 classify relief bracket by bracket or by marginal rate? At design weights, basic reads 0.66×, higher 0.53× and additional 1.37×, which takes strong opposite reweighting to fit.
  9. Stale text.
    • tools/build_uk_ledger_compile_parity_signed_differences.py:76 still says the savings rows are "uprated by the household interest index", and that's regenerated into the 13 rows' signed reason, whose values moved ×0.916. docs/uk-income-anchors-280.md says the same.
    • The changelog fragments carry intermediate counts (391/1,232, 390/1,231, "stays at 1,231") where the head is 388/1,229.
    • The W fragment's "+28.9% on main" is pre-Weight UK BRMA assignment by census private-rented households #1087.
  10. Nits.
    • The D1 earnings route silently skips SPI rows if the column is missing (spi_income.py:2082), falling back to the engine default.
    • The D2 lockstep test narrowed from ages 0–100 to 14–90 without saying why.
    • T1 compares annual pence, not DWP's monthly pence (negligible).
    • income_tax_rate_classes.py:65-71 files a schedule's top rate as "additional" even at 40%.
    • The UC skipped-fact count changed from 12 to 8 on the same feed pin with no note.

Merge order with #1145, #1198 and #1167

Checked and fine:

  • Counts: 1,168 targets = 1,229 active − 61 exclusions.
  • Values: T3's £1.34bn (50.116 − 48.774), and T4's total at 1.0182× the class rows.
  • T5's ratios: 0.916 on all 13 rows.
  • T6: a month-dated fact reaches only diagnostics.
  • Exclusions: the three salary-sacrifice exclusions were genuinely redundant.
  • Mirrors: the coverage-manifest and gate digests move consistently.
  • Take-up contract: its rate is unchanged at 0.55.

juaristi22 and others added 6 commits October 11, 2026 21:28
Pension Credit take-up read calibration-year entitlement on survey-year
values: the adapter stamped the frame with the entitlement year, so a
2024-25 State Pension sat against the 2025-26 guarantee. The UK adapter
takes a dataset_period, the stage stamps the frame with its survey year,
and policyengine-uk projects it forward as it does the released file and
as uk-data#510 reads it.

The reporter redraw screens an FRS unit that reports Universal Credit on
its carrier alone, as uc_capital_coherence records it, so its imputed
property no longer removes it from the training set. The reporter rule's
text says what a co-resident unit on the -1 sentinel takes.

A redrawn SPI row drops its FRS twin's landlord carrier: its property
income is an SPI donor's, and the twin's flag turned the residential CGT
signal on for rows with no drawn letting.

The looked-after rule states why it stops at 16: the FRS makes an older
foster child with a local authority allowance a separate benefit unit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
)

The port of uk-data#541 moved both the SPI donor rebase (2022 to 2024)
and the 13 band targets (2023-24 to 2025) to GDP per head, the index
policyengine-uk's dataset projection applies. The engine projects from
the dataset's year on. The years before it are history, and savings
interest doubled over them as rates rose: ONS household interest income
is x1.99 from 2022 to 2023, GDP per head x1.05. The rebased donors
therefore fell about 52% while the targets, whose SPI 2023-24 base holds
the rise, fell 8.4%, and the solve made up the difference through the
weights of the top bands.

The donor rebase returns to the index the variable names for its own
history. The band targets take two legs: household interest income to
the dataset's year, then GDP per head to the calibration year, a factor
of 1.192 where main has 1.182. An engine lockstep test holds every SPI
component's rebasing index and every HMRC SPI amount family's projection
index to the engine's projection table, and names savings interest's
history index apart. Compile-parity receipts follow in an amend.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The salary-sacrifice allocation re-implements policyengine-uk's
earned_income_tax by rate class. Its receipt compared the classes with
the change in income_tax, which differs for good reasons (savings,
dividends, HICBC), so a drifted schedule would not show. The measure now
compares each person's classes with the change in earned_income_tax and
refuses beyond GBP 1. A schedule with one rate above the basic class
files it as higher, not additional.

Tests hold the all-rates total as a diagnostic on the higher-rate band
and, against the pinned feed, that it resolves its exact fact: a
mismatch reads as unavailable and does not fail the compile. The UC
take-up lockstep covers ages 0 to 100 again. The changelog fragments
drop the intermediate target counts and say why the UC fan-out skips 8
facts where 12 were declared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts (#1095)

The 13 SPI savings-interest band rows move with the two-leg index, and
their signed reason says so. Regenerated against the pinned 825406f
facts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This reverts a7e5bc5. With the review's fixes the calibration puts the
West Midlands GBP 12,570-15,000 income-tax cell at +26.9%, above the 25%
bound, so María's signed deferral is needed and is not stale. The cell
was inside the bound (+23.9%) only on the head that rebased the SPI
savings donors by GDP per head, an error the review found. Across this
PR's arms it has ranged from +23.9% to +29.7%; main, this branch's base,
has it at +26.5%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What the adversarial review changed, the control on the branch's base,
the head's calibration (six of seven gates; uk_weight_ratio at 1,155
where main is at 1,213 since #1087), the D3 owner and value figures left
for María's ruling, and the test runs.

Co-Authored-By: Claude Opus 5.5 <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

Development

Successfully merging this pull request may close these issues.

1 participant