Repository navigation
Conversation
UC Regs 2013 reg. 49(1) and its legacy equivalents (HB Regs 2006 reg. 47, HB (SPC) Regs 2006 reg. 45, IS Regs 1987 reg. 49, JSA Regs 1996 reg. 111, ESA Regs 2008 reg. 113, SPC Regs 2002 reg. 19) calculate capital at market or surrender value less 10% where a sale would incur expenses and less any encumbrance secured on it. The model summed every capital source at full value. Each programme gets a capital.sale_expenses node (rate 0.1 and the sources whose sale incurs expenses: land, property and corporate_wealth, not cash savings). A shared helper applies the 10% before the encumbrance and never values an encumbered asset below nil. New household inputs carry debt secured on other residential property, non-residential property and land; the Enhanced FRS has none, so they default to zero. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Each programme's sale-expense parameters now cite its GB and NI regulations, the guidance paragraph that orders the 10% before the encumbrance, and per-asset guidance with exact PDF page anchors (DMG ch 52 for ESA, HBGM Annex F for HB savings, H1656/H1665/H1673-H1675 for UC). - The source-list descriptions and changelog say plainly that corporate_wealth also holds unit trusts, ISAs, employee share options and, in datasets built before the pension split, non-DB pension wealth. - The helper and secured-debt docs say debts net within a source category. - The property-test oracle fixes the sale-expense assets and the 10% itself instead of reading the parameters, checks pension-age HB, and runs on larger populations. New hand-computed cases cover HB second homes and premises, per-source flooring between two encumbered sources, pension-age HB, and a monthly secured debt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HBGM W1.430 states only the general 10% rule; the rule that real property always has costs of sale comes from R(IS) 21/93, as DMG 29622 applies it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Coordination note from #1950 (UC capital income). If this PR lands after #1950, please update the "reg 49 is #1960" notes #1950 adds:
Those cases enter £10,000 of property as capital. At 90% that is 12 tariff steps, not 16, so either update the expected values or restate the input as a property value of £11,111.11. If #1950 lands second, I'll do it on rebase. #1950's merge is waiting on a decision from Max (over the £1bn/yr line). 🤖 Generated with Claude Code |
|
#1950 merged (770bc06), so this PR is second. Please update the "reg 49 is #1960" notes:
At 90% valuation, £10,000 of property is 12 tariff steps, not 16, so either update the expected values or enter £11,111.11. 🤖 Generated with Claude Code |
Resolve the capital variables onto main's claimant/partner allocation and person-level sources, valuing household sources through valued_capital. Update the let-property tests #1950 added: £10,000 of property is now £9,000 of capital under reg. 49(1), so 12 tariff steps, not 16. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two tests main added after this branch's earlier base assumed full-value capital: - The CTR savings-credit-only case holds savings 10,000 and a 7,000 second property: capital is 10,000 + 90% of 7,000 = 16,300 (SPC Regs 2002 reg. 19(a)), still over the 16,000 limit, so CTR stays nil. Deemed income is 13 steps x 52 = 676, Pension Credit income 13,314 + 676 = 13,990, and savings credit 60% of (12,376 - 208.07 x 52) less 40% of (13,990 - 12,376) = 288.22. - The reported-capital property test's proxy oracle now values land, property and shares at 90% and savings at face value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
|
Stacked follow-up: #2131 lists only 🤖 Generated with Claude Code |
vahid-ahmadi
left a comment
There was a problem hiding this comment.
Thanks Max. Reviewed at head 22274cad.
The law is right in every programme. I re-fetched each provision from legislation.gov.uk:
-
UC Regs 2013 reg 49(1): "Capital is to be calculated at its current market value or surrender value less— (a) where there would be expenses attributable to sale, 10%; and (b) the amount of any encumbrances secured on it."
-
The same rule appears, in older wording ("10 per cent", "encumbrance/incumbrance"), in:
- SPC Regs 2002 reg 19;
- HB Regs 2006 reg 47 and HB (SPC) Regs 2006 reg 45;
- IS Regs 1987 reg 49;
- JSA Regs 1996 reg 111;
- ESA Regs 2008 reg 113.
The regulation numbers are right (JSA 111 and ESA 113).
-
CTR Prescribed Requirements SI 2012/2885 Sch 1 para 32 has the same 10% rule for pension-age CTR.
-
SPC Sch V has no disregard that touches share, unit trust or ISA valuation. Para 9 is business assets only.
The helper follows the guidance. valued_capital takes the 10% off gross value before the debt (ADM H1608) and floors each encumbered source on its own (H1615-H1616).
Probes all match hand arithmetic:
- savings £5,000 +
corporate_wealth£12,000 gives £15,800 for UC, HB and IS, and £15,800 for PC at 70 (main: £17,000); - a £100,000 second home with £95,000 debt plus £3,000 savings gives £3,000;
- a second home of £20,000 with £25,000 debt plus premises of £10,000 gives £9,000.
Tests:
- At the head: properties, PC reported capital and stock periods, 9 passed. DWP, family and CTR YAML, 967 passed.
- Merged into current main
b5b09231(the merge is clean; the branch is 280 commits behind): the same YAML set, 1,274 passed. But two of the new property tests fail; see finding 1. The green CI is from the stale merge ref.
Findings:
-
Blocking (rebase). On main + this branch,
test_valuation_matches_the_regulation_and_never_adds_capitalandtest_cash_only_households_have_no_deductionfail (2 failed, 8 passed). The valuation isn't the problem; the test's oracle is out of date.- Main now sets HB capital to nil for anyone on UC, IS, income-based JSA or income-related ESA (HB Regs Sch 6 para 5,
housing_benefit_on_passporting_benefit). It also switches the pension-age rules onhousing_benefit_pension_age_regulations_applyrather than age 67. - So a working-age single person with £2,441.62 of cash now has HB capital of nil (on main too), while
applies()attests/test_capital_valuation_properties.py:127still expects their savings. - I reproduced the cash-only failure directly. The other failing test uses the same
applies()predicate, so it is most likely the same cause. - Fix: rebase and key the pension-age branch on
housing_benefit_pension_age_regulations_apply. Then either skip units wherehousing_benefit_on_passporting_benefitis true, or give the generated units enough earnings to stay off UC. Skipping alone would hollow out the working-age HB branch, because the strategy's no-income singles are nearly all on UC.
- Main now sets HB capital to nil for anyone on UC, IS, income-based JSA or income-related ESA (HB Regs Sch 6 para 5,
-
Blocking (merge order with #1974). Git merges this branch and #1974 cleanly, but together they undo this PR:
- #1974 takes
corporate_wealthout of everycapital.sourceslist, while this PR'ssale_expenses.sourcesstill names it. So the 10% on shares silently stops. - On a 1969 + 1974 tree, the probe above goes back to £17,000. Seven YAML cases fail: 3 in
capital_valuation.yaml, and 2 each in the IS and ESA capital files. - #2131 fixes this. So either merge #1974 first and then this PR and #2131 together, or merge this PR and then #2131 retargeted to main (it carries #1974).
- A cheap guard in
tests/test_capital_valuation_properties.py: assertset(sale_expenses.sources) <= set(capital.sources)for each programme. The existing check at line 160 intersects both sides withcounted, so it can't see a stale name.
- #1974 takes
-
Should fix (body, disclosure). In "A few records carry each figure", the UC, PC and HB bullets, and the poverty bullet, attribute most of each year's change to one or a handful of records, with percentages and counts. Read against the impact table, that is effectively a record-level weighted amount. Keep the caveat ("driven by a handful of records; not an estimate") and drop the percentages, record counts and the median gain.
-
Nit.
parameters/gov/dwp/income_support/means_test/capital/sale_expenses/rate.yaml:9. This starts on 2010-01-01, to matchsources. IS reg 49 has applied the 10% since 11.4.1988. Add a sentence saying the date follows the sources list, as the HB and PC files already explain their own dates. -
Nit.
utils/capital_valuation.py:18-19. Netting debt across assets within a category is documented. H1616 actually allows it where one debt is secured on several assets. You could cite that, so the approximation reads as partly lawful rather than purely a data limitation. -
Coordination with #1965. Merged with #1965 there is no textual conflict, but the holding comes off at 100% while it is counted at 90%: savings £5,000 + a £10,000 holding gives £4,000 instead of £5,000. I've put the fix on #1965, since it rebases second.
Keep main's recorded benefit-unit capital (benunit_reported_capital), which replaces the valued household sources as recorded, and register the secured-debt inputs beside main's uprating entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
£20,000 of shares is £18,000 of Pension Credit capital (SPC Regs 2002 reg. 19(a)), still over the CTR limit: deemed income 16 x 52 = 832, Pension Credit income 14,146, savings credit 933.816 - 0.4 x 1,770 = 225.82. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's Housing Benefit passport (#1927 and the Universal Credit limb) gives a family on a passporting benefit nil HB capital, so the property's independent reg 47 valuation applies only to non-passported families. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Refreshed onto current main at Fleet ops' request (owner session c12c197a not running): 54ed063.
Local results on the new head:
A fresh independent review is being requested at this head. 🤖 Generated with Claude Code |
#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>
Review of 54ed063: half the generated units now decline Universal Credit, so the Housing Benefit passport cannot remove every working-age unit from the reg 47 differential, and a passported unit's HB capital is asserted to be nil rather than skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Delta review of b541677 (F1): coverage of the HB passport-nil assertion and the working-age reg 47 differential depended on Hypothesis draws. The checks move into check_valuation(), which counts the branches it ran, and a fixed test asserts both run: a UC claimant with £5,000 of savings (passported, nil HB capital) and a non-claimant with a mortgaged second home (valued under reg 47). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
vahid-ahmadi
left a comment
There was a problem hiding this comment.
Needs changes before merge. The reg 49 valuation is still right. But this head fails a main test in CI, and #1965 merged first without the rate fix I flagged last time, so on main the company holding now comes off at the wrong rate.
@MaxGhenis, this is a delta review. My last review was at 22274cad; this one is at 1efee62b. Since then:
- main was merged three times (up to
976ec91fe); - the CTR shares case was revalued under reg 19 (
f3929c19); - the HB property oracle was reworked (
54ed063b,3abaf166,1efee62b).
Main has since moved on to 714b1438c, which includes #1965.
Status of my earlier findings:
- F1 (stale HB oracle): fixed. Passported units are now asserted to have nil HB capital, and a fixed test proves both branches run. Its property tests pass at this head and on current main.
- F2 (merge order with #1974): still live. See finding 4.
- F3 (record-level figures in the body): not addressed. See finding 5.
- F4 and F5 (nits): not taken. That's fine.
- F6 (#1965): now this PR's to fix. See finding 2.
Findings:
-
must. CI
Test / Python 1/4fails at this head, and this PR causes it. The failing test istests/test_savings_credit_only_receipt_properties.py:508,test_stopping_pension_credit_payments_ends_the_pension_credit_capital_test, from main. Its case is £12,000 of savings plus £5,000 of other property, with expectations written for full value. I reproduced the failure at this head (it gets 621.816, not 601.02). It passes at976ec91fe.- Under reg 19 the property is £4,500, so PC capital is £16,500. That is still over £16,000, so the test's point (no CTR) holds.
- Deemed income is (16,500 − 10,000) / 500 = 13, so £13 a week. Income falls from £254 to £253, so the savings credit is 0.6 × 29.93 − 0.4 × (253 − 238) = £11.958 a week, or £621.82 a year. The engine agrees.
- Update the comment and two expectations: 601.02 → 621.82, and
council_tax_reduction_assessable_capital17,000 → 16,500. Thestoppedbranch is unaffected, because it uses savings only.
-
must (my F6, now on this PR).
uc_assessable_capital.pysubtracts disregarded company holdings at full value, while this PR countscorporate_wealthat 90%. That happens both inhousehold_uc_pool_company_holdings(capped at fullcorporate_wealth) and inown_holdingsagainst reported capital.-
Probes on this head merged into current main (the merge is clean): a single UC claimant who stands as sole owner of a trading company, with a £10,000 holding and no company capital.
Case This PR on main Expected savings £5,000, corporate_wealth£10,000£4,000 £5,000 savings £5,000, corporate_wealth£20,000£13,000 £5,000 + 90% × £10,000 = £14,000 Main alone gives £5,000 and £15,000. So the reg 77(2) disregard now takes £1,000 of savings with it.
-
Fix: take the holding off at the rate its source was counted at, 1 −
sale_expenses.rate, whencorporate_wealthis in UC's sale-expense list. Cap it at the valuedcorporate_wealth. Do this in the pool and, if reported capital is taken as already valued, against reported capital too. -
Main's own #1965 tests already catch it. On this head merged into main, 8 of the 26 cases in
tests/policy/baseline/finance/benefit/family/universal_credit/company/uc_company_analogous_business.yamlfail. Three properties intests/test_uc_company_owner_properties.pyfail too:test_household_capital_is_allocated_and_conserved_across_benefit_units,test_disregarded_holdings_count_for_no_benefit_unitandtest_capital_and_earnings_identities. -
Five of the eight failing YAML cases are the holding at full value: the cases at lines 114, 152, 329, 459 and 616. For example, in the property company case at line 114 (savings £2,000, a £90,000 holding, company capital £120,000), the expected £122,000 comes out as £120,000. That's 2,000 + 81,000 − 90,000, floored at nil, plus 120,000.
-
The other three (lines 210, 247 and 275) have no disregarded holding. There
corporate_wealthsimply counts at 90%, which is this PR's intended change, so they only need new expectations. -
The cases at lines 182 and 297 pass only because the nil floor absorbs the same 10% gap, so the bug reaches further than the failures show.
-
CI at this head was cut before #1965 merged, so it can't show any of this.
-
Add my two cases above as tests too.
-
-
should. There's still no guard against a sale-expense list naming a source that no capital list counts. That is exactly how #1974 silently undoes this PR.
- My earlier suggestion (
sale_expenses.sources ⊆ capital.sources) would trip on UC, whose sale list namesowned_landwhile UC capital omits it. - A version that works: assert that every name in each
sale_expenses.sourcesis in at least one programme'scapital.sources. - The current check at
tests/test_capital_valuation_properties.py:178intersects withcounted, so it can't see a stale name.
- My earlier suggestion (
-
should (merge order, still live). At these heads, a #1969 + #1974 tree gives £5,000 savings +
corporate_wealth£12,000 = £17,000 for UC and IS, and for PC at 70. This PR alone gives £15,800. Either land #2131 retargeted to main (it contains this PR and #1974) and close the other two, or land all three back to back with #2131 green first. -
should (body, my F3). "A few records carry each figure" still gives top-record shares (35-78%, 69-78%, 95-97%) and record counts. The poverty bullet still gives "56 person records in 36 benefit units", "97% of the weight" and the median gain. Read against the table, that's record-level weighted detail. Keep the caveat and drop the numbers. The table is also from before the 10/12 merges, as the body says, so mark it as superseded rather than leaving it as the PR's impact.
What I checked:
- Merge resolutions.
--remerge-diffona8857439d,503bebb4aandb5416775. The conflicts inuprating_indices.yamland theuc_assessable_capitaldocstring were resolved keeping both sides. - New CTR expectations, by hand. 18,000 capital, then (18,000 − 10,000) / 500 = 16 × £52 = £832, then 13,500 − 186 + 832 = 14,146, then 933.816 − 0.4 × 1,770 = 225.82. All match.
- Tests. I ran the capital-valuation, PC reported-capital, stock-period, share-component, wealth-component, projection-horizon and savings-credit-only property files,
code_health, and #1965's two company-owner files. I also ran the YAML undergov/dwp,finance/benefit,household/wealthandgov/local_authorities.- At this head: 1 failed (finding 1) and 2,334 passed; YAML 1,879 passed.
- Merged into current main (the merge is clean): 4 failed (finding 1, plus the three #1965 properties) and 2,379 passed; YAML 8 failed (finding 2) and 1,970 passed. Current main
714b1438con the same set: all 2,363 pass, and YAML 1,964 pass.
- Lint.
ruff checkandruff format --checkon the 13 changed Python files: clean. - CI at
1efee62b. Every check passes exceptTest / Python 1/4, which fails on finding 1 only (1 failed, 1,415 passed). - Not checked. The Enhanced FRS impact at this head; I have no data access.
🤖 Generated with Claude Code
Savings 12,000 plus a 5,000 property is 12,000 + 4,500 = 16,500 of Pension Credit capital (SPC Regs 2002 reg. 19(a)), still over the 16,000 CTR limit. Deemed income is 13 a week, so the savings credit is 0.6 x 29.93 - 0.4 x 15 = 11.958 a week (621.82). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main now values Universal Credit property on its own path (#2439: property_sources, secured debt, a 10% property_sale_cost_rate) and carries regulation 77 company substitution (#1965). Reconcile: - One UC rate: property_sale_cost_rate becomes capital.sale_expenses.rate, used by the property path and the financial pool; UC sale_expenses.sources lists the financial source with costs of sale, corporate_wealth. - The financial pool is valued with valued_capital, so shares count at 90%. - As the second of #1965 and #1969 to land, value reg. 77 holdings where they leave capital: at 90% from the pool (household_uc_pool_company_holdings), in the override floor (household_uc_reported_financial_capital) and from a complete override, which is a countable figure; in full from a recorded financial total, which is used unvalued. A shared counted_share helper. - Keep main's secured-debt inputs, documenting the legacy equivalents, and add owned_land_secured_debt in the same shape. - Tests main added that assumed full-value shares, rewritten from the law: the reg. 77 company cases, the property-capital and mock suites, the company-override conservation identities and the savings-credit-only cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Status at a7d8988 (merges main e2ea328):
The target is to land before the triple-lock re-certification (~26 Oct). The impact table in the description will be refreshed from the new runs. 🤖 Generated with Claude Code |
Aggregate impact on certified data: passBase Checks: 0 broken by this change, 1 failing on both sides, 0 fixed. Moves flagged for review: 188 (thresholds in Notes
Moves flagged for review
148 more flags are in the report artifact. Receipts by tax head
Spending by benefit and service
Caseloads
Government balance, each component counted once
Model totals
Household income totals
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
Household net income by decileMean change in household net income per household, and as a share of the decile's base income; households stay in the base run's deciles.
No change in any year: components the model counts twice; population. Checks failing on both base and head (1)
Counted-once checkEach 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
Dataset: Receipts by tax head: head levels
Spending by benefit and service: head levels
Caseloads: head levels
Government balance, each component counted once: head levels
Model totals: head levels
Components the model counts twice: head levels
Household income totals: head levels
Population: head levels
Poverty rates: head levels
People in poverty: head levels
Inequality: head levels
Mean household net income in each side's own deciles: head levels
Standard reform costings (change in gov_balance): head levels
Standard reforms: change in each reform's target: head levels
|
|
UK hub: #1995 (head 3727eb8) now depends on this PR. Its r3 finding 3, the Pension Credit savings-credit exit cliff, is Pension Credit's gross property valuation, which this PR fixes. So #1969 should land first; #1995 will then merge main and add two property boundary cases. This PR's latest review (Subfleet 20261019-181511-pr1969-review-v3b, 2026-10-20 01:43Z) requested changes, and there has been no new head since. 🤖 Generated with Claude Code |
… penny From the GPT-6.1 Sol review of a7d8988: - A recorded benunit_reported_capital total holds its owner's reg. 77 holding at full value, while the pool counts that holding at 90%. The report now consumes its known holding part at the counted 90% (household_uc_reported_financial_capital), so it can no longer eat another unit's savings; the owner's own assessment still takes the full holding off the report. The company-override conservation oracle consumes the same way, and a YAML regression pins the reviewer's case: £16,500 of savings and a £10,000 report that is all holding give [0, 16,500]. - valued_capital rounds the 90% value down to the penny in the claimant's favour (ADM H1665), in float64 with a guard against binary noise. A YAML regression puts £1,000 of savings and £16,666.67 of shares at exactly £16,000.00, which passes the limit (WRA 2012 s. 5(1)(a), UC reg. 18(1)). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inputs are float32, so 90% of £16,001 came to £14,400.8994 and the penny floor gave £14,400.89 (the HB £16,001 case failed). less_sale_expenses takes the value to the penny in float64 before the 10% deduction and rounds down to the penny (ADM H1665). The UC property path now uses it too, so every reg. 49(1)(a) valuation rounds the same way. Adds a Hypothesis property checking 90% of every whole-penny float32 input up to £131,071.99 against integer pence, and a UC YAML case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sol review v4b found that rounding the input to the penny first could round up (the ADM H1665 example gave 930.94, not 930.93), that nothing was rounded at the last stage, and that UC rounds to the nearest penny. - capital.round_down per programme: false for UC (UC Regs 2013 reg 6(1); NI reg 7(1)) and HB (HB Regs 2006 reg 80; HB (SPC) reg 61(6); NI 78(8), 59(7)); true for IS (reg 27), JSA (reg 92), ESA (reg 3(a)) and Pension Credit (reg 24A), which count a fraction only to the claimant's advantage. - exact_amount removes float32 storage error only where a value is the stored form of a whole penny; other values are kept exactly. - Each valuation, each UC holding and property share, and each programme's assessed capital (the last stage) is rounded by that rule. - A recorded financial total below its owner's holding now consumes the holding at its counted value, as an override does, so no part of the holding is left for another unit. Tests: the H1665 example (930.93 / 930.94), whole-penny properties for both rules, a never-wrong-way bound for any float32 input, UC half-penny, 16,000.00 after a holding, a three-way split, IS shared-capital rounding, and reports below the holding in the reg 77 conservation grid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merge-order note from #1974's owner. #1974 (head 6600477, main a8b2848 merged) is being finished for the Budget cut. If it lands first, this PR's next merge of main needs the following, or the 10% sale-expense deduction silently stops applying to shares. #1974 renames the share-like source in all six capital Rename in this PR.
Tests.
If this PR lands first instead, #1974 does the same reconciliation when it merges main. #2131 is then rebuilt on whichever head results. 🤖 Generated with Claude Code |
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>
Sol review v5 at b3f6f60: - exact_amount raised an underflow warning on zero inputs, failing test_warning_filters. Errors are now ignored inside np.errstate, and twice the gap is compared with the spacing. - IS, JSA and ESA read household values through benunit.max, which is float64, so exact_amount used float64 spacing and never removed float32 storage error: 16,000.01 of savings floored to 16,000.00, at the limit. A value equal to its float32 is now judged at float32 spacing. - From 131,072 float32 cannot hold every penny, so no penny is identified and the stored value is kept; rounding down then never goes against the claimant. - The Lifetime ISA's 75% and UC company capital are worked in float64 from the stored amounts; company capital is rounded under reg. 6(1). - test_uc_capital_allocation expected exact conservation; each share is now its own nearest-penny amount (1.00 three ways is 0.33 each). - Docstrings: the guard's intended tolerance; no report leaves a holding in the pool. Tests: a model-level property that every programme counts whole-penny savings, shares and Lifetime ISA balances to the penny; widened-input and above-131,072 helper checks; YAML for 16,000.01 of savings under IS, JSA and ESA and a 1,000.04 Lifetime ISA. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #1960.
Reproducer:
policyengine_uk/tests/policy/baseline/gov/dwp/capital_valuation.yaml::UC values a mortgaged holiday home as in ADM H1602,policyengine_uk/tests/policy/baseline/gov/dwp/capital_valuation.yaml::UC shares valued less sale expenses bring capital within the limit,policyengine_uk/tests/test_capital_valuation_properties.py::test_valuation_matches_the_regulation_and_never_adds_capitalUniversal Credit and every legacy means test counted each capital source at full value. The regulations value capital "at its current market value or surrender value less— (a) where there would be expenses attributable to sale, 10%; and (b) the amount of any encumbrances secured on it" (UC Regs 2013 reg 49(1)). This PR applies that valuation to UC, Housing Benefit, Income Support, income-based JSA, income-related ESA and Pension Credit.
The law
sourcesparameter starts./data.xmlon 2026-09-30. An independent re-fetch confirmed the regulation numbers: JSA is reg 111, not 109; ESA is reg 113, not 112.The DWP guidance sets the order and scope of the deductions:
What changes
capital.sale_expensesfor each programme, next to its existingcapital.sources:rateis 0.1.sourceslists the capital sources whose sale incurs expenses. Each cites that programme's regulation and guidance (ADM for UC, DMG ch 29 for IS/JSA, DMG ch 84 for PC, HBGM BW1/BP1 for HB).policyengine_uk/utils/capital_valuation.pyaddsvalued_capital(). For each source it takes 10% of gross value where the source is listed, then subtracts the debt secured on that source, and floors each encumbered source at nil. Negative equity in one asset therefore never reduces another.capital.round_downparameter for each programme sets the rule:exact_amountremoves float32 storage error, but only where a stored value is the float nearest a whole penny (£1,000.10 is stored as 1,000.0999756). Any other value, such as a share price to four decimal places, is kept exactly.property_sources,uc_*_property_capital, secured debt). This PR:property_sale_cost_rateinto the onecapital.sale_expenses.rate, so a single 10% serves property and the financial pool;corporate_wealthas UC's financial source with costs of sale, so shares count at 90% (savings stay at face value).household_uc_pool_company_holdings), in the override floor (household_uc_reported_financial_capital) and from a completeuc_reported_capital, which is a countable figure;benunit_reported_capitaltotal in full, because that total is used unvalued;counted_sharehelper keeps the rule in one place. The company capital substituted under reg 77(2) stays outside the helper (ADM H1892).*_assessable_capitalvariables call the helper instead ofsum(...). The allocation proxies (adult shares, reported-claim paths) and theuc_reported_capitaloverride are unchanged. Reported capital is taken as already assessed.uprating_indices.yamlnext to the values they offset:other_residential_property_secured_debtnon_residential_property_secured_debtowned_land_secured_debtsavings, so nothing changes there. Kingston, Westminster, Merton and Newham useuc_assessable_capitalfor UC claimants, so they inherit the UC valuation. That is what the prescribed schemes' UC pass-throughs require (2886 para 37(6); WSI 3029 Sch 6 para 9(6)).Which sources take the 10%
savings(bank and building society balances, cash ISAs)owned_land,other_residential_property_value,non_residential_property_valuecorporate_wealthThe
corporate_wealthrow is a judgement, and the parameter descriptions say so:corporate_wealthunlisted would understate it by the full 6.3 points.Invariants
These hold for every input and are tested as properties (
policyengine_uk/tests/test_capital_valuation_properties.py, Hypothesis). The first two were also checked on every Enhanced FRS record in every year of the impact runs below.Tests
policyengine_uk/tests/policy/baseline/gov/dwp/capital_valuation.yamlhas twenty-one cases, each worked by hand from the provisions above:test_stock_capital_periods.pynow also gives the second home a £5,000 mortgage: £12,000 + (£10,800 − £5,000) + £10,800 = £28,600, the same for a month as for the year.test_capital_valuation_properties.py(Hypothesis, 20-200 households per example). The oracle fixes which assets take the 10%, and the 10% itself, from the law, rather than reading the parameters. Removing HB's two property sources from its list makes these tests fail.uc_company_analogous_business.yamland the property-capital YAML and Python suites, with each figure worked in a comment;test_uc_company_override_properties.py: the conservation oracle (unit capitals plus disregarded holdings at 90% equal the valued pool) consumes holdings the same way. Its grid now includes recorded totals below the holdings: 1,280 households per layout;gov/dwp,finance/benefit/familyandcouncil_tax_reductionpass, as do the focused capital, company, allocation and warning-filter Python tests (162). Every check passed on a7d8988, including the full Python and YAML shards and the aggregate-impact job.Impact on the certified data
These are real paired runs by this repository's aggregate-impact CI on
enhanced_frs_2024_25.h5(policyengine-uk-data 1.58.1, sha256056e80fd8e91). Base is maine2ea32893; head is this PR merged onto it. Each fiscal year is a full microsimulation on each side (report).How to read these:
measure.py, group "Own-decile levels"). They move as households re-rank, and they net to the total.qualifying_benefitsand among Tax-Free Childcare'sdisqualifying_benefits.corporate_wealthstill holds unit trusts, ISAs or pension wealth (see the parameter descriptions, Exclude corporate_wealth from Universal Credit countable capital pending a pension split #1837 and policyengine-uk-data#501), part of the share deduction reaches assets the law would treat differently. These are not a fiscal estimate of reg 49 alone.Earlier runs on a private copy of the 29 September Enhanced FRS build (before #1950, #2201 and #2439 merged) are kept in the evidence folder for the record. On that build every award effect ran through
corporate_wealth, about 85% of which was pension wealth there. A run that applied the deduction to property and land only changed no award.Coordination
corporate_wealthfrom UC sources pending the pension split). Compatible: sale-expense lists are keyed by source name. Oncecorporate_wealthleaves UC'ssources, the UC listing has no effect.uc_income_from_capital.yamlGB and NI, and the savings-0 case inuc_unearned_income.yaml): £10,000 of property is now £9,000 of capital, so 12 tariff steps, £626.40 a year, and UC of £4,472.40.returnofuc_assessable_capital.py. The only expected conflict is the docstring; whichever PR merges second resolves it. The capital a person is deemed to possess under reg 77(2) stays outside the reg 49 helper:uc_company_holding_disregardat full value, while this PR counts the holding (insidecorporate_wealth) at 90%. The holding should come off at the rate its source was counted at, 1 −sale_expenses.rate, in both places Model Universal Credit reg 77 for owner-managers of a company #1965 subtracts it: the unreported household pool and a reporting benefit unit'suc_reported_capital.axiom: TheAxiomFoundation/rulespec-uk#395 queued
review_findingand hand-computed companion tests (ADM H1602 and H1616).Follow-ups (chips filed)
DVHseDebtR8_sum,DVBldDebtR8_sum,DVLUKDebtR8_sum; buy-to-letDVBltValR8_sum/DVBLtDebtR8_sumis being checked too), the pension split and a share / fund / ISA split ofcorporate_wealthfrom policyengine-uk-data. In progress as Split pensions, shares, trusts, ISAs and secured debt out of WAS wealth policyengine-uk-data#501 (draft). It exportsprivate_pension_wealth,directly_held_shares,unit_and_investment_trusts,stocks_and_shares_isa,cash_isaand the three*_secured_debtcolumns, and adds buy-to-let toother_residential_property_value. A model-side follow-up stacked on this PR will name the split variables in the sale-expense lists, so that unit trusts and stocks-and-shares ISAs are exempt.owned_land, which every legacy test counts (from Add Universal Credit capital limits #1572, no stated reason).>=); the Act says "not greater than".🤖 Generated with Claude Code