Skip to content

Read stored draws for UC, student loans and PIP - #2877

Draft
MaxGhenis wants to merge 8 commits into
mainfrom
claude/deterministic-engine-phase-1a-s1-j31my4
Draft

MaxGhenis wants to merge 8 commits into
mainfrom
claude/deterministic-engine-phase-1a-s1-j31my4

Conversation

@MaxGhenis

Copy link
Copy Markdown
Collaborator

Addresses #2874
axiom: n/a: microsimulation-only: stored inputs for engine draws, no rule changes

This PR contains #2873's two commits (the engine-randomness guard) and lands after #2873.

What and why

Three new yearly inputs, with no formula:

  • uc_new_claimant_health_element_draw (benefit unit, float, default 1.0): a uniform in [0, 1) that the dataset build stores. In each Universal Credit rebalancing year with no status for the year, add_universal_credit_reform gives a benefit unit the new-claimant LCWRA rate when the draw falls below new_claimant_share.
  • pip_phase_in_draw (person, float, default 1.0): the PIP phase-in scenario removes a person's PIP in a year from 2025 to 2029 when the draw falls below that year's share.
  • would_claim_student_loan (person, bool, default False): the stored take-up decision that uprate_student_loan_plans uses for new borrowers.

Each consumer uses the stored input when the dataset supplies it and runs its own seeded draw, unchanged and inline, when it does not. The UC modifier and the PIP scenario ask input_source whether a supplied draw reaches the year, read it through Simulation.calculate, and compare it with the share in float64. Student loan uprating reads the column from the projected table after checking its raw values. No published dataset carries these columns yet, so outputs on today's data change only where the tightened helper below changes an answer.

Reading a stored draw

utils/stored_draws.py registers the two draws with their entity, value type and definition period. input_source(population, name, period) returns the period, branch, tier and sequence number of the supplied input policyengine-core reads, None where no supplied input reaches the period, and raises otherwise. It changes nothing in storage and returns no array. It refuses:

  • a request other than one whole year in storage form (a month, year:2026:2, month:2026-06:12, year:2025-01-15);
  • a current or holder definition that changes the entity, type or period, or adds a formula, uprating, defined_for, calculate_output, adds, subtracts or requires_computation_after; a neutralised or abolished draw; core's macro cache; carry turned off where the read needs it;
  • a holder with a disk tier, naming Storage keys need a structured (branch, period) scheme: str(period) is lossy and separators collide with branch names policyengine-core#526;
  • a non-derived value in the slot core reads that no one supplied; a derived value whose carry source no one supplied, or that differs from its source; and, with no supplied source, a derived value other than the holder's default.

In a simulation built from data, Simulation.calculate runs the check after its input-lifecycle and stale-record cleanups, drops the request's fast-cache entry, and for a source-backed draw calls core with map_to=None, checks that core's answer equals the source's array (same dtype and shape) and that the source has not changed, then maps it. calculate_add, calculate_divide and calculate_output refuse a registered draw. A context variable counts running pe-uk calculations on any simulation, and set_input refuses a new draw while one runs or while the holder stores a derived value of the draw. Situations keep core's path.

Supplied-input helpers

SUPPLIED_INPUT_VARIABLES now holds the three new columns. set_model_value refuses them. A recorded key now counts only when its branch is the one core reads for the period and the tier core reads there (memory before disk) still holds the input. latest_supplied_input_period ranks with core's _latest_input_key.

What this changes for existing callers (supplied_input, supplied_input_periods, supplied_by, latest_supplied_input_period, used by current_education, the maintenance loan, PIP, trading allowance, Class 4 loss and housing benefit formulas):

  • a value cached as non-derived over an input in the same slot no longer counts as supplied (test_a_value_cached_over_an_input_in_its_slot_is_not_supplied);
  • on a child branch, a parent's input under a value the child stores no longer counts (test_a_child_branch_value_over_a_parent_input_is_not_supplied_on_the_child);
  • an input on disk under a value cached in memory no longer counts (test_an_input_on_disk_counts_only_when_memory_does_not_hide_it);
  • of supplied periods before the request, the latest is now the one core's carry-over would pick: a period of the variable's own unit before another unit, and of two that start on one day the one that ends last (test_two_periods_starting_on_one_day_rank_by_core_order).

