Repository navigation
Description
Problem
The UK program_statistics (the detailed_budget the apps chart as "Budgetary impact by programme") come from a fixed list of 14 programmes in src/policyengine/tax_benefit_models/uk/analysis.py. The headline budgetary_impact is the change in household_tax - household_benefits, and policyengine-uk builds those totals from HOUSEHOLD_TAX_VARIABLES (27 taxes) and HOUSEHOLD_BENEFIT_VARIABLES (about 50 benefits).
Any change in a programme outside the fixed list is in the headline but has no row, so the programme chart does not add up to the total and does not say why. The fixed list also has rows that are not in the total (ni_employer, vat, the overlapping tax_credits).
Example: the 2027 UC freeze (live report)
Report: https://app.policyengine.org/uk/report/49a9d5d0-85d7-4f95-b96e-eb26441e4191 (v1 report 4738). Reproduced locally on policyengine 6.10.0, policyengine-uk 2.132.6 and the certified microcosm_uk_2024_25.h5:
| Fixed list (now) | Rows from the model's own totals | |
|---|---|---|
| universal_credit | £933.5m | £933.5m |
| scottish_child_payment | not shown | £25.0m |
| Gap not shown | £25.0m | £0.0m |
| Total | £958.5m | £958.5m |
The £25.0m is Scottish Child Payment lost by Scottish families whose UC falls to zero (SCP is passported on UC).
On test households (84 families, Scotland and England, £0-£50k earnings), a 10% cut to the PIP standard daily living rate gives no rows at all with the fixed list (the whole impact is unexplained), and one pip row with rows from the totals.
Proposal
Build the UK rows from the variables the headline sums, not from a fixed list:
- Taxes:
policyengine_uk.variables.gov.hmrc.household_tax.HOUSEHOLD_TAX_VARIABLES - Benefits:
policyengine_uk.variables.household.income.household_benefits.HOUSEHOLD_BENEFIT_VARIABLES
Return a row for each one that changes. The rows then add up to the headline by construction (apart from the gov.contrib.benefit_uprating contrib terms, which are zero by default), and a new benefit in policyengine-uk shows up with no change here. The model already computes these variables to get the totals, so the extra cost is small.
Points to settle:
- Grouping. Programmes that move together can show as large bars that cancel out. With a £1,000 cut to the personal allowance, test households moved between
extended_childcare_entitlement(+£10,234) andtargeted_childcare_entitlement(-£10,234). A small grouping map (for example all childcare entitlements as one row) would avoid this. - Labels. Rows should carry the variable label so consumers do not hard-code names.
- Compatibility. Keep the existing keys for the current programmes so saved outputs and the v2 chart still read.
The app will show any remaining gap as an "Other programmes" bar, so the chart always adds up even before this lands.
Activity
PavelMakarchuk commentedon Oct 27, 2026
One more point for the output shape: the app decides each row's sign from its own list of benefit keys (
BENEFIT_PROGRAMSin policyengine-app-v2'sBudgetaryImpactByProgramSubPage.tsx). A new benefit row such asscottish_child_paymentwould be signed as a tax there. The existingis_taxflag inProgramStatisticsalready says which is which; please carry it through to thedetailed_budgetrows so consumers do not need their own list.Interim app fix: PolicyEngine/policyengine-app-v2#1451 shows the unlisted part as an "Other programmes" bar.