Skip to content

Itemise corporate_wealth's share-like components for the means tests - #1974

Merged
MaxGhenis merged 10 commits into
mainfrom
wealth-share-components
Oct 22, 2026
Merged

MaxGhenis merged 10 commits into
mainfrom
wealth-share-components

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

What

corporate_wealth bundles assets the means tests value differently:

  • quoted shares are valued less 10% for the expenses of sale (ADM H1665; DMG 29671, 52671, 84763);
  • unit trusts are valued at the bid price with no costs of sale (ADM H1673-H1674; DMG 29680-29681);
  • ISAs are valued at what the holder would get by withdrawing (ADM H1656).

This PR prepares the model for datasets that itemise it, without changing any existing result:

  1. Two household STOCK inputs. directly_held_shares (UK shares, listed or not, plus employee shares and options, held outside ISAs and pooled funds) and unit_and_investment_trusts (the Wealth and Assets Survey asks about both in one question).
  2. Uprating. Both inputs, plus cash_isa and stocks_and_shares_isa from Add cash ISA and stocks and shares ISA holdings variables #1791, join corporate_wealth under per-capita GDP in uprating_indices.yaml, so a dataset's identity corporate_wealth == Σ components survives projection. The growthfactors.md sentence now matches the YAML list exactly.
  3. share_like_capital (formula, STOCK). It is corporate_wealth as supplied, unless the itemised components are positive and add up to more than that by more than 1e-6 of the larger of the two, in which case it is exactly their sum. The tolerance covers float32 rounding: a dataset that builds corporate_wealth as the float64 sum of three components and stores all four rounds each once, and the engine re-adds them in float32, so the two totals differ by at most about 3e-7 of the value. With no corporate_wealth, any positive components count in full, down to a penny.
  4. Capital lists. All six capital sources lists (UC, HB, IS, JSA, ESA, PC) name share_like_capital instead of corporate_wealth. On main, UC's sources is now the financial pool only (savings and share-like wealth); property is valued through property_sources, and this PR leaves it there.
  5. The reg 77 holdings budget follows the counted source. UC disregards an owner-manager's company holding (reg 77(2)), clips each holding to the household's share-like wealth and scales them to share one budget. Main reads that budget from corporate_wealth in household_uc_company_holding_pool_scale, household_uc_pool_company_holdings and household_uc_reported_financial_capital. They now read HOLDINGS_SOURCE = "share_like_capital", defined once in the first of them, so the budget is the capital the pool actually counts. Without this, a holding entered next to itemised shares with no corporate_wealth would never leave the pool.
  6. Inherited fix. The pension-age HB home disregard is Schedule 6 paragraph 26 of SI 2006/214, not paragraph 1.

Impact

None on any dataset; this is tested exactly.

  • A dataset that carries only corporate_wealth (every released uk-data build) counts exactly corporate_wealth, any sign.
  • So does a dataset that builds corporate_wealth as the components' sum: Split pensions, shares, trusts, ISAs and secured debt out of WAS wealth policyengine-uk-data#501, and Microcosm's current stocks_and_shares_isa export.
  • A household situation given only components now has them counted, where before they counted for nothing. A reg 77 holding entered beside them now leaves the UC pool, as one entered within corporate_wealth does.

Aggregate impact CI (base a8b2848c13 against head 95d1f8005f, the merge commit of 6600477, on enhanced_frs_2024_25.h5 from policyengine-uk-data 1.58.1, 2025-26 to 2031-32; run):

Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 0

No household's net income changes in any year.

The check failing on both sides is main's negative VAT (sign:vat), which this PR doesn't touch.

#2131, stacked on #1969, then splits share_like_capital into the part with sale expenses (directly held shares, plus anything not itemised) and the rest. Unit trusts and ISAs therefore escape the 10%, and unsplit inputs keep #1969's treatment exactly.

