Skip to content

Name heads that are NaN at baseline in the counted-once check - #2854

Open
MaxGhenis wants to merge 7 commits into
mainfrom
fix-2773-nan-baseline-heads
Open

MaxGhenis wants to merge 7 commits into
mainfrom
fix-2773-nan-baseline-heads

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #2773

What and why

The counted-once check (policyengine_uk/utils/aggregate_impact/guard.py) raises each component, holds the other heads at their baseline values, and reads each head's change from its baseline. A head that was already NaN at baseline broke it in four ways (the issue has the minimal case):

  1. Raising the head gave policyengine-core NaN, and the check stopped with ValueError: Unable to set value for variable "tax_a", as the input contains NaN values.
  2. Holding the head at its baseline raised the same error.
  3. The head's change, NaN − NaN, failed a component it ignores, twice (tax_a moves, or is not finite, when tax_b does and the model's gov_tax moves by other than its heads, or is not finite, when tax_b does).
  4. The reset check called the baseline changed, because np.array_equal(np.array([np.nan]), np.array([np.nan])) is False.

With this change:

  • The check names each variable it cannot read, once, as a blocking result. The result gains not_finite_at_baseline when a head, a model total or a component it raises is NaN or infinite at baseline: one entry each, with the head's id and label, the households with weight, and a message. measure.record_guard_checks records a head under its own finite:<head id> check and a total or double count under finite:metrics, in the scope counted_once:<year>:<variable>. compare.ALWAYS_BLOCKING_PREFIXES makes both block, whatever the base did.
  • No rule reads a value that is not finite at baseline. judge_total takes unread masks: it judges no head in the households where that head is not finite at baseline, and no model total where the total or one of its heads is not. The check averages each head's response, and the model total's coefficient, over readable households, and omits it when none has weight.
  • The check raises and holds variables only at finite values, and restores supplied inputs as they stood. To raise a component that is not finite at baseline, it sets each such unit to 0, raises the others, and judges nothing for that component in the households that hold such a unit. If it can raise the component nowhere else, it skips it and says why.
  • It holds a head that is not finite at baseline by leaving it to its formula. In the held measurements it does not set such a head. Wherever the head does not respond to the component, its formula gives back the value the measurement holds it at (its baseline, NaN where it was NaN, or its value after the first point). There its formula reproduces the held value, so every other head reads what it would read with the head held. The check compares the head with that value unit by unit, NaN matching only NaN (_moved), and judges the household only where they match. A head that ignores the component therefore leaves every household judged and fails nothing. Where such a head does move with the component, a failure there stays and names it in heads_not_held_at_baseline.
  • The reset check matches NaN only with NaN in the same place (np.array_equal(..., equal_nan=True)). A value that turns NaN after the reset, or a NaN that moves, still counts as a change.
  • The check skips a declared double count when no household with weight lets it read both the component and its container. record_guard_checks records it as skipped (ok=None), and the report says not checked.

The issue's cases, run on each side:

