Repository navigation
Conversation
The aggregate impact report (#2335) adds gov_spending, household_benefits and spending_heads to fiscal_partition.yaml. The tax tests now check the three tax totals only, require that spending heads borrow no tax head id or variable and that no spending head names a tax total, and apply dropped_when to the tax totals alone. Checked against #2335's file with these tax sections merged in: this file's tests and #2335's partition and guard tests pass. The vat head records that 76 households have negative VAT on policyengine-uk-data 1.58.0 (consumption or the full-rate expenditure share is negative in the data; policyengine-uk-data issue 565). Refs #2159 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Finance Act 2025 s.47 takes private school education, vocational training and closely related board and lodging out of the VATA 1994 Sch 9 Group 6 exemption, with effect for supplies on or after 1 January 2025 (s.49). The new gov.hmrc.vat.private_school_fees_exempt parameter records the exemption; when it is off the fees bear the standard rate. The contributed reform rate now raises the rate instead of adding to it, so setting it never charges the fees twice. private_school_vat moves to variables/gov/hmrc. Household taxes include it as a change from baseline (private_school_vat_change), as they do VAT; government tax revenue includes the level. Fixes #2318 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
private_school_vat is now non-zero at current law, so it leaves the zero-at-baseline heads and stays in gov_tax only; household_tax sums the new private_school_vat_change head, which is zero at baseline, with baseline_private_school_vat as its baseline measure. A level-and-change guard mirrors vat and vat_change. The vat overlap guard becomes a disjointness property: vat's full-rate share was trained when the fees were exempt, and neither head reads the other's inputs or parameters. Adds Hypothesis properties: private_school_vat matches the statute computed independently, a reform rate at or below the statutory rate never taxes the fees twice, the head is monotone in both rates, vat and private_school_vat are disjoint, and the level, baseline and change agree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
baseline_private_school_vat now applies baseline policy (the exemption, the standard rate and the reform rate) to this simulation's attendance, mean fee and share of fees bearing VAT, so a reform that changes only those assumptions (such as disabling economic assumptions, which moves the fee uprating) is no longer scored as a household tax change. YAML cases now compute the change and include reforms that raise and remove it; a grid test checks the statutory rate exhaustively across the commencement, the exemption, reform rates and standard rates. The exemption parameter starts when VAT did (Finance Act 1972 Sch 5 Group 6), so no earlier lookup reads as taxable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The vat guard becomes overlapping_base: the heads read separate inputs, but vat applies one household share to all consumption including education, so a fee-paying household's fees are charged at its blended share, and in a household calculation education consumption entered as an input is charged in both heads. On calibrated data the overlap is in aggregate, because policyengine-uk-data targets vat at total OBR VAT receipts (policyengine-uk-data#567). The partition header lists the guard kinds and says data overlaps are recorded under their guards. The identity property now draws households with a child at private school and a scenario restoring the exemption, and checks the head is positive exactly where a pupil's fees are charged. The changelog says contributed reforms at 20% now score nothing from 2025. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
a68673d to
ccbab58
Compare
From the second independent review: - The identity property always includes a household with a pupil, and under a restored exemption checks the head is zero and the change is minus the baseline charge exactly where a pupil's fees bore it. - The grid's reference uses the statutory 20 per cent, not the model's parameter. - The eligibility-reads guard says its check is vacuous for the private school heads on situations; the header says formula overlaps between heads with unseparated bases are recorded under their guards. - private_school_vat notes that every imputed pupil is charged (nursery classes excepted by law, 18-year-olds chargeable), and the baseline notes it reads 1 January values where the level reads 30 April. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5f08c24 to
47eb1f7
Compare
|
Adoption hand-off for the UK hub:
|
Aggregate impact on certified data: passBase Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 14 (thresholds in Notes
Moves flagged for review
Receipts by tax head
Spending by benefit and service
Caseloads
Government balance, each component counted once
Model totals
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: 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 85 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
|
vat subtracted each household's own baseline school charge from its own grossed-up VAT, so a pupil with little recorded consumption had negative general VAT (-2,000 for one pupil, 10,000 fees and no consumption), and restoring the exemption left it there. On the certified Enhanced FRS that made 549 households' general VAT negative in 2025-26. General VAT is now gross VAT times one less private school fees' official share of VAT receipts (new gov.simulation.vat.private_school_share_of_receipts: HMRC's OBR-certified costing over OBR March 2026 cash-basis VAT receipts). It keeps the sign of gross VAT, reads neither attendance nor fees, and school-specific reforms move only private_school_vat. A repeal lowers receipts by exactly the weighted school charge. Current-law combined receipts change by the weighted school charge less the share of weighted gross VAT (-£0.17bn in 2025-26 on the certified dataset). The fiscal partition drops vat's attendance reads and restores sign: nonnegative on the vat head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Hand-off for the UK hub (P1 fix from
|
The share's denominators are OBR March 2026 receipts sheet 3.8, row 13, E13:I13; record the cells and the workbook's SHA-256 so the values can be reproduced. Also correct three passages that still said the two VAT heads sum exactly to the gross amount: they match it to within the share's accuracy (at most 0.07% below on Enhanced FRS 1.58.1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI at 9bbcef8 failed test_head_totals_agree_with_the_model_weighted_sums: receipts plus double counts were GBP 2,646 (2.8e-9) off gov_tax on GBP 935bn, against a GBP 935 tolerance. Merge-queue groups containing #2319 fail the same assertion (GBP 994 on GBP 939bn), so it is rounding, not a missing head. rel=1e-8 still catches any head above about GBP 9,000. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #2318
Private school fees have borne standard-rate VAT since 1 January 2025, but the model's contributed rate was zero at current law. This PR adds the statutory charge and separates it from receipt-calibrated general VAT, so government receipts count it once and no household's general VAT goes negative.
Statutory charge and incidence
gov.hmrc.vat.private_school_fees_exemptswitches off on that date; the standard rate then applies.private_school_vatcharges pupils × mean fee × effective VAT basis × max(statutory rate, contributed rate). Setting the contributed rate to 20% from 2025 produces no additional charge; 25% charges 25%.gov_taxcounts the level.household_taxcountsprivate_school_vat_change, as it countsvat_change, preserving baseline household net income. Baseline policy is applied to this simulation's attendance and fee assumptions.VAT receipts: the fees' share comes out of general VAT
The grossing factor (0.67 × 0.7 = 0.469) fits household VAT to OBR VAT receipts, which include VAT on private school fees. Counting
private_school_vaton top would count that VAT twice. Let G be a household's grossed-up VAT, s private school fees' official share of VAT receipts, P0 the baseline school charge and P the current school charge:s is the new parameter
gov.simulation.vat.private_school_share_of_receipts: HMRC's OBR-certified costing (Applying VAT to private school fees, Autumn Budget 2024 Table 5.1) over OBR cash-basis VAT receipts (March 2026 EFO, detailed forecast tables, table 3.8), by fiscal year from 1 April:It is zero for model year 2024, where
private_school_vatis also zero (annual policy is read before the January 2025 commencement); the £460m part-year receipt stays in general VAT.vatandbaseline_vatboth read s from the simulation's own parameters, asbaseline_private_school_vatreads its assumptions, sovat_changeis exactly zero at baseline.What this fixes
The reviewed head (
4dcfa03c2) setvat = G − P0 × r / r0, subtracting each household's own school charge from its own VAT. A pupil with £10,000 fees and no recorded consumption had general VAT of −£2,000, and restoring the exemption left combined VAT at −£2,000. On the certified Enhanced FRS (policyengine-uk-data 1.58.1), 549 households with non-negative gross VAT had negative general VAT in 2025-26 (306,750 weighted households, −£0.58bn), rising to 572 in 2030-31. With the share,vathas the sign of G (0 ≤ s < 1): 0 newly negative households in every year, at baseline and under repeal.Why the official share (design a) rather than a population share (design b)
Both were implemented and measured.
shareholding,consumption_shareholdingandcorporate_land_valuedivide by weighted national totals). On the certified dataset it preserved combined receipts to within £0.003m in 2024-25 to 2026-27. I rejected it because every household's VAT would then depend on the imputed attendance of every other household, and attendance is imputed from net income. Raising anyone's income tax, NI or council tax could move every household's VAT by G × Δs. The Aggregate impact counted-once check fails a head wherever such a response is one for one. It already failsvatin fewer than 10 households at the reviewed head (run 39830541074:counted_once:income_tax,:national_insurance,:council_tax), through the per-household subtraction. A household calculator would also measure its own school charge against its own VAT, which is the original defect, merely capped at zero.vatno longer reads attendance or fees, so those three failures have no route, and the partition dropsvat's attendance reads. Its cost is that current-law receipts are preserved only up to the share's accuracy: combined VAT changes by Σw·P0 − s·Σw·G.Impact (measured locally on the certified dataset)
Enhanced FRS 2024-25, policyengine-uk-data 1.58.1 (sha256
056e80fd8e91…, the CI pin), aggregates only:vat)vathouseholds: reviewed head → this headGross VAT matches the base
vatin run 39830541074 (£253.71bn in 2025-26). "Combined VAT vs main" is Σw(vat + private_school_vat) − Σw·G = Σw·P0 − s·Σw·G, andgov_taxmoves by the same amount. 2024-25 is unchanged (share and charge both zero). No household with non-negative gross VAT has negativevat, or negativevat + private_school_vat, at baseline or under repeal, in any year.vat_changeandprivate_school_vat_changeare unchanged at current law (max |vat_change| = 0).gov.hmrc.vat.private_school_fees_exempt): receipts fall by exactly the weighted school charge, £1.95bn in 2025-26 rising to £2.28bn in 2030-31, equal to Σw·P0 to floating-point precision.vatis identical household by household; household taxes fall by the same amount.sign: nonnegativeis restored on thevathead (main's setting; the reviewed head had loosened it toany), so Aggregate impact CI checks negativity again. The 76 data#565 households with negative recorded consumption remain negative on both sides (a pre-existing failure, now reported on both).Invariants (tested)
For any population, year from 2024 to 2030, fee, consumption and weights, with the exemption at current law or restored (
test_private_school_vat_receipts.py, including a Hypothesis property):vat ≥ 0andvat + private_school_vat ≥ 0wherever recorded consumption is non-negative.vat = G × (1 − s), with G recomputed from the inputs and s from the published costing and receipts, independently of the formula. A parameter test ties each share to its cited numbers.vatunchanged household by household;vatdoes not read attendance or fees;vat_changeis zero at baseline.Validation
Head
7ed18347c. Local, locked environment (uv sync --frozen --extra dev), foreground,-n 2, with the host gate held (load1 ≤ 110, ≥ 55 GB free):vat/vat_grossing.yaml,private_school_vat.yaml,excise_integration.yaml: 21 passed.test_private_school_vat_receipts.pyandtest_vat_grossing_legacy.py: 40 passed.test_fiscal_partition.py,aggregate_impact/test_aggregate_impact_partition.py,test_private_school_vat_properties.py,test_scenario_baseline_parameters.pyandtest_dated_reform_routes.py::test_baseline_vat_is_current_law_on_every_route: 125 passed, 1 skipped (a microsimulation-marked test, skipped without the dataset environment variable; CI's credentialed job runs it).The full suite and Aggregate impact run in CI on this head. Still a draft: CI and the hub's independent review at this head are pending.
uvx ruff format --check .anduvx ruff check .pass.Reproducer: policyengine_uk/tests/test_private_school_vat_receipts.py::test_a_pupil_with_no_recorded_consumption_has_no_negative_vat, policyengine_uk/tests/test_private_school_vat_receipts.py::test_vat_is_never_negative_and_receipts_are_counted_once, policyengine_uk/tests/policy/baseline/gov/hmrc/private_school_vat.yaml
Updated stack
Merged #2235 at
8c1b54dd9, preserving its corporate SDLT counted-once fix, then merged main. The only conflicts were in the fiscal partition: retained upstream schema, spending/uncovered-receipt rows and other guards, together with the school level/change heads. #2235 merged during that job; this PR therefore targets main.Remaining limits
axiom: TheAxiomFoundation/rulespec-uk#453 queued
The signed encoder is blocked on the Schedule 9/Finance Act provisions being added to its pinned corpus; #453 records the module, law, companion tests and blocker and is labelled
pe-parity.🤖 Generated with Claude Code