Skip to content

UK: port the remaining uk-data fixes tracked in #1095, engine items included - #1121

Merged
juaristi22 merged 17 commits into
mainfrom
uk-data-ports-1095-2
Oct 8, 2026
Merged

juaristi22 merged 17 commits into
mainfrom
uk-data-ports-1095-2

Conversation

@juaristi22

@juaristi22 juaristi22 commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

#1086 is settled: each country has its own policyengine-core pin (option 2), which landed on its own as #1143. This PR is the policyengine-uk bump that follows it: B0 moves only the UK side of the lock. C1 can still be cherry-picked on its own.

Before merge: a calibration of this head, which is queued. After merge, as for #1100: the signed-difference entries for the net-new columns (C5, C6, B2–B5) and for B1's mechanism change, whose drafts stay outside the repository until signed, then the national build and the certifier.

Summary

This ports every remaining unblocked item on #1095. It covers:

  • six fixes that work on the currently locked engine;
  • the move to policyengine-uk 2.122.2, with five fixes that need it;
  • two signed calibration-register changes.

On the locked engine (C1–C6)

On policyengine-uk 2.122.2 (B0–B5)

Calibration registers (X, Y)

The diagnoses are under Measurement.

From the review (R1, rounds 2 and 3)

  • R1. The access-fund repair is recorded. The root stage's checkpoint evidence now carries the paid awards by period code, the threshold and the repaired count. A tab with calendar-month awards but no annual-coded award refuses.
  • C6's linked share is a household-weighted average, np.average over the linked rows with the gross4 household weights. It is not a microdf MicroSeries, because microdf reaches an environment only with an engine and the FRS spine runs in the engine-free lane.
  • B0's forward-year note cites pe-uk#2173.

Where this departs from the plan

Measurement

Each arm was built and calibrated locally on Chronicle 825406f. The control is A9, main's code. The arms predate the rebase onto #1081, so their spines lack its property-income rules.

  • Arm E (C1–C6). Loss 0.00702 (A9 0.00715), 99.0% of targets within 10%, all 7 gates pass.
    • The new Housing Benefit row calibrates to within 0.1%.
    • The LCFS imputation is value-identical wherever its predictors did not move.
  • Arm B0 (the engine bump). Loss 0.00769, 98.4% within 10%.
    • uk_target_fit fails on two UC payment bands that hold no benefit unit under policyengine-uk 2.122.2: singles at £26,400–27,600 and childless couples at £24,000–25,200.
    • Each rests on fewer than 10 FRS households. The same spine under policyengine-uk 2.100.0 keeps every supporting award in its band, so the engine alone moves them out. It does so through three corrections:
      • the published LHA rates move awards to neighbouring bands;
      • the 2019 mixed-age couple saving keeps couples who report Pension Credit off UC;
      • Carer Support Payment counts as unearned income, as Carer's Allowance does.
    • Y excludes both bands, beside the 19 of this class already excluded.
    • Align Child Benefit claim exports and opt-out draws with the UK model #1107's checks: the Child Benefit export is registered claims, and the fully charged opt-out pool is exhausted with a 27.4k-family shortfall. obr.child_benefit calibrates to 0.0%.
  • Arm head (B5). Loss 0.00765, 98.3% within 10%.
    • Beyond the two UC bands, the West Midlands £12,570–15,000 income-tax cell (hmrc.spi_region.income_tax_by_region_12570_15000@E12000005) fails at +27.4%. It was +8.9% on arm E and +23.6% on B0.
    • The cell's design value is 6.1 times its target, and 6.0 times on main's code. Every other region's cell is 0.6–1.1 times.
    • The cause: the regional cells slice on policyengine-uk's total_income, which leaves out other_investment_income although income_tax charges it.
      • So fewer than 10 SPI records with mostly other investment income sit in the band with their whole tax.
      • Banded on total income including other investment income, the cell is in line with the other regions.
    • X defers the cell until policyengine-uk's total_income includes other investment income. That fix belongs upstream: total_income cites ITA 2007 s. 23, which counts all income charged to income tax.
  • With X and Y, replaying the target-fit gate on the head arm's evidence passes, and the other six seam gates passed on that arm.
    • The replay keeps the head arm's weights, which were solved with both UC bands bound.
    • A calibration of this head, the national build and the certifier have not been run.

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

