Repository navigation
Budget 2026 slower data fixes, PR C of #1418: electricity at the ONS level, pension income by area - #1420
Budget 2026 slower data fixes, PR C of #1418: electricity at the ONS level, pension income by area#1420juaristi22 wants to merge 46 commits into
Conversation
…g buys The lcfs_consumption stage raked electricity kWh to DESNZ Energy Trends' calendar-2024 volume, which priced at QEP sits about 5% above ONS Consumer Trends 04.5.1 on the 30 September 2026 release. A fuel the stage now declares in spend_level_fuels takes its level from ONS household spending instead: the class for the level year, less QEP's United Kingdom average fixed cost on every UK domestic meter DESNZ counts (Great Britain's sub-national count with Northern Ireland's row, now vendored), over QEP's United Kingdom average unit cost. Electricity is declared, giving 84.9 TWh against Energy Trends' 91.7 TWh; gas keeps Energy Trends. published_energy_level carries the new level everywhere it was read: the stage's rake, the energy_rake gate's recomputation, and the factor that puts the dense build's local DESNZ electricity rows on the stage's level, so those rows move with it. The texts that described the level as Energy Trends follow (gate notes, census fence, cross-grain route, holds, adjudication, contract notes), and the ONS resource's purpose now names the release its rows come from. microcosm#1418, item 12 of the Budget data fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OOTLAR 1.50 moves machines with a maximum charge to play above GBP 1, or a maximum prize above GBP 100, to Machine Games Duty's higher rate from 1 April 2027: Category B. The Machine Games Duty standard-rate base the stage levels has no category split, so the Gambling Commission's FY2024-25 gaming machine gross gambling yield by machine category (Industry Statistics sheet 4, all venues, Great Britain) is vendored beside it, a sixth excise_facts.json selection. The split column that reads it waits for the engine input (policyengine-uk#2719). microcosm#1418, item 5 of the Budget data fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The energy_rake gate's notes moved the gates manifest digest, its fingerprint and the spine part's manifest digest (the policy digest is unchanged), and the stage's declaration and vendored resources moved the release input coverage manifest's source digest. Regenerated and checked; the local references, the local compile parity receipt and the H2 fixture follow in the pin pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ands The dense surface had no pension income below the national SPI bands. hmrc.pension_income.amount binds each constituency's and local authority's taxpayers' private and State Pension income together from SPI 2023-24 Tables 3.15 and 3.14 (count times mean). It is grown to calendar 2025 by ITL Table 2.5's all-ranges total income growth, which the national State Pension and private pension bands match together to within 0.1%. The frame reads total_pension_income_annual, the engine's sum of the two pensions on the annual simulation. A fan-out sum bridge reconciles the cells per grain to the national bands, and may now sum several fan-outs: the State Pension and private pension bands together are the area cells' quantity. A control missing one of its fan-outs is refused. At source the bands sit 3.0% above the constituency cells and 3.2% above the local-authority cells, since the area tables leave out the taxpayers HMRC places in no area, who are more often pensioners; the bridge refuses past 4% at source or 0.6% in the index gap. The two authorities without SPI measures are deferred as for employment, and the metric is appended last at both grains. Counts are not bound here: the area tables count taxpayers with any pension, a union no national row binds, and the bridge for them is a ruling still owed (microcosm#1418, item 14 of the Budget data fixes). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR C stacks on PR A. The two conflicts were the contract's target counts (PR A's three ITL Table 2.6 savings rows and PR C's pension income by area: 452 targets, 391 national, 61 local) and the release input coverage manifest, regenerated on the merged tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The engine-free annual measure test checks every annual measure is one the resolver knows; total_pension_income_annual reads the engine's total_pension_income, which the stub now sums from its two pensions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The lcfs_consumption stage's spend_level declaration is mirrored into the fixture's stage parameters and the graph's contract hashes move with it and with the re-vendored resources; nothing else moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
María's ruling of 2026-10-28 on PR C: gas follows electricity. The 30 September 2026 Consumer Trends release puts 2024 gas spending at GBP 18.30bn, so the stage's Energy Trends level priced at QEP sat about 5% above ONS for both fuels. The stage now declares gas in spend_level_fuels: ONS 04.5.2 less QEP's United Kingdom average gas standing charge on every UK domestic gas meter, over the unit cost, 236.5 TWh against Energy Trends' 252.3 TWh. Great Britain's local gas cells follow at 234.0 TWh. The texts that named Energy Trends for gas now name the shared level. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decision D1 held ons.household_electricity_expenditure and ons.household_gas_expenditure out of the objective as published-source residuals against the stage's DESNZ-volume level. With the stage's level now the kWh ONS household spending buys, the frame starts about 1% below each row at design weights, so both exclusions are deleted and the national binds ONS 04.5.1 and 04.5.2 again (María's rulings of 2026-10-28 on microcosm#1418: PR C carries the electricity deletion PR A planned, and gas follows). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
microcosm#1278 closed into microcosm#1095 on 25 October, which now tracks every open UK data item. The 29 register entries whose tracking still named #1278 (five measure exclusions, two small-cell rules and 22 uprating holds) name #1095 (María's ruling of 2026-10-28). Text only: no entry's dates, reason or scope changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
María's ruling of 2026-10-28 on PR C (option c): SPI Tables 3.14 and 3.15 count each area's taxpayers with any pension income, State or private, and no national row counts that union, since the national SPI bands count State Pension and private pension recipients apart and most taxpayers of State Pension age are both. hmrc.pension_income.count binds the cells at HMRC's own area level, grown by ITL Table 2.5's all-ranges taxpayer growth, as a local family with no higher control, declared a subset of ITL's taxpayers. The metric follows the amount, appended last at both grains; the same two authorities are deferred. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The energy_rake gate's notes moved the gates manifest digest, its fingerprint and the spine part's manifest digest again (the policy digest is unchanged); the stage's gas declaration moved the coverage manifest's source digest and the fixture's stage parameters and contract hashes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The obr.fuel_duties_cars reason converted ONS 07.2.2 for calendar 2025 to duty on the June Consumer Trends edition; the feed carries the 30 September 2026 edition, whose figure is 3.2% lower, so the conversion is about GBP 13.1bn, 9 percent below the OBR cars line. Text only (María's ruling of 2026-10-28: refresh reason text that quotes the old release). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
policyengine-uk relieves petrol and diesel duty for a household with in_rural_fuel_duty_relief_area, which no Microcosm build wrote. HMRC Notice 2001 (SI 2011/2935 Schedule 1) opens the relief in the Inner and Outer Hebrides, the Northern Isles, the Islands of the Clyde, the Isles of Scilly, the post town of Hawes and sixteen mainland postcode districts, and names the island groups without bounding them. tools/build_uk_rfdr_area_layer.py declares Microcosm's translation into postcode districts, with unit postcodes for the islands that share a sector with the mainland (Raasay, Seil, Easdale, Luing, Kerrera, Lismore) and for Hawes, and mainland units inside island districts excluded, each part with its sources. From the ONS postcode lookup it writes uk/rfdr_area_layer.json: the output areas more than half of whose postcodes are in the scheme, 41 in England and 1,053 in Scotland. The post-geography node uk.full.geography.rfdr sets the flag on each placed household from its assigned output area, after the local authority resolve, on the BRMA node's pattern: --rfdr-localisation area is the default on the atomic assignment, none is for measurement builds and a release candidate refuses it, and the layer is pinned like the BRMA layer. Northern Ireland has no area in the scheme, and the national line leaves the engine's default of false (María's ruling of 2026-10-28). The column joins the export surface's reviewed extras. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The uk_export_surface gate's allowed_extra_columns gains the RFDR flag, moving the policy, gates manifest and fingerprint digests and the release-cut part's digests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tracking fields moved to #1095 in c4b08a5, but six texts still said an open item is tracked on #1278: the obr.fuel_duties_cars reason's business-use rebind, two uprating-hold reasons, and the high-value homes coverage decision (#1240 fix 6) in the local census description, the scope constant's comment and a test comment. Text only, completing María's ruling of 2026-10-28; texts citing #1278 as where something was decided or changed are history and stay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o PR C PR A allows the UC take-up stage gate 0.06 of DWP's caseload and names the in-household care hours carrier among the release-dropped columns. The gate battery's policy, manifest and fingerprint digests and the spine part's manifest digest are recomputed on the merged gates.json, which carries both PR A's tolerance and PR C's notes and RFDR export column; the coverage manifest and the H2 fixture don't move. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The obr.betting_and_gaming_duties exclusion held the row out as an identity: the lcfs_consumption stage levels each gambling base to HMRC's FY2024-25 receipts, or Machine Games Duty's liabilities, over the engine's rates (microcosm#1359), so the engine charged the receipts back by construction. That holds only at prior weights. Calibration moves the weights of the households that carry the bases, and nothing held their level: on the 3c0d11b6 national the bases sat about 12% above HMRC's receipts at final weights (Machine Games Duty £0.684bn against £0.609bn). The level is not held at final weights, so the exclusion is deleted and the national binds the row, as it binds the ONS electricity and gas rows (María's ruling of 2026-10-28). The notes and tests that described the row as held out now say it is bound, and the consumption-tax tool's docstring names the bound rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR A narrows the UC take-up stage gate to 0.055 and rewords the Carer's Allowance pool and the unmapped account codes. The gate battery's policy, manifest and fingerprint digests and both spine-part digests are recomputed on the merged gates.json, and the coverage manifest is regenerated; the H2 fixture follows in its own commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR A's Carer's Allowance wording and PR C's stages both reach the spine graph; tools/graph_uk_spine_fixture.py regenerates it on the merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The local surface regenerates on the 272c987 pin for PR C's two local changes, from the feed's local cut (constituency and local-authority rows and the statistical_scope rows but the small areas, as the 272c987 re-pin cut it; its source hash matches the pin): - SPI pension income and the taxpayers with it by area add 2,022 cells, 2,016 of them compiled. The two authorities without SPI local measures and the City of London's support-floor exclusion sign off the other six, as for the other SPI local families. - The DESNZ electricity and gas rows move with the energy level, by exactly the new factor over the old: x0.9258 on all 980 electricity rows and x0.9371 on all 977 gas rows, with no status changing. Nothing else moves: the references only gain the pension cells, and the receipt replaces the energy rows and adds the pension rows. The target-surface gate passes; its count pins follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stage compared each newly entitled carer's identity-keyed uniform with the take-up rate on its own, so the claiming share reached 0.6 only in expectation. On the few source households over the limit, one draw had put the 2031 taper's cost well below its expectation (the 1418-ca-pool investigation, option B). weight_balanced_uniforms places a stratum's uniforms by weight: each record takes the midpoint of its slice of the stratum's cumulative weight, in the order of its draw, as policyengine-uk's stratified_uniform does (an engine test holds the two identical). The take-up strata are the route (in-household or not), whether the benefit unit reports a means-tested benefit at the survey year, and the survey-year weekly earnings band, one weekly rate wide above the limit and open above three, read from the engine's carers_allowance_earnings as its own test reads them. Each stratum then claims 0.6 of its eligible weight to within one record. The outside pool's draw is balanced the same way over the other carers who report no allowance, by receipt and band, at its measured share. The rate, the pool and the caseload are unchanged, and the draws stay keyed on identity. The receipt lists each stratum's realised share against its target, support-banded and guarded as its other shares are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Carer's Allowance take-up stage's reviewed payload gains the carers_allowance_earnings predictor and the balanced-draw wording, so the source manifest's hash and the spine graph move. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Commit 33's regenerated national membership holds 1,403 active references (SPI Table 3.7's other income and Table 3.10's bands added), and test_uk_target_references.py already pins it. The national calibration test still asserted 1,374, which stopped engine-free CI on 6353e65. The 94 test files that read the regenerated surfaces pass locally, so CI's --maxfail=1 should hide no further pin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The national count was asserted in four places across three files, and the local count in two, so every legitimate change to the target set had to move each copy, and with CI's --maxfail=1 each missed copy cost its own CI round (twice on 29 Oct). One pin per surface stays: - national: ACTIVE_REFERENCE_COUNT in test_uk_target_references.py, now with the count's history the country spec test carried; - local: the candidate_targets pin in test_uk_battery_bindings.py. The country spec test checks the packaged spec against the committed membership's active_reference_count, the national calibration test drops its incidental count (it exists for the UC and OBR references), and the scorer test checks the scorer reads the committed membership. A change to the target set still fails one test, so it is reviewed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review of 6919f44 I read the body, all 35 commit messages and the thread first. Anthony's two rounds cover the energy level, RFDR checks, the Carer's Allowance strata, the pension bridge and the count pins, so I don't go over those again. The points below are new. Paths are relative to Findings1. should: the OBR savings path runs well ahead of ONS's household interest.
2. should: Table 3.10's two lowest bands only count people who file.
3. should (question): RFDR zero-flag refusal on sampled dense builds.
4. nit: the ratio bound would reject the previous ONS vintage.
5. nit: text that commit 30 left stale.
6. nit: the cars fuel duty hold updates the ONS figure but keeps the engine reads.
7. nit: PR state. The body says "Draft until CI passes on commit 35 and María marks it ready", but the PR isn't a draft. One of the two should change. Checked and found true
VerdictNo must-fix findings. Fine to merge once engine-free (3.13) is green and the Draft wording is settled. Findings 1 and 2 are about what the next national should measure, not reasons to hold this PR. Finding 3 is worth answering first if sampled dense builds are in regular use. What I ran
Not verified: any build, engine tests, Python 3.13 locally, the pension gaps (3.0% and 3.2%), the 41 and 1,053 output areas against NSPL, and the Gambling Commission rows. 🤖 Generated with Claude Code |
Independent review of #1420 at
|
The independent review (at b3f4ebf) and Vahid's (at 6919f44), with María's rulings of 29 October on the points that were hers. Pension income by area (independent 1, 8, 9): - The pension bridge's index bound is 0.02, the measured 1.5% gap (State Pension bands x1.224 on #1069's index, private x1.103, together x1.151 against the cells' x1.134) plus half a point. A test recomputes every fan-out bridge's index gap from the committed registers. - A bridge may declare min_factor_shift; the pension bridge's source factor must sit between 1.025 and 1.037, as the gap runs one way. - The rounded-thousand floor reaches the pension income counts and, through a new floor_reads, the amounts, which are the count times the mean (María: counts and amounts). The bridge counts the small-cell deferrals at source, grown by the bound cells' own index, and a deferral joins the reconciliation grown the same way, as an off-surface cell does (the pension cells are the first deferred ones an index moves). - The SPI-frame fence's authority, the census family and both adjudication records name the pension metrics. Data and calibration: - Table 3.10's amounts grow by the count index times OBR mixed income per head (independent 2). - The RFDR node refuses a system with nothing flagged only at full scale; sampled rungs check the codes (independent 3, Vahid 3). - The Carer's Allowance placement is rotated by a start drawn per stratum, and the stage refuses a stratum off by more than one record (independent 4 and 13). - The LCFS stage, the Carer's Allowance stage and the spine gate key their caches on energy_pricing, stochastic_assignment and stage_health (independent 5); the GB energy level factors are computed once per process (independent 6). - The zero-support floor's HMRC scopes take Table 3.7's other income and Table 3.10's bands (independent 7). - ONS electricity is measured at 2024, as road fuel is, with a signed matched-period hold (independent 10; María: at 2024). - A test holds each bound ONS energy row to a fuel levelled to ONS spending (independent 11). Texts (independent 14, 15 and smaller items; Vahid 1, 4, 5, 6): - The stale energy, betting and gaming, census, parity, SPI and gate texts are corrected; the savings path's resource notes record its scope and the HAXV comparison; the gate notes record the June vintage's 0.840; the cars fuel duty figures say which edition. - The RFDR tool pins the NSPL registration and gains --check; its 7b test reads the high value home stock's record of the zip. - --rfdr-localisation follows the geography assignment when unset; the ratio bounds are read as finite numbers; energy_pricing defines the ONS row variant once; the build graph doc describes the RFDR node. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the pinned feed (53815b2): - National references and membership: 1,403 active, as before. Only the intended values move: the eight Table 3.10 amount rows (x1.0763, the count index now applied) and ONS electricity onto its calendar-2024 figure (x0.975), which the membership now lists as a matched-period hold. - Local references and membership: 27,168 active. The rounded-thousand floor defers 40 pension income cells (17 constituency and 3 local-authority counts, and their amounts); nothing else moves. - The local census, both compile-parity receipts, the release input coverage manifest and the H2 spine fixture. - The gate battery's manifest digests and the certification parts' move with the gate notes; the policy digests are unchanged. - The battery bindings test's local pins: 27,168 candidate targets and 4,179 reviewed exclusions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks, Vahid. All seven are addressed at
🤖 Generated with Claude Code |
|
Reply to the independent review. Every finding is addressed at Must fix
Should fix: data and calibration
Should fix: tests and texts
Smaller items
ChecksRun locally for commits 36 and 37: the 107 engine-free test files that read the regenerated surfaces or that this round touches; the engine tests for HMRC uprating, Carer's Allowance and graph admission; the coverage manifest check; the H2 fixture's readers; 🤖 Generated with Claude Code |
Re-review of #1420 at
|
| # | Status | Evidence |
|---|---|---|
| 1 | Resolved | Passes at both grains as above. test_each_committed_bridge_holds_its_declared_index_gap fails on the round-1 registers, so it isn't vacuous. The source half is still checked only in a dense build (D). |
| 2 | Resolved | Table 3.10 amounts grow ×1.1176 (×1.0763 × ×1.0384) on exactly the 8 bound rows, through the one applier table. |
| 3 | Resolved | strict_flags follows full_scale end to end, every default is strict, and it enters the node key. Untested (C). |
| 4 | Partly | The per-stratum rotation gives each record the rate over starts and holds one record. But the start is keyed on the stratum code alone (G), and no stage-level test checks that the stage applies it (B). |
| 5 | Resolved | Probes re-key the LCFS stage, the Carer's Allowance stage and the spine gates when those modules change. The spine gate addition is broader than needed and has no test pin (E). |
| 6 | Resolved | lru_cache on the zero-argument default path: 0.63 s on the first call, then 0.03 ms. It returns copies, and explicit parameters stay uncached. |
| 7 | Resolved | The scopes are extended. The 2-row cap signed for seven amount families now covers nine (H). |
| 8 | Resolved | Exactly 40 references are deferred (17 constituency and 3 local-authority cells, count and amount), so active local references go from 27,208 to 27,168. Deferrals grow by their bound cells' index; nothing else moved. The floor reads the uprated value (F). |
| 9 | Resolved in text | The fence authority, the census family and both adjudications name the pension metrics. The signing is open and the filter name in the new text is wrong (H). |
| 10 | Resolved | The row is measured at 2024 under a signed matched-period hold (María's ruling), and the compile reads the 2024 fact. The diagnostic tool didn't follow (A). |
| 11 | Resolved | The new test fails if the gas exclusion is deleted, or if electricity leaves spend_level_fuels. |
| 12 | Declined | María's ruling: measure each carrier on the next national first. The notes now say what one row can't hold. |
| 13 | Resolved | "One record" everywhere. The helper tests assert half a record without starts and one with them. |
| 14 | Resolved | Every listed text is fixed. Two residuals: the receipt's new rationale also lands on the 13 count rows (I), and uk_runtime/lcfs_consumption.py:3614-3615 still says "the NEED shape at the DESNZ level". |
| 15 | Partly | The registration pins, the manifest label, --check and the anchored test are in. Two gaps remain: --built-on still defaults to today when writing, and --check without --out raises FileNotFoundError instead of reporting a difference. |
New findings
A. The consumption-tax diagnostic compares the engine's 2025 electricity with the 2024 target.
tools/diagnose_uk_consumption_taxes.py:55-61(ROW_YEARS) lists only the five excise rows.- So the electricity row is now
measurement_period2024, against ONS's 2024 £28.129bn, but the tool still measures it through the engine at--year(default 2025). - On the next national, a frame that fits the bound 2024 row exactly will report about +3.4%. That is the CPI projection the matched-period hold was added to keep out, and the PR's texts name this tool for the re-measurement.
test_excise_rows_are_measured_at_their_own_periodchecks only thatROW_YEARSis a subset of the declared periods. Road fuel has had the same gap since UK national calibration reads stored input columns at the frame period: 76 £ rows compare 2024 values with 2025 targets #1324.- Suggestion: derive
ROW_YEARSfrom each row'smeasurement_periodin the contract.
B. The Carer's Allowance stage's placement isn't tested at stage level.
- Commit 36 removed the assertions that tied the stage's flags to the balanced uniforms (
assert_array_equal(flag[eligible], positions < 0.6)and the pool's equivalent). The replacements check only share bounds. - Mutation runs: with the stage's
starts=dropped, or its identity-keyed draws replaced by row-position draws, every test intest_uk_carers_allowance_take_up.pystill passes, though 99 and 152 of the fixture's 640 flags change. - So round-1 populace-data: registry-driven loader for published populations #4 could return with CI green.
- Suggestion:
- assert that the flags equal
weight_balanced_uniforms(..., starts=stratum_starts(...)) < rate, for take-up and for the pool; - add a row-permutation check.
- assert that the flags equal
C. The RFDR strictness switch isn't exercised by any test.
- Neither of these lines is reached:
strict=full_scaleatuk_runtime/graph_population.py:1334-1336;- the kernel's
require_flagged=strict(:738).
- The new tests call
_rfdr_area_nodes(strict=False)andarea_flags(require_flagged=False)directly. - Mutation run: forcing strict everywhere passes every RFDR graph and layer test, which would bring round-1 populace-calibrate: representation operator (targets -> calibrated weights) #3 back on sampled rungs.
- The private-rent node's tests cover all three levels; the same pattern would work here.
D. The bridge's source half is still checked only inside a dense build.
test_each_committed_bridge_holds_its_declared_index_gapasserts only the index half (test_uk_fanout_sum_bridges.py:466).- The source half, including the new one-sided floor, runs only in
uk_local_target_surface: dense compilation, ortools/uk_target_surface_receipt.pywith the ledger facts. No test passes committed controls through it, so thedeferred_cellswiring (uk_runtime/ledger_targets.py:3274) is never exercised. - The constituency source factor (1.0295) clears the 1.025 floor by 0.45 points. A feed re-pin, or a change in which cells are deferred, could leave CI green and stop every dense build: round 1's failure, on the other half of the factor.
- Dropping the deferral wiring gives 1.0366, still inside the bound, so even the runtime wouldn't notice the applied factor rising by about 0.7%.
- A check of both halves on the committed registers runs engine-free in about 20 s.
E. The spine gate's hash now re-keys 14 spine stages that never read energy.
- One
UKSpineGateKernelhash serves both gate phases (uk_runtime/graph_evidence.py:374-380). - The assembled phase evaluates no energy gate, yet its key now includes
energy_pricing, and that key feedsfrs_hmrc_spine_leavesand everything after it. - Probe on the production spine graph:
- an edit to
energy_pricing.pymoves 36 of 49 stage keys; - an edit to
lcfs_consumption.pymoves 22; - the extra 14 run from
frs_hmrc_spine_leavestonts_bus_travel, including the SPI support channel and the income spine.
- an edit to
stage_healthbelongs in both phases, butenergy_pricingonly in the transferred one. It could go in as a node param onspine.gates.transferred, or through a kernel per phase.- Round 1 suggested adding both modules, so this is partly the review's own suggestion. No test pins the gate's modules.
F. The rounded-thousand floor reads the uprated count, not the published one.
tools/generate_uk_local_target_references.py:220-241compares each count's 2025-indexedresolved_valuewith 5,500.- The rule's reason, the deferral rationale ("published value … below 5500") and the changelog all describe the published count. Pension counts are the first indexed family under this rule.
- Today it defers the right 20 cells: published counts are multiples of 1,000, and 5,000 × 1.0763 is under 5,500.
- At a 2026 period (×1.1049 on the vendored ITL rows), 9 of the 17 constituency cells published at 5,000 would bind again, with their amounts. CI's pin would fail, but it would read as data movement.
- Suggestion: compare the published value (
value_before_uprating).
G. The Carer's Allowance start is keyed on the stratum code alone.
stratum_starts(uk_runtime/carers_allowance_take_up.py:342-354) hashes only the code, under seed 0. So each stratum's start is the same in every build and every FRS vintage.- A one-record stratum's outcome is then fixed per code. Computed from the public constants alone: at 0.6, the one-record strata of 9 of the 16 take-up codes always claim and 7 never do; at 0.45, 5 always claim.
- Within a build the expected claimed weight is still right. But this claim holds only across codes: "every carer claims with probability equal to the rate, however few share the stratum". It appears in the module docstring,
rate_derivation, the pool docstring and the changelog. - Suggestion, either:
- key the start on the code plus the stratum's sorted member ids. That stays reproducible and order-free, and a singleton's outcome follows its own identity;
- or reword the texts to "across strata".
H. Three signed records were widened without a recorded approval.
- Commit 36 extended these records, citing "the independent review's finding 7/9", with
approved_onleft at 22 and 14 October:- the zero-support floor's scopes (
uk/calibration_measure_exclusions.json): its 2-row cap, signed for seven amount families, now covers nine; - both pension-related fence adjudications (
uk/local_binding_adjudications.json:15-23).
- the zero-support floor's scopes (
- The cars fuel duty reason (
:392, approved 8 October) was also edited, and its adjudication doesn't record it. - Earlier changes to scope re-dated
approved_onand cited María's ruling: b176dff, and the small-cell rule in this same commit. Earlier re-wordings recorded who made them (5a0c3f6). - The floor's reason and adjudication are part of the signature its readers compare (
uk_runtime/support_floor.py), so this edit amounts to a re-signature. - The new adjudication text says the pension references "stand on the same income_tax_annual > 0 filter", but the declarations use
income_tax_before_winter_fuel_payment_charge_annual > 0. - These need María's ruling recorded, and the 2-row cap re-sized or confirmed for nine families. No more code is needed.
I. Texts.
uk/calibration_measure_exclusions.json:392: the inserted clause makes the cars fuel duty reason read "…so the next national re-measures it (…), so binding it would pull weight toward fuel buyers…".uk_runtime/energy_pricing.py:147-149: the comment says the ONS row variant serves the road-fuel level. In fact the road-fuel level and the heating recomposition (lcfs_consumption.py:2131,:3040) filter on their own literals, and the new test checks only the import.- The regenerated parity receipt gives the 13 Table 3.7 interest count rows the OBR savings rationale, but they grow by taxpayer counts. The prefix at
tools/build_uk_ledger_compile_parity_signed_differences.py:221-225matches both measures. docs/uk-full-build-graph.md:33says "postcode districts and sectors". The definition has no sectors; it works through districts, unit postcodes and excluded units.docs/uk-local-cross-grain-reconciliation-802.md:96-106and:120-121still describe a symmetric 2% source check, and count a deferral at its published value.- The PR body's items 4, 24 and 31 still give the 0.1% gap and 4%, the Great Britain-wide zero-flag refusal, and half a record, with no note of the later commit.
tools/build_uk_rfdr_area_layer.py:66pins the NSPL member as a new literal besideNSPL_DATA_MEMBERin the high-value home stock tool. The 7b test cross-checks the package, file and digest, but not the member.
Checked and found true
- Nationally only these move:
- the 8 Table 3.10 amount rows (×1.0384 → ×1.1176);
- the electricity row (2025 → 2024);
- the 04.5 rationale text.
- Locally only the 40 pension cells move, plus the re-dated rationale on the 192 higher-rate deferrals.
- The H2 fixture changes only the two stage contract digests that
source_stages.jsonchanged. The coverage manifest changes only its source digest, and the gate digests match their pins. - Every new default is safe, and every production caller passes it:
strict_flags,require_flagged,min_factor_shift,starts,deferred_cellsandfloor_reads. - An unset
--rfdr-localisationon the atomic assignment resolves toareaand pins the committed layer, before the checkpoint identity is computed. - The level-factor cache returns the uncached result and returns copies.
- The new uprating hold's approver and date match María's ruling of 29 October.
- CI passes on every job at
ead8943d6.
Not covered
- Any build or engine test. Everything ran engine-free on Python 3.13.
- Spine support for the 29 new SPI rows and the floor's 2-row cap.
- Carer's Allowance record counts per stratum.
Two disclosure-control items went to the author separately.
🤖 Generated with Claude Code
The re-review (issuecomment-6513556513) found nothing blocking; these are its groups A to I and round 1's residuals 14 and 15. - A: tools/diagnose_uk_consumption_taxes.py derives each row's period from the contract's measurement_period, so electricity and road fuel are measured at 2024 as the national binds them. - B: stage-level tests pin the Carer's Allowance placement (identity draws, member-keyed starts, flags against the pool share and the rate) and its independence from row order. - C: the RFDR strictness switch is tested through the graph builder and the kernel: a full-scale run refuses an unflagged system, a sampled rung runs. - D: each fan-out bridge's source half is checked on the committed registers, and the surface's deferral wiring is tested. - E: only the transferred spine gate keys on energy_pricing, as a node param; the kernel hash keeps the gate battery, its bindings and stage_health. - F: the small-cell floor reads each cell before its index. - G: a Carer's Allowance stratum's start is keyed on its code and its members' sorted identities. - H: the widened floor and fence records say they are pending María's confirmation (no approved_on moves); the pension adjudication names the right filter; the cars fuel duty record notes its re-wording. - I and residuals: the texts the re-review listed, the RFDR tool's build date and --check on a missing file, and the NSPL member cross-check. The national compile-parity receipt is regenerated for the interest count rows' rationale; the local references regenerate unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Reply to the re-review. Everything in it is addressed at Worth doing before the next national
Smaller items
ChecksRun locally for commit 38: the 31 engine-free test files this round touches or that read what it changed; the Carer's Allowance and HMRC uprating engine tests; the coverage manifest check; the mutation runs above, each of which now fails a test; and 🤖 Generated with Claude Code |
Re-review of #1420 at
|
| Item | Status | Evidence |
|---|---|---|
| A | Resolved | ROW_YEARS comes from each row's measurement_period in the contract, and the test requires the two to match. Two residuals remain (5). |
| B | Resolved | The stage tests spy on both placements and permute the rows. Dropping either starts= (take-up or pool) now fails, and so do row-position draws. |
| C | Resolved | Both tests go through the graph builder. Forcing strictness in the caller or in the kernel fails a test. |
| D | Partly | The source half is tested on the committed registers, and the deferral wiring has a test. But the local-authority reading isn't the runtime's (1). |
| E | Resolved | An energy_pricing.py edit now moves 22 stage keys, from lcfs_consumption on, and leaves the assembled gate alone. The new param enters the node key, and tests pin both. |
| F | Resolved | The floor reads value_before_uprating, and reverting that fails the new test. No reference moved. |
| G | Resolved | Simulated with the real helpers: a one-record stratum claims at the rate over identities (0.59 to 0.60 at a rate of 0.6). The start is order-free and the one-record bound holds. One cost remains (2). |
| H | María's | The three records say they're pending her confirmation. The filter is named correctly, and no approved_on moved. Two text gaps remain (4). |
| I | Resolved | Every listed text is fixed; small residuals are in 6. |
| #4, #14, #15 | Resolved | — |
New findings
1. The local-authority source check reads a different quantity from the runtime.
test_each_committed_bridge_holds_its_declared_source_gap(test_uk_fanout_sum_bridges.py:469-510) reads the pension bridge's local-authority source factor as 1.0321.- The runtime surface reads 1.0296, because it also counts the two authorities without SPI measures. The test's docstring says it leaves them out.
- Mutation run: with the local-authority cells ×1.005, the runtime reads 1.0245 and refuses, below the 1.025 floor. The test reads about 1.027 and passes.
- So a rise of about 0.45% to 0.7% confined to the local-authority cells keeps CI green and stops every dense build.
- The wiring test (
test_uk_off_surface_cells.py:737) replaces the check with a spy and passes no fan-out controls, so it covers only the hand-off. - Suggestion: compute the factor through
uk_local_target_surfaceon the committed pension cells, with the real controls. The review's script does this in about 20 s. Both grains would then read the runtime's quantity.
2. Keying the start on every member re-draws about half a stratum when one member changes.
stratum_starts(uk_runtime/carers_allowance_take_up.py:352-382) digests the sorted person ids of each stratum's placed set. Any change to that set re-randomises the start.- When one member leaves, about 2r(1−r) of the other members' flags flip, whatever the stratum's size: 0.48 at a rate of 0.6. Keyed on the code alone (round 2's scheme), it was 0.11 at 5 members and under 0.01 at 80.
- In a stage-like set of 320 eligible carers in 20 strata, one carer changing stratum flips on average 27.5 of the other 319 flags (1.3 with code keying). The weight-based pool share adds to this: a prior-weight change that moves one carer across the pool line re-draws whole take-up strata.
- So a policyengine-uk re-pin or an SPI refit that moves one carer's band or earnings test re-draws most of that stratum. Household-level comparisons between builds churn, while the aggregates stay within one record.
- Nothing documents this. The test comment at
test_uk_carers_allowance_take_up.py:433-434("the stream does not depend on which other people the frame carries") still holds for the uniforms, but no longer for the flags. - One alternative keeps G's per-carer property: key the start on the stratum's lowest-draw member.
- A carer alone claims at about 0.594 at a rate of 0.6.
- Churn is roughly code keying's plus 0.48/n: 0.20, 0.06, 0.011 and 0.006 at 5, 20, 80 and 200 members.
- The cost: the full ~48% re-draw comes back when the lowest-draw member leaves, or a lower one arrives, which is about 1 change in n.
- Otherwise, document the cost.
3. Re-certifying an earlier national will refuse if its floor dropped a row.
tools/certify_uk_release_cut.py:306-313loads the floor from the certifier's own package.uk_runtime/support_floor.py:571-576then requires each recorded drop'sreason,tracking,approved_by,adjudication,approved_onandexpires_onto equal that floor's.- Commits 36 and 38 changed the reason and the adjudication. A probe confirms that a drop recorded under the floor at
fddbc7073,edad8aee9oread8943d6is refused at this head, with "not under the declared signature". - Runs with no drops are unaffected. So are the in-graph read-backs, which compare against the run's own record. The runbook certifies at the build's own head, so the standard chain is fine.
- The next trigger will be María's confirmation edit of the floor's adjudication. After it, a national built at this head that dropped a row couldn't be re-certified at the later head without a rebuild.
- Per-row exclusions are compared by name only (
tools/certify_uk_release_cut.py:298-302), so the floor is the odd one out. - Suggestion: compare only the signature fields (
tracking,approved_by,approved_on,expires_on), or accept the run's own declaration digest.
4. H: two text gaps.
- The floor's note asks María to confirm or re-size the amounts scope's 2-row cap (seven families, now nine). But the counts scope widened too, from seven families to eight (Table 3.10's counts), under the same 2-row cap.
- The cars fuel duty adjudication's new note says "the ruling and its figures are unchanged". Yet this PR restated the reason's figures on the September edition (a58bc1a) and added the June comparison. The note also doesn't say who re-worded it.
- Older, and not from this PR's edits: the fence's signed reason, and its 23 October re-wording, still name
income_tax_annual > 0.
5. The diagnostic tool has two residuals.
- Weights. Rows measured at 2024 are weighted by the engine's weights at 2024.
- The national H5 stores the solved weights divided by the engine's 2024→2025 factor (1.0030), and
tests/engine/uk/test_uk_weight_year.pypins that the engine returns the solved weights at 2025 and solved ÷ factor at 2024. - So on a release H5, every 2024-period row (electricity, road fuel and the five excise rows) reads the calibration's figure ÷ 1.003. An exact fit therefore shows −0.30%, though the tool's comment says it "measures what the national binds".
- The excise rows have read this way since UK: impute alcohol, tobacco and gambling consumption so policyengine-uk's excise duties stop scoring zero #1359.
- Suggestion: weight the 2024 values with the calibration-year weights, as the certifier (
uk_solve_year_frame) and the scorer do.
- The national H5 stores the solved weights divided by the engine's 2024→2025 factor (1.0030), and
- Periods. The tool takes each row's period from the package's contract, but its value from the run's
--registry, whose specs carry their ownmeasurement_period.- On a national built before this PR, electricity's registry value is the 2025 fact, which the tool now reads off the 2024 frame: about −2.5 points, spurious.
- Suggestion: read the period from each registry spec, and fall back to the contract only without
--registry.
- Aside: the docstring at
uk_runtime/weight_year.py:11still says "0.72% from 2024 to 2025". The pinned growth is 0.3%.
6. Smaller items.
- No test pins the take-up strata's route dimension (this predates commit 38).
- With
take_up_strata = pool_strata, 61 of 800 flags change on_outside_pool_frame, and all 37 tests in the file pass. - The new stage test reads its strata from the stage's own call.
- With
- "Postcode districts and sectors" survives in two places:
tools/build_uk_rfdr_area_layer.py:8-9and:589, which writes it into the committed layer's description, so fixing it moves the layer's digest;uk_runtime/rfdr_area_layer.py:9-10.
uk_runtime/graph_evidence.py:342: the spine gate kernel's "Node parameters" list doesn't nameenergy_pricing_code.- The PR body's commit-38 checks line cites "the mutation runs above", which only the reply describes.
Checked and found true
- Every round-2 mutation, re-run against this head, now fails a test.
energy_pricing_codeis computed from the module's bytes and enters only the transferred gate's key. Nothing pinned needed regenerating: the H2 fixture has no gate nodes, and the Carer's Allowance stage constants are unchanged.- Each start digests exactly the set its placement balances over, from unique person ids: eligible carers for take-up, drawable carers for the pool.
- The diagnostic's
ROW_YEARSfollows the calibration's own period rule, and--helpruns without the engine. - Commit 38 moves no reference or membership. The national compile-parity receipt changes only in rationale text, which now gives the interest counts their own growth.
- CI at
216f22a5f: every finished job passes. engine-free (3.13) and engine-us (3.13) were still running when this was posted.
Not covered
- Any build or engine test: everything ran engine-free on Python 3.13.
- The RFDR
--checkwasn't re-run against the registered zip.
🤖 Generated with Claude Code
The re-review (issuecomment-6514923017) found nothing blocking; these are its six points. 1. The fan-out bridges' gaps are checked by running the runtime surface itself on the committed registers (cells, controls, small-cell deferrals and off-surface cells), so each grain reads what a build reads; local-authority pension cells x1.005 now refuse in a test. 2. A Carer's Allowance stratum's start is keyed on its code and its lowest-draw member, so a membership change re-draws a stratum only when that member changes; a carer alone still claims at the rate. 3. The certifier compares a run's support-floor drops with the floor's signature fields only (tracking, approver, approval and expiry), so a re-worded floor can still certify a run built under it. 4. The floor's note names both widened caps; the cars fuel duty note says what this PR changed and that it awaits María. 5. tools/diagnose_uk_consumption_taxes.py weights every row at --year and takes each row's period from the run's registry when given one; weight_year's docstring says 0.3%. 6. A test pins the take-up strata's route; the RFDR wording reads "districts, unit postcodes and excluded units" in the tool, the runtime docstring and the rebuilt layer (same areas; description and built_on only); the spine gate docstring names its new param. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Reply to the round-3 re-review. All six points are addressed at
ChecksRun locally for commit 39: the 34 engine-free test files this round touches or that read what it changed; the Carer's Allowance and weight-year engine tests; the coverage manifest check; the re-review's mutations (local-authority pension cells x1.005, and the take-up strata without the route), each of which now fails a test; and 🤖 Generated with Claude Code |
The first PR C national (built from 10f434d) refused at the national problem: obr.betting_and_gaming_duties declares the frame's period 2024, but its value, the engine's gambling_duty, is not a stored column. The national solve measures a declared period only on a stored column or a CGT dated measure, so it cannot read the engine's 2024 duty. Commit 16 had deleted the row's exclusion under María's ruling of 2026-10-28 to bind it; no PR C national had been built since, and the tests do not run the national problem's measure routes. María's signed exclusion of 2026-10-23 is restored as it was, with a note in its adjudication that the bind ruling cannot take effect until the solve can measure the duty (a dated duty measure, or bases bound at the frame's period), pending her decision. The row stays measured on every evaluation, beside the other excise rows, which main holds for the same reason. The row's notes, the consumption-tax tool's docstring and the tests' census follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Commit 40 (
🤖 Generated with Claude Code |
Re-review of #1420 at
|
| Item | Status | Evidence |
|---|---|---|
| 1 | Resolved | The test runs uk_local_target_surface with each bridge's real control, the committed deferrals and the off-surface cells. It reads 1.0295 and 1.0296, applies ×1.0450 and ×1.0451, and refuses under the ×1.005 mutation. Two residuals (3). |
| 2 | Resolved | The start is keyed on the lowest-draw member, by the placement's own uniform, with ties broken by person id. A carer alone in a stratum claims at 0.59 at a rate of 0.6. Moving one carer between strata now flips 1.7 of the other 319 flags on average, against 1.3 with code keying; the full re-draw happens only when the lowest member changes. Four start mutants fail the new test. |
| 3 | Resolved | Only the signature fields are compared. A re-worded record certifies, and each moved signature field refuses. Its consequence is finding 2. |
| 4 | Resolved | Both caps are named, and the cars note says what changed. The fence text stays María's. |
| 5 | Resolved | Weights are read at --year, values at each row's own period summed to households, and periods come from the run's registry. One residual (4). |
| 6 | Resolved | The route test fails the mutant, and the wording is fixed. The rebuilt layer flags the same areas, and its provenance digest matches. |
New findings
1. No engine-free test checks that a bound row with a declared period can be measured.
- The solve refuses a bound plain-column row whose declared period (2024) isn't held on the frame.
_refuse_ignored_measurement_period(packages/microcosm-build/src/microcosm/build/target_materialization.py:991-1032) raises aMeasurementPeriodError, which stops the whole run. - "Held" means one of the frame's own columns (
UKFrameTargetAdapter.value_period,uk_runtime/ledger_targets.py:1806-1818) or a CGT dated measure. - Nothing runs this rule on the packaged contract:
- the existing tests use synthetic bindings;
- the exclusion census simply moved with commit 16;
tools/check_uk_measurement_periods.pyruns only after a build.
- That is why commit 16 passed CI and the national refused at
uk.full.national_problem, after the spine and engine stages had run. - The same refusal will come back if any excise row is bound again, or if one of the five excise exclusions is left to lapse when they expire on 2027-01-08.
- A guard is cheap. It would run the production predicate over the packaged contract, membership and exclusions, taking the stored columns from
uk_spine_graph()/uk_spine_endpoint(...).inputsand the frame period fromload_uk_frs_release().- A prototype flags exactly
obr.betting_and_gaming_dutieson commit 16's register, with the build's message, and nothing at this head. - It takes milliseconds after the graph import (about 20 s, shared with the graph tests).
- Its natural home is
tests/engine_free/uk/test_uk_measurement_period.py, next to:536.
- A prototype flags exactly
2. The signature-only floor check no longer ties a drop to the scope it was made under.
- After commit 39, a recorded drop certifies if its
tracking,approved_by,approved_onandexpires_onmatch the floor (uk_runtime/support_floor.py:98,:577-582). Scope membership is re-checked only against the certifier's own floor. - Commit 36 widened the floor to Table 3.7's other income and Table 3.10 without moving
approved_on. So a national built at this head that drops one of those rows certifies under the 22 October signature, which never covered them. A probe confirms this for drops recorded atedad8aee9through this head. - The reply's point (that María's confirmation edit won't block re-certification) holds only if she confirms by editing the text and leaves
approved_onat 22 October. If she movesapproved_on, every earlier run with drops refuses again, which is round 3's finding 3 back. - Options:
- record a digest of the scope (families and caps) in each drop, and compare it;
- or move
approved_onwith any widening, and let the reader accept named superseded signatures.
- The share ceiling isn't re-checked at certification either, but that predates this PR.
3. Two test residuals on the bridge.
- The ×1.005 test is brittle.
tests/engine_free/uk/test_uk_fanout_sum_bridges.py:582-595hard-codes the scale. At ×1.005 the local-authority factor lands only 0.052% below the 1.025 floor. So if the factor legitimately rises past 1.0301, still well inside the bounds, the test fails with "DID NOT RAISE". Deriving the scale from the baseline (baseline ÷ floor × 1.001) keeps it a mutation test. - Lost deferrals in the data aren't caught. If a regenerated membership drops the pension deferrals, nothing notices: the factors read 1.0366 and about 1.030, both inside [1.025, 1.037]. Code-level losses are caught elsewhere. A one-line pin on the committed
small_cell_deferralscounts (17 and 3) would cover the data.
4. The diagnostic tool assumes the weights were divided for release.
- It always projects the stored weights to
--year(tools/diagnose_uk_consumption_taxes.py:174-213). That equals the solved weights only on H5s divided for release, which started with 0f0bb86 on 14 October. - On H5s written as solved, every row now reads +0.3%. These are nationals released before 14 October, and the engine scratch file. The registry-period path is aimed at exactly those older nationals.
main()'s--registrywiring and themap_toargument are untested, and--yearis hard-coded at 2025 rather than read from the release's calibration year.- Suggestion: load the weights the way the certifier does, with
load_uk_national_frameand thenuk_solve_year_frame.
5. Smaller.
- The restored betting exclusion still gives María's 23 October reason ("Binding it would move weight onto the households that gamble to hold a level the build already sets"). Her 28 October ruling rejected that argument, and only the appended note says so. Re-signing, or choosing one of the two routes the note names, is her call.
- Commit 40's message, PR body item 40, the reply and the tool's docstring say main holds the other excise rows "for the same reason". Their registered reason is María's 23 October ruling: the level is set by the build. They would hit the same refusal if bound, but that is a different statement.
- The cars fuel duty note doesn't say who re-worded it.
Checked and found true
- Commit 40's entry: its name, reason, tracking, approver and dates are byte-identical to the entry commit 16 deleted, and to main's. Only the adjudication gains a paragraph. The entry isn't expired; the census test, the tool and the row's notes match it; and PR body item 16 says "Undone by commit 40".
- Period-declared rows: 30 national targets declare 2024. 24 of them are bound:
- 8 read stored household columns (bus support and fares, electricity, road fuel);
- 1 reads a CGT dated measure;
- 15 are CGT threshold rows that carry the year in the measure name.
The 5 excise rows and the ORR rail row are held. Local bindings can't declare a period (they're refused at load), and the DESNZ kWh rows are resolver measures read at the frame's period.
- Commit 39: the start keying, the signature fields, the weighting path and the rebuilt RFDR layer are as in the table, and nothing pins the old layer digest.
- CI at
5aba83cd2: every finished job passes (engine-uk 3.14, integration-uk, lint, select-countries, wheels). The engine-free, engine-uk 3.13 and engine-us jobs were still running when this was posted.
Not covered
- Any build or engine test: everything ran engine-free on Python 3.13.
- The national now rebuilding from
5aba83cd2.
🤖 Generated with Claude Code
The re-review (issuecomment-6516126464) found nothing blocking. 1. test_every_bound_row_with_a_declared_period_can_be_measured_there runs the solve's own period check over the packaged contract, with the spine's stored columns and the release's frame period: it passes at this head and flags exactly the betting and gaming row without its exclusion, the refusal the first PR C national hit. 3. The bridge mutation's scale is derived from the committed baseline, and the pension bridge's committed small-cell deferrals (17 and 3) are pinned. 4. tools/diagnose_uk_consumption_taxes.py reads the solved weights as the certifier does (load_uk_national_frame, uk_solve_year_frame) by household id, and --year defaults to the FRS release's calibration year; tests cover the weighting, the households' alignment and main()'s --registry wiring. 5. The tool's docstring gives the excise rows' registered reason (María's ruling of 2026-10-23), and the cars fuel duty note says who re-worded it. Finding 2 (the floor's drops and its widened scope) is María's, with H. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Reply to the round-4 re-review. Points 1, 3, 4 and 5 are addressed at
ChecksRun locally for commit 41: the touched engine-free files (measurement period, fan-out bridges, consumption-tax diagnostics, measure simulation, support floor and off-surface cells). All pass. The national is building from 🤖 Generated with Claude Code |
Re-review of #1420 at
|
| Item | Status | Evidence |
|---|---|---|
| 1 | Resolved | test_every_bound_row_with_a_declared_period_can_be_measured_there runs the solve's own _refuse_ignored_measurement_period, through UKFrameTargetAdapter.value_period, against exactly the spine's carried columns and the release's frame period. It passes engine-free, even with policyengine-uk blocked. With the betting exclusion removed, it flags exactly obr.betting_and_gaming_duties. It isn't vacuous: with no stored columns, it flags the eight stored-column rows. |
| 2 | María's | The reply's description is right. More precisely, what matters is a drop outside the 22 October scope (Table 3.7's other income or Table 3.10), not any drop at all. |
| 3 | Resolved | The mutation's scale is now derived from the baseline, so it crosses the floor for any baseline in [1.025, 1.037]. The deferral pin (17 and 3) fails if either grain loses its deferrals. |
| 4 | Resolved | The solved weights come from load_uk_national_frame and then uk_solve_year_frame, aligned to the engine's households by household_id. Checked on synthetic H5s: a release-divided file gives back the solved weights exactly, a solved file gives them back as stored, and a reversed engine order still aligns. |
| 5 | Resolved | The tool's docstring and PR body item 40 give the excise rows' registered reason. The cars note names who re-worded it. Commit 40's message keeps the old wording, but that's history. |
Remaining, all low severity
1. A lapsed exclusion still passes CI.
- The exclusion loader (
uk_runtime/measure_simulation.py:1770-1845) neither drops nor refuses an expired entry. The window is enforced only when the national targets are applied. - So suppose the excise exclusions are still in the register when they expire on 2027-01-08. CI stays green, and every national then refuses at
uk.full.national_targets, after the spine, with the window error. - The new test catches a deleted exclusion, but not an expired one. The earliest committed exclusion expires on 2026-12-11.
- Suggestion: a check on the committed register, like the uprating holds'
test_no_committed_hold_is_about_to_lapse, would show each lapse in CI ahead of time.
2. The tool's real weight path has no test.
- The weighting test passes
weights=, and themain()test replacesengine_estimates. So a regression anywhere in load, then solve year, then alignment by id would pass CI. - Running the default path on a small synthetic H5 inside the test would close this.
3. The tool's scope and robustness.
- The docstring (
:20) and the--datasethelp (:244) still say "any H5 the engine loads". The tool now needs a file thatload_uk_national_frameaccepts. - The id check refuses engine households that are missing from the frame, but silently drops frame households the engine lacks. On the default path both sides read one file, so this only matters for callers that pass
weights=. - With
--registryand no--year, the default year comes from the package's release, not the run's. - The tool reads the whole H5 a second time, all three tables with Frame validation, after the engine has already loaded it. The household id and weight would be enough, and that matters on the dense file.
4. Test details.
- The measurability test uses a literal 2025 as the calibration period (
test_uk_measurement_period.py:630), rather thanload_uk_frs_release().calibration_year. - It doesn't run the solve's fact-period check for
require_matching_fact_periodrows (packages/microcosm-build/src/microcosm/build/target_materialization.py:748-754). No row is affected today. - Its second assertion pins today's held betting row, so it will change when María picks a route.
CI and coverage
- CI at
125d6e73b: lint, select-countries, wheels, integration-uk and engine-uk (3.13 and 3.14) pass. engine-free and engine-us were still running when this was posted. - Not covered: any build or engine test.
- The national building from
5aba83cd2: commit 41 changes only tests, the tool and one register text, so it doesn't affect that build.
🤖 Generated with Claude Code
The re-review (issuecomment-6516738969) found nothing blocking and only low-severity items. 1. test_no_committed_exclusion_is_about_to_lapse fails 14 days before the earliest exclusion expires, as the uprating holds' clock does, so a lapse shows in CI and not at a national's target step. 2. A test runs the tool's real weight path on synthetic H5s, one divided for release and one written as solved, through load_uk_national_frame, uk_solve_year_frame and the alignment by id. 3. The tool says it needs a national H5 load_uk_national_frame accepts, refuses a household on either side alone, and with --registry takes the run's own calibration period when --year is not given. 4. The measurability test reads the release's calibration year and also runs the solve's fact-period check for rows that require their fact at the declared period. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Reply to the round-5 re-review. The low-severity items are addressed at
National build: blocked at certificationThe national built from
Every lower band of both families fits. I've asked María whether to hold the new £1m-and-over other-income row as a measured distortion, as the ten other £1m-and-over cells are held. That needs her signature, so the build waits for her. The figures stay out of this thread because the band rests on the SPI tape. ChecksRun locally for commit 42: the measurement-period, consumption-tax diagnostics and measure-simulation test files. All pass. 🤖 Generated with Claude Code |
Re-review of #1420 at
|
| Item | Status | Evidence |
|---|---|---|
| 1 | Partly | test_no_committed_exclusion_is_about_to_lapse uses the holds' clock: 14 days ahead of date.today(). Under fake clocks it passes until 2026-11-26 and fails from 2026-11-27, naming 2026-12-11. It doesn't cover the support_floor block in the same register (finding 1). |
| 2 | Resolved | The new test runs the real loader and the real solve-year step on two synthetic H5s. Skipping the solve-year step, aligning by position, or reverting the id check each makes it fail. |
| 3 | Resolved | The id check refuses a household that appears on only one side. The registry's single compiled period sets the default year, with a fallback to the release's year. The second read of the H5 stays, by choice. |
| 4 | Partly | The test reads the release's calibration year and runs the fact-period check, which catches a mutated plain-column row. It skips the 15 bound CGT rows on the provider route (finding 2). |
Findings (all low)
1. The lapse clock skips the support floor.
- The floor block (
uk/calibration_measure_exclusions.json:3-82, in force until 2027-01-08) is enforced at the same targets step.uk_runtime/full_targets.py:122-139calls the floor'sassert_in_force. - But the new test reads only the per-row list, and
test_uk_support_floor.pypins its own date. - Today the floor is covered only by coincidence: seven per-row entries also expire on 2027-01-08. If those are renewed and the floor isn't, CI stays green and every national refuses from 2027-01-09 with "UK support floor is outside its reviewed window".
- Adding the floor's
expires_onto the clock closes it. - Also,
min()there has no default. If the last per-row exclusion is ever bound, the test errors instead of passing. The sibling clocks share this pattern.
2. The fact-period check misses the CGT provider rows.
- The test skips any binding with a
kind, but the solve checks the fact period before it branches on kind. - 18 bound rows require their fact period. The test checks 3 of them (CGT liability, electricity and road fuel), and not the 15
parameter_gated_thresholdCGT rows. - So a re-pin that resolves one of those rows to a 2025 fact passes CI, and the national stops after the spine. A bound row missing from the membership also passes.
- The reply's "every bound row" overstates the coverage.
3. Smaller.
- The registry-year test hard-codes 2026, so it goes vacuous once the release's calibration year is 2026. Using
calibration_year + 1would hold. - A registry with mixed periods falls back to the release's year silently.
- Older than this commit:
composition()reads the H5's stored (dataset-year) weights, while the estimates now use the solved weights. Its totals therefore sit about 0.3% off; the shares are unaffected.
The certification block
- The national built from
5aba83cd2stopped at the calibration-seam fit gate on two £1m-and-over amount rows: savings interest, and the other income this PR binds. As in the reply, no figures for that band here. - Round 1's removed-behaviour pass raised the other-income row: that £1m-and-over cell is bound without the distortion measurement that held ten sibling cells out. The verifier set it aside under the 21 October rule, which holds a row out only on a measured distortion. The build has now measured it.
- The savings-interest £1m-and-over row was already bound on PR A's certified national. What this PR changed for it is commit 32's uprating: the 13 interest amounts now grow a further ×1.134 on the OBR savings path, which Vahid's finding 1 questioned.
- So before deciding, it may be worth checking whether holding only the new other-income row clears the gate, or whether the interest row's new level is part of the miss. That is María's call.
CI and coverage
- CI at
162690525: lint, select-countries, wheels, integration-uk and engine-uk 3.13 pass. The other jobs were still running when this was posted. - Not covered: any build or engine test.
🤖 Generated with Claude Code
- The measure-exclusion lapse clock also reads the register's support floor, which the national targets refuse at the same step, and passes on an empty register instead of erroring. - The measurability test runs the solve's fact-period check on every binding kind, before it branches on kind, as the solve does, so the 15 CGT provider rows are covered. A required row missing from the committed membership also fails. A new test mutates one CGT row to a later fact and drops another from the membership; both fail. - diagnose_uk_consumption_taxes.py weights its composition with the solved weights by household id, as the estimates are, reading them once in main(). With --registry and no --year, a registry whose specs span several periods refuses rather than falling back. The registry-year test uses the release's calibration year plus one, not a literal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Reply to the round-6 re-review. All three findings are addressed at
The certification blockI checked your question against the build's own fit report: is commit 32's interest uprating part of the £1m-and-over miss, or is it only the new other-income row? The answer, and the band's ratios, went to María privately with the decision. The band's figures, and the direction of either miss, stay out of this thread. Before she signs, I've also offered her a local, unsigned re-solve of this national problem with the other-income row held, to confirm the gate clears. ChecksRun locally for commit 43: the consumption-tax diagnostics, measurement-period and measure-simulation test files, and ruff on the touched files. All pass. 🤖 Generated with Claude Code |
Re-review of #1420 at
|
| Finding | Status | Evidence |
|---|---|---|
| 1 | Resolved | Under fake clocks, renewing all 39 per-row entries but not the floor now fails from 2026-12-25, naming support_floor. An empty per-row list no longer errors. |
| 2 | Resolved | The fact-period check now runs before the kind branch. Moving any of the 18 bound required rows to 2025 flags exactly that row, and so does dropping one from the membership; that includes the 15 CGT provider rows. The plain-column part still flags betting when its exclusion is removed. |
| 3 | Resolved | composition() uses the same refusal and id alignment as the estimates. main() reads the solved weights once and never weights by the stored column. A registry with mixed periods refuses. |
Low items
1. The new clock's first deadline. It is by design, but worth noting now: from 2026-11-27, test_no_committed_exclusion_is_about_to_lapse fails on every PR until orr.rail_government_support (expires 2026-12-11) is renewed or bound.
2. The measurability helper's account of holds differs from the solve's (packages/microcosm-build/tests/engine_free/uk/test_uk_measurement_period.py:613-645).
- Possible false positives. Some of the 15 CGT provider targets fan out into one row per region or band, which the check reaches only since commit 43. Two cases misfire:
- The helper still checks a fan-out row that is held on its own by a per-row exclusion, although the national drops that spec before materialising it. Example: a re-pin moves one CGT region's fact, and the reviewer holds just that row. The national runs, but this test fails.
- It also flags a fan-out target whose rows are all signed out one by one.
- Wider loop. It loops over all 456 contract targets, against the 394-target national membership. So a local-only target that later declares
require_matching_fact_periodwould be flagged too. - Latent miss. Held rows inside the floor's scope are compiled as probes and checked by the solve, but the helper skips every held target. No such row declares a period today.
- Suggestion: skip a row whose own name is held, unless its target is in the floor's scope, and restrict the loop to national targets.
3. The docstrings overstate the "no active candidate" rule (:572-576, :678-681).
- They present it as the solve's own refusal ("stops the national after the spine"). In fact the national never reads
uk/target_reference_membership.json: it compilesuk/target_references.json, which holds active rows only. - So a re-pin that defers a required row drops it from the national silently.
- The test does catch that case, which makes it the only guard. The docstrings should say so rather than describe a build refusal.
CI and coverage
- CI at
059225c89: lint, select-countries, wheels, integration-uk and engine-uk (3.13 and 3.14) pass. engine-free and engine-us were still running when this was posted. - Note on engine-free: that job was cancelled unfinished on the previous three heads by later pushes, so the PR's engine-free result is still to come.
- Not covered: any build or engine test.
🤖 Generated with Claude Code
The measurability helper now reads the rows the national compiles, as graph_national does: - the targets on the national line only (national geography and the national calibration line), not every contract target; - less the rows signed out of a fan-out; - less the rows a per-row exclusion holds, except held rows in the support floor's scope, which the national compiles as probes and so checks. A required row that is deferred (not active) is now flagged, like a row missing from the membership. The national compiles only the active references, so either one leaves the national without a refusal, and this test is the only guard. The docstrings now say so, instead of describing a build refusal. A new test covers the three hold cases: a row held on its own with a moved fact passes; the same row in the floor's scope is flagged; a fan-out signed out row by row passes. The deferral case is added to the fact-period test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Reply to the round-7 re-review. Items 2 and 3 are addressed at
ChecksRun locally for commit 44: 🤖 Generated with Claude Code |
PR C of microcosm#1418 covers the Budget 2026 data fixes that weren't quick ones. It is stacked on PR A (#1419), whose branch is merged in. #1418 stays open.
Ready for review. Merging waits on CI at the head and on María. The feed is re-pinned onto PolicyEngine/chronicle#394's merge (53815b2), which carries SPI Table 3.10 and the October 2026 EFO. Anthony's two rounds, Vahid's review and the independent review are answered (commits 36 and 37), and so are the six re-reviews since (commits 38, 39, 41, 42, 43 and 44).
A national built from commit 40 (staging only) stopped at certification on two £1m-and-over SPI rows. Whether to hold the new £1m-and-over other-income row out, as the ten other £1m-and-over cells are held, is María's call; the national is rebuilt after it.
A deadline: from 2026-11-27,
test_no_committed_exclusion_is_about_to_lapse(commit 42) fails on every PR untilorr.rail_government_support, which expires on 2026-12-11, is renewed or bound.Not in this PR:
Commits, in order
The energy stage's level becomes ONS household spending (UK: data fixes for the Budget 2026 household measures #1418 item 12; the commit itself is titled for electricity, and gas follows in commit 6).
lcfs_consumptionstage raked kWh to DESNZ Energy Trends' calendar-2024 volume. Priced at QEP, that sits about 5% above ONS Consumer Trends for both fuels. The 30 September 2026 release revised 2024 spending up, electricity from £26.10bn to £28.13bn and gas from £14.64bn to £18.30bn.spend_level_fuelstakes the kWh ONS household spending buys: its class (04.5.1, 04.5.2) for 2024, less QEP's UK average fixed cost on every UK domestic meter DESNZ counts (Great Britain's sub-national count plus Northern Ireland's row, now vendored), divided by QEP's UK average unit cost. Electricity: 84.9 TWh, against Energy Trends' 91.7 TWh.energy_pricing.published_energy_levelcarries the level to the stage's rake, to theuk_stage_lcfs_consumption_energy_rakegate's recomputation, and to the factor that levels the dense build's local DESNZ kWh rows. So the 980 local electricity rows move with it (Great Britain's cells now sum to 82.7 TWh, not 89.2), and no £/kWh bridge is needed.energy_price_basisstays calendar 2024.The Gambling Commission's machine yield by category is vendored (item 5, row 56).
excise_facts.jsongains the Commission's FY2024-25 machine GGY by category for Great Britain, all venues: Category B £2.29bn of £2.59bn.Re-pin of the gate digests and the coverage manifest.
SPI pension income by area (item 14).
hmrc.pension_income.amount: each area's taxpayers' private and State Pension income together, SPI 2023-24 Tables 3.15 and 3.14, count times mean.total_pension_income_annual.Merge of PR A, then a test-stub fix and the H2 fixture.
Gas follows electricity (María's ruling; commit 30 takes gas back to Energy Trends): ONS 04.5.2 gives 236.5 TWh against 252.3 TWh. Great Britain's local gas cells now sum to 234.0 TWh, not 249.7.
The ONS electricity and gas rows are bound again (commit 30 holds the gas row out again). Decision D1's two measure exclusions are deleted (María's ruling: PR C carries the electricity deletion PR A had planned).
The 29 register entries tracking UK local surface, consumption and high-value homes: remaining items (consolidation) #1278 now track UK: every open data item: uk-data parity, engine locks, the local surface, consumption and high-value homes #1095, which UK local surface, consumption and high-value homes: remaining items (consolidation) #1278 closed into. Text only.
The taxpayers with pension income by area (María's ruling, option c).
hmrc.pension_income.countbinds at HMRC's own area level, grown by ITL Table 2.5's all-ranges taxpayer growth.Re-pin of the gate digests, the coverage manifest and the H2 fixture.
The cars fuel duty hold quotes the 30 September ONS edition (María's ruling: refresh text that quotes the old release). Text only: ONS 07.2.2 for 2025 converts to about £13.1bn of duty, 9% below the OBR cars line, where the June edition gave £13.5bn and 7%.
Rural Fuel Duty Relief on the dense build (item 6, row 3).
in_rural_fuel_duty_relief_area, which no build wrote.tools/build_uk_rfdr_area_layer.pydeclares Microcosm's translation into postcodes, each part with its sources. Islands that share a sector with the mainland (Raasay, Seil, Easdale, Luing, Kerrera, Lismore) and Hawes are listed by unit postcode, and mainland units inside island districts are taken out. The points the sources leave open are recorded in the tool; none changes an area's flag.uk/rfdr_area_layer.jsonlists the output areas more than half of whose postcodes are in the scheme: 41 in England and 1,053 in Scotland.uk.full.geography.rfdrsets the flag on each placed household from its assigned output area, after the local-authority resolve, on the BRMA node's pattern.--rfdr-localisation areais the default on the atomic assignment;noneis for measurement builds, and a release candidate refuses it. Northern Ireland has no area in the scheme.Re-pin of the gate digests for the RFDR export column.
The six texts that still said UK local surface, consumption and high-value homes: remaining items (consolidation) #1278 tracks an item now name UK: every open data item: uk-data parity, engine locks, the local surface, consumption and high-value homes #1095, finishing commit 8. Text only.
Merge of PR A's newer commits (the UC take-up gate at 0.06 of DWP's caseload, and the care-hours test). The gate digests are recomputed on the merged
gates.json.OBR's betting and gaming receipts are bound (María's ruling).
uk-national-1418-7757add7a, H5a912a7044836), at final weights:Merge of PR A's review round (the UC take-up gate at 0.055, and the Carer's Allowance wording), with the gate and spine-part digests and the coverage manifest recomputed.
The H2 fixture, regenerated on the merged tree.
The local references, membership and compile-parity receipt are regenerated on the 272c987 pin. It ran from the feed's local cut, whose source hash matches the pin.
Merge of main after PR A merged (two changelog lines).
The Carer's Allowance take-up draws are balanced by weight within strata, for the next pair build (option B of the pool investigation).
stratified_uniformdoes (an engine test holds the two identical). The strata are:Re-pin of the coverage manifest and the H2 fixture for the stage.
Anthony's review: the scorer test's pin.
test_uk_local_candidate_scorer.pypins the 27,208 active local references that commit 19 left (the 2,016 pension-income cells added).Anthony's review: the Rural Fuel Duty Relief checks.
--rfdr-localisation noneand the committed-layer requirement.Anthony's review: the energy level's bound and Northern Ireland's meters.
Anthony's review: the Carer's Allowance strata are enforced. The stage refuses a take-up or pool stratum whose drawn weight misses its share by more than one record. The receipt records the check, and the H2 fixture carries the stage's new notes.
Anthony's review: the pension bridge's tolerance.
spi_pension_amount_bands_vs_areaallows the larger measured gap plus half a point, 3.7%, not 4%. The coverage manifest is regenerated for commits 23 to 27.Anthony's re-review: the council tax pin. The support-floor membership has the City of London's two SPI pension income cells (51 rows, 95 area ids). This was the only line both engine-free jobs failed on.
SPI Table 3.7's other income by total-income band is bound (María's ruling of 29 October). No row held that column, so the Income Tax on savings rows were met mostly through it.
Gas back on Energy Trends; the QEP payment method checked directly (María's ruling of 29 October, Anthony's re-review 2 and 3).
liquid_fuel_consumption. Bottled gas is 2.2% to 3.2% of the class on ONS's CPI weights.ons.household_gas_expenditurereturns to the measure exclusions, tracked on UK: every open data item: uk-data parity, engine locks, the local surface, consumption and high-value homes #1095. Electricity stays on ONS 04.5.1.payment_methodagainstqep_payment_method.Anthony's re-review: count pins, RFDR per system, the Carer's Allowance tolerance, and 7b.
SPI savings rows on the OBR's savings income; SPI Table 3.10 bound (María's rulings of 29 October).
obr_savings_income_growth_facts.json, in place of ×1.192.Re-pin onto Add SPI 2023-24 Table 3.10 and the OBR October 2026 EFO vintage (PolicyEngine/microcosm#1418) chronicle#394's merge (53815b2).
obr.winter_fuel_allowanceis pinned to March's expenditure table, since October's FY2025-26 outturn would match it twice.The national calibration test's pin moves to 1,403 active references. It had stopped engine-free CI on commit 33; the 94 test files that read the regenerated surfaces pass locally.
Each UK active reference count is pinned once in the tests (María's request).
test_uk_target_references.py'sACTIVE_REFERENCE_COUNT, which now carries the count's history.candidate_targets.The independent review and Vahid's review (María's rulings of 29 October where the points were hers).
--check.Regenerated surfaces: the national and local references, the census, both parity receipts, the coverage manifest, the H2 fixture and the gate digests.
The re-review of commits 36 and 37.
tools/diagnose_uk_consumption_taxes.pyreads each row's measurement period from the contract.The round-3 re-review of commit 38.
The betting and gaming row is held out again, pending María's decision.
gambling_duty, not a stored column.The round-4 re-review of commits 39 and 40.
The round-5 re-review of commit 41.
--registry, its default year is the run's calibration period, and its texts say it needs a national H5.The round-6 re-review of commit 42.
--yearis given.The round-7 re-review of commit 43.
Checks run locally
All on the merged tree, with policyengine-uk 2.162.14:
Also run since: the RFDR engine admission test (
test_uk_full_graph_admission.py), which passes, and the local surface tests after commit 19 (battery bindings, local targets, target references, local census, local release preflight, fan-out bridges, cross-grain declarations), which pass.For commits 23 to 27: the RFDR layer and node tests, the atomic, BRMA, rent, property and home-value graph tests, the full target and population graphs, the CLI, Carer's Allowance, stage health, energy pricing, fan-out bridges, cross-grain declarations, national reconciliation, country spec, scorer, battery bindings, gate-battery pins and contract tests, and the coverage manifest check.
For commits 28 to 33: 31 touched engine-free test files (target references and membership, local targets, battery bindings, local census, local release preflight, fan-out bridges, cross-grain declarations, the full target graph, energy pricing, domestic energy targets, the constituency energy resource, high-value homes, stage health, measure simulation, Carer's Allowance, the RFDR layer and graph, subset relations, council tax, HMRC uprating, population targets, the feed pin, full targets, the scorer, national reconciliation, bus and CGT targets, LCFS consumption, country spec, gate-battery pins and contract), the HMRC uprating and RFDR admission engine tests, the coverage manifest check, and the feed-gated high-value home stock check in the re-pin. CI runs the rest.
For commits 36 and 37: the 107 engine-free test files that read the regenerated surfaces or that this round touches; the engine tests for HMRC uprating, Carer's Allowance and graph admission; the coverage manifest check; the H2 fixture's readers;
tools/build_uk_rfdr_area_layer.py --checkagainst the registered NSPL zip, which reproduces the committed layer byte for byte; andtools/uk_target_surface_receipt.py, which compiles the dense target surface end to end on the pinned feed. All pass. The vendoring register's check that each consumer names its file failed once and is fixed in commit 36. The Orrery reconstruction test needs a Node package this checkout doesn't have; CI runs it.For commit 38: the 31 engine-free test files this round touches or that read what it changed; the Carer's Allowance and HMRC uprating engine tests; the coverage manifest check; the round-2 mutations, each of which now fails a test; and
tools/build_uk_rfdr_area_layer.py --checkagainst the registered NSPL zip, which still reproduces the committed layer byte for byte. All pass.For commit 39: the 34 engine-free test files this round touches or that read what it changed; the Carer's Allowance and weight-year engine tests; the coverage manifest check; the re-review's mutations (local-authority pension cells x1.005, and the take-up strata without the route), each of which now fails a test; and
tools/build_uk_rfdr_area_layer.py --checkagainst the registered NSPL zip, which reproduces the rebuilt layer byte for byte. All pass.For commits 40 to 44: the touched engine-free test files (measure simulation, measurement period, consumption-tax diagnostics, excise quantity inputs, uprating holds, support floor, fan-out bridges and off-surface cells). All pass.
CI runs the suites (this PR targets main).
🤖 Generated with Claude Code