Calculation itself never caches a value over a supplied input, so these cases arise only from low-level holder writes or branch caches; the required test files below pass unchanged.

The core Dataset route

Before it exports, Simulation.build_from_dataset refuses a dataset that carries any stored column (the UC deduction draws, months_since_last_birthday, warm_home_discount_random_draw and the three new columns) when any person, benefit unit or household identifier fails to survive the int32 cast, naming the entity and identifiers. It also refuses a dataset whose stored columns, attends_private_school_random_draw, uc_receives_new_claimant_health_element or attends_private_school it supplies for some period but not the first, or whose draws change between periods.

Datasets the route now refuses: core-format datasets carrying one of those columns with identifiers beyond int32, and core-format datasets supplying one of them only for later periods, or with a draw that changes between periods. None of pe-uk's own core-route fixtures carries such a column, so the route refuses none of them.

Core versions

QUALIFIED_CORE_VERSIONS records 3.33.3 and 3.34.1. tests/code_health/test_core_version_gate.py fails when uv.lock or the installed core holds another version, and says what to rerun before adding one. It reads uv.lock from the source tree or the directory the tests run from, so it runs without a skip when CI tests the built wheel.

Invariants

  • Qualified core: the lock and the installed core are 3.33.3 or 3.34.1.
  • Output-neutral by construction: without the columns each consumer runs today's draw; with columns built by the reference encoder from today's draws, the UC statuses, LCWRA amounts and PIP amounts agree bit for bit on the DataFrame and core Dataset routes.
  • Representation: an encoded draw lies in [0, 1), survives a float32 round trip, sits within 2^-24 of its legacy draw, and each consumer compares it in float64, so it keeps the legacy decision at every covered threshold (property test). Compared in float32, a stored 3/8 would fall on the wrong side of a share of 3/8 + 2^-28.
  • Reformability: new-claimant status is monotone in the share and PIP phase-out in the phase-in share (property tests); no student loan call passes a rate, and the stored decision holds in every projected year.
  • Fail closed (read rules): every read of a registered draw passes input_source before core and the answer check after; ADD, DIVIDE and output refuse draws; the fast cache never answers one.
  • Cache survival: a surviving draw cache unequal to its current proven source raises; an invalidated cache recomputes from the current source.
  • Precedence unchanged: a status supplied for the year wins; one supplied only earlier leaves the later year to the draw; a repeated modifier, a reform after construction and an export replayed under another share keep the statuses.
  • Keys and order: the core route refuses stored columns with identifiers that do not survive the int32 cast, and stored columns not supplied for the first period.
  • Determinism: applying the PIP scenario with stored draws for every year leaves numpy's global state as it was.