Testing

  • Each commit: touched-surface tests.
  • B0: the full suite on 2.122.2.
  • B5: the full suite, 13,378 passed and 128 skipped. The two failures are environmental:
    • the extras test expects no UK engine in the venv;
    • the live Hugging Face pointer test fails on main too.
  • X and Y: the register, gate, target and graph tests, 276 passed.
  • Rebased head, on main e3e3d881f:
  • Review rounds 2 and 3: the FRS spine and lock-level tests pass (115), and no generated surface moved. The rebase onto Give each country its own policyengine-core pin (#1086) #1143's merge left the tree unchanged.
  • US lane (policyengine-uk 2.102.4+ needs policyengine-core 3.32.9+, which the certified US default refuses #1086): on the shared lock, test_batched_scoring_matches_whole_pool_policyengine_us failed on both Pythons. It's a Maryland child-care rate lookup under policyengine-us 2.2.1 on core 3.32.19. With the per-country pins (Give each country its own policyengine-core pin (#1086) #1143), a US install from the new lock resolves core 3.32.5, and test_us_post_export_scoring.py passes there (4 tests). The lock-level test passes on this head.
  • Lint: ruff check and ruff format --check pass on all changed Python files.
  • CI plan: tools/ci_test_plan.py verify and tools/graph_acceptance_burndown.py --verify pass.

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

🤖 Generated with Claude Code

@vahid-ahmadi

Copy link
Copy Markdown
Contributor

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

Verdict: nothing in the code blocks it, but it isn't mergeable yet. That's for reasons the description already lists: the #1086 ruling, the signed-difference entries, and a calibration, national build and certifier run of this head. The ports read correctly against their uk-data counterparts. The engine bump is measured separately from the data fixes (arm E against arm B0), which is the right shape. Two of the calibration-register changes treat a defect as a deferral, and I'd fix those at source.

Findings

  1. Should-fix: X defers a cell whose cause is a defect in the band variable.
    • The receipts diagnose it: the regional income-tax bands slice on policyengine-uk's total_income, which leaves out other_investment_income, while income_tax charges it. So a few SPI records carry their whole tax into the £12,570–15,000 band: design £339.2m against a £55.1m target (experiments/1095-uk-data-ports-2-receipts.md:100).
    • That's a measurement bug in every regional band, not a West Midlands quirk. It shows up there first because that cell is thinnest.
    • Fix: band on total income including other_investment_income (or on the engine's adjusted_net_income, if that matches the SPI band definition) and drop the deferral. Or keep X, but open an issue now and link it from the register entry, so the four-week expiry has a fix to land against.
  2. Should-fix: Y adds two more single-household UC payment bands to an exclusion class that keeps growing.
    • With these, 21 cells of the uk-data#452 class are excluded. Each engine or spine change pushes another one-to-nine-household cell out of its £1,200 band. A census of the class with pooled bands (or a minimum-support rule applied when the registry is compiled) would stop it costing one signed entry per release.
    • Fine to keep Y for this PR; worth an issue.
  3. Should-fix: B4 moves CVPAY off the payer, but the householder's receipt is still uncredited, and UK FRS spine: stop counting lodgers' rent and property losses as property income; sub-letting rent for every tenure #1081 removes the same line.
  4. Question, for policyengine-uk 2.102.4+ needs policyengine-core 3.32.9+, which the certified US default refuses #1086: the bump pulls in about 600 policyengine-uk commits.
    • The rule changes that move this build include:
      • the published LHA rates (#2022/#2134);
      • the mixed-age couple saving (#1940/#1944);
      • Carer Support Payment as unearned income;
      • the Child Benefit opt-out gate (#2140), which activates Align Child Benefit claim exports and opt-out draws with the UK model #1107's export;
      • the legacy severe disability premium (#1946);
      • is_claimant_or_partner (#1896);
      • the NICs threshold freeze (#1959);
      • BADR's own elasticity (#1980), which matters for forward years only.
    • Not in 2.122.2: the working-age HB abolition (#1910), the published DWP rates (#1925), the HB/CTR non-dependant deductions (#2007), the Pension Credit earnings disregards (#2019), and the property finance-cost relief (#2172).
    • Also still in 2.122.2: salary_sacrifice_returned_to_income flows into employment_income and so into adjusted net income from April 2029. HMRC says the 2029 cap changes NI only. That doesn't touch the 2024-25 build, but it does affect forward-year users of this release, so it's worth a line in the release notes.
  5. Question: is the Align Child Benefit claim exports and opt-out draws with the UK model #1107 opt-out shortfall expected? The fully charged pool gives 599.6k opt-out families against a 627.0k target, 27.4k short (receipts:90). obr.child_benefit still fits, so the payment side is fine. But it means the dataset has about 4% fewer registered opt-outs than HMRC's count. Is the rest meant to come from the taper pool now that policyengine-uk pays taper opt-outs, or is the shortfall accepted?
  6. Nit: C2's repair threshold depends on the data, and is silent when there's no annual-coded award.
    • The rule reads a calendar-month award as annual when it annualises to more than twice the largest annual-coded award on the tab, so the cut-off moves with each survey year. A tab with no annual-coded award "repairs nothing" without saying so.
    • Suggest: record the repaired count and the threshold in the stage evidence, and raise when the tab has calendar-month awards but no annual-coded ones, instead of passing silently.
    • The arithmetic for the one 2024-25 record is right: £4,500 reported per month, weeklyised to £1,035.62, read as annual gives £1,035.62 × 52/12 ≈ £4,488.
  7. Nit: C6 weights the linked-claim share by hand (gross4 sums). That's consistent with the stage's other derived shares and is computed from the data (0.324). The rule follows reg 63 as substituted from 23 September 2020 (SI 2019/1152): no longer limited to new trades, applied at claim start or when a new trade is reported. The limitations listed in the docstring (re-claims, five-year repeats, work-related-requirements group moves) are honest.

Checked and correct

  • C1: the regenerated target_references.json carries the same 5,028 numeric values as before, as a multiset; only labels and metadata move. I couldn't rerun the byte-for-byte regeneration test, because the pinned Chronicle consumer feed isn't on this machine, so it's skipped locally.
  • C4: 3/12 × £6,851.0m + 9/12 × £7,114.7m = £7,048.8m. The source_measure_id_by_opening_year selector picks each fiscal year's own DWP measure.
  • C3: the LCFS counts are named for what they are and derived from age. The receipts show the imputation is value-identical where predictors didn't move.
  • B0: the lock is policyengine-uk 2.122.2 and policyengine-core 3.32.19. The pins, the coverage review, the H2 fixture dependency and APPROVED_UV_LOCK_SHA256 are re-derived.
  • Measurement: arm E (C1–C6 on the locked engine), arm B0 (the bump) and the head arm are each calibrated against the A9 control. The gate failures are diagnosed record by record.
  • Tests: the 17 test files the PR touches pass locally on 2.122.2 / 3.32.19.

Before merge (from the description, still open)

@juaristi22
juaristi22 force-pushed the uk-data-ports-1095-2 branch from a0f292a to a8dc66f Compare October 7, 2026 10:18
juaristi22 added a commit that referenced this pull request Oct 7, 2026
…h no annual-coded award (#1095)

From the review of #1121: the repair's threshold is twice the tab's
largest annual-coded award, so it moves with each survey year, and a
tab with calendar-month awards but no annual-coded award used to repair
nothing without saying so. access_fund_annual now refuses that tab, and
records the paid awards by period code, the threshold and the repaired
count; the root stage carries the record in its checkpoint evidence
beside the capital sentinel count. The docstring and changelog describe
the 2024-25 repair in aggregate terms. The spec scope names the
refusal, and the stage's surfaces are regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22 added a commit that referenced this pull request Oct 7, 2026
The commits, where the plan changed during implementation (C5 reads the
child tab too; C6 puts one identity-keyed draw on the root stage; B1 mirrors
the engine's formula rather than handing the flag to it; B2 leaves the UC
take-up population to Max's branch; B4 is rebased on #1081, which merged
first), and the measurement: arm E (value-identical LCFS, 7/7 gates), arm
B0 (the engine bump empties two sparse UC payment bands; #1107's rollout
checks), arm B2 and the head arm, which also takes the West Midlands 12,570
to 15,000 income-tax cell past the 25% bound. Both diagnoses are recorded:
the regional bands leave out other investment income, and policyengine-uk
2.122.2's corrections move every award that supported the two UC bands. X
defers the cell and Y excludes the bands; replaying the target-fit gate on
the head arm's evidence then passes. Review round 1 on #1121: the rebase
onto #1081, the battery clock CI tripped on, and R1's access-fund evidence.

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

juaristi22 commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator Author

Thanks, Vahid. Round 1 is addressed on the rebased head; details by item below.

Rebase. The branch is rebased onto main after #1081, and CI's engine-free failure is fixed:

  • The failing test was test_fully_armed_battery_evaluates_gate_for_gate. Its fixed register clock predated X's approval, so X read as not yet in force. X and Y now move the clock forward, as the test's note asks.
  • The engine-free job's test files pass locally on the rebased head, apart from the two that need a live Hugging Face pointer or a venv without the UK engine. CI is rerunning.
  • The rebase changed nothing else in the branch's own diff.

1. X and the band variable. Agreed, it's a measurement bug, and wider than the regional cells. 47 HMRC targets band on policyengine-uk's total_income:

  • the 30 regional SPI cells;
  • 14 national SPI band tables, which group by it;
  • 3 ITL rows.

I measured the fix through the calibration's own measure path on the head spine, adding other_investment_income to the band. The West Midlands £12,570–15,000 cell goes from 6.15 times its target to 0.92 at design weights. The other 119 regional income-tax cells move by 8% at most.

The fix belongs upstream:

  • total_income cites ITA 2007 s. 23, which counts all income charged to income tax. But it leaves out the other investment income that its own income_tax charges as savings interest.
  • That also understates post_tax_income, and the incomes that Winter Fuel Payment's test and the Housing Benefit and Council Tax Reduction non-dependant deductions read.

I'm drafting a policyengine-uk issue, and X's register entry will link it once it's filed. X keeps the cell bound in calibration and expires on 2026-11-03.

2. Y and the growing class. Kept for this PR. Agreed the class needs a rule rather than one signed entry per sparse band. The options are:

3. B4 and #1081. #1081 merged first, and B4 is rebased on it, so the CVPAY line is now #1081's own frs_property_income. B4 keeps only the payer's rent_paid_as_boarder / rent_paid_as_lodger and the shared-rent liability.

The householder's receipt stays uncredited, as #1081's docstring records. policyengine-uk 2.122.2 has no householder-side input for lodger or boarder receipts. It has only the payer's two columns and pays_rent_to_householder. So crediting it would start with an engine variable.

4. The bump. Thanks for the list. B0's changelog fragment now carries the forward-year note:

policyengine-uk 2.122.2 models the April 2029 salary-sacrifice cap by adding sacrifice above £2,000 (salary_sacrifice_returned_to_income) to employment_income, so it also raises adjusted net income from 2029-30. The government's change applies to National Insurance only, so for 2029-30 and later the rules that read adjusted net income (the personal allowance taper, the High Income Child Benefit Charge, Tax-Free Childcare eligibility) see higher incomes for people sacrificing more than £2,000. The 2024-25 build is unaffected.

5. The opt-out shortfall. It's expected under #1107's contract (docs/uk-child-benefit-claim-contract.md):

  • With policyengine-uk 2.122.2's default opt_out_charge_share of 1, only fully charged families can opt out.
  • The stage records pool_shortfall_families and pool_exhausted instead of drawing taper families, because the installed model would pay them.
  • A share below 1 would admit taper families. That's an engine parameter, not a data change.

So the 27.4k shortfall is recorded, not filled, and obr.child_benefit still fits.

6. C2's threshold. Done in d083f8ff5:

  • access_fund_annual now refuses a tab that has calendar-month awards but no annual-coded award.
  • It records the paid awards by period code, the threshold and the repaired count.
  • The root stage carries that record in its checkpoint evidence, beside the capital sentinel count.

7. C6. Thanks; no change.

Still open before merge:

juaristi22 added a commit that referenced this pull request Oct 7, 2026
The review of #1121 agreed the West Midlands 12,570-15,000 cell fails
because policyengine-uk's total_income leaves out other investment
income, which its own income_tax charges, and María ruled the fix
belongs upstream. pe-uk#2174 now tracks it, and X's register entry
names it as what the four-week deferral waits on. The entry's approval,
adjudication and dates are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@juaristi22
juaristi22 force-pushed the uk-data-ports-1095-2 branch from a8dc66f to 980ffef Compare October 7, 2026 10:46
juaristi22 added a commit that referenced this pull request Oct 7, 2026
The commits, where the plan changed during implementation (C5 reads the
child tab too; C6 puts one identity-keyed draw on the root stage; B1 mirrors
the engine's formula rather than handing the flag to it; B2 leaves the UC
take-up population to Max's branch; B4 is rebased on #1081, which merged
first), and the measurement: arm E (value-identical LCFS, 7/7 gates), arm
B0 (the engine bump empties two sparse UC payment bands; #1107's rollout
checks), arm B2 and the head arm, which also takes the West Midlands 12,570
to 15,000 income-tax cell past the 25% bound. Both diagnoses are recorded:
the regional bands leave out other investment income, and policyengine-uk
2.122.2's corrections move every award that supported the two UC bands. X
defers the cell and Y excludes the bands; replaying the target-fit gate on
the head arm's evidence then passes. Review round 1 on #1121: the rebase
onto #1081, the battery clock CI tripped on, and R1's access-fund evidence.

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

Copy link
Copy Markdown
Contributor

Automated review pass (Claude Code, high effort) — round 2 at 980ffef0

Verdict: nothing blocking in the code; round 1 is answered. Two small follow-ups: link the two upstream issues filed since round 1, and regenerate the concept-coverage files against main. What stands before merge is what the description already lists: the #1086 ruling, the signed-difference entries, and a calibration, national build and certifier run of this head.

Round-1 item Status Evidence
1. West Midlands deferral is a band-variable defect Agreed; link the issue Confirmed upstream at policyengine-uk 2.122.2. total_income (variables/gov/hmrc/income_tax/total_income.py, cites ITA 2007 s. 23) adds savings_interest_income but not other_investment_income, while taxable_savings_interest_income charges both. It's now filed as policyengine-uk#2174. The X entry in uk/target_fit_reviewed_exclusions.json explains the defect but cites only microcosm#1095. Please add #2174 so the deferral points at the fix that retires it before 2026-11-03. Your 6.15× → 0.92 measurement on the head spine, with the other 119 regional cells moving at most 8%, is a convincing case that the band, not the data, is wrong.
2. Single-household UC band exclusions (Y) Fine as stated Kept for this PR. A pooling or minimum-support rule is the right follow-up; worth a tracking item on #1095 so it doesn't stay one signed entry per release.
3. B4 overlap with #1081 Closed The branch contains the #1081 merge (a50b9cc0). CVPAY handling is only #1081's frs_property_income; B4 keeps the payer's rent_paid_as_boarder/rent_paid_as_lodger and the shared-rent liability. The uncredited householder receipt is documented in #1081's docstring, and the engine has no householder-side input yet.
4. Engine bump and the 2029 salary-sacrifice note Closed; one link The B0 changelog fragment's forward-year note is accurate. It would help to cite policyengine-uk#2173, filed today for exactly this: the fix routes the excess to NI-able earnings only.
5. Child Benefit opt-out shortfall (27.4k) Closed Recorded rather than filled, as #1107's contract sets out (pool_shortfall_families, pool_exhausted, default opt_out_charge_share = 1), and obr.child_benefit still fits.
6. Access-fund repair threshold Closed d083f8ff5: access_fund_annual (frs_spine.py:1390) refuses a tab that has calendar-month awards but no annual-coded award. Its evidence records paid awards by period code, the threshold and the repaired count, and the root stage carries that in its checkpoint evidence. The tests cover repair, refusal and the evidence record (test_uk_frs_spine.py:1062, :2954-3013), and they pass locally.
7. C6 linked-claim share hand-weighted Still open (nit) The reply says "no change", but the round-1 nit was about the weighting, not the rule. frs_spine.py:1353-1362 still computes the linked share from a hand-built gross4 array (weight[linked].sum()). Under the microdf-throughout convention, a MicroSeries over the person rows with household weights would do it. Not blocking.

Rebase and CI

  • The engine-free fix is legitimate. In test_uk_battery_bindings.py::test_fully_armed_battery_evaluates_gate_for_gate, the fixed register clock predated X's approval, so X read as not yet in force. Moving the clock forward is what the test's note asks. It passes locally.
  • New: conflicts with main. Main has moved by Add Axiom graph adapter surfaces and simulate.rules_by_ref@1 (NZ v0, G1) #1118 and Add the liquid financial assets concept, schema version 2 (NZ v0, G3a) #1120 (the NZ hub: Axiom adapter and the liquid financial assets concept). git merge-tree shows two conflicts, both in generated files: docs/concept-coverage/policyengine-uk.md and packages/microcosm-frame/tests/golden/concept-coverage/policyengine-uk.json. Regenerate them after merging main; nothing hand-written conflicts.
  • CI at this head: wheels passed. engine-free (3.13, 3.14), lint and select-countries were still pending when I checked.

@juaristi22
juaristi22 force-pushed the uk-data-ports-1095-2 branch from 980ffef to c098fdc Compare October 7, 2026 14:09
juaristi22 added a commit that referenced this pull request Oct 7, 2026
…h no annual-coded award (#1095)

From the review of #1121: the repair's threshold is twice the tab's
largest annual-coded award, so it moves with each survey year, and a
tab with calendar-month awards but no annual-coded award used to repair
nothing without saying so. access_fund_annual now refuses that tab, and
records the paid awards by period code, the threshold and the repaired
count; the root stage carries the record in its checkpoint evidence
beside the capital sentinel count. The docstring and changelog describe
the 2024-25 repair in aggregate terms. The spec scope names the
refusal, and the stage's surfaces are regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22 added a commit that referenced this pull request Oct 7, 2026
The review of #1121 agreed the West Midlands 12,570-15,000 cell fails
because policyengine-uk's total_income leaves out other investment
income, which its own income_tax charges, and María ruled the fix
belongs upstream. pe-uk#2174 now tracks it, and X's register entry
names it as what the four-week deferral waits on. The entry's approval,
adjudication and dates are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22 added a commit that referenced this pull request Oct 7, 2026
The commits, where the plan changed during implementation (C5 reads the
child tab too; C6 puts one identity-keyed draw on the root stage; B1 mirrors
the engine's formula rather than handing the flag to it; B2 leaves the UC
take-up population to Max's branch; B4 is rebased on #1081, which merged
first), and the measurement: arm E (value-identical LCFS, 7/7 gates), arm
B0 (the engine bump empties two sparse UC payment bands; #1107's rollout
checks), arm B2 and the head arm, which also takes the West Midlands 12,570
to 15,000 income-tax cell past the 25% bound. Both diagnoses are recorded:
the regional bands leave out other investment income, and policyengine-uk
2.122.2's corrections move every award that supported the two UC bands. X
defers the cell and Y excludes the bands; replaying the target-fit gate on
the head arm's evidence then passes. Review round 1 on #1121: the rebase
onto #1081, the battery clock CI tripped on, and R1's access-fund evidence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@juaristi22
juaristi22 force-pushed the uk-data-ports-1095-2 branch from c098fdc to bdffbe2 Compare October 7, 2026 17:14
juaristi22 added a commit that referenced this pull request Oct 7, 2026
…h no annual-coded award (#1095)

From the review of #1121: the repair's threshold is twice the tab's
largest annual-coded award, so it moves with each survey year, and a
tab with calendar-month awards but no annual-coded award used to repair
nothing without saying so. access_fund_annual now refuses that tab, and
records the paid awards by period code, the threshold and the repaired
count; the root stage carries the record in its checkpoint evidence
beside the capital sentinel count. The docstring and changelog describe
the 2024-25 repair in aggregate terms. The spec scope names the
refusal, and the stage's surfaces are regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22 added a commit that referenced this pull request Oct 7, 2026
The review of #1121 agreed the West Midlands 12,570-15,000 cell fails
because policyengine-uk's total_income leaves out other investment
income, which its own income_tax charges, and María ruled the fix
belongs upstream. pe-uk#2174 now tracks it, and X's register entry
names it as what the four-week deferral waits on. The entry's approval,
adjudication and dates are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22 added a commit that referenced this pull request Oct 7, 2026
The commits, where the plan changed during implementation (C5 reads the
child tab too; C6 puts one identity-keyed draw on the root stage; B1 mirrors
the engine's formula rather than handing the flag to it; B2 leaves the UC
take-up population to Max's branch; B4 is rebased on #1081, which merged
first), and the measurement: arm E (value-identical LCFS, 7/7 gates), arm
B0 (the engine bump empties two sparse UC payment bands; #1107's rollout
checks), arm B2 and the head arm, which also takes the West Midlands 12,570
to 15,000 income-tax cell past the 25% bound. Both diagnoses are recorded:
the regional bands leave out other investment income, and policyengine-uk
2.122.2's corrections move every award that supported the two UC bands. X
defers the cell and Y excludes the bands; replaying the target-fit gate on
the head arm's evidence then passes. Review round 1 on #1121: the rebase
onto #1081, the battery clock CI tripped on, and R1's access-fund evidence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@juaristi22
juaristi22 marked this pull request as ready for review October 7, 2026 17:15
@juaristi22
juaristi22 force-pushed the uk-data-ports-1095-2 branch from bdffbe2 to 692e8b7 Compare October 7, 2026 23:45
juaristi22 added a commit that referenced this pull request Oct 7, 2026
…h no annual-coded award (#1095)

From the review of #1121: the repair's threshold is twice the tab's
largest annual-coded award, so it moves with each survey year, and a
tab with calendar-month awards but no annual-coded award used to repair
nothing without saying so. access_fund_annual now refuses that tab, and
records the paid awards by period code, the threshold and the repaired
count; the root stage carries the record in its checkpoint evidence
beside the capital sentinel count. The docstring and changelog describe
the 2024-25 repair in aggregate terms. The spec scope names the
refusal, and the stage's surfaces are regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22 added a commit that referenced this pull request Oct 7, 2026
The review of #1121 agreed the West Midlands 12,570-15,000 cell fails
because policyengine-uk's total_income leaves out other investment
income, which its own income_tax charges, and María ruled the fix
belongs upstream. pe-uk#2174 now tracks it, and X's register entry
names it as what the four-week deferral waits on. The entry's approval,
adjudication and dates are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22 added a commit that referenced this pull request Oct 7, 2026
The commits, where the plan changed during implementation (C5 reads the
child tab too; C6 puts one identity-keyed draw on the root stage; B1 mirrors
the engine's formula rather than handing the flag to it; B2 leaves the UC
take-up population to Max's branch; B4 is rebased on #1081, which merged
first), and the measurement: arm E (value-identical LCFS, 7/7 gates), arm
B0 (the engine bump empties two sparse UC payment bands; #1107's rollout
checks), arm B2 and the head arm, which also takes the West Midlands 12,570
to 15,000 income-tax cell past the 25% bound. Both diagnoses are recorded:
the regional bands leave out other investment income, and policyengine-uk
2.122.2's corrections move every award that supported the two UC bands. X
defers the cell and Y excludes the bands; replaying the target-fit gate on
the head arm's evidence then passes. Review round 1 on #1121: the rebase
onto #1081, the battery clock CI tripped on, and R1's access-fund evidence.

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

Copy link
Copy Markdown
Contributor

Automated review pass (Claude Code, high effort) — round 3 at 692e8b7b

Verdict: nothing blocking in the code; #1086 is settled and CI is green on every lane. What stands before merge is what the description lists: the signed-difference entries, and a calibration, national build and certifier run of this head. One question on how the core-pin commit lands.

What changed since round 2 (git range-diff of a50b9cc0..980ffef0 against e3e3d881..692e8b7b)

  • New first commit, P (261bb8cd): per-country policyengine-core pins, per the policyengine-uk 2.102.4+ needs policyengine-core 3.32.9+, which the certified US default refuses #1086 ruling (option 2).
  • B0 (eba82ce4): now moves only the UK side of the lock, and reviews the liquid financial assets concept's unmapped reason against 2.122.2. That reason now names car_list_price, lifetime_isa_balance and UC's claimant-and-partner capital split.
  • Every other port commit is either identical or differs only in the regenerated gate-battery digests from the two rebases.
  • The receipts file records the two extra rebases and the US-lane evidence.

Findings

  1. Should (process): land P as its own PR, or confirm with the policyengine-uk 2.102.4+ needs policyengine-core 3.32.9+, which the certified US default refuses #1086 ruling's author that it can ride here. The ruling's last instruction was "Keep it to the lock. The PR should change no engine version. The policyengine-uk bump follows as its own PR." P itself matches that exactly:

    • In uv.lock, engine versions are identical at e3e3d881 and 261bb8cd: core 3.32.5, policyengine-uk 2.100.0, policyengine-us 2.2.1, spm-calculator 1.0.0.
    • The fork appears only at B0: core 3.32.5 and 3.32.19, policyengine-uk 2.122.2.

    But here P ships in the same PR as B0's bump and fourteen ports. The description already says P can be cherry-picked alone. I'd do that: P goes in fast as a small lock-only PR, and this PR rebases onto it.

  2. Nit: B0's forward-year note still doesn't cite pe-uk#2173 (changelog.d/1095-policyengine-uk-2-122.changed.md). This was round 2's item 4. #2173 is the issue for the 2029 salary-sacrifice excess reaching ANI, so the note should point at it the same way X points at #2174.

  3. Nit, carried from round 2: C6's linked share is still hand-weighted. At frs_spine.py:1355-1359, weight[linked].sum() is taken over a raw gross4 array. A household-weighted MicroSeries would follow the microdf-throughout convention. Not blocking.

Round-2 items

Item Status
Link pe-uk#2174 from X Done. The register reason, the receipts and test_uk_battery_bindings.py (assert "pe-uk#2174" in deferral.reason) all cite it. X expires 2026-11-03.
Regenerate concept coverage against main Done. Regenerated on the new concept schema (d824f5e97d58), with 285 inputs and 40 covered. The branch merges cleanly into main.
Cite pe-uk#2173 in B0's note Open (nit 2).
Y's sparse-band class needs a rule No tracking item on #1095 yet. Worth one line so it doesn't stay one signed entry per release.
C6 hand-weighted share Open (nit 3).

#1086 / P checked

  • [tool.uv] conflicts pairs every US-installing extra with every UK-installing extra. The US-installing extras are microcosm-build[us], microcosm-data[us] and microcosm-frame[policyengine]; the UK-installing ones are each package's [uk].
  • No other pyproject.toml in the workspace installs an engine.
  • CI installs one extra per lane (--extra us, --extra uk), which the conflicts allow.
  • APPROVED_UV_LOCK_SHA256 is re-pinned at P and again at B0.

Tests run locally at 692e8b7b (fresh worktree, uv sync --all-packages --locked, the engine-free environment)

  • test_engine_core_pins.py: 14 passed.
  • All 18 engine-free test files this PR touches: about 1,220 passed, 7 skipped, 0 failed.
  • ruff check .: clean.
  • ruff format --check: none of the PR's files would be reformatted.
  • CI at this head is green: lint, wheels, select-countries, engine-free, engine-us and engine-uk (each on 3.13 and 3.14), and integration-uk.

Still before merge (from the description, and unchanged)

  • The signed-difference entries for the net-new columns (C5, C6, B2–B5) and for B1's mechanism change. The drafts are still outside the repo.
  • A calibration of this head, the national build and the certifier. The receipts' head arm was built as 65a08ddb6 with both UC bands bound; the Y replay reuses those weights.

juaristi22 and others added 4 commits October 8, 2026 13:58
…1095)

#1089 (2fe7d8b) moved the SPI Table 3.6 employment, self-employment
and private-pension amounts by total-income band to HMRC's band growth
(hmrc.itl_2026.total_income_growth_by_total_income_band) by editing the
generated uk/target_references.json, which it also re-serialised with
literal pound signs. The population contract still declared the flat
OBR average-earnings, per-capita mixed-income and private-pension
indices for the three targets, so regenerating from feed 825406f gave
e8f829... against the committed bf7549..., and the feed-gated
test_committed_surfaces_regenerate_from_pinned_feed[national] failed on
main (CI skips it without the licensed feed).

The contract now declares the band-growth index, with notes saying so,
for the three targets, and both generated resources are regenerated:
the references equal the committed ones as parsed JSON (only the
encoding returns to the generator's), and the membership report, which
still recorded the flat-index uprating for the 39 references, now
records the band factors (-2.3% for 15,000-20,000, +0.3% for
20,000-30,000). Compiled values are unchanged: the three lowest
employment bands stay at GBP 17.7bn, 47.5bn and 172.5bn, and the
national compile parity regenerates and passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vahid's should-fix on #1100: retiring the access_fund input-mass
exclusion left the period-code defect it recorded with no active
record. The FRS weeklyises ACCSSAMT from the reported amount and its
period code ACCSSPD, and an access-fund award is paid per academic year
or term. On the 2024-25 tab the 16 awards' calendar-month amounts run up
to the size of a whole annual award, so an award read as monthly can
annualise to more than ten times every annual-coded award; #1089 had to
sign an exclusion when calibration took such a household to the weight
bound.

access_fund_annual reads ACCSSPD: a calendar-month award (code 5) that
annualises to more than twice the largest annual-coded award (code 52)
on the tab is read as its per-period amount, ACCSSAMT x 52/12. On the
2024-25 tab that repairs fewer than 10 awards and leaves large but
plausible monthly amounts as reported, as it does the awards with no
period code. A tab without the ACCSSPD column refuses. The spec names
the rule, the test and H2 fixtures carry ACCSSPD, and the coverage
manifest and H2 fixture are regenerated.

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

The rest of uk-data#486 on microcosm. The LCFS donor's G018 and G019,
household counts of adults and children by the survey's age-18 split,
were renamed to the engine's person flags is_adult and is_child, and the
recipient materialised those engine flags and summed them per household.
policyengine-uk#1896 deprecates both flags. The predictors are now
household_adult_count and household_child_count, in the same positions
of the predictor list, and the recipient counts its members from age
(18 and over, under 18), which is exactly the engine's 2.100.0 rule, so
the values and the seeded forest are unchanged; the stage no longer asks
the engine for the two flags.

The lcfs_consumption notes were a plain YAML scalar containing " #890",
which YAML reads as a comment, so 642 characters were dropped at load
and from source_stages.json; the notes are now single-quoted and gain a
sentence on the counts. Coverage manifest and H2 fixture regenerated.

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

DWP's benefit expenditure and caseload tables give each fiscal year its own
measure (expenditure_2024, expenditure_2025), so calendar_year_window read the
two years as two one-year series and refused the window; #1100 dropped the row
for that reason. A window selector can now declare
source_measure_id_by_opening_year, mapping each overlapping year to its
measure: a fact matches only its own year's measure, and the declared measures
form the window's one series. The map's form is checked when the reference is
built; its years are checked when the window resolves, so a compile that
restamps the reference to another period (the 2023 parity receipt) defers it
like any reference without facts.

dwp.hb.amount_pension_age binds the line "Housing Benefit over Pension Credit
qualifying age" at 3/12 of the FY2024-25 outturn (6,851.0m) plus 9/12 of the
FY2025-26 forecast (7,114.7m), 7,048.8m, on housing_benefit over benefit units
whose eldest adult is 66 or over in Great Britain households. It has its own
dwp_benefit_expenditure family, which allows source projections. The national
references, the membership report and the 2025 parity receipt gain the row and
nothing else.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
juaristi22 and others added 13 commits October 8, 2026 13:58
The spine never wrote is_blind, so every person took policyengine-uk's
default False. A person is now blind where their own FRS tab records SPCREG1 =
1, registered blind or severely sight impaired with the local authority:
registration follows the consultant ophthalmologist's certificate the engine's
is_blind names. The adult and child tabs ask the same question, so children
are read too; partial-sight registration (SPCREG2) is not read (uk-data#523).
FRS 2024-25 has 29 registered adult records (57.3k at gross4 design weights)
and fewer than 10 child records. On policyengine-uk 2.100.0 the TV licence
concession, the Disabled Students' Allowance condition, the Council Tax
Reduction non-dependant exemption and the Tax-Free Childcare age limit and
higher amount read it.

person.is_blind joins the export-surface allow-list (gates.json and its
mirrored constant) and the graph's root booleans. Regenerated:
source_stages.json, the H2 spine fixture (synthetic SPCREG1 on adults and
children), the coverage manifest's source-manifest pins and the mirrored gate
digests. The net-new-column adjudication is drafted for signature.

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

uc_is_in_startup_period was never set, so the engine's default False
applied the Universal Credit minimum income floor from the first assessment
period of every self-employed claim. Port uk-data#527's rule (UC Regs 2013
reg 63 as substituted by SI 2019/1152): a self-employed person (EMPSTATI 3/4,
a held self-employed JOB row, or nonzero SEINCAM2) is in a start-up period
when the benefit unit's UC claim (UCSTART, month/day/year) or the first
self-employed job (SEJBLONG, main job first) began less than 12 calendar
months before INTDATE; a year-round trader's new self-employed job that is
not a business is a new engagement and does not date the trade.

UC records with no linked claim date (288 of 2,402 FRS 2024-25 UC benefit
units) draw their claim recency, keyed on the benefit unit, at the GROSS4
share of linked self-employed claimants inside the window (0.324). The draw
is declared on frs_spine as assign_binary_from_rate, and the stage notes now
name it as the root's one identity-keyed draw: only this stage reads the raw
claim, interview and job dates. UCSTART is kept as read before numeric
conversion, and missing raw date columns refuse. SPI-redrawn rows keep the
flag only while their status or imputed self-employment income still shows
self-employment. At design weights 222 person records (488.2k) hold it, 96
(208.3k) in UC benefit units, no child.

person.uc_is_in_startup_period joins the export allow-list and the graph's
root booleans and SPI rewrites. Regenerated: source_stages.json, the H2
spine fixture (synthetic INTDATE, UCSTART, job dates, SAMESIT and SDEMP, and
UC rows), the coverage manifest's source pins and the mirrored digests.

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

The remaining #1095 items read inputs policyengine-uk added after 2.100.0
(pe-uk#1896, #1946, #2018/#2070, #2006, #1940/#1904, #2140), so the UK
extras of microcosm-build, microcosm-data and microcosm-frame require
>=2.122.2, and the UK fork of the lock moves to policyengine-uk 2.122.2
with core 3.32.19. Under #1086's per-country pins the US fork does not
move: the extras that install policyengine-us keep core 3.32.5, so the
certified US default still loads and the US lane runs on the engine it
was built with.

Re-derived on the new lock, no data change: the uprating pin stamp (values
unchanged), the policyengine-uk concept coverage (285 inputs, 40 covered),
the concept mapping reviewed against 2.122.2 (targets unchanged; the Child
Benefit notes quote the documentation that datasets export registered
claims; the liquid financial assets reason names the person-level stocks
car_list_price and lifetime_isa_balance, and Universal Credit's household
capital split by claimant and partner count beside its Lifetime ISA
source), the H2 fixture's numerical dependency, APPROVED_UV_LOCK_SHA256 and
the CGT stages' declared dependency. policyengine-uk 2.112.0 replaced the
male and female State Pension age parameters with the timetable by date of
birth, so the UC take-up population reads the cohort's whole-year age from
that timetable (66 for 2024 and 2025) and refuses a year where none exists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
is_severely_disabled_for_benefits counted any Attendance Allowance and any
Armed Forces Compensation Scheme payment. It is now the tax credit severe
disability condition (CTC Regs 2002 reg 8, WTC Regs 2002 reg 17) read from the
categories the stage derives: DLA care at the highest rate, PIP daily living
at the enhanced rate, or higher-rate Attendance Allowance (uk-data#494). That
is policyengine-uk's own formula since #1946, and an engine-lane test checks
the stored flag against it category by category. FRS code 8 cannot separate
Armed Forces Independence Payment, so AFCS no longer counts.

The incumbent's coverage manifest marks the flag required, so the spine keeps
storing it rather than handing it to the formula. Regenerated: the stage
notes in source_stages.json, the coverage manifest's source pins and the H2
fixture's stage record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
policyengine-uk#1896 defines is_claimant_or_partner and
is_hbai_dependent_child and infers both from ages when a dataset does not
supply them. Every FRS person is on the adult table or the child table: the
adult table holds each benefit unit's head and any partner (uk-data#524), the
child table its HBAI dependent children (uk-data#486), so the spine writes
adult-table membership and its complement. The existing claimant derivation
already refuses a benefit unit without one or two adult records.

Both join the export allow-list and the graph's root booleans. Regenerated:
source_stages.json, the H2 fixture, the coverage manifest's source pins and
the mirrored gate digests. The UC take-up population's move to the engine's
rule is left to Max's uk-take-up-per-person-spa branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
policyengine-uk#2018 and #2070 add pension_credit_reported_capital: 0 or more
replaces every capital source in Pension Credit's assessable capital, and the
default -1 leaves the household proxy. The UC capital coherence stage now
writes it beside uc_reported_capital from the same final capital: FRS
TOTCAPB4 for the survey units, the capital redraw for SPI-channel units, and
the named unavailable sentinel -1 where none is recorded (uk-data#513). Like
UC's override it replaces property too; the engine issue about that is held
for María's go.

It joins the export allow-list and the stage's graph cells. Regenerated:
source_stages.json, the H2 fixture, the coverage manifest's source pins and
the mirrored gate digests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
policyengine-uk#2006 adds liable_for_share_of_household_rent,
rent_paid_as_boarder and rent_paid_as_lodger. The spine ports uk-data#512's
rule for the first: a later benefit unit of a shared household (HHSTAT 2) is
liable where its adults report a rent share (SRENTAMT), it reports Housing
Benefit (HBOTHAMT) or a linked UC record carries a housing element
(UCHOUSEL); conventional households are left out. CVPAY, the rent a boarder
or lodger pays the householder, is recorded on the payer and was counted as
the payer's property income; it now feeds rent_paid_as_boarder (CONVBL 1) or
rent_paid_as_lodger, and leaves property_income by the same line #1081
removes (coordination with Max owed).

FRS 2024-25 at gross4: 103 shared households (319.9k) hold 178 liable later
units (615.9k); 43 adult records (138.4k) pay the householder, fewer than 10
as boarders; 806.7m a year leaves property income. The three columns join the
export allow-list; regenerated: source_stages.json, the H2 fixture (synthetic
HHSTAT, CONVBL, HBOTHAMT and UCHOUSEL), the coverage manifest's source pins
and the mirrored gate digests.

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

policyengine-uk#1940 adds has_mixed_age_couple_pension_credit_saving, the SI
2019/37 saving for a couple entitled as that couple on 14 May 2019. Its
default formula reads the older member's birth date from age and the model
year, and microcosm holds ages fixed, so each later model year would drop
another protected cohort (uk-data#519). The Pension Credit take-up stage
already materializes the engine once on the post-SPI receipts at the frame's
survey-year period; it now asks for the saving in the same call and stores
it, so the entitlement it redraws take-up on and every later model year read
the same answer: a mixed-age couple whose older member was born by 5 February
1954, reporting Pension Credit or pension-age Housing Benefit without an
income-related legacy benefit, and no Universal Credit.

The stage declares the stored output and a second materialize operation; the
column joins the export allow-list and the stage's graph cells. Regenerated:
source_stages.json, the H2 fixture, the coverage manifest's source pins and
the mirrored gate digests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…leave out other investment income (#1095)

The head arm (65a08ddb6) blocks on
hmrc.spi_region.income_tax_by_region_12570_15000@E12000005 at +27.4%
(target 55.1m, design 339.2m) beside the two UC payment bands. The
regional cells slice on policyengine-uk's total_income, which leaves out
other_investment_income although income_tax charges it as savings
interest. Fewer than 10 SPI records whose income is mostly other
investment income sit in this band with their whole tax, so the cell's
design value is 6.1 times its target (6.0 times before these ports;
every other region's cell 0.6-1.1 times). Banded on total income
including other investment income, it falls in line with the other
regions.

The solver pulled the cell to +8.9% with the engine-free ports;
policyengine-uk 2.122.2 lands it at +23.6% and B3-B5 at +27.4% (a
counterfactual arm with CVPAY back in property income lands at +25.4%).
It is deferred four weeks in the target-fit register, approved on
2026-10-06, while the band variable is corrected. The entry would be
stale before B3, where the cell fits at +23.6%, so it lands after B5.
The two UC bands still block the national artifact.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dwp/uc_payment_dist/SINGLE_annual_payment_26_400_to_27_600 and
COUPLE_NO_CHILDREN_annual_payment_24_000_to_25_200 each rest on fewer
than 10 FRS households. The same spine under policyengine-uk 2.100.0
keeps every supporting award in its band; 2.122.2 moves all of them out
through corrections, not data changes: the published LHA rates move
awards to neighbouring bands, the 2019 mixed-age couple saving keeps
couples who report Pension Credit off UC, and Carer Support Payment
counts as unearned income. Both bands sit at -100% on arms B0, B2 and
head, so they join the uk-data#452 measure exclusions beside their
siblings, approved on 2026-10-07 and expiring with the class on
2026-11-26.

Replaying the target-fit gate on the head arm's evidence without the
two bands passes, with the West Midlands deferral in force.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h no annual-coded award (#1095)

From the review of #1121: the repair's threshold is twice the tab's
largest annual-coded award, so it moves with each survey year, and a
tab with calendar-month awards but no annual-coded award used to repair
nothing without saying so. access_fund_annual now refuses that tab, and
records the paid awards by period code, the threshold and the repaired
count; the root stage carries the record in its checkpoint evidence
beside the capital sentinel count. The docstring and changelog describe
the 2024-25 repair in aggregate terms. The spec scope names the
refusal, and the stage's surfaces are regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The review of #1121 agreed the West Midlands 12,570-15,000 cell fails
because policyengine-uk's total_income leaves out other investment
income, which its own income_tax charges, and María ruled the fix
belongs upstream. pe-uk#2174 now tracks it, and X's register entry
names it as what the four-week deferral waits on. The entry's approval,
adjudication and dates are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The commits, where the plan changed during implementation (C5 reads the
child tab too; C6 puts one identity-keyed draw on the root stage; B1 mirrors
the engine's formula rather than handing the flag to it; B2 leaves the UC
take-up population to Max's branch; B4 is rebased on #1081, which merged
first), and the measurement: arm E (value-identical LCFS, 7/7 gates), arm
B0 (the engine bump empties two sparse UC payment bands; #1107's rollout
checks), arm B2 and the head arm, which also takes the West Midlands 12,570
to 15,000 income-tax cell past the 25% bound. Both diagnoses are recorded:
the regional bands leave out other investment income, and policyengine-uk
2.122.2's corrections move every award that supported the two UC bands. X
defers the cell and Y excludes the bands; replaying the target-fit gate on
the head arm's evidence then passes. Review round 1 on #1121: the rebase
onto #1081, the battery clock CI tripped on, and R1's access-fund evidence.

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

Copy link
Copy Markdown
Collaborator Author

Thanks, Vahid. Rounds 2 and 3 are addressed: #1143 has merged, and this PR is rebased onto it with the fixes (05b8f073d).

Round 3

  1. P as its own PR. It landed as Give each country its own policyengine-core pin (#1086) #1143, so it's no longer part of this PR.
  2. Salary sacrifice above the April 2029 NI cap raises adjusted net income, but HMRC says the cap leaves ANI unchanged policyengine-uk#2173. B0's forward-year note now cites it, the same way X cites total_income leaves out other_investment_income, which income_tax charges policyengine-uk#2174.
  3. C6's linked share. It's now a household-weighted average over the linked rows: np.average with the gross4 household weights. That is the idiom microcosm's stages already use for weighted shares (provenance_flags.py, frs_take_up.py).
    • I didn't use a MicroSeries. microdf only reaches an environment with an engine, because policyengine-core, -uk and -us depend on it. The FRS spine runs in the engine-free lane, so microdf would become a new runtime dependency of microcosm-build, and the lock would move for one share.
    • The FRS spine tests pass (101), and no generated surface moved.

Round 2

Before merge

@juaristi22

Copy link
Copy Markdown
Collaborator Author

Calibration of this head (arm head-r3). Built locally from c7fe76e8a, the same tree as the pushed head 05b8f073d, on Chronicle feed 825406f, with local staging only.

  • Fit. Loss 0.00712, against 0.00715 for arm A9 (main's code without this PR). 98.3% of 1,167 targets are within 10%, and the ESS is 4,874.
  • Gates. With X and Y in force, all 7 calibrated-seam gates pass, and so do the exported file's terminal gates. Unlike the earlier replay of Y, this solve runs without the two UC payment bands.
  • Input mass. The certifier's input-mass gate passes, with no stale, expired or dormant exclusion.
  • Rows to watch:
  • C6. The weighted-share rewrite leaves uc_is_in_startup_period unchanged: no value differs on the 142,129 person rows this spine shares with the earlier head arm.

The receipts in experiments/1095-uk-data-ports-2-receipts.md still list this calibration as owed. The follow-up PR records it there, so this PR needs no further CI run. The signed-difference entries, the national build and the certifier follow the merge, as for #1100.

@juaristi22
juaristi22 merged commit 75167a6 into main Oct 8, 2026
10 checks passed
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.

2 participants