Add council_tax_benefit to net income tree (matches HBAI methodology)#1668
Merged
Conversation
The `gov.contrib.abolish_council_tax` switch removed gross council tax from `household_tax` and `gov_tax` but left `council_tax_benefit` untouched. CTR was not in any of the household benefit aggregates (`household_benefits`, `hbai_household_net_income`, `pre_budget_ change_household_benefits`) or in `gov_spending`, so CTR never flowed through net income or the government balance in the first place. Abolition therefore refunded the full gross amount to every household — overstating CTR-recipient households' real-world out- of-pocket saving by their CTR amount, ~£4bn aggregate. Add `council_tax_benefit` to the four aggregates. The existing `if abolish_council_tax: [b for b in benefits if b != "council_ tax_benefit"]` filters become meaningful (no-ops before). Applied the same filter pattern to `hbai_household_net_income.formula` and a fresh `gov_spending.formula`. Now abolition: household net income rises by net council tax, and government balance falls by net council tax revenue. Added regression test for a CTR-recipient household.
MaxGhenis
added a commit
to PolicyEngine/uk-land-value-tax
that referenced
this pull request
May 11, 2026
PolicyEngine/policyengine-uk#1668 fixes a long-standing accounting issue where `gov.contrib.abolish_council_tax` refunded gross council tax to households rather than net, because `council_tax_benefit` was not in any of the benefit aggregates feeding `household_net_income`. After installing the dev branch of pe-uk locally and re-running the pipeline, the household-side change drops by the aggregate CTR amount (~£4bn). Distributional results shift, mostly at the bottom: D1 average net change: +£774 -> +£522 (CTR no longer windfalls into baseline-low net income) D9 average loss: -£980 -> -£1,011 D10 average loss: -£941 -> -£967 Aggregate winners: 72% -> 68% BHC poverty change: -0.84 -> -0.57 pp Income Gini change: +0.13% -> +0.58% Wealth-decile pattern shifts modestly: top wealth decile loss £5,533 -> £5,539, range of gains across deciles 2-7 narrows from £509-£1,537 to £483-£1,454. Article updates: - Lead bullet 1 reflects new D1/D9/D10 numbers - Bullet 3 reflects new poverty + Gini - Table 1 (rate sensitivity) regenerated - Table 2 (decile impact) regenerated - Land-distribution prose updated (new D2/D3/D4 inversion pattern) - Decile prose updated - Wealth-decile prose updated - Within-decile (Figure 6 follow-up) updated - CTR paragraph reframed: abolition retires CTR alongside CT (the model now correctly handles this); a future variant could preserve CTR-equivalent transfers, which would lift bottom-decile gains - Charts re-rendered: 6 SVGs + 6 PNG screenshots CTR-recipient households still gain on average from the reform (savings net of CTR loss are positive at modest LVT bills) but the bottom-decile windfall shrinks meaningfully, as expected once the gross-vs-net accounting bug is fixed.
vahid-ahmadi
approved these changes
May 11, 2026
Collaborator
vahid-ahmadi
left a comment
There was a problem hiding this comment.
Approving. Traced the math and the fix is structurally correct: CTR was orphaned (variable existed, no aggregate consumed it), so baseline hbai_household_net_income deducted gross CT and added zero CTR back, and gov_balance showed gross CT revenue with no offsetting CTR spending. Adding CTR to the four aggregates and applying the abolish_council_tax filter consistently gives the right deltas on both sides.
Spot checks:
- Reform delta with CTR=£800, gross CT=£2,000:
hbai_net_income+£1,200,gov_balance−£1,200. Matches the new test. - Zero-CTR case (existing test, yaml parametric tests in
hbai_council_tax_lvt.yaml): unchanged, since CTR was effectively absent from baseline anyway. - BenUnit→Household aggregation via
add(...)is consistent with howchild_benefit(also BenUnit-scoped) is handled in these same lists, so no member-projection double-count. household_benefits.formulauprating: CTR will now be uprated byuprating.non_sp, which is consistent with how other non-SP benefits are treated.
Worth flagging (non-blocking):
- Baseline poverty rates shift materially (PR description shows −1.00 pp on relative AHC). Cached baselines, dashboards, and any published comparisons downstream of
policyengine_ukwill need refreshing. A minor version bump rather than patch seems warranted given the user-facing effect. gov_spendinggains aformulafor the first time. Functionally equivalent to theadds-only path whenabolish_council_taxis false, but worth being aware of for any future reform that touchesgov_spending.- Calibration sanity check (for whoever owns calibration, not blocking this PR): the change adds roughly £4 bn of CTR to baseline
household_benefitsandgov_spending. If calibration targets a national benefit-spending total, double-check CTR isn't already being absorbed viahousing_benefitor the UC housing element.
Pre-existing nit (not introduced here): pre_budget_change_household_benefits.adds is missing several items present in household_benefits.adds (e.g., iidb, maternity_allowance). Out of scope for this PR.
This was referenced May 11, 2026
MaxGhenis
added a commit
to PolicyEngine/uk-land-value-tax
that referenced
this pull request
May 11, 2026
The CTR-in-net-income fix shipped as PolicyEngine/policyengine-uk#1668 and auto-bumped the version from 2.88.14 to 2.88.15. The analysis JSON and charts are bit-identical to the pre-merge run (same source, just a version label change upstream), so this is a footnote-only update — no recomputation required.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
council_tax_benefit(CTR) was completely absent from PolicyEngine UK's net income tree: not inhousehold_benefits.adds, not inhbai_household_net_income.adds, not inpre_budget_change_household_benefits.adds, and not ingov_spending.adds. Thecouncil_tax_benefitvariable existed (adds = ["council_tax_benefit_reported"]) but nothing consumed it.Meanwhile
council_tax(gross billing, before CTR) is inhousehold_tax.adds, so the model deducted the gross billed amount fromhousehold_net_incomeand never added CTR back. This diverges from DWP HBAI methodology, which counts CTR as benefit income and treats council tax as a deduction at the gross amount — net effect: income measured at gross-CT-minus-CTR (i.e. net CT).Bug consequences before the fix
Baseline (no reform):
Under any reform touching council tax (e.g.
gov.contrib.abolish_council_tax):gov_balancefalls by gross council tax revenue, not net.Fix
council_tax_benefittohousehold_benefits.adds,hbai_household_net_income.adds,pre_budget_change_household_benefits.adds, andgov_spending.adds.if abolish_council_tax: [b for b in benefits if b != "council_tax_benefit"]filters inhousehold_benefits.formulaandpre_budget_change_household_benefits.formulabecome meaningful (no-ops before).hbai_household_net_income.formula(uses its ownaddslist) and a freshgov_spending.formula(previously had no formula — just anaddslist).After the fix:
Effect on baseline UK poverty rates (2026-27)
Direct
policyengine_uk.Microsimulationon the Enhanced FRS 2023-24 dataset, calibrated to 2026-27:Relative AHC moves most because AHC subtracts housing costs (the threshold is a fixed share of median AHC income, which itself shifts when CTR is added to the income tree). All four baselines fall under the fix because the CTR-recipient households previously misclassified as below the poverty line now have their CTR counted as income.
Anyone running historical poverty comparisons should expect baseline numbers to shift under this PR. Reform-state values for
abolish_council_taxreforms are unchanged (CT and CTR both vanish from the income equation regardless of which side they sit on) — only the baseline shifts, which means the change under reform shifts too, by the size of the baseline correction.Test plan
test_abolish_council_tax_removes_budget_impact(zero-CTR case, unchanged behaviour) — passes.test_abolish_council_tax_nets_out_council_tax_benefit(CTR-recipient case: gross CT £2,000, CTR £800, expected refund £1,200) — passes; would have failed pre-fix.pytestsuite locally withHUGGING_FACE_TOKEN— 140 passed, 1 skipped, no regressions.Motivating analysis
Council-tax-to-LVT replacement scoring in PolicyEngine/uk-land-value-tax#8.