Test plan

  • test_supplied_input_tightening.py: registration, ranking (with a Hypothesis property against core's order), the tightened key and the set_model_value refusal.
  • test_stored_draw_input_source.py: provenance, requests, definitions, disk tier, dispatch, setter, lifecycle, restore and the differential against core's own calculate.
  • test_stored_draw_consumers.py: each consumer with and without its column, precision, precedence, validation and properties.
  • test_stored_draw_core_route.py: identities for each entity, supplied periods, and the with/without differential on both routes.
  • code_health/test_core_version_gate.py and code_health/test_stored_draw_reads.py (no package read of a draw skips calculate).

On core 3.34.1 (the lock) and 3.33.3 (uv run --frozen --with 'policyengine-core==3.33.3', which printed 3.33.3), the 17 required existing files, these six files and the five other core-Dataset-route files gave 1069 passed, 0 failed on each. policyengine_uk/tests/code_health gave 3556 passed. Mutations of each refusal, of each consumer's float64 promotion, of the fast-cache eviction, of the set_model_value refusal, of both core-route checks, of #6's validation and of its loader each fail a test. ruff format --check, ruff check and git diff --check pass.

What later stages finish

Stage 2 adds protected storage and sealed populations, with a storage check inside input_source. Phase 2 fails closed: it removes the seeded fallbacks, registers the other required draws and outcomes, and adds months_since_last_birthday's answer check and inclusive lookup.


Generated by Claude Code

claude and others added 8 commits November 5, 2026 04:39
Port policyengine-us's scanner for engine-time randomness and hash draws
as a code-health test. EXEMPT lists the package's SHA-256 digests, which
fingerprint, key caches or check content; RATCHET lists today's draws:
the splitmix64 helper, the formulas and uprating that call it, and the
seeded numpy draws in participation and two scenarios. Cases that need
newer syntax are filtered at collection, so no Python reports a skip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6H1FBndAKeuXdSGuft
The guard's docstring now says it looks for draws and not for the clock
(#2875). The comments on the splitmix64 and Universal Credit entries say
which constants the scan recognises, which entries reach the hash and how
Simulation.__init__ applies the modifier. Two test rows follow
PolicyEngine/policyengine-us#10447 at 221f2fe44, and a changelog fragment
records the guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e reads it

Define uc_new_claimant_health_element_draw, pip_phase_in_draw and
would_claim_student_loan as yearly inputs with no formula, and register
them with the supplied-input helpers. supplied_input_periods, and with it
supplied_by, latest_supplied_input_period and supplied_input, now count a
recorded key only when its branch is the one core reads for the period and
the tier core reads there still holds the input. latest_supplied_input_period
ranks with core's _latest_input_key. set_model_value refuses the three new
columns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6HUE9hj2BPEjEXLL68
input_source(population, name, period) names the supplied input
policyengine-core reads for a registered draw, returns None where none
reaches the period, and raises for anything else. In a simulation built
from data, Simulation.calculate applies it before core, drops the request's
fast-cache entry, and checks core's answer against the source after.
calculate_add, calculate_divide and calculate_output refuse a draw, and
each counts as a running calculation. set_input refuses a new draw while a
calculation runs or a derived value of the draw is stored, and checks
would_claim_student_loan's raw values before core casts them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6HUE9hj2BPEjEXLL68
…cenario

Each consumer asks input_source whether a stored input reaches the year.
The UC rebalancing modifier compares uc_new_claimant_health_element_draw,
promoted to float64, with the new-claimant share; the PIP scenario does the
same with pip_phase_in_draw and the phase-in share; student loan uprating
uses the stored would_claim_student_loan decision after checking its raw
values. Without the column, each runs its existing seeded draw, inline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6HUE9hj2BPEjEXLL68
…e core versions

The core Dataset route refuses a dataset carrying a stored column whose
person, benefit unit or household identifiers do not survive the int32
cast, and one whose stored columns or exempting outcomes are supplied for
a later period but not the first, or whose draws change between periods,
before it exports anything. A code-health test fails when uv.lock or the
installed policyengine-core holds a version outside the qualified set, and
another fails on any package read of a stored draw that skips calculate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6HUE9hj2BPEjEXLL68
…eachable branch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6HUE9hj2BPEjEXLL68
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Aggregate impact on certified data: pass

Base d4655eb56c against head 0728128dc8, 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

Each of 92 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.

  • 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 d4655eb56c uk 2.175.3 (files 889511591b1d), core 3.34.1, py 3.13.18 aarch64 2be386090644 3bcfb3473960 2559.39s
head #2877 merged into main 0728128dc8 uk 2.175.3 (files f2a1599c1f3b), core 3.34.1, py 3.13.18 aarch64 2be386090644 3bcfb3473960 2587.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,024 † £28,623 † £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.

Review status: COMPLETE at 5a784a7e926d4bc5b1471770ae67d4f96f139140. No findings or material evidence gaps.

Independent code/test and policy/source review covered the stored UC health and PIP draws, student-loan take-up, supplied-input precedence, provenance/cache/lifecycle checks and Core Dataset routing. The change introduces allocation inputs rather than new statutory rules.

Validation: 202/202 focused repository tests passed on the lock-matched Core 3.34.1 environment, plus one synthetic final-output/order check. The stored UC draw changes LCWRA, final Universal Credit and household net income by the same £2,550.48; reversing calculation order gives identical results. The unchanged #2873 randomness guard was checked byte-for-byte against its prior reviewed head; its earlier 384 passes were reused, not rerun or added to the 202 count. All 20 hosted checks currently pass at this exact head.

Equivalence remains conditional on the supplied reference decisions and covered thresholds. This review does not certify producer calibration, private datasets, population effects, arbitrary-share float64 equivalence or the author's full dual-Core-version/mutation totals. The disclosed shadowed-input/carry-ranking changes and seeded fallback boundary remain explicit.

Approval withheld: this PR is still a draft and its description requires #2873 to land first. The completed review can be reused when readiness, parent integration and exact-head checks are reassessed.

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.

3 participants