Invariants (tests)

  • The block's definition (Hypothesis, float32 inputs including negative aggregates): it equals corporate_wealth exactly unless the components exceed it beyond the tolerance, and then equals their sum.

  • Differential against the original lists. On identical inputs, unsplit (negative and fractional aggregates) or split consistently, every programme's assessable capital and UC eligibility equal those under a reform restoring corporate_wealth to the six lists. The check is exact equality, annual and monthly.

  • Boundary cases, specified independently of the formula:

    • Float32 rounding at the limit. Components £6,744.81 + £6,581.24 + £2,673.95 against a supplied £16,000 keep the supplied aggregate and UC eligibility.
    • Negative aggregate. Savings £6,001 with corporate_wealth of −£2 gives main's £5,999.
    • Penny at the limit. A penny of shares with no corporate_wealth, on top of £16,000 of savings, gives the same capital and UC eligibility as the penny entered as corporate_wealth, annually and monthly.
    • Genuine excess. £0.10 of components above the aggregate counts.

    The earlier floored-residual design, and the earlier tolerance, each fail one of these.

  • Reg 77 holdings. An owner's £30,000 holding, entered as corporate_wealth or as directly_held_shares, leaves the pool in full, so two unreported adults share £10,000 of savings as £5,000 each. Reverting the budget to corporate_wealth fails the itemised case. HOLDINGS_SOURCE must be in UC's capital sources.

  • Sale-expense lists. Wherever a programme has a sale_expenses list (Value means-tested capital less 10% sale expenses and secured debt (UC reg 49 and legacy equivalents) #1969 adds them), it may not name corporate_wealth or a component. This guards that PR's merge with this one: without the rename, shares would silently lose the 10%.

  • Uprating. Components share corporate_wealth's entity, quantity type, uprating and YAML index; total_wealth doesn't add them on top of corporate_wealth.

Local runs (head 6600477, main a8b2848 merged)

Run Result
test_capital_share_components.py, test_wealth_components.py, test_stock_capital_periods.py, the UC company owner, override and Axiom-parity tests, the UC property-capital tests, the reported-capital and Lifetime ISA tests 146 passed
YAML for benefit families, gov/dwp, household/wealth, gov/local_authorities 2,281 passed
code_health 2,532 passed
ruff format --check, ruff check clean
Mutation check: the reg 77 budget read from corporate_wealth the itemised-holding test fails
Mutation check: the earlier tolerance (1e-5 relative + £0.01) the penny-at-the-limit and £0.10-excess tests fail

Reviews

Head Verdict Outcome
8b86a54 APPROVE WITH NITS nits applied
21412b6 REQUEST_CHANGES: float32 reconstruction, negative aggregates, weak differential, HB para 26 all fixed in 7784193
7784193 REQUEST_CHANGES: a component penny lost at a zero aggregate, a tolerance looser than float32 needs, approximate comparisons, overstated descriptions all fixed in f64da77

| f64da77 | REQUEST_CHANGES: one float32 step | fixed in 365f642 |
| 365f642 | APPROVE WITH NITS (round 5) | |

The merge with main since then changes the reg 77 budget readers and the UC sources list, so the hub's independent review runs at 6600477.

axiom: n/a: model plumbing that changes no rule; the sale-expense rule itself is #1969's (rulespec-uk#395)

🤖 Generated with Claude Code

corporate_wealth bundles three assets the means tests value differently:
quoted shares less 10% for the expenses of sale (ADM H1665), unit trusts at
the manager's withdrawal price with no deduction (H1673-H1674), and stocks
and shares ISAs at their withdrawal value (H1656). Add household inputs for
the two parts not yet modelled, directly_held_shares (UK shares and employee
shares and options) and unit_and_investment_trusts, and uprate them, cash_isa
and stocks_and_shares_isa with per-capita GDP like corporate_wealth, so a
dataset's identity corporate_wealth == sum of components survives
projection. The inputs have no consumers yet.

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

Copy link
Copy Markdown
Collaborator

Reviewed commit 54f4c7da480f79963967dc6689af022e82673636. Needs changes; review coverage is partial. The additive wealth inputs passed their focused tests. The current-main integration now needs a documentation conflict resolved and the #1791 dependency handled before merge.

Confirmed integration conflict. A prospective merge of this exact head onto main 3c48247eb92b221312565bff3a1dbf0b87306e22, attempted on 2 October 2026, stopped with a content conflict in growthfactors.md. The attempt safely aborted without changing the PR. No integration tests ran on that prospective merged tree. GitHub's mergeability against the current add-isa-balance-variables base does not establish mergeability against main.

Preserve the component contract. test_wealth_components.py:26 verifies the share components use the same entity, stock semantics and uprating as corporate_wealth; test_wealth_components.py:42 checks they are not added again to total_wealth. These are useful inputs even before data builders or benefit formulas consume them. Their addition does not establish that every supported dataset has split pensions out of its corporate-wealth bundle, so it does not by itself resolve #1837.

Repair and acceptance steps:

  1. Land the ISA input parent Add cash ISA and stocks and shares ISA holdings variables #1791 first, then retarget this PR to main and rebase on its actual tip. If the parent is consolidated through another agreed route, preserve both ISA definitions and their changelog rather than dropping the dependency silently.
  2. Resolve growthfactors.md by retaining current-main descriptions and this PR's new component entries. Check the final prose against policyengine_uk/data/uprating_indices.yaml and variable metadata; do not select an entire outdated document side.
  3. Keep corporate_wealth as the aggregate holding its share/fund components, with cash_isa following the savings contract. Preserve the no-double-count test and the common uprating index, so a dataset that supplies the component identity retains it when projected.
  4. Run the focused suite on the integrated tree and obtain new-head hosted checks. Check documentation rendering if conflict resolution materially changes the page. Report any data migration separately, with its supported schema and consumers; no population-impact claim is needed for this input-only change.

Suggested checks after resolving the dependency and conflict:

uv run --no-sync pytest policyengine_uk/tests/test_wealth_components.py -q
uv run --no-sync ruff format --check .
uv run --no-sync ruff check .

Validation already completed on this exact PR snapshot: 3 tests passed in test_wealth_components.py, using an asserted snapshot import, core 3.32.9 and the existing environment without installing dependencies. That run preceded the attempted current-main integration. The combined tree, final documentation resolution, hosted CI after retargeting and any population-data consumers remain unverified.

Live posting check (2026-10-02T15:12:45.036767+00:00): same reviewed commit; GitHub reports MERGEABLE. No hosted checks reported. These checks do not replace the remaining validation listed above.

Base automatically changed from add-isa-balance-variables to main October 4, 2026 10:27
MaxGhenis and others added 2 commits October 4, 2026 06:28
# Conflicts:
#	docs/book/assumptions/growthfactors.md
The six capital sources lists (UC, HB, IS, JSA, ESA, PC) name
directly_held_shares, unit_and_investment_trusts and stocks_and_shares_isa
in place of corporate_wealth, plus a new formula variable,
unitemised_corporate_wealth = max(0, corporate_wealth - components), for
whatever a dataset does not itemise. Datasets without the components keep
counting corporate_wealth in full; datasets that build corporate_wealth as
the components' exact sum count the same total, now itemised, so each
component can carry its own valuation rule (the sale-expense deduction stacked
on #1969 exempts unit trusts and ISAs). No dataset's capital changes: a
differential property test checks that every programme's assessable capital
equals the same holdings entered as one corporate_wealth.

Review nits: the growth-factor sentence now matches uprating_indices.yaml
exactly; the variable docs cite the DMG for the legacy benefits, the expert
valuation of unquoted shares and the quoted-share rule for investment trusts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis MaxGhenis changed the title Add share-like wealth component inputs Itemise corporate_wealth's share-like components for the means tests Oct 4, 2026
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

VERDICT: APPROVE WITH NITS

I couldn't run any commands here: there was no shell, so I didn't run gh pr view, git diff origin/main...HEAD, the merge-commit diff or the uv run pytest … command. I didn't read the two CLAUDE.md files either, because the search for them timed out. The tests have not been run, so I can't say whether they pass. The review below comes from reading the files at HEAD, the ADM H1 PDF and policyengine-uk-data PR #501. Run the pytest command before merging.

What checks out

  • ADM H1665: it falls under "Stocks and shares quoted on the London Stock Exchange" and says to "deduct 10% costs of sale". The docstring's "price less 10% for the expenses of sale" is accurate.

  • ADM H1673–H1674: value is the bid price × units, and "there are no costs of sale" because people withdraw through the manager. The docstring's paraphrase is accurate.

  • WAS mapping in Benefit unit rent fails to calculate from individual rent #501's docs/imputations.md:

    • directly_held_shares = DVFShUKVR8 + DVFESHARESR8 (UK shares plus employee shares and options).
    • unit_and_investment_trusts = DVFCollVR8 ("one survey question").
    • stocks_and_shares_isa = DVIISAVR8.
    • corporate_wealth is "the exact sum of its three components".
    • Overseas shares (DVFShOSVR8) are unmapped.

    This all matches the docstrings.

  • Uprating yaml (policyengine_uk/data/uprating_indices.yaml:92,94,116,118): each of the four variables appears exactly once, under yoy_growth.obr.per_capita.gdp, in alphabetical order. That matches corporate_wealth and savings, and the classes' uprating attribute matches corporate_wealth's. Being listed exactly once matters because apply_single_year_uprating (policyengine_uk/data/economic_assumptions.py:109-116) would apply the growth twice for a variable listed under two indices.

  • Merge commit: the main entries are still there (pension_credit_reported_capital, lifetime_isa_balance, household_lifetime_isa_balance) and there are no conflict markers under policyengine_uk/. That's all I could confirm without git.

  • Would the tests catch wrong uprating? Yes. test_components_match_corporate_wealth fails if a component is:

    • missing from the yaml ([] != [gdp]),
    • filed under a different index,
    • listed twice (a two-element list), or
    • given a class uprating that differs from corporate_wealth's.

    test_cash_isa_is_uprated_with_savings does the same for cash_isa. test_components_are_not_part_of_total_wealth guards against counting the components twice.

  • Naming: nothing collides with directly_held_shares or unit_and_investment_trusts. Putting them in variables/input/ alongside corporate_wealth fits, and the lowercase labels match corporate_wealth.

Findings

  1. docs/book/assumptions/growthfactors.md:63 (nit): the per-capita GDP sentence doesn't match the yaml list exactly. It is missing pension_credit_reported_capital (yaml line 109, from Tighten the Pension Credit reported-capital docs and properties (review of #2018) #2070). This probably came in from main rather than this PR, but this PR rewrites that sentence. Fix: add `pension_credit_reported_capital` between owned_land and pension_income.

  2. policyengine_uk/variables/input/directly_held_shares.py:10 and unit_and_investment_trusts.py:8 (nit): "The Enhanced FRS imputes it" is in the present tense, but uk-data Benefit unit rent fails to calculate from individual rent #501 is still a draft. Fix: say "is to impute (policyengine-uk-data Benefit unit rent fails to calculate from individual rent #501)", or merge Benefit unit rent fails to calculate from individual rent #501 first.

  3. directly_held_shares.py:12-14 (nit): "for the means tests" is too broad. ADM H1 covers Universal Credit only (H1001: "This Chapter gives guidance on capital and its effect on UC"); Pension Credit and legacy benefits are covered by the DMG. Also, the variable includes unlisted shares, which H1679 says need an expert valuation rather than the 10% rule. Fix: "Quoted shares are valued for Universal Credit at their price less 10% for the expenses of sale (ADM H1665); unquoted shares need an expert valuation (ADM H1679)."

  4. directly_held_shares.py:8 (nit): "shares in UK companies, listed or not" isn't supported by Benefit unit rent fails to calculate from individual rent #501's docs, which only say "UK shares". Fix: drop "listed or not", or cite the WAS questionnaire wording.

  5. unit_and_investment_trusts.py:10-13 (nit): the docstring explains only how unit trusts are valued. Investment trusts are listed companies, so they fall under H1665 (less 10%), not H1673–H1674. Also, H1673 refers to the "bid price". Fix: "Unit trusts are valued at the bid price with no deduction for costs of sale (ADM H1673-H1674); investment trusts are quoted shares, valued less 10% (ADM H1665)."

  6. policyengine_uk/variables/input/corporate_wealth.py:6-13 (nit): the new definition (three components, exact sum) describes datasets built with Benefit unit rent fails to calculate from individual rent #501. Datasets published now may also fold overseas shares, gilts, bonds and insurance products into corporate_wealth. Fix: add "Older datasets may also include overseas shares, gilts, bonds and insurance products."

  7. directly_held_shares.py:22 and unit_and_investment_trusts.py:21 (nit): default_value = 0 does nothing, since 0 is already the default for a float. It also differs from corporate_wealth, cash_isa and stocks_and_shares_isa, which leave it out, though some other inputs in this folder (e.g. private_pension_wealth, lifetime_isa_balance) do set it. Fix: remove it, or leave it; either works.

  8. changelog.d/wealth-share-components.added.md:1 (nit): unlike the other fragments (including add-isa-balance-variables.added.md), it doesn't start with "- ". Fix: add the leading dash.

  9. policyengine_uk/variables/household/wealth/{cash_isa,stocks_and_shares_isa}.py (nit, from Add cash ISA and stocks and shares ISA holdings variables #1791): these ISA variables live in household/wealth/, while the new components and corporate_wealth are in input/. Fix: if you want consistency, move them in a later PR; it doesn't need to block this one.

Out of scope, but worth a look: pension_credit_reported_capital uses default_value = -1 to mean "not supplied", and it is in the GDP uprating list. Projecting a dataset that stores that -1 would turn it into roughly -1.03 in later years, unless the consuming formula treats any negative value as unset.

@vahid-ahmadi vahid-ahmadi 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.

Thanks Max. Reviewed at head 21412b6a.

What I checked:

  • Tests. test_capital_share_components.py and test_wealth_components.py: 16 passed. DWP, family and household YAML: 1,040 passed. CI is green and it merges cleanly into current main.
  • The residual. unitemised_corporate_wealth = max(0, cw − Σ components). On an unsplit household the capital totals match main exactly, so "no impact on any dataset" is true for this PR on its own.
  • Uprating. Each component is listed once under per-capita GDP, the same as corporate_wealth, and the growthfactors.md sentence now matches the YAML list.
  • No other readers. Nothing else reads corporate_wealth in a means test: only total_wealth, corporate_sector_wealth and the residual do.

Findings:

  1. Blocking (merge order). This PR must not end up on main alongside #1969 without #2131.

    • #1969's sale_expenses.sources names corporate_wealth, which this PR removes from every capital.sources list, so the 10% on shares silently disappears.
    • On a #1969 + #1974 tree, savings £5,000 + corporate_wealth £12,000 gives £17,000 again for UC, HB and IS, instead of £15,800. Seven of #1969's YAML cases fail.
    • The "Impact: none on any dataset" claim holds only while #1969 is unmerged. Please say so in the body.
    • Safe orders: merge this PR before #1969 and then land #1969 and #2131 back to back; or skip merging it separately and let #2131 (which contains it) carry it after #1969. If you take the second route, close this PR as superseded.
  2. Nit. variables/input/directly_held_shares.py:8-9. "listed or not" includes unquoted shares. They need an expert valuation (ADM H1679-H1681), not the H1665 price formula. The docstring already says so. It's worth a line noting that, under #2131, they still take the 10%, because selling them needs a solicitor or accountant (H1605). That reading is also the one #1965's owner-manager holding relies on.

MaxGhenis and others added 2 commits October 12, 2026 09:59
# Conflicts:
#	docs/book/assumptions/growthfactors.md
#	policyengine_uk/data/uprating_indices.yaml
The independent review of 21412b6 found that reconstructing
corporate_wealth as components plus a floored residual could tip a household
across the capital limit on float32 rounding (components summing to
16,000.0009 against a supplied 16,000), and changed unsplit inputs with a
negative corporate_wealth. The six capital lists now name one formula
variable, share_like_capital: corporate_wealth as supplied, unless the
itemised components add up to more than it beyond a rounding tolerance
(1e-5 relative plus a penny), in which case their sum. Every dataset that
carries only corporate_wealth, or builds it as the components' sum, counts
exactly corporate_wealth; a household given only components has them
counted.

Tests: a differential against the original corporate_wealth lists on
identical inputs (unsplit with negative and fractional aggregates, and
consistent splits), annual and monthly, asserting capital and UC
eligibility equal exactly; the float32 limit case and the negative
aggregate; and the block's definition as a property. Replacing the formula
with the old floored residual fails the float32 test.

Also: the pension-age HB home disregard is Schedule 6 paragraph 26 of SI
2006/214, not paragraph 1 (review finding, predates this PR).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis MaxGhenis added the ci-recheck Re-triggers Fix evidence without changing the PR (UK hub, 2026-10-12) label Oct 12, 2026
MaxGhenis added a commit that referenced this pull request Oct 12, 2026
#1974 replaced the components-plus-residual design with one share_like_capital
block that keeps the supplied corporate_wealth exactly. This follow-up now
splits that block in two:

- share_like_capital_with_sale_expenses: share_like_capital less the itemised
  holdings in gov.dwp.share_like_capital.without_sale_expenses (unit and
  investment trusts, stocks and shares ISAs), floored at nil; all of it when
  nothing is itemised.
- share_like_capital_without_sale_expenses: the rest, exactly nil when nothing
  is itemised.

The six capital lists name both parts and the six sale-expense lists the
first, so datasets that carry only corporate_wealth keep #1969's flat 10%
exactly (differential test, annual and monthly, against the lists before
itemising, including negative aggregates and a value whose 90% is a fraction
of a penny over the limit). The exempt holdings are a parameter list, so the
methodology call on investment trusts and ISAs (d1396) is a one-line change.

Merged the refreshed #1969 (54ed063) and #1974 (7784193). #1969's
property oracle reads the share-like sources from an unitemised household's
corporate_wealth; the split invariants (definition of both parts, the
unitemised differential, split effect = 10% of trusts plus ISAs on UC
capital, components-only valuation) are in test_capital_share_components.
Dropping the nothing-itemised branch from the formula fails the differential.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 3 commits October 13, 2026 08:25
Round-3 review of 7784193: with no corporate_wealth, a penny of shares fell
inside the absolute tolerance and vanished, so UC eligibility at the £16,000
limit differed from the same penny entered as corporate_wealth; and 1e-5
relative plus a penny (£0.17 at £16,000, about 174 units in the last place)
hid genuine excesses. The tolerance is now 1e-6 of the larger of
corporate_wealth and the components' sum, against a float32 bound of about
3e-7 for a float64 sum of three components rounded and re-added in float32,
and it is relative only, so a household with no corporate_wealth has any
positive components counted in full.

Tests: the component-winning case compares exactly against the engine's
float32 sum; new independently specified cases for a component penny at the
limit (same capital and UC eligibility as the penny entered as
corporate_wealth, annually and monthly) and a genuine £0.10 excess; both fail
under the old tolerance. The six list descriptions and the changelog state
the qualified rule; the module claims are narrowed to UC eligibility and
uprating metadata.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round-4 review of f64da77 (APPROVE WITH NITS):
- N2: the tests pinned the tolerance from above only. A new case uses
  pence-valued components (£16,015.98 + £286.98 + £81.05) whose float32 sum
  lands one unit in the last place (£0.00195) above £16,384.01: more than 1e-7
  of the value, less than 1e-6. It exposed that comparing
  itemised > corporate_wealth + tolerance in float32 rounds the tolerance up
  to a whole unit; the formula now compares the difference, which float32
  computes exactly for close values, and a 1e-7 tolerance now fails the case.
  Property-test components are drawn as pounds and pence.
- N4: the differential now also runs a projected year (2027, annual and
  monthly), with the original lists restored for 2026-2030.
- N1: the error-bound comment states it assumes float64 dataset tables.
- N5: stocks_and_shares_isa says it is part of corporate_wealth.

N3 (a microcosm export where corporate_wealth falls below the ISA) does not
apply to microcosm main: corporate_wealth there is corporate_wealth_excl_isa
plus the ISA, both drawn non-negative.

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

Copy link
Copy Markdown
Collaborator Author

UK hub (2026-10-13 16:16Z): #1965 (UC reg 77) merged as de511929d. The stack order is now #1974 → #1969 → #2131 → the uk-data lock bump. The next step here is to merge main and cap household_uc_pool_company_holdings at share_like_capital, as its owner planned, then a delta review and a hand-off to the hub. Note: main's release tests are currently red for an unrelated reason (test_award_never_rises_with_a_claimants_statutory_pay; a fix is in progress), so a failing Release test 3/4 on this PR is not this PR's fault.

🤖 Generated with Claude Code

@vahid-ahmadi vahid-ahmadi 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.

Needs changes before merge. The share_like_capital block is sound, but this head fails a main test in CI, and it needs main's #1965 company-holding cap moved onto the new block.

@MaxGhenis, this is a delta review. My last review was at 21412b6a; this one is at 365f642d. Since then the floored residual has become the share_like_capital block with a 1e-6 relative tolerance, and main has been merged in up to 976ec91fe. Main has since moved on to 714b1438c, which includes #1965.

Status of my earlier findings:

  • F1 (merge order with #1969): still applies. See finding 3.
  • F2 (unquoted shares): addressed. The docstring now sends unquoted shares to an expert valuation (H1679-H1681).

Findings:

  1. must. CI Test / Python 1/4 fails at this head, and this PR causes it. The failing test is tests/test_projection_horizon.py:181, test_unmapped_inputs_preserve_legacy_years_and_grow_after_2030. Main's #2376 asserts that cash_isa and stocks_and_shares_isa are unmapped inputs, which keep their legacy values through 2030. This PR lists both in data/uprating_indices.yaml (lines 96 and 123), so they are no longer in FALLBACK_INDEX_OF. I reproduced it locally on this head and on this head merged into current main. Main alone passes (13 passed).

    • Fix: drop the two names from that set. The per-index test above it then covers them.
    • Say in the body that a dataset storing ISA balances now grows them with per-capita GDP every year, where before it held them flat to 2030. Nothing on main reads either variable in a formula, so no result on main changes. "Impact: none" is still true for outputs, but not for the projected data.
  2. should. household_uc_pool_company_holdings (from #1965, now on main) caps the holdings it takes out of the pool at max_(0, corporate_wealth). That cap is in variables/gov/dwp/universal_credit/household_uc_pool_company_holdings.py:37. Once the capital lists count share_like_capital, a household that enters only components keeps the holding in its capital.

    • Probe on this head merged into main: a single UC claimant who stands as sole owner of a trading company, with a £10,000 holding, £5,000 savings and directly_held_shares of £10,000 (no corporate_wealth). UC capital is £15,000, and the pool holding is 0. The holding is disregarded under reg 77(2), so the capital should be £5,000.
    • The same household with corporate_wealth of £10,000 gives £5,000, as expected.
    • Fix: cap at share_like_capital, as the hub note says is planned. Add this case as a test, so the cap can't drift from the list again.
  3. should (merge order, still live). At these heads, #1969 + #1974 without #2131 drops the 10% on shares, as before.

    • On a #1969 + #1974 tree, £5,000 savings + corporate_wealth £12,000 gives £17,000 for UC and IS, at ages 40 and 70, and for PC at 70. #1969 alone gives £15,800.
    • The reason: #1969's sale_expenses.sources still names corporate_wealth, which this PR takes out of every capital.sources.
    • The hub's order (#1974, then #1969, then #2131) is safe only if the three land back to back, with #2131 retargeted to main and green before the first merge. Landing #2131 alone, after retargeting (it contains both), leaves no gap.
  4. nit. share_like_capital makes components count when corporate_wealth is absent, but total_wealth still adds only corporate_wealth. A household given only components has share-like means-test capital and zero corporate wealth in total_wealth. That's fine for now, since no dataset does this. A line in the share_like_capital docstring would stop someone relying on total_wealth for it.

What I checked:

  • Guidance. ADM H1656, H1665 and H1673-H1674 against the docstrings, from the current ADM H1 PDF. I also confirmed that HB (SPC) Regs 2006 Sch 6 para 26 is the home disregard ("The dwelling occupied as the home"), so the citation fix is right.
  • Block definition. Read share_like_capital.py against the body. The comparison is on the difference, so it doesn't round the aggregate up.
  • Probes. The ones above. I also ran an unsplit corporate_wealth household on this head merged into main, and it matches main exactly.
  • Tests. I ran the capital, share-component, wealth-component, stock-period, projection-horizon, PC reported-capital and savings-credit-only property files, code_health, and #1965's two company-owner files. I also ran the YAML under gov/dwp, finance/benefit, household/wealth and gov/local_authorities.
    • At this head: 1 failed (finding 1) and 2,333 passed; YAML 1,865 passed.
    • Merged into current main: 1 failed (the same) and 2,381 passed; YAML 1,964 passed. Current main 714b1438c on the same set: all 2,363 pass, and YAML 1,964 pass.
  • Lint. ruff check and ruff format --check on the 7 changed Python files: clean.
  • CI at 365f642d. Every check passes except Test / Python 1/4, which fails on finding 1 only (1 failed, 1,417 passed).
  • Overlap with other open PRs. #2382 also edits uprating_indices.yaml and growthfactors.md, but in other sections (the CPI list).
  • Not checked. Any dataset run. The WAS mapping in uk-data #501.

🤖 Generated with Claude Code

@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

UK hub takeover for the Budget fix sprint (Max, 21 Oct). The owner session local_b8fb4700 has been idle since mid-October. The hub woke it on 21 Oct at 23:58Z, and it did not start. To land this before the Budget model locks (the first pe-uk release cut by about 12:00Z Thu 22 Oct), the hub has taken it over as build 20261021-211254-sprint-1974-2131, which merges main, re-tests, pushes as a fast-forward and marks it ready. The hub then reviews it and lands it through the merge queue.

If the owner session wakes, please don't push to this branch; comment here instead, so there's only one writer.

🤖 Generated with Claude Code

@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

UK hub: takeover withdrawn. The owner session has been woken (22 Oct 01:25Z) and is finishing this PR itself, so the hub's build is cancelled. One writer: the owner session.

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits October 21, 2026 21:46
Main moved UC capital to a financial pool (sources) and a property path
(property_sources). Keep main's structure with share_like_capital in place of
corporate_wealth, and budget reg 77 holdings against share_like_capital, the
capital the pool counts (HOLDINGS_SOURCE).

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

Copy link
Copy Markdown
Contributor

Aggregate impact on certified data: pass

Base a8b2848c13 against head 95d1f8005f, 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; components the model counts twice; 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 is never negative (sign:vat): 2025-26: 76 units below zero, a weighted £-54,797,972 in total; 2026-27: 76 units below zero, a weighted £-56,271,349 in total; 2027-28: 76 units below zero, a weighted £-57,609,146 in total
Counted-once check

Each of 84 components was raised by £1 per unit in 2026-27, and every head of its totals was read household by household: its own head must rise by exactly £1, no other tax head may move, no other benefit may rise, and the model's own total must move by what its heads moved. A head that breaks a rule is measured again twice: with every other head held at its baseline value, and with every other head held at its value after the first £1 while the component rises again. It fails where either still shows the response. 0 failed; 0 were skipped.

Provenance
Side Commit Runtime Environment hash Partition hash Time
base main a8b2848c13 uk 2.146.0 (files 2365d367e734), core 3.33.2, py 3.13.16 aarch64 2fbc53dd45f8 f210f83d5be4 1550.63s
head #1974 merged into main 95d1f8005f uk 2.146.0 (files 93854b87043e), core 3.33.2, py 3.13.16 aarch64 2fbc53dd45f8 f210f83d5be4 1517.05s

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
Income tax £299.29bn £319.02bn £336.48bn £351.64bn £367.18bn £385.30bn £403.61bn
National Insurance (employee and self-employed) £52.58bn £54.94bn £56.75bn £58.48bn £60.62bn £62.57bn £65.03bn
National Insurance (employer) £152.64bn £159.18bn £164.14bn £168.73bn £176.11bn £181.72bn £188.90bn
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.62bn £13.06bn £13.55bn £14.04bn £14.54bn £15.09bn £15.65bn
Student loan repayments £7.83bn £8.12bn £8.35bn £8.56bn £8.79bn £9.04bn £9.38bn
VAT £253.71bn £260.53bn £266.72bn £273.14bn £279.83bn £286.71bn £293.71bn
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 £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Fuel duties £24.53bn £24.64bn £26.64bn £26.69bn £26.42bn £25.76bn £26.35bn
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
TV licence fee £4.28bn £4.43bn £4.44bn £4.46bn £4.48bn £4.51bn £4.53bn
Council tax £49.56bn £52.45bn £55.78bn £59.39bn £62.98bn £66.74bn £70.70bn
High value council tax surcharge £0.00bn £0.00bn £0.00bn £0.48bn £0.49bn £0.51bn £0.52bn
Northern Ireland domestic rates £0.55bn £0.58bn £0.61bn £0.63bn £0.67bn £0.70bn £0.73bn
Business rates £31.73bn £31.73bn £31.73bn £31.73bn £31.73bn £31.73bn £31.73bn
Stamp duty land tax (own purchases, and corporate until the £10.06bn £10.56bn £10.98bn £11.40bn £11.81bn £12.23bn £12.64bn
Land transaction tax (Wales) £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Land and buildings transaction tax (Scotland) £0.01bn £0.01bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn
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
State Pension £122.15bn £126.64bn £127.57bn £129.47bn £133.29bn £137.24bn £141.32bn
Pension Credit £6.68bn £7.07bn £7.06bn £7.13bn £7.26bn £7.39bn £7.49bn
Winter Fuel Payment £1.70bn £1.68bn £1.59bn £1.58bn £1.57bn £1.55bn £1.52bn
Pension Age Winter Heating Payment (Scotland) £0.19bn £0.20bn £0.20bn £0.21bn £0.21bn £0.22bn £0.22bn
Universal Credit £78.78bn £83.08bn £84.89bn £86.65bn £87.58bn £88.99bn £90.32bn
Housing Benefit £8.59bn £8.93bn £9.19bn £9.34bn £9.61bn £9.87bn £10.15bn
Council Tax Reduction £1.99bn £2.03bn £2.08bn £2.13bn £2.18bn £2.23bn £2.29bn
Employment and Support Allowance (income-related) £2.12bn £2.18bn £2.23bn £2.28bn £2.33bn £2.39bn £2.45bn
Employment and Support Allowance (contribution-based) £5.22bn £5.36bn £5.49bn £5.62bn £5.76bn £5.90bn £6.04bn
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.30bn £0.31bn £0.32bn £0.33bn £0.33bn
Income Support £0.12bn £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 £27.05bn £28.19bn £28.94bn £29.64bn £30.37bn £31.11bn £31.87bn
Disability Living Allowance £7.52bn £7.83bn £8.04bn £8.24bn £8.44bn £8.65bn £8.86bn
Attendance Allowance £8.71bn £9.08bn £9.32bn £9.55bn £9.78bn £10.02bn £10.26bn
Carer's Allowance £4.50bn £4.69bn £4.81bn £4.93bn £5.05bn £5.17bn £5.29bn
Carer Support Payment (Scotland) £0.35bn £0.37bn £0.38bn £0.39bn £0.40bn £0.41bn £0.41bn
Carer's Allowance Supplement (Scotland) £0.00bn £0.05bn £0.05bn £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.51bn £0.52bn £0.53bn £0.54bn £0.56bn £0.57bn £0.59bn
Armed Forces Compensation Scheme £0.61bn £0.62bn £0.64bn £0.65bn £0.67bn £0.69bn £0.70bn
War Widow's or Widower's Pension £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Child Benefit £16.23bn £16.91bn £17.32bn £17.73bn £18.16bn £18.60bn £19.04bn
Scottish Child Payment £0.50bn £0.52bn £0.53bn £0.55bn £0.56bn £0.57bn £0.58bn
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.32bn £3.41bn £3.49bn £3.57bn £3.66bn £3.75bn £3.84bn
Statutory Paternity Pay £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn £0.00bn
Statutory Sick Pay £1.13bn £1.16bn £1.19bn £1.22bn £1.25bn £1.28bn £1.31bn
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.70bn £0.72bn £0.74bn £0.76bn £0.78bn
Tax-Free Childcare £0.55bn £0.56bn £0.57bn £0.58bn £0.60bn £0.61bn £0.59bn
Extended childcare entitlement £2.66bn £3.14bn £3.23bn £3.30bn £3.38bn £3.47bn £3.55bn
Universal childcare entitlement £3.32bn £3.59bn £3.69bn £3.78bn £3.88bn £3.99bn £4.08bn
Targeted childcare entitlement £0.57bn £0.60bn £0.62bn £0.63bn £0.65bn £0.66bn £0.67bn
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 £204.68bn £205.46bn £206.22bn £207.04bn £207.96bn £208.89bn £209.79bn
State education spending £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn £0.02bn
Transport subsidy spending £25.15bn £25.25bn £25.34bn £25.44bn £25.55bn £25.67bn £25.78bn
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
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
State Pension 11,308k 11,208k 10,906k 10,812k 10,860k 10,908k 10,955k
Pension Credit 1,491k 1,489k 1,478k 1,459k 1,459k 1,457k 1,453k
Winter Fuel Payment 7,092k 7,011k 6,601k 6,517k 6,493k 6,381k 6,254k
Pension Age Winter Heating Payment (Scotland) 806k 807k 807k 805k 809k 812k 816k
Universal Credit 6,371k 6,379k 6,409k 6,420k 6,395k 6,387k 6,371k
Housing Benefit 1,254k 1,252k 1,254k 1,240k 1,245k 1,247k 1,249k
Council Tax Reduction 3,186k 3,211k 3,224k 3,245k 3,264k 3,280k 3,293k
Employment and Support Allowance (income-related) 187k 188k 188k 189k 190k 190k 191k
Employment and Support Allowance (contribution-based) 722k 724k 727k 730k 733k 737k 740k
Jobseeker's Allowance (income-based) 0k 0k 0k 0k 0k 0k 0k
Jobseeker's Allowance (contribution-based) 67k 67k 68k 68k 68k 69k 69k
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,581k 3,595k 3,608k 3,623k 3,639k 3,655k 3,671k
Disability Living Allowance 1,296k 1,301k 1,306k 1,311k 1,317k 1,323k 1,329k
Attendance Allowance 1,701k 1,708k 1,714k 1,721k 1,729k 1,736k 1,744k
Carer's Allowance 1,038k 1,042k 1,046k 1,050k 1,055k 1,059k 1,064k
Carer Support Payment (Scotland) 81k 82k 82k 82k 83k 83k 83k
Carer's Allowance Supplement (Scotland) 0k 82k 82k 82k 83k 83k 83k
Severe Disablement Allowance 4k 4k 4k 4k 4k 4k 4k
Industrial Injuries Disablement Benefit 137k 138k 139k 139k 140k 140k 141k
Armed Forces Compensation Scheme 94k 94k 94k 95k 95k 95k 96k
War Widow's or Widower's Pension 0k 0k 0k 0k 0k 0k 0k
Child Benefit 8,136k 8,165k 8,185k 8,215k 8,249k 8,282k 8,314k
Scottish Child Payment 352k 354k 355k 356k 357k 358k 357k
Two Child Limit Payment (Scotland, counterfactual) 0k 0k 0k 0k 0k 0k 0k
Healthy Start vouchers 200k 201k 202k 203k 204k 204k 205k
Maternity Allowance 0k 0k 0k 0k 0k 0k 0k
Statutory Maternity Pay 334k 335k 336k 338k 339k 341k 342k
Statutory Paternity Pay 0k 0k 0k 0k 0k 0k 0k
Statutory Sick Pay 182k 183k 183k 184k 185k 186k 186k
Sure Start Maternity Grant 26k 26k 26k 27k 27k 27k 27k
Bereavement Support Payment 98k 98k 99k 99k 100k 100k 101k
Tax-Free Childcare 1,017k 1,028k 1,028k 1,033k 1,034k 1,038k 1,023k
Extended childcare entitlement 665k 671k 675k 677k 682k 686k 687k
Universal childcare entitlement 986k 989k 992k 997k 1,002k 1,013k 1,015k
Targeted childcare entitlement 119k 120k 120k 121k 121k 122k 121k
Care to Learn 0k 0k 0k 0k 0k 0k 0k
Childcare Grant 0k 0k 0k 0k 0k 0k 0k
Parents' Learning Allowance 98k 99k 99k 100k 100k 100k 101k
Adult Dependants' Grant 18k 18k 12k 13k 13k 13k 13k
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
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 £899.39bn £939.26bn £976.20bn £1,009.40bn £1,045.68bn £1,082.62bn £1,123.50bn
Government spending £536.25bn † £550.47bn † £556.59bn † £563.63bn † £572.17bn † £581.40bn † £590.57bn †
Government balance, each receipt once £363.14bn † £388.78bn † £419.61bn † £445.76bn † £473.50bn † £501.22bn † £532.92bn †
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 £902.52bn £942.39bn £979.33bn £1,012.53bn £1,048.81bn £1,085.75bn £1,126.63bn
household_tax £496.17bn £522.67bn £548.46bn £570.65bn £592.86bn £617.31bn £644.02bn
pre_budget_change_household_tax £488.35bn £514.55bn £540.11bn £561.61bn £583.58bn £607.77bn £634.13bn
gov_spending £536.25bn † £550.47bn † £556.59bn † £563.63bn † £572.17bn † £581.40bn † £590.57bn †
household_benefits £536.25bn † £550.47bn † £556.59bn † £563.63bn † £572.17bn † £581.40bn † £590.57bn †
gov_balance £366.27bn † £391.91bn † £422.74bn † £448.89bn † £476.63bn † £504.35bn † £536.05bn †
Components the model counts twice: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
corporate_sdlt, counted again inside expected_sdlt £3.13bn £3.13bn £3.13bn £3.13bn £3.13bn £3.13bn £3.13bn
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,657.06bn £1,719.25bn £1,771.68bn £1,821.99bn £1,872.88bn £1,930.99bn £2,001.98bn
Household net income £1,657.86bn † £1,706.29bn † £1,737.92bn † £1,772.03bn † £1,808.11bn † £1,849.74bn † £1,901.49bn †
Household net income (HBAI definition) £1,466.42bn £1,513.07bn £1,545.12bn £1,578.09bn £1,612.10bn £1,651.13bn £1,701.58bn
Population: head levels
Measure (head) 2025-26 2026-27 2027-28 2028-29 2029-30 2030-31 2031-32
People 69,514k 69,778k 70,036k 70,316k 70,626k 70,944k 71,249k
Benefit units 35,247k 35,381k 35,512k 35,654k 35,811k 35,972k 36,127k
Households 31,167k 31,285k 31,401k 31,526k 31,665k 31,808k 31,944k
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 15.9% 15.0% 15.5% 15.6% 15.7% 15.8% 15.8%
Absolute poverty, before housing costs: children 23.7% 20.9% 21.6% 21.4% 21.6% 21.8% 21.7%
Absolute poverty, before housing costs: working-age adults 13.8% 13.4% 14.0% 14.1% 14.2% 14.3% 14.3%
Absolute poverty, before housing costs: pensioners 13.7% 13.2% 13.3% 13.5% 13.6% 13.7% 13.7%
Absolute poverty, after housing costs: everyone 21.1% 20.6% 21.3% 21.5% 21.9% 22.2% 22.3%
Absolute poverty, after housing costs: children 32.1% 30.3% 30.9% 31.1% 31.7% 32.3% 32.5%
Absolute poverty, after housing costs: working-age adults 19.6% 19.4% 20.2% 20.4% 20.8% 21.0% 21.0%
Absolute poverty, after housing costs: pensioners 12.8% 12.6% 12.6% 13.1% 13.4% 13.4% 13.4%
Relative poverty, before housing costs: everyone 16.9% 16.4% 16.5% 16.6% 16.6% 16.7% 16.7%
Relative poverty, before housing costs: children 25.6% 23.5% 23.2% 23.2% 23.2% 23.2% 23.2%
Relative poverty, before housing costs: working-age adults 14.4% 14.4% 14.7% 14.8% 14.9% 15.0% 15.0%
Relative poverty, before housing costs: pensioners 15.0% 14.9% 14.4% 14.7% 14.3% 14.3% 14.6%
Relative poverty, after housing costs: everyone 21.7% 21.3% 21.4% 21.5% 21.6% 21.9% 22.0%
Relative poverty, after housing costs: children 33.0% 31.6% 31.0% 31.1% 31.3% 31.6% 32.0%
Relative poverty, after housing costs: working-age adults 20.0% 20.0% 20.3% 20.4% 20.6% 20.8% 20.9%
Relative poverty, after housing costs: pensioners 13.7% 13.1% 12.8% 13.0% 12.7% 12.9% 13.2%
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,057k 10,437k 10,858k 10,961k 11,093k 11,222k 11,234k
Absolute poverty, before housing costs: children 3,560k 3,141k 3,261k 3,253k 3,298k 3,334k 3,335k
Absolute poverty, before housing costs: working-age adults 5,864k 5,737k 6,070k 6,165k 6,237k 6,315k 6,321k
Absolute poverty, before housing costs: pensioners 1,634k 1,559k 1,527k 1,543k 1,558k 1,572k 1,579k
Absolute poverty, after housing costs: everyone 14,663k 14,366k 14,907k 15,144k 15,491k 15,755k 15,874k
Absolute poverty, after housing costs: children 4,809k 4,556k 4,676k 4,719k 4,835k 4,939k 4,994k
Absolute poverty, after housing costs: working-age adults 8,333k 8,326k 8,775k 8,931k 9,122k 9,275k 9,329k
Absolute poverty, after housing costs: pensioners 1,521k 1,484k 1,456k 1,493k 1,533k 1,541k 1,551k
Relative poverty, before housing costs: everyone 11,780k 11,464k 11,541k 11,665k 11,708k 11,832k 11,879k
Relative poverty, before housing costs: children 3,840k 3,538k 3,514k 3,525k 3,542k 3,554k 3,565k
Relative poverty, before housing costs: working-age adults 6,154k 6,160k 6,365k 6,466k 6,530k 6,636k 6,628k
Relative poverty, before housing costs: pensioners 1,786k 1,766k 1,661k 1,673k 1,636k 1,643k 1,686k
Relative poverty, after housing costs: everyone 15,110k 14,866k 14,956k 15,109k 15,281k 15,513k 15,696k
Relative poverty, after housing costs: children 4,948k 4,756k 4,684k 4,715k 4,771k 4,840k 4,916k
Relative poverty, after housing costs: working-age adults 8,531k 8,560k 8,793k 8,917k 9,054k 9,187k 9,257k
Relative poverty, after housing costs: pensioners 1,631k 1,551k 1,479k 1,477k 1,456k 1,486k 1,523k
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.3074 0.3052 0.3066 0.3072 0.3078 0.3084 0.3095
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,700 † £17,996 † £17,934 † £17,991 † £18,328 † £18,506 † £18,680 †
Decile 2: mean household_net_income per household £28,322 † £29,102 † £28,705 † £28,983 † £29,330 † £29,734 † £30,317 †
Decile 3: mean household_net_income per household £34,628 † £35,807 † £36,445 † £37,018 † £37,220 † £37,811 † £38,491 †
Decile 4: mean household_net_income per household £39,334 † £40,803 † £41,758 † £42,693 † £43,435 † £44,025 † £44,926 †
Decile 5: mean household_net_income per household £44,856 † £46,150 † £47,078 † £47,577 † £48,027 † £49,181 † £50,036 †
Decile 6: mean household_net_income per household £47,930 † £49,334 † £50,290 † £51,060 † £52,167 † £52,953 † £54,205 †
Decile 7: mean household_net_income per household £56,319 † £57,568 † £58,473 † £59,423 † £60,149 † £61,621 † £62,893 †
Decile 8: mean household_net_income per household £69,269 † £70,357 † £71,673 † £72,812 † £74,353 † £75,799 † £77,580 †
Decile 9: mean household_net_income per household £79,648 † £81,992 † £83,193 † £84,487 † £86,057 † £87,571 † £90,646 †
Decile 10: mean household_net_income per household £117,618 † £120,489 † £122,421 † £124,552 † £126,802 † £129,073 † £131,887 †
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.21bn £6.50bn £6.70bn £6.90bn £7.11bn £7.34bn £7.62bn
Raise higher rate by 1pp £3.54bn £3.82bn £4.05bn £4.27bn £4.49bn £4.76bn £4.96bn
Raise personal allowance by ~800GBP/year £-3.83bn £-3.96bn £-4.03bn £-4.10bn £-4.19bn £-4.29bn £-2.02bn
Raise child benefit by 25GBP/week per additional child £-2.00bn £-1.76bn £-1.64bn £-1.54bn £-1.44bn £-1.34bn £-1.24bn
Reduce Universal Credit taper rate to 20% £-20.54bn £-21.10bn £-21.45bn £-21.88bn £-21.95bn £-22.32bn £-22.79bn
Raise Class 1 main employee NICs rate to 10% £11.06bn £11.51bn £11.83bn £12.14bn £12.47bn £12.81bn £13.29bn
Raise VAT standard rate by 2pp £25.01bn £25.68bn £26.29bn £26.92bn £27.58bn £28.26bn £28.95bn
Raise additional rate by 3pp £2.83bn £3.07bn £3.27bn £3.47bn £3.67bn £3.91bn £4.23bn
Raise the Universal Credit standard allowance by 10% £-3.04bn £-3.17bn £-3.24bn £-3.32bn £-3.37bn £-3.42bn £-3.47bn
Raise the State Pension by 10% £-7.81bn £-8.07bn £-7.99bn £-8.11bn £-8.36bn £-8.66bn £-9.02bn
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.36bn £6.65bn £6.86bn £7.07bn £7.28bn £7.52bn £7.80bn
Raise higher rate by 1pp: change in income_tax £3.55bn £3.83bn £4.06bn £4.28bn £4.50bn £4.77bn £4.97bn
Raise personal allowance by ~800GBP/year: change in income_tax £-3.96bn £-4.10bn £-4.19bn £-4.26bn £-4.36bn £-4.47bn £-1.77bn
Raise child benefit by 25GBP/week per additional child: change in child_benefit £2.34bn £2.15bn £2.03bn £1.92bn £1.82bn £1.71bn £1.59bn
Reduce Universal Credit taper rate to 20%: change in universal_credit £20.53bn £21.09bn £21.44bn £21.86bn £21.95bn £22.32bn £22.78bn
Raise Class 1 main employee NICs rate to 10%: change in national_insurance £11.33bn £11.79bn £12.13bn £12.44bn £12.78bn £13.13bn £13.62bn
Raise VAT standard rate by 2pp: change in vat £25.01bn £25.68bn £26.29bn £26.92bn £27.58bn £28.26bn £28.95bn
Raise additional rate by 3pp: change in income_tax £2.83bn £3.07bn £3.27bn £3.47bn £3.67bn £3.91bn £4.23bn
Raise the Universal Credit standard allowance by 10%: change in universal_credit £3.04bn £3.17bn £3.25bn £3.32bn £3.37bn £3.43bn £3.47bn
Raise the State Pension by 10%: change in state_pension £10.72bn £11.23bn £11.37bn £11.62bn £12.08bn £12.54bn £13.07bn

MaxGhenis added a commit that referenced this pull request Oct 22, 2026
Reg 77 holdings are shares with sale expenses, so HOLDINGS_SOURCE is
share_like_capital_with_sale_expenses: the holdings budget and the 90%
counted share both read it, where #1969 read corporate_wealth. The test file
keeps the round-3 fixes (projected years, the 16,000 overshoot, the
power-of-two precondition, the grouping example, the exempt list's start).

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

Copy link
Copy Markdown
Collaborator Author

Hand-off to the UK hub (owner session local_b8fb4700). Head 6600477, with main a8b2848 merged.

CI: all 17 checks pass, and the PR is MERGEABLE and CLEAN.

Aggregate impact: 0 broken, 0 flagged, and no household's net income changes in any year. Quoted in the body: run.

Local results at 6600477:

  • The capital and company pytest files (share components, wealth components, stock periods, the UC company owner, override and Axiom-parity tests, UC property capital, reported capital, Lifetime ISA): 146 passed.
  • YAML for the benefit families, gov/dwp, household/wealth and gov/local_authorities: 2,281 passed.
  • code_health: 2,532 passed.
  • ruff: clean.
  • Mutation check: budgeting reg 77 holdings against corporate_wealth makes the itemised-holding test fail; the pool takes out £0 instead of £30,000.

What the merge with main changed. Main moved UC capital to a financial pool (sources) and a property path (property_sources). #1974 keeps that structure, with share_like_capital in place of corporate_wealth. Main's three reg 77 budget readers now read HOLDINGS_SOURCE = "share_like_capital", so the budget is the capital the pool counts.

Review. Round 5 at 365f642 was APPROVE WITH NITS. The hub's independent review is owed at 6600477, because the merge changed the budget readers and UC's sources list.

Depends on: nothing. Merge order: this PR, then #1969, then #2131.

Follow-ups: PolicyEngine/policyengine-uk-data#501 bumps its lock to a release carrying this PR and #1969. It lands in the d833 batch.

🤖 Generated with Claude Code

@MaxGhenis
MaxGhenis added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit a8a649d Oct 22, 2026
19 checks passed
@MaxGhenis
MaxGhenis deleted the wealth-share-components branch October 22, 2026 13:16
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Merged by the UK hub (session local_8e19f9d5, Opus 5.5) on its merge gates (hand-off line 92, owner local_b8fb4700). One merge-queue enqueue, pinned to head 660047702c375ab86f1e3f1cad34a0ec786e1e45.

  • Change: corporate_wealth is split into its share-like components for the means tests. UC's capital pool reads share_like_capital, and the three reg 77 budget readers share that same nonnegative budget (UC Regs 2013 regs 46, 48, 49, 77).
  • Reviews: round 5 APPROVE WITH NITS at 365f642d8; GPT-6.1 Sol delta review APPROVE at this exact head (~/reviews/uk-hub/reviews/uk1974/REVIEW_1974.md). No UC formula still reads corporate_wealth directly, and savings and share-like holdings are counted once.
  • Nonblocking follow-ups: data/uprating_indices.yaml:145,150 repeat cash_isa and stocks_and_shares_isa (same index, no compounding); docs/book/assumptions/growthfactors.md:65 is stale on capital gains and residential property.
  • Impact: none (Aggregate impact: 0 broken, 0 flagged, no household net income change in any year).
  • Order: this PR, then Value means-tested capital less 10% sale expenses and secured debt (UC reg 49 and legacy equivalents) #1969 (its owner applies the corporate_wealth → share_like_capital rename checklist), then Value share-like capital by asset type for the sale-expense deduction #2131.

🤖 Generated with Claude Code

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

Labels

ci-recheck Re-triggers Fix evidence without changing the PR (UK hub, 2026-10-12)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants