Repository navigation
Charge VAT on private school fees at current law - #2319
Conversation
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 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
|
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>
#2189 adds VAT on household energy bills after grossing, and derives the survey coverage against receipts net of it. Private school fees' share of receipts now comes out of the grossed-up amount on other consumption only: vat = G x (1 - s) + E. Energy bills bear no school fees, and a reform to energy VAT moves vat by exactly the change in energy VAT. The doc places #2189's energy section before the private school section, which now defines E and restates the identity with it. Figures measured before the split are labelled as such. The published receipts check uses the split inputs, and a new test checks that energy-bill VAT carries no school share. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merged main (with #2189's energy split) at
Locally: 163 pytest passed (1 skipped) across the private school, VAT legacy, domestic electricity, fiscal partition and code-sharing files, and 1,164 YAML passed on |
- Add test_school_share_never_touches_energy_bill_vat: households with electricity and gas bills across GB and Northern Ireland, 2024-2030. It checks vat = G x (1 - s) + E with E from the statutory rates, that restoring the exemption leaves vat unchanged, that zero-rating electricity moves vat by exactly the change in domestic_energy_vat and leaves private_school_vat unchanged, and the national identity with E. - Tighten the published receipts check to 0.1%. - Restate the doc, the share and coverage parameter metadata, the fiscal partition note, the vat documentation and the test docstrings with E, and replace pre-split figures with CI run 40116746457 at 4f35228. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review r4's N6: the data_note measured 76 negative households on policyengine-uk-data 1.58.0 (-£72m in 2026). CI's Aggregate impact run 40116746457 on 1.58.1 finds 76 in every year from 2025 to 2031, -£55.7m weighted in 2026. The 1.58.0 cause breakdown is kept and labelled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merged by the UK hub (Claude session 8e19f9d5) at head
|
docs/engineering/vat-coverage.md conflicted because both sides added a section after "Energy bills are not grossed up". Both are kept: main's private school split first, since it describes the current formula, then the later-cuts record. vat_grossing.yaml merged cleanly; the 2024 case is unaffected because the school-fee share is zero in model year 2024. 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 on consumption other than energy bills to OBR VAT receipts net of energy-bill VAT (#2189), and those receipts 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 on that consumption, E its VAT on energy bills (domestic_energy_vat, which is not grossed up), s private school fees' official share of VAT receipts, P0 the baseline school charge and P the current school charge:E0 is
baseline_domestic_energy_vat. The school share comes out of G only: energy bills bear no school fees, so an energy-VAT reform movesvatby exactly the change in E (main'stest_extending_the_zero_rate_to_fy2027asserts −50 exactly).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), measured locally before #2189's energy split, 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,vatis G × (1 − s) + E with 0 ≤ s < 1, so it is negative only where G or E is: no newly negative household. At4f3522828(run 40116746457) and at this head (run 40130442489), CI's sign check finds the same 76 households below zero on main and on this branch in every year from 2025-26 to 2031-32 (data#565's negative recorded consumption).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, measured locally before Model the temporary 0% VAT on GB domestic electricity #2189's energy split, 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 (CI Aggregate impact on the certified dataset)
Run 40130442489 at this head (
8f205015d, merged with maind1cbf3d9d) against maind1cbf3d9d, on Enhanced FRS 2024-25, policyengine-uk-data 1.58.1 (sha256056e80fd8e91…, the CI pin). Status pass, no failures. Aggregates only.vat(Σw·(G + E))Against run 40116746457 (head
4f3522828against main6fa9fb8f5), which the doc and the fiscal partition cite, every year is within £9m except 2030-31 (−£6.3m there). That year's school charge is £2.21bn here against £2.28bn there. This PR's commits since4f3522828change tests, docs and parameter metadata, not formulas or values, so the difference comes from main's commits between the two runs."Combined VAT vs main" is Σw(vat + private_school_vat) − main's Σw·vat = Σw·P0 − s·Σw·G.
gov_taxandgov_balancemove by the same amounts in every year. 2024-25 is unchanged (share and charge both zero).vat_changeandprivate_school_vat_changeare unchanged at current law (deltas of 0 in every year).gov.hmrc.vat.private_school_fees_exempt):vatis unchanged household by household and combined receipts fall by exactly the school charge (invariants 3 and 4, tested). Before the energy split, a local repeal run measured £1.95bn in 2025-26 rising to £2.28bn in 2030-31, equal to Σw·P0 to floating-point precision.sign:private_school_vatpasses.sign:vatfails on both sides with the same 76 households (pre-existing, data#565). The counted-once checks forvat,vat_change,private_school_vatandprivate_school_vat_changepass.sign: nonnegativestays on thevathead (main's setting).Invariants (tested)
For any population, year from 2024 to 2030, fee, consumption, energy bills and weights, with the exemption at current law or restored (
test_private_school_vat_receipts.py, including Hypothesis properties):vat ≥ 0andvat + private_school_vat ≥ 0wherever recorded consumption and energy bills are non-negative.vat = G × (1 − s) + E, with G recomputed from the inputs, s from the published costing and receipts, and E from the energy bills at the statutory electricity and gas rates, independently of the formula. A parameter test ties each share to its cited numbers.vatunchanged household by household, including households with energy bills;vatdoes not read attendance or fees;vat_changeis zero at baseline.vatby exactly the change indomestic_energy_vatand leavesprivate_school_vatunchanged (test_school_share_never_touches_energy_bill_vat). Standard-rate and grossing-factor reforms move general VAT by ΔG × (1 − s) and the school head on its own base.The energy property fails both mutants the review tried, each run alone on it: (G + E) × (1 − s) in
vatandbaseline_vatfails the per-household differential, andbaseline_vatreadingdomestic_energy_vatin place ofbaseline_domestic_energy_vatfails the zero-rating check.Validation
Head
8f205015d. Local, foreground,-n 4undernice, with the host gate held (load1 ≤ 110, ≥ 55 GB free):test_private_school_vat_receipts.py,test_private_school_vat_properties.py,test_domestic_electricity_vat.py,test_vat_grossing_legacy.pyandtest_fiscal_partition.py: 167 passed (atc1882c2a9; the fiscal partition file again at this head, 41 passed).policyengine-core test policyengine_uk/tests/policy/baseline/gov/hmrc -c policyengine_uk: 1,059 passed.ruff format --checkandruff checkpass on the changed files.The full suite and Aggregate impact run in CI on this head.
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/test_private_school_vat_receipts.py::test_school_share_never_touches_energy_bill_vat, 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.Then merged main
6fa9fb8f5after #2189 (4f3522828), resolvingvat.py,baseline_vat.py, the coverage doc, the coverage parameter's metadata, the fiscal partition and the receipts tests toG × (1 − s) + E. The next two commits address the hub's review r4: the energy property test, a tighter published receipts check (0.1%), doc and metadata passages restated with E, and thevathead's negative-household note restated on 1.58.1.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