Repository navigation
Conversation
The domestic_rates note said only that the OBR adds NI domestic rates to council tax receipts. The OBR's March 2026 tables show them in two places, labelled neither regional nor district: expenditure Table 4.1 "NI domestic rates" (£0.494bn flat, inside council tax) and receipts Table 3.2 "Northern Ireland domestic rates" (£485.3m in 2024-25, other taxes). domestic_rates charges the whole bill. The note now gives both, with the regional and district attribution stated as inference, as in the finance_ni.domestic_rates comment (d870). The head keeps the Council tax row, where the OBR says it adds NI domestic rates; the Other taxes row of uncovered_receipts records that its two NI lines are partly measured. Fixes #2662 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Aggregate impact on certified data: passBase Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 0 (thresholds in Notes
Moves flagged for reviewNone. Spending by benefit and service
Caseloads
Standard reform costings (change in gov_balance)
Standard reforms: change in each reform's target
Household net income by decileNo household's net income changes in any year. No change in any year: receipts by tax head; government balance, each component counted once; model totals; household income totals; population; poverty rates; people in poverty; inequality; mean household net income in each side's own deciles. Checks failing on both base and head (1)
Counted-once checkEach of 88 components was raised by £1 per unit in 2026-27, and every head of its totals was read household by household: its own head must rise by exactly £1, no other tax head may move, no other benefit may rise, and the model's own total must move by what its heads moved. A head that breaks a rule is measured again twice: with every other head held at its baseline value, and with every other head held at its value after the first £1 while the component rises again. It fails where either still shows the response. 0 failed; 0 were skipped.
Provenance
Dataset: Receipts by tax head: head levels
Spending by benefit and service: head levels
Caseloads: head levels
Government balance, each component counted once: head levels
Model totals: head levels
Household income totals: head levels
Population: head levels
Poverty rates: head levels
People in poverty: head levels
Inequality: head levels
Mean household net income in each side's own deciles: head levels
Standard reform costings (change in gov_balance): head levels
Standard reforms: change in each reform's target: head levels
|
- State that the reading rests on where each OBR line sits, not its size: the £0.494bn Table 4.1 line is nearer ONS's regional row than its district row, and the two lines together exceed LPS and ONS. Add the same clause to the finance_ni.domestic_rates comment. - Say the 2024-25 Table A.5 council tax row (£47.417bn) is £0.273bn below Table 4.1's total, so its outturn may not include NI domestic rates. - Define uncovered_receipts by what the closure test checks (rows no head's obr_line names) and let a note record partial measurement. - Explain obr_table_3_1: null in the note; reword the LPS figure so it does not read as the head's own level. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Keep uncovered_receipts' documented meaning and drop the Other taxes note: a schema_version bump would break the pinned aggregate-impact tool copies in policyengine.py and policyengine-uk-data for a prose change no code reads. The two heads' notes carry the overlap. - Hedge 'the head appears to span two Table A.5 rows'; ground the yoy_growth 'neither line covers the whole bill' in size. - Drop the repeated regional sentence; define ONS and the Family Resources Survey. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hand-off to the UK hub
|
Fixes #2662
What changed
Documentation only: the
domestic_rateshead and a source entry inpolicyengine_uk/data/fiscal_partition.yaml, one comment inpolicyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml, and a changelog fragment. No model code, parameter value or variable changes.domestic_rates.obr_line_noterewritten. It used to say only that "The OBR adds Northern Irish domestic rates to its council tax receipts".domestic_ratescharges the whole NI domestic bill (regional rate plus district rate, levied as one bill). The note now gives both places the OBR shows NI domestic rates. The OBR labels neither as regional or district.The OBR defines neither line, so "appears to" stays throughout. The note also:
obr_table_3_1: null;gov.economic_assumptions.yoy_growth.finance_ni.domestic_ratescomment (d870), which has the evidence.domestic_rates.data_note(new). It gives the head's measured level on the certified dataset against LPS, and points to Northern Ireland domestic rates: low level and district-only OBR target policyengine-uk-data#520, which tracks the gap and uk-data'sobr/domestic_ratestarget.sources.ni_domestic_rates(new). The two OBR tables, the ONS rows and the LPS series.uncovered_receiptsis unchanged (it matches main).Other taxescontradicted the list's definition, "rows of Table A.5 the model has no household measure for". The r1 fix redefined the list, and review r2 then found that redefinition needs aschema_versionbump under the file's own rule.provenance/uk_candidate.pyruns a vendored copy ofpe_uk_aggregate_impacton the installed policyengine-uk partition. policyengine-uk-data'spull_request.yamlruns the tool at a pinned policyengine-uk commit. Both accept only version 1, and no code readsuncovered_receiptsexcept the closure test.business_ratesincludes NI. A schema change to fix it would have to be coordinated with those consumers.The
finance_ni.domestic_ratescomment inyoy_growth.yamlgets the same "placement, not size" clause. Its opening "Neither OBR March 2026 line covers the whole bill" now rests on size: each line is about half of LPS's £889.5m.obr_lineandobr_table_3_1: unchanged, and whyobr_line: Council taxstays.Other taxeswould also take that row out ofuncovered_receipts. The row is £12.97bn in 2024-25, almost all unmodelled.obr_table_3_1: nullstays.high_value_council_tax_surchargecarriesPSNB-neutral receiptsbecause it sits wholly on the council tax row. This head appears to span two rows.What reads these fields
policyengine_uk/tests/test_fiscal_partition.py::test_partition_schema. It checks thatobr_lineis a Table A.5 tax row, and that heads'obr_linevalues anduncovered_receiptsare disjoint and together cover every row.policyengine_uk/utils/aggregate_impact/partition.py. It copiesobr_lineand the head notes intoHead.as_dict()exports them in the report JSON'spartitionblock.obr_linewith OBR figures, so this PR moves no aggregate.compare.py: "This change edits the fiscal partition…"), since the file's SHA-256 changes. Its numbers should be unchanged.targets/sources/obr.py(_parse_council_tax) buildsobr/domestic_ratesfrom the Table 4.1 line alone. That is uk-data#520 and is not changed here.Real run (level, for context)
domestic_rateson the pinned certified dataset (policyengine-uk-data 1.58.1, HF commit 2526edbb, SHA-256056e80fd…verified), weighted byhousehold_weight.c7cdbfc98.git statusshowspolicyengine_uk/{variables,parameters,reforms,data}clean. Output:measure_domestic_rates_r2.json.git rev-parse HEADprintedeb0b8087d(main). It gave identical totals.LPS reports £889.5m of domestic rates paid in 2024-25 and £947.9m in 2025-26. Script and outputs:
~/reviews/uk-ni-domestic-rates-partition-2662/measure_domestic_rates{.py,.json,.log,_r2.json,_r2.log}.Cells verified
The local copies of the OBR workbooks have the same SHA-256 as the files policyengine-uk-data commits in
targets/source_manifests/obr_march_2026.yaml(receiptsf65b6cb7…, expenditure700bfd70…). obr.uk returns a bot challenge to scripted downloads.Review
review --tier hard, served by Opus 5.5 because no Codex lane was open;~/reviews/uk-ni-domestic-rates-partition-2662/review/review_r1.md): REQUEST_CHANGES, eight findings. All are addressed in c7cdbfc:yoy_growth.yaml.uncovered_receiptsdefinition was changed to match the closure test. r2 reverted this; the note was dropped instead.obr_table_3_1: nullis explained in the note.review --tier hard, GPT-6.1 Sol;review/review_r2.md): REQUEST_CHANGES. It confirmed every number and mechanism claim, and marked r1 findings 1-6 and 8 resolved. Addressed in 1e7b265:schema_versionbump for the redefinition: the redefinition is reverted instead, for the reason given under "What changed".yoy_growth.yamlopening now rests on size.review/review_r3.md): APPROVE at0ba211760. All r2 findings are resolved. One LOW nit on PR-body wording ("appears to span") is fixed in this body.Tests
uv run --frozen pytest -n 4 policyengine_uk/tests/test_fiscal_partition.py policyengine_uk/tests/aggregate_impact/test_aggregate_impact_partition.py policyengine_uk/tests/test_domestic_rates_uprating.py: 67 passed.load_partitionparses the file.ruff format --check .andruff check .are clean.Reproducer: n/a: documentation-only change to partition metadata and a parameter comment; the fragment is
changed, notfixed.axiom: n/a: partition metadata and a comment documenting OBR receipt lines; no policy provision changes
Related: #2343 and #2344 (the NI business rates note, now merged), PolicyEngine/policyengine-uk-data#520, PolicyEngine/policyengine-uk-data#584.
🤖 Generated with Claude Code