Case Before (#2759 at 89bd11f) After
1. Raise tax_a, NaN at baseline ValueError tax_a skipped ("the check could raise it only in households with no weight or where it is not finite at baseline"); tax_b passes; tax_a and gov_tax named
2. Hold tax_a while measuring tax_c = tax_b ValueError tax_b fails for tax_c only: "gov_tax: tax_c moves when tax_b does in fewer than 10 households"
3. Check tax_b alone fails twice, for tax_a and gov_tax passes, with tax_a and gov_tax named
4. Reset check False True

Every report today has heads and totals that are finite at baseline, and for those the published report does not change. counted_once_base_reference.py freezes the check as it stood at 89bd11f: counted_once, record_guard_checks, publishable_guard and the report's _guard section. A differential test asserts that the result, the recorded checks (ids, statuses, scopes, details), the published guard and the report text serialise to the same JSON, in the same order, on the guard tests' own models and on Hypothesis-drawn heads and weights. The new keys appear only when something is not finite at baseline.

Invariants

These hold for every input:

  1. No error, no NaN input. For any baseline values (finite, NaN, ±inf), counted_once returns, and every value it raises or holds a variable at is finite: the check sets a component's non-finite units to 0 and never sets a head that is not finite at baseline. Restoring the inputs afterwards puts back what the caller supplied, ±inf included.
  2. Exact naming. not_finite_at_baseline names exactly the heads, model totals and raised components with a value that is not finite at baseline, each once. The key is absent when there are none. Each entry fails a finite: check, which blocks the comparison.
  3. No attribution. No problem names a head in a household where that head is not finite at baseline. No model_total problem falls in a household where the total or one of its heads is not finite at baseline. No rule judges a household that holds a unit where the raised component is not finite at baseline.
  4. Locality. Adding heads that no head reads and that respond to nothing, each NaN or infinite at baseline in any set of households, changes no verdict in any household. The one exception: the model-total rule of the totals that add those heads goes unread in their non-finite households.
  5. Reset. reset_reproduces_baseline is true exactly when each head's recomputed household array equals its first computation, NaN matching only NaN in the same place.
  6. Finite baselines publish as before. With every head, total and checked component finite at baseline, double counts included, the published output is identical to 89bd11f's, in content and order.
  7. Finite values keep every bit. _settable returns finite values unchanged at the byte level and 0 elsewhere.

Test plan

  • For policy changes: not applicable (no policy change: a check on the model's totals)
  • uvx ruff@0.17.2 format --check, uvx ruff@0.17.2 check and --select F811,F821 on every changed Python file; git diff --check
  • Changelog fragment changelog.d/2773.fixed.md

New tests, policyengine_uk/tests/aggregate_impact/test_aggregate_impact_guard_baseline.py: 48 passed at 48f57f9.

  • the issue's four cases on policyengine-core (case 1 yearly and monthly), each docstring giving the output before and after;
  • the reset check against seven pairs of first and recomputed values, through a fake simulation that changes only the final recomputation;
  • a head, a component, a model total, a container and a double count not finite at baseline in some or all households (NaN, +inf and −inf), and a benefit unit not finite at baseline on the fake entity simulation;
  • a held head NaN at baseline that ignores the component (its formula reproduces the held value, and the result equals the run with that head finite), one that moves with it in other households, and a failure naming heads not held for both reasons;
  • the finite: scope, label and blocking; small counts dropped from the published guard and the report;
  • Hypothesis property (invariants 2 to 5): random heads from the guard tests' shapes, with up to two extra heads each not finite at baseline in any subset of up to three households, against the same model with those heads removed, household by household;
  • Hypothesis property (invariant 1): on the same random models, whose supplied inputs are finite, every value given to set_input is finite;
  • Hypothesis differential (invariant 6) against counted_once_base_reference.py, plus eight named models and the twelve-household two-total model;
  • Hypothesis property for _settable (invariant 7).

Existing tests, each file run alone at e388e1f:

File Result
test_aggregate_impact_guard.py 184 passed
test_aggregate_impact_measure.py 46 passed
test_aggregate_impact_compare.py 142 passed
test_aggregate_impact_childcare_provenance.py 42 passed, over three runs in collection order (25, 13 and 4), as the file ran past the 1500-second cap
test_household_subset.py::test_every_walk_up_to_an_ancestor_simulation_is_reviewed 1 passed

No existing test's expected value changed. core_counted_once, a helper in test_aggregate_impact_guard.py, now passes keyword options on to counted_once.

Before and after, with this branch's tests copied onto each tree:

Tree New test file
89bd11f (#2850's branch, the fix for #2759) 32 failed, 14 passed. Every test of a value not finite at baseline fails, 22 with the issue's ValueError. The 14 that pass are the 11 differential tests, which shows the frozen reference reproduces the base exactly, and 3 reset cases whose expected outcome the base already gives (inf against inf, a value turning NaN, inf against −inf)
48f57f9 (this branch) 48 passed

I applied each mutation to a scratch copy of the package and ran the new test file against it (--rss-gb 2). The tests kill all eight:

Mutant What it breaks New tests failing
M1 reset check without equal_nan 12 of 44
M2 tax-head rule reads households where the head is not finite at baseline 18 of 46
M3 holds a head not finite at baseline with set_input (at 0) instead of its formula 9 of 46
M4 _moved counts NaN as differing from NaN 5 of 46
M5 model-total rule reads households where the total or a head is not finite at baseline 20 of 46
M6 record_guard_checks records no finite: failure 5 of 46
M7 a household holding a unit not finite at baseline still counts towards the raised units 1 of 46

M1 ran against the file's first 44 tests, and M2 to M7 against its first 46. M8 replaces _moved's unit comparison with a household-sum comparison, and both cases of test_units_that_cancel_within_a_household_still_count_as_moved fail on it.

Reproducer

Reproducer: policyengine_uk/tests/aggregate_impact/test_aggregate_impact_guard_baseline.py::test_a_head_nan_at_baseline_fails_no_component_it_ignores

axiom: n/a: a check on the model's aggregate report, not a rule of law

🤖 Generated with Claude Code

MaxGhenis and others added 6 commits October 31, 2026 21:39
The counted-once check no longer feeds NaN to set_input or fails a
component for a head that is NaN at baseline. It sets each value that is
not finite at baseline to 0 wherever it raises or holds a variable,
judges no reading that rests on those values, names each variable not
finite at baseline once (recorded as a finite: failure), and treats NaN
as equal to NaN in the same place when it checks the reset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In the held measurements the check no longer sets a head that is not
finite at baseline: it leaves the head to its formula and judges each
household where the head came back at the value the measurement holds it
at, NaN matching only NaN. A head that ignores the component is then held
exactly everywhere, so it unjudges no household and fails no component.
A head with no readable household gets no response or coefficient.

Add tests for the issue's cases, the reset check, a Hypothesis property
over random heads with extra heads not finite at baseline, and a
differential against the check as it stood at 89bd11f.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep a 0.0 response from judge_total when it skips no household, as the
existing differential on finite responses pins, and say in the module
docstring that the 0s set for a raised component change values only in
the households that hold them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a Hypothesis property that records every value the check sets while
heads are NaN or infinite at baseline, and run case 2 on monthly heads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a regression for changes that cancel between units, group the
held-heads assertion as guard.py computes it, and shorten the changelog
and README text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
render.py keeps not_finite_note from this branch and withheld_text from main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@juaristi22 juaristi22 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 9ee51800e0c6e2df9896eabec9aa5cc3bbceb24b: COMPLETE — no findings or material evidence gaps.

Baseline-invalid heads/components are named and remain blocking; held-path masks keep unrelated readable households judged. Restoration, entity/month mapping, suppressed small-cell counts and frozen-reference independence were checked.

All 420 focused checks passed on the head's locked Core 3.34.1. This covers the actual child head; no prospective merge with the newer #2850 parent was certified. Hosted checks are absent at this captured head, separately from these local passes.

Approval is deferred because the PR remains a draft; readiness, parent integration and applicable CI must be checked before approval.

Base automatically changed from fix-2759-non-finite-responses to main November 5, 2026 15:54
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review November 5, 2026 16:14
@MaxGhenis
MaxGhenis requested a review from juaristi22 November 5, 2026 16:14

@juaristi22 juaristi22 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incremental review COMPLETE at 793ce35d0a504e79785621a5e8eabf6e7178850a, with no findings or material evidence gaps.

The #2850 merge-first hold is satisfied: its reviewed head, actual merge commit 451a9bc9 and the captured main base are ancestors of this child. The base retarget is therefore supported by the current integration, rather than inferred from the branch name.

The scoped change adopts the upstream own-target privacy API in measure.py; the other seven authored paths retain their bytes. 126 focused cases passed: 48 baseline guards, 46 measurement cases and 32 withheld-reason cases. The older 420 checks remain separate retained evidence. An initial reviewer filename typo produced zero test calls and was corrected once; no passing tests were repeated.

Applicable exact-head hosted checks are still pending, so this is a comment. Approval can be considered after they pass without repeating the completed engineering review.

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Aggregate impact on certified data: pass

Base 5c0850da5f against head 3ffcf7f4e0, both on enhanced_frs_2024_25.h5 (policyengine-uk-data 1.58.1, sha256 056e80fd8e91), fiscal years 2025-26 to 2031-32. Each year is a full microsimulation run on each side.

Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 0 (thresholds in report_spec.yaml; a flag does not fail the job).

Workflow run and full report

Notes

  • In the years a figure from fewer than 10 records is suppressed (spending.disabled_students_allowance), every total containing it, and the decile incomes, are rebuilt from their other components, so no published total includes it. Those totals are marked †. A change is withheld where the two sides leave out different figures and can't be lined up.

Moves flagged for review

None.

Spending by benefit and service

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed

Caseloads

Measure 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed

Household net income by decile

No 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; standard reform costings (change in gov_balance); standard reforms: change in each reform's target.

Checks failing on both base and head (1)
  • VAT excluding private school fees is never negative (sign:vat): 2025-26: 76 units below zero, a weighted £-53,888,684 in total; 2026-27: 76 units below zero, a weighted £-55,777,260 in total; 2027-28: 76 units below zero, a weighted £-57,727,917 in total
Counted-once check

The check raised each of 92 components by £1 per unit in 2026-27 and read every head of its totals 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 response that is not finite breaks every rule. The check measures a head that breaks a rule twice more: once holding every other head at its baseline value, and once holding every other head at its value after the first £1 while the component rises again. The head fails where either measurement still shows the response. The check cannot hold a head at a value that is not finite. In each unit where another head is not finite after the first £1, it holds that head at its baseline value, and the second measurement judges no household the unit belongs to. There a head fails wherever it first broke a rule, or wherever the check could not confirm whether its response contains the component. 0 failed and the check skipped 0.

  • widowed_parents_allowance: pension_credit responds only through other heads in fewer than 10 households (other heads that moved there: carers_allowance; gov_spending; not a failure)
  • widowed_parents_allowance: pension_credit responds only through other heads in fewer than 10 households (other heads that moved there: carers_allowance; household_benefits; not a failure)
Provenance
Side Commit Runtime Environment hash Partition hash Time
base main 5c0850da5f uk 2.175.7 (files caf48f036f2e), core 3.34.1, py 3.13.18 aarch64 be7a15d6f63f 3bcfb3473960 2441.57s
head #2854 merged into main 3ffcf7f4e0 uk 2.175.7 (files 1522182b9beb), core 3.34.1, py 3.13.18 aarch64 be7a15d6f63f 3bcfb3473960 2435.65s

Dataset: hf://policyengine/policyengine-uk-data-private/enhanced_frs_2024_25.h5@2526edbbb687a3b54dbb11272e5389e4b62d2ac2, sha256 056e80fd8e9151d78f432a25cde863340baee81304c6216a37fb575ce8563784 (verified on both sides), policyengine-uk-data 1.58.1.

Receipts by tax head: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Final charge on cash interest in non-cash ISAs £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Lifetime ISA withdrawal charge £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Income tax £294.46bn £315.23bn £335.82bn £351.28bn £366.96bn £385.36bn £399.99bn
National Insurance (employee and self-employed) £51.70bn £54.14bn £56.13bn £57.73bn £59.76bn £61.64bn £63.51bn
National Insurance (employer) £148.71bn £155.37bn £160.88bn £165.09bn £172.06bn £177.36bn £182.57bn
Employer NI change borne by capital £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Employer NI change borne by consumers £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Capital gains tax £12.60bn £13.15bn £13.60bn £14.11bn £14.67bn £15.24bn £15.80bn
Student loan repayments £7.82bn £8.13bn £8.39bn £8.58bn £8.79bn £9.04bn £9.29bn
VAT excluding private school fees £249.37bn £256.87bn £266.33bn £272.70bn £278.97bn £285.24bn £291.59bn
Change in VAT from its baseline £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
VAT on private school fees £1.95bn £2.01bn £2.09bn £2.14bn £2.20bn £2.26bn £2.31bn
Change in private school VAT from its baseline £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Fuel duties £24.71bn £24.13bn £26.00bn £26.83bn £26.51bn £25.78bn £24.63bn
Alcohol duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Tobacco duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Vehicle excise duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Betting and gaming duties £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
TV licence fee £4.26bn £4.40bn £4.41bn £4.42bn £4.43bn £4.44bn £4.45bn
Council tax £49.21bn £52.16bn £54.89bn £57.95bn £61.16bn £64.47bn £67.91bn
High value council tax surcharge £0.00bn £0.00bn £0.00bn £0.48bn £0.49bn £0.50bn £0.51bn
Northern Ireland domestic rates £0.55bn £0.58bn £0.60bn £0.63bn £0.66bn £0.69bn £0.72bn
Business rates £34.68bn £36.20bn £38.00bn £39.46bn £42.27bn £43.25bn £44.29bn
Stamp duty land tax on the household's own purchases £6.88bn £7.32bn £7.66bn £8.06bn £8.47bn £8.91bn £9.35bn
Stamp duty land tax paid by companies £4.72bn £4.57bn £5.29bn £6.03bn £6.37bn £6.92bn £7.45bn
Land transaction tax (Wales) £0.07bn £0.08bn £0.08bn £0.09bn £0.10bn £0.10bn £0.11bn
Land and buildings transaction tax (Scotland) £0.31bn £0.32bn £0.33bn £0.35bn £0.36bn £0.38bn £0.40bn
Unspecified tax change borne by capital £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Unspecified tax change borne by consumers £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Wealth tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Wealth tax excluding the main residence £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Land value tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Carbon tax £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Spending by benefit and service: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Lifetime ISA bonus £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
State Pension £121.68bn £125.92bn £126.72bn £129.48bn £132.98bn £136.65bn £140.41bn
Pension Credit £6.21bn £6.46bn £6.68bn £6.82bn £6.89bn £7.07bn £7.22bn
Winter Fuel Payment £1.86bn £1.85bn £1.79bn £1.77bn £1.78bn £1.78bn £1.78bn
Pension Age Winter Heating Payment (Scotland) £0.19bn £0.20bn £0.20bn £0.21bn £0.21bn £0.22bn £0.22bn
Universal Credit £75.48bn £81.56bn £84.02bn £86.07bn £87.22bn £88.76bn £89.38bn
Housing Benefit £8.56bn £8.51bn £8.83bn £9.05bn £9.28bn £9.56bn £9.83bn
Council Tax Reduction £2.07bn £2.20bn £2.32bn £2.45bn £2.59bn £2.73bn £2.87bn
Employment and Support Allowance (income-related) £0.79bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Employment and Support Allowance (contribution-based) £5.20bn £5.37bn £5.56bn £5.69bn £5.82bn £5.95bn £6.09bn
Jobseeker's Allowance (income-based) £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Jobseeker's Allowance (contribution-based) £0.29bn £0.30bn £0.31bn £0.31bn £0.32bn £0.33bn £0.34bn
Income Support £0.11bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Working Tax Credit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Child Tax Credit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Incapacity Benefit £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Personal Independence Payment £26.95bn £28.03bn £29.05bn £30.09bn £30.75bn £31.51bn £32.21bn
Disability Living Allowance £7.49bn £7.79bn £8.07bn £8.36bn £8.55bn £8.76bn £8.95bn
Attendance Allowance £8.68bn £9.03bn £9.36bn £9.69bn £9.90bn £10.15bn £10.38bn
Carer's Allowance £4.48bn £4.66bn £4.83bn £5.00bn £5.11bn £5.27bn £5.42bn
Carer Support Payment (Scotland) £0.31bn £0.32bn £0.33bn £0.34bn £0.35bn £0.36bn £0.37bn
Carer's Allowance Supplement (Scotland) £0.00bn £0.04bn £0.04bn £0.05bn £0.05bn £0.05bn £0.05bn
Severe Disablement Allowance £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn £0.03bn
Industrial Injuries Disablement Benefit £0.50bn £0.52bn £0.54bn £0.55bn £0.56bn £0.58bn £0.59bn
Armed Forces Compensation Scheme £0.61bn £0.63bn £0.65bn £0.66bn £0.68bn £0.69bn £0.71bn
War Widow's or Widower's Pension £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Child Benefit £16.19bn £16.82bn £17.39bn £18.00bn £18.39bn £18.84bn £19.25bn
Scottish Child Payment £0.49bn £0.52bn £0.53bn £0.55bn £0.56bn £0.57bn £0.56bn
Two Child Limit Payment (Scotland, counterfactual) £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Healthy Start vouchers £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn £0.05bn
Maternity Allowance £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Statutory Maternity Pay £3.30bn £3.41bn £3.54bn £3.62bn £3.70bn £3.78bn £3.87bn
Statutory Paternity Pay £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Statutory Sick Pay £1.12bn £1.16bn £1.20bn £1.23bn £1.26bn £1.29bn £1.32bn
Sure Start Maternity Grant £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn £0.01bn
Bereavement Support Payment £0.67bn £0.69bn £0.71bn £0.73bn £0.75bn £0.76bn £0.78bn
Widowed Parent's Allowance £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Tax-Free Childcare £0.56bn £0.57bn £0.57bn £0.58bn £0.60bn £0.61bn £0.59bn
Extended childcare entitlement £2.99bn £3.50bn £3.59bn £3.71bn £3.78bn £3.86bn £3.93bn
Universal childcare entitlement £2.06bn £2.22bn £2.29bn £2.37bn £2.43bn £2.48bn £2.53bn
Targeted childcare entitlement £0.50bn £0.51bn £0.52bn £0.54bn £0.55bn £0.57bn £0.56bn
Childcare Offer for Wales £0.26bn £0.28bn £0.28bn £0.29bn £0.30bn £0.31bn £0.32bn
Care to Learn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Childcare Grant £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Parents' Learning Allowance £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn £0.20bn
Adult Dependants' Grant £0.06bn £0.06bn £0.04bn £0.04bn £0.04bn £0.04bn £0.04bn
Travel Grant £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed
16 to 19 Bursary Fund £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
NHS spending £203.89bn £204.30bn £204.56bn £204.91bn £205.34bn £205.85bn £206.35bn
State education spending £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn
Transport subsidy spending £25.05bn £25.10bn £25.14bn £25.18bn £25.23bn £25.30bn £25.36bn
Cost-of-living support payment £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Energy Bills Rebate £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Household Energy Payment £0.00bn £2.11bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Energy Price Guarantee subsidy £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Basic income £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Disability basic income £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Other public spending budget change £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Caseloads: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Lifetime ISA bonus 0k 0k 0k 0k 0k 0k 0k
State Pension 11,264k 11,145k 10,819k 10,700k 10,723k 10,750k 10,776k
Pension Credit 1,489k 1,484k 1,476k 1,454k 1,452k 1,457k 1,466k
Winter Fuel Payment 7,844k 7,783k 7,473k 7,375k 7,391k 7,409k 7,427k
Pension Age Winter Heating Payment (Scotland) 803k 802k 800k 797k 799k 801k 802k
Universal Credit 6,342k 6,418k 6,408k 6,369k 6,363k 6,343k 6,295k
Housing Benefit 1,249k 1,182k 1,181k 1,164k 1,164k 1,166k 1,168k
Council Tax Reduction 3,180k 3,199k 3,205k 3,218k 3,233k 3,244k 3,251k
Employment and Support Allowance (income-related) 71k 0k 0k 0k 0k 0k 0k
Employment and Support Allowance (contribution-based) 719k 720k 721k 722k 724k 726k 728k
Jobseeker's Allowance (income-based) 0k 0k 0k 0k 0k 0k 0k
Jobseeker's Allowance (contribution-based) 67k 67k 67k 67k 67k 68k 68k
Income Support 25k 0k 0k 0k 0k 0k 0k
Working Tax Credit 0k 0k 0k 0k 0k 0k 0k
Child Tax Credit 0k 0k 0k 0k 0k 0k 0k
Incapacity Benefit 0k 0k 0k 0k 0k 0k 0k
Personal Independence Payment 3,567k 3,575k 3,579k 3,585k 3,593k 3,602k 3,611k
Disability Living Allowance 1,291k 1,294k 1,296k 1,298k 1,300k 1,304k 1,307k
Attendance Allowance 1,695k 1,698k 1,700k 1,703k 1,707k 1,711k 1,715k
Carer's Allowance 1,034k 1,037k 1,038k 1,040k 1,042k 1,060k 1,062k
Carer Support Payment (Scotland) 71k 71k 71k 72k 72k 72k 72k
Carer's Allowance Supplement (Scotland) 0k 71k 71k 72k 72k 72k 72k
Severe Disablement Allowance 4k 4k 4k 4k 4k 4k 4k
Industrial Injuries Disablement Benefit 137k 137k 137k 138k 138k 138k 139k
Armed Forces Compensation Scheme 93k 93k 94k 94k 94k 94k 94k
War Widow's or Widower's Pension 0k 0k 0k 0k 0k 0k 0k
Child Benefit 8,123k 8,120k 8,120k 8,130k 8,145k 8,161k 8,178k
Scottish Child Payment 351k 352k 352k 353k 350k 348k 337k
Two Child Limit Payment (Scotland, counterfactual) 0k 0k 0k 0k 0k 0k 0k
Healthy Start vouchers 200k 200k 200k 201k 201k 201k 202k
Maternity Allowance 0k 0k 0k 0k 0k 0k 0k
Statutory Maternity Pay 333k 333k 334k 334k 335k 336k 337k
Statutory Paternity Pay 0k 0k 0k 0k 0k 0k 0k
Statutory Sick Pay 181k 182k 182k 182k 182k 183k 183k
Sure Start Maternity Grant 26k 26k 26k 26k 26k 26k 26k
Bereavement Support Payment 98k 98k 98k 98k 98k 99k 99k
Widowed Parent's Allowance 0k 0k 0k 0k 0k 0k 0k
Tax-Free Childcare 1,043k 1,027k 1,014k 1,009k 1,011k 1,009k 993k
Extended childcare entitlement 795k 800k 796k 797k 798k 798k 792k
Universal childcare entitlement 1,071k 1,072k 1,072k 1,073k 1,076k 1,079k 1,074k
Targeted childcare entitlement 165k 156k 154k 154k 155k 155k 150k
Childcare Offer for Wales 39k 39k 39k 39k 39k 39k 39k
Care to Learn 0k 0k 0k 0k 0k 0k 0k
Childcare Grant 0k 0k 0k 0k 0k 0k 0k
Parents' Learning Allowance 98k 98k 98k 99k 99k 99k 99k
Adult Dependants' Grant 18k 18k 12k 12k 12k 12k 12k
Travel Grant 0k 0k 0k 0k 0k 0k 0k
Disabled Students' Allowance suppressed suppressed suppressed suppressed suppressed suppressed suppressed
16 to 19 Bursary Fund 0k 0k 0k 0k 0k 0k 0k
Cost-of-living support payment 0k 0k 0k 0k 0k 0k 0k
Energy Bills Rebate 0k 0k 0k 0k 0k 0k 0k
Household Energy Payment 0k 21,065k 0k 0k 0k 0k 0k
Energy Price Guarantee subsidy 0k 0k 0k 0k 0k 0k 0k
Basic income 0k 0k 0k 0k 0k 0k 0k
Disability basic income 0k 0k 0k 0k 0k 0k 0k
Government balance, each component counted once: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Government receipts, each component once £892.00bn £934.66bn £980.50bn £1,015.93bn £1,054.24bn £1,091.56bn £1,124.89bn
Government spending £528.93bn † £544.94bn † £549.99bn † £558.68bn † £566.30bn † £575.00bn † £582.59bn †
Government balance, each receipt once £363.07bn † £389.72bn † £430.51bn † £457.24bn † £487.94bn † £516.56bn † £542.31bn †
Benefits in household_benefits but not in gov_spending £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Model totals: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
gov_tax £892.00bn £934.66bn £980.50bn £1,015.93bn £1,054.24bn £1,091.56bn £1,124.89bn
household_tax £491.97bn £520.41bn £551.21bn £576.00bn £601.00bn £626.69bn £648.42bn
pre_budget_change_household_tax £484.15bn £512.28bn £542.82bn £566.95bn £591.72bn £617.16bn £638.61bn
gov_spending £528.93bn † £544.94bn † £549.99bn † £558.68bn † £566.30bn † £575.00bn † £582.59bn †
household_benefits £528.93bn † £544.94bn † £549.99bn † £558.68bn † £566.30bn † £575.00bn † £582.59bn †
gov_balance £363.07bn † £389.72bn † £430.51bn † £457.24bn † £487.94bn † £516.56bn † £542.31bn †
Household income totals: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Household market income £1,637.96bn £1,702.77bn £1,757.35bn £1,806.41bn £1,855.97bn £1,912.43bn £1,970.44bn
Household net income £1,636.24bn † £1,687.10bn † £1,714.68bn † £1,746.67bn † £1,777.79bn † £1,816.07bn † £1,858.68bn †
Household net income (HBAI definition) £1,449.13bn £1,499.11bn £1,530.09bn £1,564.35bn £1,596.99bn £1,634.64bn £1,675.60bn
Population: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
People 69,245k 69,383k 69,473k 69,592k 69,738k 69,912k 70,080k
Benefit units 35,110k 35,181k 35,226k 35,286k 35,360k 35,449k 35,534k
Households 31,046k 31,108k 31,149k 31,201k 31,267k 31,345k 31,420k
Poverty rates: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Absolute poverty, before housing costs: everyone 16.3% 15.5% 16.4% 16.3% 16.5% 16.5% 16.6%
Absolute poverty, before housing costs: children 24.7% 22.1% 23.3% 22.9% 23.1% 23.2% 23.5%
Absolute poverty, before housing costs: working-age adults 14.1% 13.8% 14.7% 14.7% 14.8% 14.8% 15.0%
Absolute poverty, before housing costs: pensioners 13.7% 13.4% 14.0% 13.8% 14.0% 14.0% 13.9%
Absolute poverty, after housing costs: everyone 21.6% 21.1% 22.0% 22.6% 22.9% 23.1% 23.5%
Absolute poverty, after housing costs: children 33.3% 31.3% 32.3% 32.5% 32.9% 33.4% 34.2%
Absolute poverty, after housing costs: working-age adults 19.9% 19.8% 20.8% 21.6% 21.9% 22.0% 22.3%
Absolute poverty, after housing costs: pensioners 12.7% 12.4% 13.2% 13.2% 13.3% 13.4% 13.6%
Relative poverty, before housing costs: everyone 17.0% 16.0% 16.4% 16.3% 16.4% 16.3% 16.6%
Relative poverty, before housing costs: children 25.9% 22.9% 23.3% 22.9% 23.0% 22.9% 23.5%
Relative poverty, before housing costs: working-age adults 14.5% 14.1% 14.6% 14.7% 14.7% 14.7% 15.0%
Relative poverty, before housing costs: pensioners 14.5% 14.1% 14.0% 13.8% 13.8% 13.9% 13.9%
Relative poverty, after housing costs: everyone 21.5% 21.0% 21.2% 21.3% 21.3% 21.5% 21.7%
Relative poverty, after housing costs: children 33.1% 31.1% 31.1% 31.0% 31.1% 31.2% 31.8%
Relative poverty, after housing costs: working-age adults 19.9% 19.8% 20.2% 20.4% 20.5% 20.6% 20.8%
Relative poverty, after housing costs: pensioners 12.5% 12.4% 12.2% 11.8% 11.7% 11.8% 12.1%
People in poverty: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Absolute poverty, before housing costs: everyone 11,277k 10,784k 11,408k 11,373k 11,482k 11,521k 11,657k
Absolute poverty, before housing costs: children 3,688k 3,314k 3,496k 3,445k 3,480k 3,502k 3,549k
Absolute poverty, before housing costs: working-age adults 5,965k 5,895k 6,311k 6,366k 6,423k 6,434k 6,524k
Absolute poverty, before housing costs: pensioners 1,624k 1,575k 1,601k 1,562k 1,579k 1,585k 1,584k
Absolute poverty, after housing costs: everyone 14,944k 14,607k 15,318k 15,729k 15,946k 16,150k 16,440k
Absolute poverty, after housing costs: children 4,975k 4,682k 4,837k 4,888k 4,945k 5,040k 5,179k
Absolute poverty, after housing costs: working-age adults 8,463k 8,465k 8,976k 9,348k 9,496k 9,588k 9,715k
Absolute poverty, after housing costs: pensioners 1,506k 1,461k 1,506k 1,494k 1,506k 1,522k 1,546k
Relative poverty, before housing costs: everyone 11,750k 11,088k 11,394k 11,363k 11,422k 11,423k 11,657k
Relative poverty, before housing costs: children 3,872k 3,423k 3,496k 3,443k 3,466k 3,454k 3,549k
Relative poverty, before housing costs: working-age adults 6,159k 6,014k 6,304k 6,361k 6,392k 6,393k 6,524k
Relative poverty, before housing costs: pensioners 1,719k 1,651k 1,594k 1,560k 1,564k 1,576k 1,584k
Relative poverty, after housing costs: everyone 14,864k 14,560k 14,740k 14,811k 14,883k 15,023k 15,241k
Relative poverty, after housing costs: children 4,944k 4,660k 4,660k 4,662k 4,679k 4,707k 4,805k
Relative poverty, after housing costs: working-age adults 8,431k 8,444k 8,687k 8,823k 8,880k 8,974k 9,067k
Relative poverty, after housing costs: pensioners 1,489k 1,456k 1,393k 1,327k 1,325k 1,342k 1,369k
Inequality: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Gini coefficient, equivalised HBAI income before housing costs 0.3078 0.3051 0.3057 0.3056 0.3061 0.3067 0.3083
Mean household net income in each side's own deciles: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Decile 1: mean household_net_income per household £17,681 † £18,010 † £17,918 † £18,160 † £18,301 † £18,547 † £18,732 †
Decile 2: mean household_net_income per household £27,942 † £29,023 † £28,622 † £29,004 † £29,287 † £29,699 † £30,089 †
Decile 3: mean household_net_income per household £34,316 † £35,426 † £36,075 † £36,523 † £36,995 † £37,682 † £38,154 †
Decile 4: mean household_net_income per household £38,934 † £40,687 † £41,716 † £42,770 † £43,423 † £44,111 † £44,446 †
Decile 5: mean household_net_income per household £43,235 † £45,555 † £46,034 † £46,930 † £47,714 † £48,578 † £50,274 †
Decile 6: mean household_net_income per household £47,931 † £49,240 † £50,932 † £51,984 † £52,963 † £53,976 † £54,714 †
Decile 7: mean household_net_income per household £55,659 † £56,577 † £57,445 † £57,649 † £58,750 † £59,668 † £61,096 †
Decile 8: mean household_net_income per household £68,784 † £70,805 † £71,866 † £73,514 † £73,888 † £75,324 † £77,180 †
Decile 9: mean household_net_income per household £78,889 † £81,012 † £80,859 † £82,645 † £85,463 † £86,945 † £89,508 †
Decile 10: mean household_net_income per household £116,861 † £120,160 † £124,545 † £126,419 † £125,752 † £128,367 † £130,693 †
Standard reform costings (change in gov_balance): head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise basic rate by 1pp £6.41bn £6.66bn £6.63bn £6.85bn £7.03bn £7.25bn £7.49bn
Raise higher rate by 1pp £3.61bn £3.87bn £4.00bn £4.22bn £4.44bn £4.71bn £4.83bn
Raise personal allowance by ~800GBP/year £-3.81bn £-3.93bn £-3.99bn £-4.05bn £-4.11bn £-4.24bn £-1.95bn
Raise child benefit by 25GBP/week per additional child £-1.99bn £-1.75bn £-1.55bn £-1.38bn £-1.30bn £-1.18bn £-1.07bn
Reduce Universal Credit taper rate to 20% £-20.77bn £-21.32bn £-21.75bn £-22.00bn £-22.35bn £-22.75bn £-22.94bn
Raise Class 1 main employee NICs rate to 10% £10.89bn £11.30bn £11.71bn £12.01bn £12.31bn £12.62bn £13.00bn
Raise VAT standard rate by 2pp £24.89bn £25.71bn £26.63bn £27.24bn £27.87bn £28.50bn £29.14bn
Raise additional rate by 3pp £2.90bn £3.15bn £3.22bn £3.41bn £3.61bn £3.85bn £4.14bn
Raise the Universal Credit standard allowance by 10% £-3.06bn £-3.22bn £-3.30bn £-3.39bn £-3.48bn £-3.53bn £-3.54bn
Raise the State Pension by 10% £-7.76bn £-7.99bn £-8.22bn £-8.44bn £-8.79bn £-8.52bn £-8.86bn
Standard reforms: change in each reform's target: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
Raise basic rate by 1pp: change in income_tax £6.55bn £6.86bn £6.79bn £7.00bn £7.20bn £7.44bn £7.66bn
Raise higher rate by 1pp: change in income_tax £3.61bn £3.92bn £4.01bn £4.23bn £4.45bn £4.72bn £4.84bn
Raise personal allowance by ~800GBP/year: change in income_tax £-3.93bn £-4.07bn £-4.13bn £-4.18bn £-4.22bn £-4.40bn £-1.75bn
Raise child benefit by 25GBP/week per additional child: change in child_benefit £2.33bn £2.14bn £1.94bn £1.76bn £1.65bn £1.52bn £1.40bn
Reduce Universal Credit taper rate to 20%: change in universal_credit £20.74bn £21.31bn £21.79bn £22.05bn £22.37bn £22.78bn £22.99bn
Raise Class 1 main employee NICs rate to 10%: change in national_insurance £11.15bn £11.63bn £12.00bn £12.29bn £12.61bn £12.94bn £13.31bn
Raise VAT standard rate by 2pp: change in vat £24.69bn £25.51bn £26.42bn £27.03bn £27.65bn £28.28bn £28.91bn
Raise additional rate by 3pp: change in income_tax £2.90bn £3.15bn £3.22bn £3.42bn £3.62bn £3.86bn £4.12bn
Raise the Universal Credit standard allowance by 10%: change in universal_credit £3.06bn £3.22bn £3.32bn £3.41bn £3.47bn £3.53bn £3.55bn
Raise the State Pension by 10%: change in state_pension £10.68bn £11.17bn £11.28bn £11.60bn £12.03bn £12.46bn £12.96bn

@juaristi22 juaristi22 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving unchanged current head 793ce35d0a504e79785621a5e8eabf6e7178850a. The completed review remains COMPLETE with no findings or material gaps, and all latest exact-head checks now pass.

Head, description, project/lock inputs and retained evidence were revalidated. The prior 126 focused passing cases are retained, with the older 420 checks kept separate; no tests or model calculations were repeated for this approval. The #2850 merge-first hold remains satisfied by the verified parent head/merge and current child ancestry.

Readiness, current activity, source/parent guards, required branch rules and exact-head CI were refreshed immediately before approval.

MaxGhenis added a commit that referenced this pull request Nov 6, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Nov 6, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The counted-once check raises or misreports when a head's baseline value is NaN

2 participants