Repository navigation
Conversation
…le-expense-share-split
…eduction corporate_wealth is now itemised in the capital sources lists (#1974), so the sale-expense lists name directly_held_shares, which DWP values less 10% for costs of sale (ADM H1665; DMG 29671/52671/84763; HBGM W1.513), and unitemised_corporate_wealth, which keeps the previous flat treatment on datasets that do not itemise. Unit trusts have no costs of sale even when sold through a broker (ADM H1673-H1674; DMG 29680-29681, 52680-52681, 84771-84772; HBGM W1.530-W1.531), and an ISA is valued at what the holder would get by withdrawing it, with no paragraph applying the 10% (ADM H1656-H1658; DMG 29662-29664, 52662, 84748-84750). The survey bucket of unit and investment trusts is treated as having no costs of sale; investment trusts were 8.2% and 6.9% of the pooled funds in stocks and shares ISAs at 5 April 2022 and 2025 (HMRC ISA Table 9.6). The property tests keep their own legal asset set and assert each programme's list against it, and gain the split invariants: the residual, backward compatibility with every component nil, conservation at a zero rate, split effect = rate x (trusts + ISA), and no double counting. #1974's itemising-invariance test no longer holds by design and is replaced by them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
vahid-ahmadi
left a comment
There was a problem hiding this comment.
Thanks Max. Reviewed at head b1a66004.
The "+10.5k lines" is the main merge carried in through #1974. The real diff over #1969 + #1974 (merged at their heads) is 11 files, +710/−125:
- the six
sale_expenses.sourceslists; - the new
capital_valuation_share_split.yaml; - the property tests;
test_stock_capital_periods.py;- a changelog fragment.
That's what I reviewed.
Guidance:
- ADM H1674 is explicit: unit trusts have "no costs of sale. This applies even if persons use an agent, such as a stockbroker."
- H1665 applies the 10% to quoted shares.
- H1656 values an ISA at "what people would get if they withdrew their investment".
The lists now apply the 10% to directly_held_shares and the unitemised residual only, which is right for shares and unit trusts. SPC Sch V and UC Sch 10 have no disregard that changes this.
Probes:
- savings £5,000, shares £4,000, trusts £3,000, ISA £5,000 (
corporate_wealth£12,000) gives £16,600 for UC, HB and IS. The same holdings with only the components entered also give £16,600. - An unsplit
corporate_wealthof £12,000 still gives #1969's £15,800. - Your monthly case (£12,300) matches.
Tests:
- At the head: the capital-valuation, share-component, wealth-component, stock-period and PC reported-capital property tests, 26 passed. DWP, family and household YAML, 1,064 passed.
- Merged into current main (the merge is clean): YAML, 1,389 passed. Properties: 19 passed and 2 failed. The two failures are the same stale
applies()oracle as on #1969 (here attests/test_capital_valuation_properties.py:240), not the new share-split properties. They'll clear when #1969's fix is carried through. - There's no CI on this PR, because its base isn't main, so these local runs are the only test evidence so far.
Findings:
-
Should fix (merge order). Retarget to main as soon as #1969 merges, and get CI green on the retargeted head before merging. Until then, nothing hosted has run on these lists. This PR must land in the same window as #1969 and #1974 (see my notes there): with those two in and this one out, the 10% on shares disappears.
-
Should fix.
parameters/gov/dwp/universal_credit/means_test/capital/sale_expenses/sources.yaml:20-22, and the same sentence in the other five lists. The ISA exemption is weaker than the unit-trust one:- H1674 says outright that unit trusts have no costs of sale. For ISAs, the guidance is silent and you're inferring from H1656 ("withdrawal value").
- A stocks-and-shares ISA holding quoted shares has to sell them through the ISA manager. That is a broker sale under H1605, and reg 49(1)(a) applies wherever "there would be expenses attributable to sale".
I'd keep ISAs exempt, since most ISA money is in funds, but describe it as a judgement, like the investment-trust one, rather than "no ISA paragraph applies the 10%". Give the directly-held-shares share of stocks-and-shares ISAs from HMRC Table 9.6 alongside it.
-
Nit.
variables/input/directly_held_shares.py:9. Employee share options now take the 10% as "shares". An unexercised option isn't sold through a broker and is often not realisable at all. It's fine as an approximation, but say so in the list descriptions next to the investment-trust caveat. -
Nit.
tests/test_capital_share_components.py. You removed the itemising-invariance test on purpose. A one-line comment pointing to the replacement differential intest_capital_valuation_properties.pywould stop someone restoring it.
|
Independent review (GPT-6.1 Sol, UK hub sweep, 2026-10-12): REQUEST_CHANGES at VERDICT: REQUEST_CHANGES
The stated mechanisms and invariants match the code under the chosen methodology. Property tests cover the load-bearing logic; replacing the old itemising-invariance test is justified. The exemption commit adds no skips or xfails. Ruff formatting and lint passed for its three changed Python files. Shell testing was attempted below the 30 GB compressor threshold. GB regulations and programme manuals were reviewed; several NI references and the HMRC percentages remain unverified. Requested |
…ale-expense-share-split # Conflicts: # docs/book/assumptions/growthfactors.md # policyengine_uk/data/uprating_indices.yaml # policyengine_uk/tests/test_capital_valuation_properties.py
…le-expense-share-split # Conflicts: # docs/book/assumptions/growthfactors.md # policyengine_uk/data/uprating_indices.yaml # policyengine_uk/tests/test_capital_share_components.py
#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 <[email protected]>
…uping Review of b7e6bc5: - gov.dwp.share_like_capital.without_sale_expenses started in 2013, after the legacy capital lists (JSA from 1996-10-07), so a 2012 simulation with unitemised corporate_wealth raised ParameterNotFoundError; it now starts at 1996-10-07, and Income Support and JSA capital in 2012 are tested (both fail with the 2013 start). - An empty exempt list made add() return None; the exempt holdings are now summed from a numeric zero, and an empty-list reform (every share-like holding less 10%) is tested. - The property oracle now follows the engine's float32 grouping (components left to right, exempt holdings summed before subtraction) and compares exactly; the reviewer's example (trusts £200,000, ISA £199,999.984375) is pinned. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ale-expense-share-split
…le-expense-share-split # Conflicts: # policyengine_uk/tests/test_capital_share_components.py
…ale-expense-share-split
…le-expense-share-split # Conflicts: # policyengine_uk/tests/test_capital_share_components.py
vahid-ahmadi
left a comment
There was a problem hiding this comment.
Needs changes before merge. The split itself is right and well tested. But this branch carries both CI failures from #1969 and #1974, it inherits the #1965 holding problem in both forms, and nothing hosted has run on it.
@MaxGhenis, this is a delta review. My last review was at b1a66004; this one is at 6a5a13a4. Since then the split has been rebuilt on #1974's block: share_like_capital_with_sale_expenses and _without_sale_expenses, with an exempt-holdings parameter. I reviewed the diff against #1969 + #1974 merged at their current heads: 21 files, +678/−186.
Status of my earlier findings:
- F1 (retarget to main, CI): still open. See finding 2.
- F2 (ISA exemption described as a judgement): partly addressed. The body now has a methodology note, but the parameter text still states it as settled. See finding 3.
- F3 (employee share options caveat): not taken. See finding 5.
- F4 (invariance test comment): addressed. The module docstring now points to the differential.
Findings:
-
must (carried in from #1969 and #1974). This head contains both CI failures and the #1965 interaction:
test_projection_horizon.py:181fails, as on #1974. The ISA variables are now mapped inuprating_indices.yaml.test_savings_credit_only_receipt_properties.py:508fails, as on #1969. It expects 601.02 where reg 19 gives 621.82.- Company holdings, probed on this head merged into current main. Take a single UC claimant who is sole owner of a trading company, with a £10,000 holding and £5,000 savings:
- with
corporate_wealth£10,000, UC capital is £4,000 (expected £5,000); - with only
directly_held_shares£10,000, UC capital is £14,000 (expected £5,000), because the pool cap still readscorporate_wealth.
- with
- On this head merged into main, the same 8
uc_company_analogous_business.yamlcases and 3test_uc_company_owner_properties.pyproperties fail as on #1969. Five of those YAML cases are the holding problem; three are the plain 90% revaluation and only need new expectations (see my #1969 review). - Here the holding should come off
share_like_capital_with_sale_expensesat 90%, since an owner's shares are directly held shares.
Once the fixes land on #1969 and #1974, merge them through and re-run.
-
must. No CI has run on this PR, because its base is
uc-capital-sale-expenses. The body's "Local runs" line is still "pending". Retarget to main and get the full suite green before anything in the stack merges. If this PR lands alone, it carries #1969 and #1974 with no window where the 10% on shares disappears. On a #1969 + #1974 tree without this PR, £5,000 savings +corporate_wealth£12,000 gives £17,000 for UC; this head gives £15,800. -
should.
parameters/gov/dwp/share_like_capital/without_sale_expenses.yaml:10-12still says "no ISA paragraph applies the deduction", as if that settled it. The changelog fragment states the ISA exemption flatly too.- ADM H1656 and DMG 29662 value an ISA at "what people would get if they withdrew their investment" and say nothing about costs of sale.
- The body parks this, and the investment-trust question, for a decision (d1396).
- Until it's decided, the parameter text should call the exemption a judgement, as it already does for investment trusts. I'm not suggesting which way to decide.
-
nit. The six
capital/sources.yamldescriptions each have an unwrapped line of about 175 characters. An example isuniversal_credit/means_test/capital/sources.yaml:8("share_like_capital_with_sale_expensesandshare_like_capital_without_sale_expensessplit ..."). Rewrap to match the rest. -
nit.
share_like_capital_with_sale_expenses.py:3hardcodesCOMPONENTSfor the "is anything itemised" gate, while the exempt list is a parameter. A reform adding a new exempt variable outsideCOMPONENTSwould be ignored for a household that itemises only that variable. Consider a comment saying the exempt list must be a subset of the components, or a test asserting it. Separately, employee share options still take the 10% as "directly held shares" with no caveat (my F3). One clause inwithout_sale_expenses.yamlwould cover it.
What I checked:
-
Guidance, re-read from the current PDFs:
- ADM H1656-H1658 (ISA at withdrawal value; nothing on costs of sale);
- ADM H1673-H1674 and DMG 29680-29681 (unit trusts: "no costs of sale ... even if persons use an agent");
- DMG 29662 and 29664 (ISA), and DMG 29671 (10% on shares).
The paragraph numbers in the parameters match. I didn't check the NI or HBGM numbers, or the HMRC Table 9.6 percentages.
-
Probes on this head:
- unsplit £5,000 +
corporate_wealth£12,000 gives £15,800 (UC, IS; PC at 70), as on #1969; - split shares £4,000, trusts £3,000, ISA £5,000 gives £16,600;
- components only with £1,000 savings gives £1,000 + 90% × £4,000 + £3,000 + £5,000 = £12,600, matching the body.
- unsplit £5,000 +
-
Revert check.
capital_valuation_share_split.yamlrun on #1969 + #1974 without this PR's source changes: 10 failed. At this head: 10 passed. -
Tests. I ran the same property files and YAML directories as on #1969 and #1974, plus #1965's company-owner tests.
- At this head: 2 failed (the two carried CI failures) and 2,360 passed; YAML 1,889 passed.
- Merged into current main (clean): 5 failed (those two plus the three #1965 properties) and 2,405 passed; YAML 8 failed (company cases) and 1,980 passed. Current main
714b1438con the same set: all 2,363 pass, and YAML 1,964 pass.
-
Lint.
ruff checkandruff format --checkon the 11 changed Python files: clean. -
Repo process. The
.fixedfragment and the Reproducer and axiom lines are present, and the named reproducer case exists. The branch also carries #1974's.addedfragment, which is fine if it lands as one unit. -
Overlap with my open PRs. #2382 merges cleanly with it.
-
Not checked. Any dataset run, or uk-data #501.
🤖 Generated with Claude Code
|
UK hub takeover for the Budget fix sprint (Max, 21 Oct). The owner session 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 |
|
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 |
…ale-expense-share-split # Conflicts: # policyengine_uk/parameters/gov/dwp/universal_credit/means_test/capital/sale_expenses/sources.yaml # policyengine_uk/parameters/gov/dwp/universal_credit/means_test/capital/sources.yaml
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 <[email protected]>
|
Hand-off to the UK hub (owner session local_b8fb4700). Head e78257a: #1969's b3f6f60 plus #1974's 6600477, with this PR's split on top. Depends on:
This PR targets #1969's branch, so GitHub runs no CI on it. Retarget it to main once #1969 lands; CI and the Aggregate impact report then run. Local results at e78257a:
Reviews. Round 3 (6a5a13a) asked for changes. All its findings are fixed here: the projected-year differential, the £16,000 overshoot test and the power-of-two precondition from #1974 are restored; the grouping Methodology. d1396 is no longer parked as a blocker. Under Max's 2026-10-20 delegation it is a logged session ruling, Follow-ups owed:
🤖 Generated with Claude Code |
|
Hand-off addendum. Since the hand-off above, #1969 has moved past b3f6f60 to 9b9ef66 after its Sol v4b and v5 reviews, and is in review v6. So e78257a is not the head to review or land. Agreed sequence, with #1969's owner (local_c12c197a):
What the rebase changes on this side, which e78257a already shows on b3f6f60:
The newer #1969 code also has a 🤖 Generated with Claude Code |
#2131 last merged #1969 at b3f6f60. The merge brings in #1969's float64, penny-rounded valuation, Lifetime ISA surrender values, cash ISAs in every capital list, the land secured-debt input and main at 2da212a. Conflicts and how they were resolved: - Six sale-expense lists: #1969 renamed corporate_wealth to share_like_capital; this branch names share_like_capital_with_sale_expenses. Kept this branch's lists (land, both property sources and the part with sale expenses) and descriptions, and restored #1969's note that an unitemised corporate_wealth still folds in unit trusts, ISAs and, on older datasets, pension wealth. Savings and cash ISAs stay out of every list. - UC capital/sources.yaml: savings, cash_isa (from #1969) and both share-like parts. - household_uc_pool_company_holdings, household_uc_reported_financial_capital and uc_assessable_capital: #1969's code (exact_amount, valued_person_capital, penny rounding, the recorded-total consumption rule) with this branch's wording: a reg. 77 holding sits in, and is budgeted against, the part with sale expenses (HOLDINGS_SOURCE). - test_capital_share_components.py: this branch's version, which already carries #1969's 11,800 and 27,000 expectations and replaces each #1974 test #1969 adapted with its split equivalent. - test_capital_valuation_properties.py: #1969's oracle (asset() lookup, cash ISAs generated, per-person LISA rounding) with the split mapped into it: the part with sale expenses reads corporate_wealth, the part without is nil. - growthfactors.md: #1969's text and savings-income section, with the per-capita GDP row as the union of both sides less other_investment_income and savings_interest_income, which main moved to per_capita.savings_income. Long description lines in five legacy capital lists are wrapped, and Pension Credit's list description names the two parts. Co-Authored-By: Claude Opus 5.5 <[email protected]>
#1969's fc066bd made main's test_isa_balances.py expect UC's 10% on a stored stocks-and-shares ISA total, because the ISA then reached capital through share_like_capital, which #1969 values less 10%. With the split, an itemised ISA sits in share_like_capital_without_sale_expenses and counts at its withdrawal value (ADM H1656), so UC capital is (1,000 + 3,000 + 2,000) x growth: 6,000 x 1.07224 = 6,433.44 in 2027. A new parametrised test pins the person-balance route in all six means tests: a £1,000 saver with a £3,000 cash ISA and a £2,000 stocks-and-shares ISA has £6,000 of capital, the ISA wholly in the part without sale expenses. The changelog fragment loses its leading list marker, which towncrier would render as a double bullet. Co-Authored-By: Claude Opus 5.5 <[email protected]>
A Hypothesis property gives single adults only itemised holdings, with the ISAs as their own person-level balances, and checks all six means tests against the law: savings, cash ISAs, unit trusts and stocks and shares ISAs in full, directly held shares less 10%, each rounded by the programme's rule, to within a penny of float32 noise. A pinned example gives £15,600. The list test also asserts that each capital list names cash_isa and that no sale-expense list names savings or cash_isa. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…R renamed test_ci_test_shards.py fails on any shard_durations.toml entry that names a missing test. Main's table lists three tests that this PR replaced with its split versions: - test_capital_matches_the_original_lists became test_unitemised_inputs_match_the_lists_before_itemising, the same four-period differential on the unitemised households; - test_share_like_capital_keeps_the_supplied_aggregate became test_share_like_capital_and_its_parts; - test_float32_rounding_at_the_limit_keeps_eligibility became test_float32_rounding_at_the_limit_keeps_the_aggregate. Each line keeps its measured duration, as the table's header asks of a renaming pull request, until the table is regenerated. Every one of the 566 entries now names a test that exists. Co-Authored-By: Claude Opus 5.5 <[email protected]>
When trusts and an ISA exceed corporate_wealth only by float32 rounding, share_like_capital keeps corporate_wealth and subtracting the exempt holdings goes below nil. £16,000 of corporate_wealth with £8,000.01 of trusts and an £8,000 ISA leaves -£0.009765625 without the floor. No fixed case reached the floor before: the grouping case lands at exactly nil either way. A fixed test and a matching @example on the property pin it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This PR values share-like capital by asset type in the means tests. It stacks on #1969 (head
3a3241cf6), where PolicyEngine deducts 10% for expenses of sale, and includes main at2da212abcand #1974. This PR adds the asset split and its tests, updates the CI shard table, and documents secured-debt uprating.Reproducer:
policyengine_uk/tests/test_isa_balances.py::test_person_isa_balances_count_in_full_in_every_means_testWhat changes
Under #1969, PolicyEngine takes 10% off all of
share_like_capital. DWP tells decision makers to value its parts differently in ADM chapter H1:This PR splits
share_like_capitalin two:share_like_capital_with_sale_expensesby subtracting the holdings ingov.dwp.share_like_capital.without_sale_expensesfromshare_like_capital, with a floor at nil. The list contains unit and investment trusts and stocks and shares ISAs. For households that itemise nothing, PolicyEngine keeps all share-like capital in this part and retains Value means-tested capital less 10% sale expenses and secured debt (UC reg 49 and legacy equivalents) #1969's flat 10%.share_like_capital_without_sale_expenses, or nil when the household itemises no holding.All six capital lists name both parts beside savings and cash ISAs: Universal Credit, Housing Benefit, Income Support, income-based JSA, income-related ESA and Pension Credit. Their sale-expense lists name the first part and exclude savings and cash ISAs.
Stocks and shares ISAs
On main, PolicyEngine sums
cash_isaandstocks_and_shares_isafrom each person's balance (#2712), and readsstocks_and_shares_isaas an itemised holding. It puts stocks and shares ISAs in the part without sale expenses, whether a dataset stores household totals or person balances. Each means test counts the ISA at its withdrawal value.Under #1969, PolicyEngine takes 10% off the ISA, so this PR changes the expectation in
test_isa_balances.py. The stored case supplies £1,000 of savings, a £3,000 cash ISA and a £2,000 stocks and shares ISA for 2025. UC capital in 2027 rises from (£1,000 + £3,000 + 90% × £2,000) × 1.07224 = £6,218.99 to (£1,000 + £3,000 + £2,000) × 1.07224 = £6,433.44. Per capita GDP growth of 4.0% in 2026 and 3.1% in 2027 compounds to 1.07224. A new test gives a single adult the same amounts and supplies their own ISA balances. It checks £6,000 of capital in all six means tests.Regulation 77 company holdings
An owner-manager holds company shares that UC disregards under reg 77(2), and selling shares incurs expenses. This PR moves
HOLDINGS_SOURCEfromshare_like_capitaltoshare_like_capital_with_sale_expenses. PolicyEngine budgets company holdings against that source in the UC pool, so it never deducts them from unit trusts or ISAs. It removes the holding from the pool and any complete override at 90%, matching UC's valuation of shares. On data that don't itemise, PolicyEngine calculates the same value for both sources.CI shard table
Main's
shard_durations.tomlnames three share-component tests that this PR replaces, andtest_ci_test_shards.pyfails when a line names a test that doesn't exist. This PR points those lines at the successors and keeps their measured durations.Growth factors page
This PR adds three secured-debt inputs to the per capita GDP list on
growthfactors.md:owned_land_secured_debt,other_residential_property_secured_debtandnon_residential_property_secured_debt. PolicyEngine already uprates them by per capita GDP throughuprating_indices.yaml.Methodology
The Wealth and Assets Survey asks about unit and investment trusts in one question. Investment trusts trade as quoted shares; we infer that decision makers apply H1665's 10% deduction to directly held investment trusts. HMRC's ISA statistics, Table 9.6, put investment trusts at 8.2% of pooled funds in stocks and shares ISAs at 5 April 2022 and 6.9% at 5 April 2025. Those figures describe ISA holdings, and we use that composition as a proxy when treating the combined trust bucket outside ISAs as unit trusts. The survey does not separate the two trust types.
We give the whole trust bucket the treatment of unit trusts and count ISAs at their withdrawal value with no deduction. Under H1656, decision makers value an ISA at what its holder would get by withdrawing it; the paragraph does not mention the 10%. PolicyEngine retains the 10% for directly held shares and all of an unitemised
corporate_wealth.A parameter edit can put a whole holding such as stocks and shares ISAs back under the 10%: remove it from
gov.dwp.share_like_capital.without_sale_expenses. Applying the 10% to part of a holding, such as the investment trusts in the bucket, would require a weight parameter and a formula change.Impact
CI compared #1969's head
3a3241cf6with2f1051e9fon the certified Enhanced FRS (enhanced_frs_2024_25.h5, policyengine-uk-data 1.58.1). It found no change in any household's net income from fiscal year 2025-26 to 2031-32, or in receipts, the government balance, poverty or inequality.policyengine-uk-data (
mainate1acdf966) imputes onecorporate_wealth. It sums WAS non-defined-benefit pension wealth, employee shares and options, UK shares, investment ISAs, and unit and investment trusts. It doesn't impute the three holdings separately, so PolicyEngine retains #1969's flat 10% for every household in that dataset.Microcosm's UK build writes
directly_held_shares,unit_and_investment_trustsandstocks_and_shares_isaso that they sum tocorporate_wealth(was_wealth.pyatc0513dbae). On data from that build, PolicyEngine excludes trusts and stocks and shares ISAs from the 10% deduction. CI's aggregate report doesn't cover those data.Invariants
test_capital_share_components.pyuses Hypothesis to check these properties on pounds-and-pence inputs that the engine stores as float32:share_like_capitalkeeps the suppliedcorporate_wealthunless the itemised holdings exceed it beyond float32 rounding, and then equals their sum.share_like_capitaland the other part holds nil. Otherwise the first equalsshare_like_capitalless trusts and ISAs, never below nil, and the two parts sum toshare_like_capital.1e-5.The capital property suite from #1969 (
test_capital_valuation_properties.py) keeps its oracle. The oracle specifies which assets take the 10% from the law, independently of the parameters. It maps the part with sale expenses to the household'scorporate_wealthand the part without to nil. It still raises aKeyErrorfor any source it doesn't map.Fixed cases pin these results:
corporate_wealthbeside £200,000 of trusts and a £199,999.984375 ISA, PolicyEngine leaves nil with sale expenses. It rounds both totals to £400,000 in float32 and sums the trusts and ISA before subtracting them;corporate_wealthbeside £8,000.01 of trusts and an £8,000 ISA, PolicyEngine sums the holdings in float32 to £16,000.009765625. It leaves nil with sale expenses and £16,000 without;corporate_wealthas £11,800 in 2012;corporate_wealthwhen float32 rounding pushes the components' sum above £16,000 or £16,384.01;test_stock_capital_periods.py).Validation
6a0d26d616a0d26d616a0d26d612f1051e9f3a3241cf6(run 41225446226)2f1051e9f3a3241cf6for 400 households that don't itemise: each programme's assessable capital and award, UC eligibility and the UC company-holding variables in 2025, 2026 and 2027, and UC capital, eligibility and award in January 2026 (58 outputs, 27,616 values)2f1051e9ftest_isa_balances.py,test_wealth_components.py,test_isa_accounting.py,test_lifetime_isa_capital.py,test_uc_property_capital_mock.py,test_uc_company_override_properties.py,test_uc_company_owner_axiom_parity.pyce360946acode_health/test_ci_test_shards.py,test_stock_capital_periods.py6a0d26d61capital_valuation_share_split.yaml,cash_isa_capital.yaml,isa_balances.yaml,lifetime_isa_capital.yaml6a0d26d61ruff format --check,ruff check,--select F811,F821,git diff --check6a0d26d61ce360946adiffers from2f1051e9fonly inshard_durations.toml, and6a0d26d61adds the floor case totest_capital_share_components.py. CI at6a0d26d61passed every case in these files:test_capital_share_components.pytest_capital_valuation_properties.pytest_uc_property_capital.pytest_uc_company_owner_properties.pytest_uc_tax_free_childcare_choice.pytest_input_records.pytest_fiscal_partition.pyCI also passed the 70 cases in
capital_valuation.yaml,lifetime_isa_capital.yaml,capital_valuation_share_split.yaml,cash_isa_capital.yamlandisa_balances.yaml.We ran each mutant with Hypothesis shrinking off:
2f1051e9fHOLDINGS_SOURCEon all ofshare_like_capital2f1051e9f2f1051e9f6a0d26d61@examplefind −£0.009765625 with sale expenses6a0d26d61corporate_wealthaxiom: TheAxiomFoundation/rulespec-uk#395 queued
We added the asset-type distinction and hand-computed companion cases to the issue (TheAxiomFoundation/rulespec-uk#395 (comment)).
🤖 Generated with Claude Code