diff --git a/changelog.d/uk-pension-wealth-split-750.added.md b/changelog.d/uk-pension-wealth-split-750.added.md new file mode 100644 index 0000000000..44c6268923 --- /dev/null +++ b/changelog.d/uk-pension-wealth-split-750.added.md @@ -0,0 +1 @@ +UK WAS wealth stage emits `private_pension_wealth` (WAS private pension wealth less the current-employment defined-benefit component) as its own household output and keeps `corporate_wealth` to the share-like holdings, so the means-tested capital tests stop counting pension rights; spec, support bounds, export allow-list, coverage manifest and gate-battery digests move in lockstep (#750, uk-data#452). diff --git a/experiments/452-uk-pension-wealth-split-coupling-receipt.json b/experiments/452-uk-pension-wealth-split-coupling-receipt.json new file mode 100644 index 0000000000..5ef19ce3be --- /dev/null +++ b/experiments/452-uk-pension-wealth-split-coupling-receipt.json @@ -0,0 +1,243 @@ +{ + "n_estimators": 30, + "truth": { + "P(owned_land_pos)": 0.0076383512194625365, + "P(owned_land_pos&property_wealth_pos)": 0.0076383512194625365, + "ratio_vs_indep(owned_land_pos,property_wealth_pos)": 1.558018820525906, + "P(property_wealth_pos|owned_land_pos)": 1.0, + "P(property_wealth_pos|not owned_land_pos)": 0.6390839669376266, + "P(owned_land_pos&pension_pos)": 0.007427542905194072, + "ratio_vs_indep(owned_land_pos,pension_pos)": 1.3157807591526687, + "P(pension_pos|owned_land_pos)": 0.9724013326683218, + "P(pension_pos|not owned_land_pos)": 0.7372336138234814, + "P(owned_land_pos&shares_excl_pos)": 0.003678065948690654, + "ratio_vs_indep(owned_land_pos,shares_excl_pos)": 2.7087590308557132, + "P(shares_excl_pos|owned_land_pos)": 0.4815261622585426, + "P(shares_excl_pos|not owned_land_pos)": 0.17542825051479743, + "P(owned_land_pos&isa_pos)": 0.0015102923671691392, + "ratio_vs_indep(owned_land_pos,isa_pos)": 1.5441250279671401, + "P(isa_pos|owned_land_pos)": 0.19772491782269852, + "P(isa_pos|not owned_land_pos)": 0.12751351150960955, + "P(property_wealth_pos)": 0.6418407703588922, + "P(property_wealth_pos&pension_pos)": 0.5384180116374168, + "ratio_vs_indep(property_wealth_pos,pension_pos)": 1.1350899158187926, + "P(pension_pos|property_wealth_pos)": 0.8388653954412315, + "P(pension_pos|not property_wealth_pos)": 0.5601192967136539, + "P(property_wealth_pos&shares_excl_pos)": 0.15801676395202507, + "ratio_vs_indep(property_wealth_pos,shares_excl_pos)": 1.3849252939692862, + "P(shares_excl_pos|property_wealth_pos)": 0.24619309219585458, + "P(shares_excl_pos|not property_wealth_pos)": 0.055141870670218765, + "P(property_wealth_pos&isa_pos)": 0.11703642780332342, + "ratio_vs_indep(property_wealth_pos,isa_pos)": 1.4240158258447393, + "P(isa_pos|property_wealth_pos)": 0.1823449572046993, + "P(isa_pos|not property_wealth_pos)": 0.030749963077466483, + "P(pension_pos)": 0.7390299074554981, + "P(pension_pos&shares_excl_pos)": 0.16338613792697204, + "ratio_vs_indep(pension_pos,shares_excl_pos)": 1.2436657586410924, + "P(shares_excl_pos|pension_pos)": 0.22108190247606535, + "P(shares_excl_pos|not pension_pos)": 0.05510285031153905, + "P(pension_pos&isa_pos)": 0.11449208905849834, + "ratio_vs_indep(pension_pos,isa_pos)": 1.2098583447414697, + "P(isa_pos|pension_pos)": 0.1549221322486095, + "P(isa_pos|not pension_pos)": 0.051951247363071155, + "P(shares_excl_pos)": 0.17776633387224022, + "P(shares_excl_pos&isa_pos)": 0.056780288630039935, + "ratio_vs_indep(shares_excl_pos,isa_pos)": 2.4944174642082237, + "P(isa_pos|shares_excl_pos)": 0.3194096845741761, + "P(isa_pos|not shares_excl_pos)": 0.08667794228888633, + "P(isa_pos)": 0.12804981089064132 + }, + "D1_pension_first:same_seed_per_segment(stage as-is)": { + "P(owned_land_pos)": 0.00196559740097869, + "P(owned_land_pos&property_wealth_pos)": 0.00196559740097869, + "ratio_vs_indep(owned_land_pos,property_wealth_pos)": 1.5176233931560639, + "P(property_wealth_pos|owned_land_pos)": 1.0, + "P(property_wealth_pos|not owned_land_pos)": 0.658253267242873, + "P(owned_land_pos&pension_pos)": 0.00196559740097869, + "ratio_vs_indep(owned_land_pos,pension_pos)": 1.3491400871721204, + "P(pension_pos|owned_land_pos)": 1.0, + "P(pension_pos|not owned_land_pos)": 0.7407031998150472, + "P(owned_land_pos&shares_excl_pos)": 0.000297902582448321, + "ratio_vs_indep(owned_land_pos,shares_excl_pos)": 0.8991488025158029, + "P(shares_excl_pos|owned_land_pos)": 0.15155829077714103, + "P(shares_excl_pos|not owned_land_pos)": 0.16859099779208564, + "P(owned_land_pos&isa_pos)": 0.00046631199335324164, + "ratio_vs_indep(owned_land_pos,isa_pos)": 1.8754150093940034, + "P(isa_pos|owned_land_pos)": 0.23723677754206451, + "P(isa_pos|not owned_land_pos)": 0.12628018644840672, + "P(property_wealth_pos)": 0.6589250037325733, + "P(property_wealth_pos&pension_pos)": 0.5571833986840257, + "ratio_vs_indep(property_wealth_pos,pension_pos)": 1.1408255185540233, + "P(pension_pos|property_wealth_pos)": 0.8455945601210789, + "P(pension_pos|not property_wealth_pos)": 0.5395572125235916, + "P(property_wealth_pos&shares_excl_pos)": 0.1646804701791328, + "ratio_vs_indep(property_wealth_pos,shares_excl_pos)": 1.482716027087137, + "P(shares_excl_pos|property_wealth_pos)": 0.24992293393979148, + "P(shares_excl_pos|not property_wealth_pos)": 0.011367142742040188, + "P(property_wealth_pos&isa_pos)": 0.12056520593331854, + "ratio_vs_indep(property_wealth_pos,isa_pos)": 1.4464431722099018, + "P(isa_pos|property_wealth_pos)": 0.18297257692508248, + "P(isa_pos|not property_wealth_pos)": 0.017395225587018327, + "P(pension_pos)": 0.7412128729315728, + "P(pension_pos&shares_excl_pos)": 0.1542624998744278, + "ratio_vs_indep(pension_pos,shares_excl_pos)": 1.2347222750335949, + "P(shares_excl_pos|pension_pos)": 0.20812172252797476, + "P(shares_excl_pos|not pension_pos)": 0.05523852223622324, + "P(pension_pos&isa_pos)": 0.1175458693344547, + "ratio_vs_indep(pension_pos,isa_pos)": 1.2536600604161776, + "P(isa_pos|pension_pos)": 0.1585858444006089, + "P(isa_pos|not pension_pos)": 0.034593734249617494, + "P(shares_excl_pos)": 0.16855751834744545, + "P(shares_excl_pos&isa_pos)": 0.05973213266404448, + "ratio_vs_indep(shares_excl_pos,isa_pos)": 2.801400898486773, + "P(isa_pos|shares_excl_pos)": 0.3543724020717925, + "P(isa_pos|not shares_excl_pos)": 0.0803015857918814, + "P(isa_pos)": 0.12649828243548186, + "seconds": 1276.0 + }, + "D1_pension_first:fresh_seed_per_segment": { + "P(owned_land_pos)": 0.0015973632526921759, + "P(owned_land_pos&property_wealth_pos)": 0.0015973632526921759, + "ratio_vs_indep(owned_land_pos,property_wealth_pos)": 1.5098761783602952, + "P(property_wealth_pos|owned_land_pos)": 1.0, + "P(property_wealth_pos|not owned_land_pos)": 0.6617656823677158, + "P(owned_land_pos&pension_pos)": 0.0013844443337485213, + "ratio_vs_indep(owned_land_pos,pension_pos)": 1.164817932291937, + "P(pension_pos|owned_land_pos)": 0.8667060115569807, + "P(pension_pos|not owned_land_pos)": 0.7438737344368557, + "P(owned_land_pos&shares_excl_pos)": 0.000909661310198591, + "ratio_vs_indep(owned_land_pos,shares_excl_pos)": 3.5371755857077884, + "P(shares_excl_pos|owned_land_pos)": 0.5694767978826728, + "P(shares_excl_pos|not owned_land_pos)": 0.1603440714510367, + "P(owned_land_pos&isa_pos)": 0.0007956739252036313, + "ratio_vs_indep(owned_land_pos,isa_pos)": 4.223483498686684, + "P(isa_pos|owned_land_pos)": 0.49811708380207975, + "P(isa_pos|not owned_land_pos)": 0.11733161468468208, + "P(property_wealth_pos)": 0.6623059654375011, + "P(property_wealth_pos&pension_pos)": 0.5659089834417722, + "ratio_vs_indep(property_wealth_pos,pension_pos)": 1.1483496977307, + "P(pension_pos|property_wealth_pos)": 0.8544524932188227, + "P(pension_pos|not property_wealth_pos)": 0.5275810068472689, + "P(property_wealth_pos&shares_excl_pos)": 0.14792809326502496, + "ratio_vs_indep(property_wealth_pos,shares_excl_pos)": 1.3873069980410693, + "P(shares_excl_pos|property_wealth_pos)": 0.22335310413112122, + "P(shares_excl_pos|not property_wealth_pos)": 0.03870222873679029, + "P(property_wealth_pos&isa_pos)": 0.10961554698704633, + "ratio_vs_indep(property_wealth_pos,isa_pos)": 1.4033075229096037, + "P(isa_pos|property_wealth_pos)": 0.1655059031736749, + "P(isa_pos|not property_wealth_pos)": 0.024650481089924018, + "P(pension_pos)": 0.7440699422025718, + "P(pension_pos&shares_excl_pos)": 0.14817902031984218, + "ratio_vs_indep(pension_pos,shares_excl_pos)": 1.236953980820041, + "P(shares_excl_pos|pension_pos)": 0.19914662844894315, + "P(shares_excl_pos|not pension_pos)": 0.05008628069789383, + "P(pension_pos&isa_pos)": 0.1104765515918856, + "ratio_vs_indep(pension_pos,isa_pos)": 1.2589129866514228, + "P(isa_pos|pension_pos)": 0.14847603071407034, + "P(isa_pos|not pension_pos)": 0.029161544652293484, + "P(shares_excl_pos)": 0.16099760503371235, + "P(shares_excl_pos&isa_pos)": 0.05461090569975432, + "ratio_vs_indep(shares_excl_pos,isa_pos)": 2.876069181401818, + "P(isa_pos|shares_excl_pos)": 0.33920321788835917, + "P(isa_pos|not shares_excl_pos)": 0.07548126451176541, + "P(isa_pos)": 0.11793986740020936, + "seconds": 1757.9 + }, + "D2_shares_first:same_seed_per_segment(stage as-is)": { + "P(owned_land_pos)": 0.00196559740097869, + "P(owned_land_pos&property_wealth_pos)": 0.00196559740097869, + "ratio_vs_indep(owned_land_pos,property_wealth_pos)": 1.5176233931560639, + "P(property_wealth_pos|owned_land_pos)": 1.0, + "P(property_wealth_pos|not owned_land_pos)": 0.658253267242873, + "P(owned_land_pos&pension_pos)": 0.0009746705564756995, + "ratio_vs_indep(owned_land_pos,pension_pos)": 0.668350101335664, + "P(pension_pos|owned_land_pos)": 0.49586479713007436, + "P(pension_pos|not owned_land_pos)": 0.7424083308422835, + "P(owned_land_pos&shares_excl_pos)": 0.0011692906218771662, + "ratio_vs_indep(owned_land_pos,shares_excl_pos)": 3.9137745375500335, + "P(shares_excl_pos|owned_land_pos)": 0.5948779853366539, + "P(shares_excl_pos|not owned_land_pos)": 0.15112373498894455, + "P(owned_land_pos&isa_pos)": 0.0003240965753945136, + "ratio_vs_indep(owned_land_pos,isa_pos)": 1.2361621336152444, + "P(isa_pos|owned_land_pos)": 0.1648845156353701, + "P(isa_pos|not owned_land_pos)": 0.1333221760100451, + "P(property_wealth_pos)": 0.6589250037325733, + "P(property_wealth_pos&pension_pos)": 0.5540640092367449, + "ratio_vs_indep(property_wealth_pos,pension_pos)": 1.1333516813226265, + "P(pension_pos|property_wealth_pos)": 0.8408605017235216, + "P(pension_pos|not property_wealth_pos)": 0.5507871240410442, + "P(property_wealth_pos&shares_excl_pos)": 0.14211059347518623, + "ratio_vs_indep(property_wealth_pos,shares_excl_pos)": 1.418921507399471, + "P(shares_excl_pos|property_wealth_pos)": 0.21567036107323412, + "P(shares_excl_pos|not property_wealth_pos)": 0.028983020810954458, + "P(property_wealth_pos&isa_pos)": 0.12863443635850574, + "ratio_vs_indep(property_wealth_pos,isa_pos)": 1.4635812039973677, + "P(isa_pos|property_wealth_pos)": 0.19521862978311325, + "P(isa_pos|not property_wealth_pos)": 0.013925906490523065, + "P(pension_pos)": 0.7419237255131907, + "P(pension_pos&shares_excl_pos)": 0.1380483861486046, + "ratio_vs_indep(pension_pos,shares_excl_pos)": 1.2241650628035117, + "P(shares_excl_pos|pension_pos)": 0.18606816496280146, + "P(shares_excl_pos|not pension_pos)": 0.05404445282400248, + "P(pension_pos&isa_pos)": 0.12074941170340445, + "ratio_vs_indep(pension_pos,isa_pos)": 1.2201727661123043, + "P(isa_pos|pension_pos)": 0.16275178640483795, + "P(isa_pos|not pension_pos)": 0.04895763155486333, + "P(shares_excl_pos)": 0.15199597719010127, + "P(shares_excl_pos&isa_pos)": 0.057176770336181564, + "ratio_vs_indep(shares_excl_pos,isa_pos)": 2.8202206600142823, + "P(isa_pos|shares_excl_pos)": 0.37617291847580026, + "P(isa_pos|not shares_excl_pos)": 0.08986684317143111, + "P(isa_pos)": 0.13338421486278143, + "seconds": 1096.4 + }, + "D2_shares_first:fresh_seed_per_segment": { + "P(owned_land_pos)": 0.0015973632526921759, + "P(owned_land_pos&property_wealth_pos)": 0.0015973632526921759, + "ratio_vs_indep(owned_land_pos,property_wealth_pos)": 1.5098761783602952, + "P(property_wealth_pos|owned_land_pos)": 1.0, + "P(property_wealth_pos|not owned_land_pos)": 0.6617656823677158, + "P(owned_land_pos&pension_pos)": 0.001571638068171162, + "ratio_vs_indep(owned_land_pos,pension_pos)": 1.304785123022258, + "P(pension_pos|owned_land_pos)": 0.9838952195265185, + "P(pension_pos|not owned_land_pos)": 0.7536991523680879, + "P(owned_land_pos&shares_excl_pos)": 0.0005922878672022869, + "ratio_vs_indep(owned_land_pos,shares_excl_pos)": 2.421421382773783, + "P(shares_excl_pos|owned_land_pos)": 0.37079096830608343, + "P(shares_excl_pos|not owned_land_pos)": 0.15278122731631025, + "P(owned_land_pos&isa_pos)": 0.0008416337858327415, + "ratio_vs_indep(owned_land_pos,isa_pos)": 4.327119390484086, + "P(isa_pos|owned_land_pos)": 0.5268894125454949, + "P(isa_pos|not owned_land_pos)": 0.1211163060327941, + "P(property_wealth_pos)": 0.6623059654375011, + "P(property_wealth_pos&pension_pos)": 0.5633989056025666, + "ratio_vs_indep(property_wealth_pos,pension_pos)": 1.1280996853400094, + "P(pension_pos|property_wealth_pos)": 0.850662586483576, + "P(pension_pos|not property_wealth_pos)": 0.5646174761456335, + "P(property_wealth_pos&shares_excl_pos)": 0.14344728639004395, + "ratio_vs_indep(property_wealth_pos,shares_excl_pos)": 1.414408626290002, + "P(shares_excl_pos|property_wealth_pos)": 0.21658764057075436, + "P(shares_excl_pos|not property_wealth_pos)": 0.02867146198716859, + "P(property_wealth_pos&isa_pos)": 0.11361904301768104, + "ratio_vs_indep(property_wealth_pos,isa_pos)": 1.408873065502994, + "P(isa_pos|property_wealth_pos)": 0.17155068646049024, + "P(isa_pos|not property_wealth_pos)": 0.024120740169248284, + "P(pension_pos)": 0.754066859106681, + "P(pension_pos&shares_excl_pos)": 0.13870312468977797, + "ratio_vs_indep(pension_pos,shares_excl_pos)": 1.2012063430973448, + "P(shares_excl_pos|pension_pos)": 0.18394008835515613, + "P(shares_excl_pos|not pension_pos)": 0.05865961506089223, + "P(pension_pos&isa_pos)": 0.11376760358643971, + "ratio_vs_indep(pension_pos,isa_pos)": 1.2390480890624502, + "P(isa_pos|pension_pos)": 0.1508720376880328, + "P(isa_pos|not pension_pos)": 0.03251643705513199, + "P(shares_excl_pos)": 0.15312946806529623, + "P(shares_excl_pos&isa_pos)": 0.05476757708045537, + "ratio_vs_indep(shares_excl_pos,isa_pos)": 2.9372719673791416, + "P(isa_pos|shares_excl_pos)": 0.35765537340665104, + "P(isa_pos|not shares_excl_pos)": 0.07911114329194596, + "P(isa_pos)": 0.12176447308206823, + "seconds": 52.8 + }, + "elapsed_seconds": 4188.6 +} diff --git a/experiments/452-uk-pension-wealth-split-receipt.json b/experiments/452-uk-pension-wealth-split-receipt.json new file mode 100644 index 0000000000..6f78abce1d --- /dev/null +++ b/experiments/452-uk-pension-wealth-split-receipt.json @@ -0,0 +1,934 @@ +{ + "title": "UC caseload effect of splitting private pension wealth out of corporate_wealth (uk-data#452 mechanism M2)", + "date": "2026-08-23", + "method": "WAS wealth block re-imputed on every household of the released enhanced FRS h5 with the microcosm WAS stage code (clean_was_household_table -> impute_was_wealth -> support_clip_to_donor; seed 0; 100 trees), once with the pre-split arithmetic (OLD: pension inside corporate_wealth_excl_isa) and once with the split (NEW: private_pension_wealth drawn first in chain segment 2, share-like components conditional on it, segment 3 conditional on both); household wealth columns swapped into the single-year dataset before the simulation is built so stocks uprate 2024->2025 by the engine's registry; UC measured at 2025 with policyengine-uk 2.91.0 (c93e1a05); benefit-unit weights = the artifact's calibrated weights, unchanged.", + "artifact": { + "filename": "enhanced_frs_2024_25.h5", + "sha256": "e433e532b17bd8ce76030156285816e33d44e93edabd2204adbef71d19a68712", + "release": "policyengine-uk-data 1.56.16 (HF revision a9e52499)", + "dataset_year": 2024 + }, + "was_donor": { + "tab": "was_round_8_hhold_eul_may_2025_230525.tab", + "sha256": "18b3eb980c02c99f3d8a3254af859bee31682b2bdc11703877677292b3ce9374", + "households": 15128 + }, + "recipients": 52846, + "engine": "policyengine-uk 2.91.0 @ c93e1a05, policyengine-core 3.31.0", + "seed": 0, + "n_estimators": 100, + "year": 2025, + "donor_aggregates_weighted_bn": { + "total_private_pension_wealth_totalpen": 4807.7, + "current_db_dvvaldbt_scape": 1148.7, + "private_pension_wealth_remainder": 3659.0, + "share_like_corporate_wealth": 659.4, + "pension_share_of_old_corporate_wealth_mass": 0.847, + "negative_remainder_rows": 0 + }, + "draw_aggregates_2024": { + "incumbent": { + "owned_land": { + "weighted_total_bn": 397.625, + "nonzero_weighted_share": 0.0166, + "over_16k_weighted_share": 0.0152, + "weighted_mean_gbp": 12779.0 + }, + "property_wealth": { + "weighted_total_bn": 8029.152, + "nonzero_weighted_share": 0.6658, + "over_16k_weighted_share": 0.6656, + "weighted_mean_gbp": 258044.0 + }, + "corporate_wealth": { + "weighted_total_bn": 9149.019, + "nonzero_weighted_share": 0.7754, + "over_16k_weighted_share": 0.6376, + "weighted_mean_gbp": 294035.0 + }, + "gross_financial_wealth": { + "weighted_total_bn": 7554.757, + "nonzero_weighted_share": 0.9821, + "over_16k_weighted_share": 0.5875, + "weighted_mean_gbp": 242798.0 + }, + "net_financial_wealth": { + "weighted_total_bn": 7446.631, + "nonzero_weighted_share": 0.9986, + "over_16k_weighted_share": 0.5448, + "weighted_mean_gbp": 239323.0 + }, + "main_residence_value": { + "weighted_total_bn": 6733.325, + "nonzero_weighted_share": 0.6266, + "over_16k_weighted_share": 0.6263, + "weighted_mean_gbp": 216398.0 + }, + "other_residential_property_value": { + "weighted_total_bn": 1793.852, + "nonzero_weighted_share": 0.1019, + "over_16k_weighted_share": 0.1004, + "weighted_mean_gbp": 57652.0 + }, + "non_residential_property_value": { + "weighted_total_bn": 190.364, + "nonzero_weighted_share": 0.0134, + "over_16k_weighted_share": 0.0133, + "weighted_mean_gbp": 6118.0 + }, + "savings": { + "weighted_total_bn": 2979.734, + "nonzero_weighted_share": 0.6538, + "over_16k_weighted_share": 0.3865, + "weighted_mean_gbp": 95764.0 + }, + "num_vehicles": { + "weighted_total_bn": 0.041, + "nonzero_weighted_share": 0.7731, + "over_16k_weighted_share": 0.0, + "weighted_mean_gbp": 1.0 + } + }, + "old_redraw": { + "owned_land": { + "weighted_total_bn": 119.939, + "nonzero_weighted_share": 0.0126, + "over_16k_weighted_share": 0.0112, + "weighted_mean_gbp": 3855.0 + }, + "property_wealth": { + "weighted_total_bn": 11411.434, + "nonzero_weighted_share": 0.6313, + "over_16k_weighted_share": 0.6313, + "weighted_mean_gbp": 366745.0 + }, + "corporate_wealth": { + "weighted_total_bn": 8433.52, + "nonzero_weighted_share": 0.7681, + "over_16k_weighted_share": 0.6233, + "weighted_mean_gbp": 271040.0 + }, + "gross_financial_wealth": { + "weighted_total_bn": 6537.319, + "nonzero_weighted_share": 0.9792, + "over_16k_weighted_share": 0.5924, + "weighted_mean_gbp": 210099.0 + }, + "net_financial_wealth": { + "weighted_total_bn": 6259.321, + "nonzero_weighted_share": 0.9918, + "over_16k_weighted_share": 0.5338, + "weighted_mean_gbp": 201165.0 + }, + "main_residence_value": { + "weighted_total_bn": 8495.688, + "nonzero_weighted_share": 0.5931, + "over_16k_weighted_share": 0.5931, + "weighted_mean_gbp": 273038.0 + }, + "other_residential_property_value": { + "weighted_total_bn": 535.644, + "nonzero_weighted_share": 0.0448, + "over_16k_weighted_share": 0.0442, + "weighted_mean_gbp": 17215.0 + }, + "non_residential_property_value": { + "weighted_total_bn": 91.833, + "nonzero_weighted_share": 0.0144, + "over_16k_weighted_share": 0.0135, + "weighted_mean_gbp": 2951.0 + }, + "savings": { + "weighted_total_bn": 1478.066, + "nonzero_weighted_share": 0.6326, + "over_16k_weighted_share": 0.2636, + "weighted_mean_gbp": 47503.0 + }, + "num_vehicles": { + "weighted_total_bn": 0.035, + "nonzero_weighted_share": 0.7586, + "over_16k_weighted_share": 0.0, + "weighted_mean_gbp": 1.0 + }, + "cash_isa": { + "weighted_total_bn": 348.03, + "nonzero_weighted_share": 0.3511, + "over_16k_weighted_share": 0.1414, + "weighted_mean_gbp": 11185.0 + }, + "stocks_and_shares_isa": { + "weighted_total_bn": 1108.837, + "nonzero_weighted_share": 0.2154, + "over_16k_weighted_share": 0.1711, + "weighted_mean_gbp": 35636.0 + } + }, + "new_redraw": { + "owned_land": { + "weighted_total_bn": 119.939, + "nonzero_weighted_share": 0.0126, + "over_16k_weighted_share": 0.0112, + "weighted_mean_gbp": 3855.0 + }, + "property_wealth": { + "weighted_total_bn": 11411.434, + "nonzero_weighted_share": 0.6313, + "over_16k_weighted_share": 0.6313, + "weighted_mean_gbp": 366745.0 + }, + "corporate_wealth": { + "weighted_total_bn": 2499.183, + "nonzero_weighted_share": 0.3497, + "over_16k_weighted_share": 0.2389, + "weighted_mean_gbp": 80320.0 + }, + "private_pension_wealth": { + "weighted_total_bn": 5105.551, + "nonzero_weighted_share": 0.7299, + "over_16k_weighted_share": 0.5911, + "weighted_mean_gbp": 164084.0 + }, + "gross_financial_wealth": { + "weighted_total_bn": 5952.685, + "nonzero_weighted_share": 0.981, + "over_16k_weighted_share": 0.6004, + "weighted_mean_gbp": 191310.0 + }, + "net_financial_wealth": { + "weighted_total_bn": 5616.748, + "nonzero_weighted_share": 0.9931, + "over_16k_weighted_share": 0.5329, + "weighted_mean_gbp": 180513.0 + }, + "main_residence_value": { + "weighted_total_bn": 8506.967, + "nonzero_weighted_share": 0.593, + "over_16k_weighted_share": 0.593, + "weighted_mean_gbp": 273400.0 + }, + "other_residential_property_value": { + "weighted_total_bn": 469.83, + "nonzero_weighted_share": 0.0426, + "over_16k_weighted_share": 0.0419, + "weighted_mean_gbp": 15100.0 + }, + "non_residential_property_value": { + "weighted_total_bn": 90.385, + "nonzero_weighted_share": 0.0154, + "over_16k_weighted_share": 0.0129, + "weighted_mean_gbp": 2905.0 + }, + "savings": { + "weighted_total_bn": 1488.956, + "nonzero_weighted_share": 0.6387, + "over_16k_weighted_share": 0.2853, + "weighted_mean_gbp": 47853.0 + }, + "num_vehicles": { + "weighted_total_bn": 0.036, + "nonzero_weighted_share": 0.7656, + "over_16k_weighted_share": 0.0, + "weighted_mean_gbp": 1.0 + }, + "cash_isa": { + "weighted_total_bn": 313.302, + "nonzero_weighted_share": 0.3609, + "over_16k_weighted_share": 0.1476, + "weighted_mean_gbp": 10069.0 + }, + "stocks_and_shares_isa": { + "weighted_total_bn": 845.516, + "nonzero_weighted_share": 0.2013, + "over_16k_weighted_share": 0.1633, + "weighted_mean_gbp": 27174.0 + } + }, + "new_redraw_per_segment_seeds": { + "owned_land": { + "weighted_total_bn": 110.987, + "nonzero_weighted_share": 0.0154, + "over_16k_weighted_share": 0.0134, + "weighted_mean_gbp": 3567.0 + }, + "property_wealth": { + "weighted_total_bn": 11368.05, + "nonzero_weighted_share": 0.6503, + "over_16k_weighted_share": 0.6502, + "weighted_mean_gbp": 365351.0 + }, + "corporate_wealth": { + "weighted_total_bn": 1794.458, + "nonzero_weighted_share": 0.3532, + "over_16k_weighted_share": 0.2306, + "weighted_mean_gbp": 57671.0 + }, + "private_pension_wealth": { + "weighted_total_bn": 5673.223, + "nonzero_weighted_share": 0.7081, + "over_16k_weighted_share": 0.5849, + "weighted_mean_gbp": 182328.0 + }, + "gross_financial_wealth": { + "weighted_total_bn": 4812.26, + "nonzero_weighted_share": 0.9819, + "over_16k_weighted_share": 0.6265, + "weighted_mean_gbp": 154658.0 + }, + "net_financial_wealth": { + "weighted_total_bn": 4548.989, + "nonzero_weighted_share": 0.9981, + "over_16k_weighted_share": 0.5664, + "weighted_mean_gbp": 146197.0 + }, + "main_residence_value": { + "weighted_total_bn": 8594.245, + "nonzero_weighted_share": 0.6004, + "over_16k_weighted_share": 0.6004, + "weighted_mean_gbp": 276205.0 + }, + "other_residential_property_value": { + "weighted_total_bn": 679.914, + "nonzero_weighted_share": 0.0563, + "over_16k_weighted_share": 0.055, + "weighted_mean_gbp": 21851.0 + }, + "non_residential_property_value": { + "weighted_total_bn": 137.889, + "nonzero_weighted_share": 0.0133, + "over_16k_weighted_share": 0.0124, + "weighted_mean_gbp": 4432.0 + }, + "savings": { + "weighted_total_bn": 898.63, + "nonzero_weighted_share": 0.6472, + "over_16k_weighted_share": 0.2824, + "weighted_mean_gbp": 28881.0 + }, + "num_vehicles": { + "weighted_total_bn": 0.036, + "nonzero_weighted_share": 0.7587, + "over_16k_weighted_share": 0.0, + "weighted_mean_gbp": 1.0 + }, + "cash_isa": { + "weighted_total_bn": 351.255, + "nonzero_weighted_share": 0.3493, + "over_16k_weighted_share": 0.1753, + "weighted_mean_gbp": 11289.0 + }, + "stocks_and_shares_isa": { + "weighted_total_bn": 747.627, + "nonzero_weighted_share": 0.2097, + "over_16k_weighted_share": 0.1722, + "weighted_mean_gbp": 24028.0 + } + } + }, + "uc_2025": { + "incumbent_artifact": { + "as_is": { + "uk_m": 6.3462, + "gb_m": 6.199, + "records": 4830, + "spend_bn": 75.301, + "eligible_m": 10.5156, + "eligible_unweighted": 14886, + "reporters_unweighted": 6447, + "reporters_m": 5.9252, + "reporters_over_16k_unweighted": 2958, + "reporters_over_16k_m": 1.3778, + "reporters_on_uc_m": 4.3178, + "reporters_not_on_uc_m": 1.6073, + "on_uc_not_reported_m": 2.0284, + "gb_housing_m": 4.8684, + "gb_lcwra_m": 2.1017, + "gb_carer_m": 0.6414, + "gb_childcare_m": 0.4712, + "gb_child_m": 3.0183, + "gb_housing_spend_bn": 37.05, + "gb_lcwra_spend_bn": 11.324, + "gb_carer_spend_bn": 1.552, + "gb_childcare_spend_bn": 2.037, + "gb_child_spend_bn": 18.944, + "gb_single_m": 2.9688, + "gb_lone_parent_m": 2.1347, + "gb_couple_no_children_m": 0.225, + "gb_couple_with_children_m": 0.8705 + }, + "reporters_anchored": { + "uk_m": 6.3632, + "gb_m": 6.214, + "records": 4892, + "spend_bn": 75.401, + "eligible_m": 10.5156, + "eligible_unweighted": 14886, + "reporters_unweighted": 6447, + "reporters_m": 5.9252, + "reporters_over_16k_unweighted": 2958, + "reporters_over_16k_m": 1.3778, + "reporters_on_uc_m": 4.3348, + "reporters_not_on_uc_m": 1.5904, + "on_uc_not_reported_m": 2.0284, + "gb_housing_m": 4.8734, + "gb_lcwra_m": 2.1115, + "gb_carer_m": 0.6422, + "gb_childcare_m": 0.4712, + "gb_child_m": 3.0197, + "gb_housing_spend_bn": 37.091, + "gb_lcwra_spend_bn": 11.374, + "gb_carer_spend_bn": 1.554, + "gb_childcare_spend_bn": 2.037, + "gb_child_spend_bn": 18.949, + "gb_single_m": 2.9807, + "gb_lone_parent_m": 2.1347, + "gb_couple_no_children_m": 0.2268, + "gb_couple_with_children_m": 0.8718 + } + }, + "incumbent_counterfactual_C_corporate_wealth_zero": { + "as_is": { + "uk_m": 6.8811, + "gb_m": 6.6933, + "records": 6594, + "spend_bn": 78.231, + "gb_housing_m": 5.0564, + "gb_lcwra_m": 2.1931, + "gb_carer_m": 0.682, + "gb_childcare_m": 0.4923, + "gb_child_m": 3.1004, + "gb_single_m": 3.2767, + "gb_lone_parent_m": 2.1602, + "gb_couple_no_children_m": 0.3271, + "gb_couple_with_children_m": 0.9293 + }, + "reporters_anchored": { + "uk_m": 6.9518, + "gb_m": 6.7615, + "records": 6724, + "spend_bn": 78.665, + "gb_housing_m": 5.0934, + "gb_lcwra_m": 2.2439, + "gb_carer_m": 0.683, + "gb_childcare_m": 0.4994, + "gb_child_m": 3.1104, + "gb_single_m": 3.3282, + "gb_lone_parent_m": 2.1604, + "gb_couple_no_children_m": 0.3338, + "gb_couple_with_children_m": 0.9391 + } + }, + "microcosm_stage_old_arithmetic": { + "as_is": { + "uk_m": 5.1926, + "gb_m": 5.036, + "records": 4683, + "spend_bn": 63.14, + "eligible_m": 9.7699, + "eligible_unweighted": 13335, + "reporters_unweighted": 6447, + "reporters_m": 5.9252, + "reporters_over_16k_unweighted": 3165, + "reporters_over_16k_m": 2.0397, + "reporters_on_uc_m": 3.6493, + "reporters_not_on_uc_m": 2.2758, + "on_uc_not_reported_m": 1.5432, + "gb_housing_m": 4.0819, + "gb_lcwra_m": 1.6962, + "gb_carer_m": 0.5612, + "gb_childcare_m": 0.2599, + "gb_child_m": 2.3445, + "gb_housing_spend_bn": 30.214, + "gb_lcwra_spend_bn": 9.14, + "gb_carer_spend_bn": 1.358, + "gb_childcare_spend_bn": 1.196, + "gb_child_spend_bn": 14.508, + "gb_single_m": 2.4714, + "gb_lone_parent_m": 1.6566, + "gb_couple_no_children_m": 0.2223, + "gb_couple_with_children_m": 0.6857 + }, + "reporters_anchored": { + "uk_m": 5.2172, + "gb_m": 5.0583, + "records": 4771, + "spend_bn": 63.306, + "eligible_m": 9.7699, + "eligible_unweighted": 13335, + "reporters_unweighted": 6447, + "reporters_m": 5.9252, + "reporters_over_16k_unweighted": 3165, + "reporters_over_16k_m": 2.0397, + "reporters_on_uc_m": 3.6739, + "reporters_not_on_uc_m": 2.2513, + "on_uc_not_reported_m": 1.5432, + "gb_housing_m": 4.0879, + "gb_lcwra_m": 1.7107, + "gb_carer_m": 0.5614, + "gb_childcare_m": 0.2616, + "gb_child_m": 2.3475, + "gb_housing_spend_bn": 30.274, + "gb_lcwra_spend_bn": 9.213, + "gb_carer_spend_bn": 1.359, + "gb_childcare_spend_bn": 1.216, + "gb_child_spend_bn": 14.524, + "gb_single_m": 2.4871, + "gb_lone_parent_m": 1.6567, + "gb_couple_no_children_m": 0.226, + "gb_couple_with_children_m": 0.6885 + } + }, + "microcosm_stage_new_arithmetic": { + "as_is": { + "uk_m": 6.2656, + "gb_m": 6.0637, + "records": 6537, + "spend_bn": 71.747, + "eligible_m": 16.2551, + "eligible_unweighted": 25623, + "reporters_unweighted": 6447, + "reporters_m": 5.9252, + "reporters_over_16k_unweighted": 1757, + "reporters_over_16k_m": 1.2815, + "reporters_on_uc_m": 4.1635, + "reporters_not_on_uc_m": 1.7617, + "on_uc_not_reported_m": 2.1021, + "gb_housing_m": 4.5921, + "gb_lcwra_m": 2.0547, + "gb_carer_m": 0.645, + "gb_childcare_m": 0.4027, + "gb_child_m": 2.8687, + "gb_housing_spend_bn": 34.147, + "gb_lcwra_spend_bn": 11.186, + "gb_carer_spend_bn": 1.561, + "gb_childcare_spend_bn": 1.767, + "gb_child_spend_bn": 17.846, + "gb_single_m": 2.8797, + "gb_lone_parent_m": 1.9224, + "gb_couple_no_children_m": 0.3263, + "gb_couple_with_children_m": 0.9353 + }, + "reporters_anchored": { + "uk_m": 6.3397, + "gb_m": 6.1349, + "records": 6696, + "spend_bn": 72.251, + "eligible_m": 16.2551, + "eligible_unweighted": 25623, + "reporters_unweighted": 6447, + "reporters_m": 5.9252, + "reporters_over_16k_unweighted": 1757, + "reporters_over_16k_m": 1.2815, + "reporters_on_uc_m": 4.2376, + "reporters_not_on_uc_m": 1.6875, + "on_uc_not_reported_m": 2.1021, + "gb_housing_m": 4.6319, + "gb_lcwra_m": 2.106, + "gb_carer_m": 0.646, + "gb_childcare_m": 0.4135, + "gb_child_m": 2.8842, + "gb_housing_spend_bn": 34.391, + "gb_lcwra_spend_bn": 11.446, + "gb_carer_spend_bn": 1.563, + "gb_childcare_spend_bn": 1.911, + "gb_child_spend_bn": 17.916, + "gb_single_m": 2.929, + "gb_lone_parent_m": 1.923, + "gb_couple_no_children_m": 0.333, + "gb_couple_with_children_m": 0.9499 + } + }, + "microcosm_stage_new_arithmetic_per_segment_seeds": { + "as_is": { + "uk_m": 6.2682, + "gb_m": 6.0898, + "records": 6591, + "spend_bn": 71.41, + "eligible_m": 15.5714, + "eligible_unweighted": 25009, + "reporters_unweighted": 6447, + "reporters_m": 5.9252, + "reporters_over_16k_unweighted": 1742, + "reporters_over_16k_m": 1.239, + "reporters_on_uc_m": 4.1938, + "reporters_not_on_uc_m": 1.7314, + "on_uc_not_reported_m": 2.0745, + "gb_housing_m": 4.586, + "gb_lcwra_m": 2.057, + "gb_carer_m": 0.6224, + "gb_childcare_m": 0.4007, + "gb_child_m": 2.817, + "gb_housing_spend_bn": 34.135, + "gb_lcwra_spend_bn": 11.201, + "gb_carer_spend_bn": 1.506, + "gb_childcare_spend_bn": 1.675, + "gb_child_spend_bn": 17.614, + "gb_single_m": 3.0129, + "gb_lone_parent_m": 1.8969, + "gb_couple_no_children_m": 0.2706, + "gb_couple_with_children_m": 0.9093 + }, + "reporters_anchored": { + "uk_m": 6.3449, + "gb_m": 6.1634, + "records": 6748, + "spend_bn": 71.915, + "eligible_m": 15.5714, + "eligible_unweighted": 25009, + "reporters_unweighted": 6447, + "reporters_m": 5.9252, + "reporters_over_16k_unweighted": 1742, + "reporters_over_16k_m": 1.239, + "reporters_on_uc_m": 4.2704, + "reporters_not_on_uc_m": 1.6547, + "on_uc_not_reported_m": 2.0745, + "gb_housing_m": 4.6242, + "gb_lcwra_m": 2.1099, + "gb_carer_m": 0.6233, + "gb_childcare_m": 0.4099, + "gb_child_m": 2.8305, + "gb_housing_spend_bn": 34.364, + "gb_lcwra_spend_bn": 11.469, + "gb_carer_spend_bn": 1.508, + "gb_childcare_spend_bn": 1.793, + "gb_child_spend_bn": 17.671, + "gb_single_m": 3.0647, + "gb_lone_parent_m": 1.8972, + "gb_couple_no_children_m": 0.2792, + "gb_couple_with_children_m": 0.9223 + } + } + }, + "split_effect_new_minus_old": { + "uk_m": 1.073, + "gb_m": 1.0277, + "records": 1854, + "spend_bn": 8.607, + "eligible_m": 6.4852, + "eligible_unweighted": 12288, + "reporters_unweighted": 0, + "reporters_m": 0.0, + "reporters_over_16k_unweighted": -1408, + "reporters_over_16k_m": -0.7582, + "reporters_on_uc_m": 0.5142, + "reporters_not_on_uc_m": -0.5141, + "on_uc_not_reported_m": 0.5589, + "gb_housing_m": 0.5102, + "gb_lcwra_m": 0.3585, + "gb_carer_m": 0.0838, + "gb_childcare_m": 0.1428, + "gb_child_m": 0.5242, + "gb_housing_spend_bn": 3.933, + "gb_lcwra_spend_bn": 2.046, + "gb_carer_spend_bn": 0.203, + "gb_childcare_spend_bn": 0.571, + "gb_child_spend_bn": 3.338, + "gb_single_m": 0.4083, + "gb_lone_parent_m": 0.2658, + "gb_couple_no_children_m": 0.104, + "gb_couple_with_children_m": 0.2496 + }, + "split_effect_new_minus_old_anchored": { + "uk_m": 1.1225, + "gb_m": 1.0766, + "records": 1925, + "spend_bn": 8.945, + "eligible_m": 6.4852, + "eligible_unweighted": 12288, + "reporters_unweighted": 0, + "reporters_m": 0.0, + "reporters_over_16k_unweighted": -1408, + "reporters_over_16k_m": -0.7582, + "reporters_on_uc_m": 0.5637, + "reporters_not_on_uc_m": -0.5638, + "on_uc_not_reported_m": 0.5589, + "gb_housing_m": 0.544, + "gb_lcwra_m": 0.3953, + "gb_carer_m": 0.0846, + "gb_childcare_m": 0.1519, + "gb_child_m": 0.5367, + "gb_housing_spend_bn": 4.117, + "gb_lcwra_spend_bn": 2.233, + "gb_carer_spend_bn": 0.204, + "gb_childcare_spend_bn": 0.695, + "gb_child_spend_bn": 3.392, + "gb_single_m": 0.4419, + "gb_lone_parent_m": 0.2663, + "gb_couple_no_children_m": 0.107, + "gb_couple_with_children_m": 0.2614 + }, + "allocation_key_concentration_new_draw": { + "narrow": { + "mass_share_by_income_decile": [ + 0.1409, + 0.0571, + 0.0785, + 0.0347, + 0.1194, + 0.0475, + 0.0393, + 0.1052, + 0.1672, + 0.2102 + ], + "top10pct_households_share": 0.8672, + "top1pct_households_share": 0.2871, + "weighted_total_bn": 2499.183 + }, + "preserved": { + "mass_share_by_income_decile": [ + 0.0701, + 0.0441, + 0.0665, + 0.0554, + 0.097, + 0.0804, + 0.0889, + 0.1298, + 0.171, + 0.1967 + ], + "top10pct_households_share": 0.5441, + "top1pct_households_share": 0.1455, + "weighted_total_bn": 7604.734 + } + }, + "caveats": [ + "Weights are the artifact's calibrated weights and are not recalibrated; the UK calibration surface now carries a UC caseload target (microcosm#735), which would absorb part of any move in a real build.", + "OLD and NEW are the same microcosm machinery at the same seed, so NEW-OLD isolates the arithmetic split; OLD is not the incumbent's own draw (microimpute 10-point quantile grid vs RegimeGatedQRF continuous quantile over the full leaf conditional) - the OLD re-draw sits 1.15m benefit units below the incumbent artifact on the same households, a draw-machinery/predictor-surface difference outside this change.", + "The recipient predictor surface is materialised from the released h5 (num_bedrooms is absent there, so the engine default 0 is used; incomes are the post-SPI h5 incomes), not from the FRS spine the build runs on.", + "SPI-synthetic clone households are re-imputed like any household.", + "private_pension_wealth is engine-unknown at policyengine-uk 2.91.0 and is ignored by the loader; the NEW numbers are the model-side-unchanged outcome (total_wealth and the exposure keys shrink there until the model PR lands).", + "Aggregates only (UKDS EUL); no unit records leave the licensed environment." + ], + "segment_seeds_from_declared_seed_0": [ + 3757552657, + 673228719, + 3241444873 + ], + "per_segment_seed_fix": { + "what": "impute_was_wealth now derives one child seed per chain segment from the declared stage seed (SeedSequence(seed).spawn(3)); previously one RegimeGatedQRF was reused and start_chain respawned identical fit/draw streams per segment, coupling the k-th target of every segment.", + "hold_out_receipt_30_trees": { + "P(shares_excl_isa>0 | property_wealth=0)": { + "truth": 0.0551, + "stage_as_is_same_seed": 0.0114, + "production_child_seeds": 0.0387 + }, + "P(stocks_and_shares_isa>0 | property_wealth=0)": { + "truth": 0.0307, + "stage_as_is_same_seed": 0.0174, + "production_child_seeds": 0.0247 + }, + "P(private_pension_wealth>0 | property_wealth=0)": { + "truth": 0.5601, + "stage_as_is_same_seed": 0.5396, + "production_child_seeds": 0.5276 + }, + "source": "adversarial-review coupling check re-run with was_wealth_segment_seeds (the exact production derivation), train/hold-out halves of the donor, aggregates only; an earlier variant with modulo-2^31 seeds measured 0.045" + }, + "uc_effect_new_with_fix_minus_new_without": { + "uk_m": 0.0026, + "gb_m": 0.0261, + "eligible_m": -0.6837, + "reporters_over_16k_m": -0.0425, + "gb_housing_m": -0.0061, + "gb_lcwra_m": 0.0023, + "gb_carer_m": -0.0226, + "gb_childcare_m": -0.002, + "gb_child_m": -0.0517, + "gb_single_m": 0.1332, + "gb_lone_parent_m": -0.0255, + "gb_couple_no_children_m": -0.0557, + "gb_couple_with_children_m": -0.026 + } + }, + "allocation_key_concentration_new_draw_per_segment_seeds": { + "narrow": { + "mass_share_by_income_decile": [ + 0.1138, + 0.0227, + 0.0277, + 0.0379, + 0.1059, + 0.0595, + 0.0761, + 0.1036, + 0.2016, + 0.2512 + ], + "top10pct_households_share": 0.8391, + "top1pct_households_share": 0.2669, + "weighted_total_bn": 1794.458 + }, + "preserved": { + "mass_share_by_income_decile": [ + 0.0461, + 0.0516, + 0.0372, + 0.0475, + 0.0719, + 0.0803, + 0.0939, + 0.1298, + 0.2014, + 0.2404 + ], + "top10pct_households_share": 0.5432, + "top1pct_households_share": 0.1354, + "weighted_total_bn": 7467.681 + } + }, + "fixed_draw_legal_counterfactual": { + "method": "On the final code's own draws (per-segment seeds), the drawn private_pension_wealth column is folded back into corporate_wealth on the SAME rows and UC recomputed: every draw identical, only the capital-test classification differs. Split minus folded is therefore the fixed-draw legal effect of the disregard (cross-family review design).", + "folded_back": { + "as_is": { + "uk_m": 4.8691, + "gb_m": 4.7612, + "records": 4584, + "spend_bn": 59.707, + "eligible_m": 8.6708, + "eligible_unweighted": 12690, + "reporters_unweighted": 6447, + "reporters_m": 5.9252, + "reporters_over_16k_unweighted": 3195, + "reporters_over_16k_m": 2.3732, + "reporters_on_uc_m": 3.385, + "reporters_not_on_uc_m": 2.5402, + "on_uc_not_reported_m": 1.4841, + "gb_housing_m": 3.7693, + "gb_lcwra_m": 1.7026, + "gb_carer_m": 0.5653, + "gb_childcare_m": 0.2352, + "gb_child_m": 2.2109, + "gb_housing_spend_bn": 27.806, + "gb_lcwra_spend_bn": 9.303, + "gb_carer_spend_bn": 1.368, + "gb_childcare_spend_bn": 1.029, + "gb_child_spend_bn": 13.764, + "gb_single_m": 2.3636, + "gb_lone_parent_m": 1.5224, + "gb_couple_no_children_m": 0.1916, + "gb_couple_with_children_m": 0.6837 + }, + "reporters_anchored": { + "uk_m": 4.889, + "gb_m": 4.7788, + "records": 4665, + "spend_bn": 59.835, + "eligible_m": 8.6708, + "eligible_unweighted": 12690, + "reporters_unweighted": 6447, + "reporters_m": 5.9252, + "reporters_over_16k_unweighted": 3195, + "reporters_over_16k_m": 2.3732, + "reporters_on_uc_m": 3.4049, + "reporters_not_on_uc_m": 2.5203, + "on_uc_not_reported_m": 1.4841, + "gb_housing_m": 3.774, + "gb_lcwra_m": 1.7062, + "gb_carer_m": 0.5656, + "gb_childcare_m": 0.2427, + "gb_child_m": 2.2187, + "gb_housing_spend_bn": 27.867, + "gb_lcwra_spend_bn": 9.322, + "gb_carer_spend_bn": 1.369, + "gb_childcare_spend_bn": 1.127, + "gb_child_spend_bn": 13.795, + "gb_single_m": 2.3693, + "gb_lone_parent_m": 1.5224, + "gb_couple_no_children_m": 0.1957, + "gb_couple_with_children_m": 0.6914 + } + }, + "split": { + "as_is": { + "uk_m": 6.2682, + "gb_m": 6.0898, + "records": 6591, + "spend_bn": 71.41, + "eligible_m": 15.5714, + "eligible_unweighted": 25009, + "reporters_unweighted": 6447, + "reporters_m": 5.9252, + "reporters_over_16k_unweighted": 1742, + "reporters_over_16k_m": 1.239, + "reporters_on_uc_m": 4.1938, + "reporters_not_on_uc_m": 1.7314, + "on_uc_not_reported_m": 2.0745, + "gb_housing_m": 4.586, + "gb_lcwra_m": 2.057, + "gb_carer_m": 0.6224, + "gb_childcare_m": 0.4007, + "gb_child_m": 2.817, + "gb_housing_spend_bn": 34.135, + "gb_lcwra_spend_bn": 11.201, + "gb_carer_spend_bn": 1.506, + "gb_childcare_spend_bn": 1.675, + "gb_child_spend_bn": 17.614, + "gb_single_m": 3.0129, + "gb_lone_parent_m": 1.8969, + "gb_couple_no_children_m": 0.2706, + "gb_couple_with_children_m": 0.9093 + }, + "reporters_anchored": { + "uk_m": 6.3449, + "gb_m": 6.1634, + "records": 6748, + "spend_bn": 71.915, + "eligible_m": 15.5714, + "eligible_unweighted": 25009, + "reporters_unweighted": 6447, + "reporters_m": 5.9252, + "reporters_over_16k_unweighted": 1742, + "reporters_over_16k_m": 1.239, + "reporters_on_uc_m": 4.2704, + "reporters_not_on_uc_m": 1.6547, + "on_uc_not_reported_m": 2.0745, + "gb_housing_m": 4.6242, + "gb_lcwra_m": 2.1099, + "gb_carer_m": 0.6233, + "gb_childcare_m": 0.4099, + "gb_child_m": 2.8305, + "gb_housing_spend_bn": 34.364, + "gb_lcwra_spend_bn": 11.469, + "gb_carer_spend_bn": 1.508, + "gb_childcare_spend_bn": 1.793, + "gb_child_spend_bn": 17.671, + "gb_single_m": 3.0647, + "gb_lone_parent_m": 1.8972, + "gb_couple_no_children_m": 0.2792, + "gb_couple_with_children_m": 0.9223 + } + }, + "fixed_draw_split_effect_as_is": { + "uk_m": 1.3991, + "gb_m": 1.3286, + "records": 2007, + "spend_bn": 11.703, + "eligible_m": 6.9006, + "eligible_unweighted": 12319, + "reporters_unweighted": 0, + "reporters_m": 0.0, + "reporters_over_16k_unweighted": -1453, + "reporters_over_16k_m": -1.1342, + "reporters_on_uc_m": 0.8088, + "reporters_not_on_uc_m": -0.8088, + "on_uc_not_reported_m": 0.5904, + "gb_housing_m": 0.8167, + "gb_housing_spend_bn": 6.329, + "gb_lcwra_m": 0.3544, + "gb_lcwra_spend_bn": 1.898, + "gb_carer_m": 0.0571, + "gb_carer_spend_bn": 0.138, + "gb_childcare_m": 0.1655, + "gb_childcare_spend_bn": 0.646, + "gb_child_m": 0.6061, + "gb_child_spend_bn": 3.85, + "gb_single_m": 0.6493, + "gb_lone_parent_m": 0.3745, + "gb_couple_no_children_m": 0.079, + "gb_couple_with_children_m": 0.2256 + } + }, + "note": "weights unchanged (incumbent calibrated weights); WAS wealth block re-imputed on all h5 households with microcosm's stage code and swapped into the dataset before build; aggregates only. The OLD/NEW redraw pair shares one seed pattern and is a whole-implementation comparison, not a fixed-draw effect - the fixed_draw_legal_counterfactual block is the causal number for the disregard itself.", + "microcosm_code_state": "branch uk-was-pension-wealth-split-452; receipts regenerated at the post-review commit (see git log)" +} diff --git a/experiments/452-uk-pension-wealth-split.md b/experiments/452-uk-pension-wealth-split.md new file mode 100644 index 0000000000..9d3c9ada9a --- /dev/null +++ b/experiments/452-uk-pension-wealth-split.md @@ -0,0 +1,167 @@ +# #750 / uk-data#452 (M2): private pension wealth split out of `corporate_wealth` — measurement receipt + +Committed receipt for the WAS-stage change that emits `private_pension_wealth` +as its own household column and keeps `corporate_wealth` to the share-like +holdings. Companion JSON: `452-uk-pension-wealth-split-receipt.json` +(aggregates only — UKDS EUL; no unit records). Scripts and the full +diagnosis live outside the repo (`~/PolicyEngine/_uk-defects-452-448/`, +`measure/pension_split/`). + +## Why + +On the incumbent enhanced FRS (`enhanced_frs_2024_25.h5`, uk-data 1.56.14 / +1.56.16) at 2025, 46% of benefit-unit records that report Universal Credit +fail the £16,000 capital test, 96% of them on `corporate_wealth` alone. The +WAS stage folds WAS private pension wealth less the current-employment DB +component (`totalpenr8_aggr − dvvaldbt_scaper8_aggr`) into +`corporate_wealth`, and the model counts that variable as UC capital. The +value of a right to receive a pension under an occupational or personal +pension scheme is disregarded capital (UC Regs 2013 Sch 10 para 10; HB Regs +2006 Sch 6 paras 31–32 and HB(SPC) Regs 2006 Sch 6 para 24; JSA Regs 1996 +Sch 8 paras 28–29; ESA Regs 2008 Sch 9 paras 28–29; IS Regs 1987 Sch 10 +paras 23–23A; SPC Regs 2002 Sch V paras 22–23, Part I). + +## What the remainder is (donor, weighted £bn) + +WAS round 8 household tab (sha `18b3eb98…`, 15,128 households, 26.99m +weighted): total private pension wealth 4,807.7; current-employment DB at +the SCAPE rate 1,148.7; remainder 3,659.0 = current occupational DC 313.9 + +AVCs 12.2 + retained DC 550.0 + current personal pensions 203.7 + retained +DB 863.6 + pensions in payment 1,692.4 + expected from a former spouse 23.3 +(the ten-component identity is exact on 100% of rows; the remainder is never +negative). Share-like holdings (employee shares, UK shares, unit and +investment trusts, stocks-and-shares ISA) 659.4, so the pension component is +84.7% of the old `corporate_wealth` mass. Hence the column is **not** +DB-free (retained DB and DB pensions in payment stay inside it): it is named +`private_pension_wealth` with the current-DB exclusion stated first in its +documentation, not `non_db_pension_wealth`. + +## The fixed-draw legal effect (the causal number) + +On the final code's own draws (per-segment seeds, seed 0, 100 trees), the +drawn `private_pension_wealth` column was folded back into `corporate_wealth` +on the same rows and UC recomputed — every draw identical, only the +capital-test classification differs. Split minus folded, 2025, weights +unchanged: + +| | folded back (pension counted) | split (pension disregarded) | fixed-draw effect | +|---|---|---|---| +| UC benefit units, UK | 4.869 | 6.268 | **+1.399** | +| UC benefit units, GB | 4.761 | 6.090 | +1.329 | +| UC spend, £bn | 59.7 | 71.4 | +11.7 | +| eligible benefit units | 8.67 | 15.57 | +6.90 | +| reporter records over £16k | 3,195 | 1,742 | −1,453 | +| GB housing / LCWRA / carer / childcare / child elements | 3.77 / 1.70 / 0.57 / 0.24 / 2.21 | 4.59 / 2.06 / 0.62 / 0.40 / 2.82 | +0.82 / +0.35 / +0.06 / +0.17 / +0.61 | +| GB single / lone parent / couple no children / couple with children | 2.36 / 1.52 / 0.19 / 0.68 | 3.01 / 1.90 / 0.27 / 0.91 | +0.65 / +0.37 / +0.08 / +0.23 | + +With reporters anchored: 4.889 → 6.345 (+1.456). + +## Whole-implementation comparison (redraw pair, shared-seed variant) + +The WAS wealth block was re-imputed on every household of the 1.56.16 +artifact (seed 0, 100 trees), once with the pre-split arithmetic (OLD) and +once with the split (NEW), both under the pre-fix shared-seed pattern, the +household columns swapped into the single-year dataset before the simulation +is built, and UC measured at 2025 with policyengine-uk 2.91.0. OLD and NEW +are independently fitted and drawn, so this pair compares whole +implementations rather than isolating the classification (the fixed-draw +table above does that); no matched OLD′ exists for the final per-segment-seed +branch. Benefit units in millions; GB = excluding Northern Ireland. + +| | incumbent artifact | OLD re-draw | NEW re-draw | NEW − OLD | +|---|---|---|---|---| +| UC benefit units, UK | 6.346 | 5.193 | 6.266 | **+1.073** | +| UC benefit units, GB | 6.199 | 5.036 | 6.064 | +1.028 | +| UC spend, £bn | 75.3 | 63.1 | 71.7 | +8.6 | +| eligible benefit units (WA adult, capital ≤ £16k) | 10.52 | 9.77 | 16.26 | +6.49 | +| reporter records over £16k (weighted m) | 2,958 (1.38) | 3,165 (2.04) | 1,757 (1.28) | −1,408 (−0.76) | +| GB housing element | 4.868 | 4.082 | 4.592 | +0.510 | +| GB LCWRA element | 2.102 | 1.696 | 2.055 | +0.359 | +| GB carer element | 0.641 | 0.561 | 0.645 | +0.084 | +| GB childcare element | 0.471 | 0.260 | 0.403 | +0.143 | +| GB child element | 3.018 | 2.345 | 2.869 | +0.524 | +| GB single | 2.969 | 2.471 | 2.880 | +0.408 | +| GB lone parent | 2.135 | 1.657 | 1.922 | +0.266 | +| GB couple, no children | 0.225 | 0.222 | 0.326 | +0.104 | +| GB couple with children | 0.871 | 0.686 | 0.935 | +0.250 | + +With FRS reporters anchored (`would_claim_uc |= universal_credit_reported > 0`): +OLD 5.217 → NEW 6.340 (UK), NEW′ 6.345. The incumbent's own upper bound (counterfactual +C, `corporate_wealth` → 0 on the incumbent draws) is 6.881 UK. + +Allocation-key check on the NEW draw (why the model keeps +`corporate_sector_wealth = corporate_wealth + private_pension_wealth` as the +key for shareholding, corporate land value and the employer-NI capital +response): the narrowed key puts 86.7% of its mass in the top 10% of +households and 28.7% in the top 1% (preserved key: 54.4% / 14.6%). + +Regression receipt on the same run: chain segment 1 is untouched by the +split, so the `owned_land` and `property_wealth` draws are identical OLD vs +NEW at seed 0 (weighted totals 119.939 and 11,411.434 £bn in both); only the +segment-2/3 columns move. On this re-draw the recipient-side `corporate_wealth` +mass is 2,499 vs the incumbent's 9,149 £bn at 2024 (drift −0.73), inside the +`uk_input_mass_parity` tolerance. + +Engine coupling: `private_pension_wealth` is engine-unknown until +policyengine-uk publishes the companion variable; bump the `uk` extra lock to +that release before any national build or calibration (the UK targets +`ons.land.corporate_land_value` / `land_value` are materialised through the +engine's `corporate_sector_wealth` key), and re-pin the `efrs-post-calibration` +input-mass reference only after the uk-data mirror ships (re-pinning first +would breach the gate on `corporate_wealth`). + +## Per-segment child seeds (review finding, second commit) + +`impute_was_wealth` reused one `RegimeGatedQRF` across its three chain +segments, and `start_chain` respawns the fit and draw streams from the model +seed on every call — so the k-th target of every segment consumed the same +quantile and sign-gate uniforms per recipient, coupling `owned_land` with +the first segment-2 target and `property_wealth` with the second (now the +share-like holdings, the countable quantity). The adversarial review measured +it on train/hold-out halves of the donor (30 trees; companion JSON +`452-uk-pension-wealth-split-coupling-receipt.json`): + +| hold-out probability | observed | stage as-is (same seed) | production child seeds | +|---|---|---|---| +| P(shares excl. ISA > 0 \| property_wealth = 0) | 0.055 | 0.011 | 0.039 | +| P(stocks-and-shares ISA > 0 \| property_wealth = 0) | 0.031 | 0.017 | 0.025 | +| P(private pension wealth > 0 \| property_wealth = 0) | 0.560 | 0.540 | 0.528 | + +(The receipt was re-run with `was_wealth_segment_seeds` — the exact production +derivation — after review caught that the first variant derived its child +seeds with a modulo-2^31 truncation and measured 0.045; the unit test now +golden-pins the production tuple.) + +The stage now derives one child seed per segment from the declared seed +(`SeedSequence(0).spawn(3)` → 3757552657, 673228719, 3241444873); the +declared `fit_weighted_qrf_chain.seed: 0` stays the root and the spec digest +is unchanged (the note is documentation). Re-measured with the fix (NEW′): +UC benefit units 6.268m UK (vs 6.266m without the fix), eligible 15.57m (vs +16.26m), reporter records over £16k 1,742 (vs 1,757); GB elements within +±0.05m except singles +0.13m and couples without children −0.06m. The +caseload effect of the split is unchanged; the fix corrects who holds the +countable assets. Seed-to-seed swings in the tail-dominated totals +(`corporate_wealth` 2,499 → 1,794 £bn, `savings` 1,489 → 899 £bn at 2024 for +the two draws) are the stage's known realisation variance (cf. the +`owned_land` exclusion receipt), not an effect of either change. + +## Caveats (travel with the numbers) + +- Weights are not recalibrated; the UK surface now compiles a UC caseload + target (microcosm#735), which absorbs part of any move in a real build. +- OLD is not the incumbent's own draw: this package draws a continuous + quantile over the full leaf conditional, the incumbent's microimpute QRF a + random point on a 10-quantile grid, and the predictor surface here is the + released h5 (no `num_bedrooms`, post-SPI incomes), not the FRS spine. On + the same households the OLD re-draw sits 1.15m benefit units below the + incumbent artifact — a draw-machinery difference outside this change and + worth its own look (the re-draw also carries lower savings / other-property + mass than the incumbent and higher property wealth). +- `private_pension_wealth` is engine-unknown at 2.91.0, so the NEW numbers + are the model-side-unchanged outcome; the policyengine-uk companion PR + makes the disregard explicit and keeps `total_wealth` and the exposure + keys whole. +- `uk_input_mass_parity` will report `corporate_wealth` at roughly −0.85 + against the incumbent reference (pension mass moved to a column the + reference lacks); inside the armed tolerance (4.52), so no exclusion + receipt is added — the drift is explained here. diff --git a/packages/microcosm-build/src/microcosm/build/uk/gates.json b/packages/microcosm-build/src/microcosm/build/uk/gates.json index ed3447dab5..09ae60a691 100644 --- a/packages/microcosm-build/src/microcosm/build/uk/gates.json +++ b/packages/microcosm-build/src/microcosm/build/uk/gates.json @@ -526,6 +526,7 @@ "household.msoa_code", "household.num_vehicles", "household.oa_code", + "household.private_pension_wealth", "household.property_purchased", "household.rail_usage", "household.region_code_oa", diff --git a/packages/microcosm-build/src/microcosm/build/uk/release_input_coverage_manifest.json b/packages/microcosm-build/src/microcosm/build/uk/release_input_coverage_manifest.json index 92b351673d..a84f49346e 100644 --- a/packages/microcosm-build/src/microcosm/build/uk/release_input_coverage_manifest.json +++ b/packages/microcosm-build/src/microcosm/build/uk/release_input_coverage_manifest.json @@ -473,7 +473,7 @@ "capital_gains" ], "source_manifest": "source_stages.json", - "source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", + "source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", "source_vintages": { "source": "HMRC Capital Gains Tax statistics, July 2025, Table 2.1a", "survey": "HMRC Capital Gains Tax statistics Table 2.1a and Advani-Summers capital-gains incidence" @@ -496,7 +496,7 @@ "capital_gains" ], "source_manifest": "source_stages.json", - "source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", + "source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", "source_vintages": { "source": "Advani and Summers (2020), Capital Gains and UK Inequality, CAGE Working Paper 465", "survey": "Family Resources Survey 2024-25, SPI synthetic support, and Advani-Summers capital-gains incidence" @@ -525,7 +525,7 @@ "required_mass_change_reason": "E5 source-stage transform preserves household rows and typed household weights; total household mass is conserved.", "rewrites": [], "source_manifest": "source_stages.json", - "source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", + "source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", "source_vintages": { "source": "UK Data Service SN 8856 Effects of Taxes and Benefits household tab, DfT rail fare index, and public NHS activity/cost table.", "survey": "Effects of Taxes and Benefits 1977-2024 and NHS age-gender public table" @@ -545,7 +545,7 @@ "required_mass_change_reason": "E5 source-stage transform preserves household rows and typed household weights; total household mass is conserved.", "rewrites": [], "source_manifest": "source_stages.json", - "source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", + "source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", "source_vintages": { "source": "UK Data Service SN 8856 Effects of Taxes and Benefits household tab and cited VAT anchor resource.", "survey": "Effects of Taxes and Benefits 1977-2024" @@ -565,20 +565,20 @@ "capital_gains" ], "required_mass_change_reason": "Amounts-only capital gains redraw: household weights pass through unchanged and total household mass is conserved.", - "source_manifest": "cgt_source_stages.json", - "source_manifest_sha256": "71104111b4b9f2da00ce49ad5abec54a35d300196032c9742d62e02aa1730774", - "superseded_by": { - "reason": "The FRS spine build executes hmrc_cgt_gains_spine, which applies the same HMRC Table 3 amounts redraw directly in source_stages.json before calibration.", - "source_manifest": "source_stages.json", - "source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", - "stage": "hmrc_cgt_gains_spine" - }, - "source_vintages": { - "hmrc_surface": "2023-24", - "mapped_build_period": "2024" + "source_manifest": "cgt_source_stages.json", + "source_manifest_sha256": "71104111b4b9f2da00ce49ad5abec54a35d300196032c9742d62e02aa1730774", + "source_vintages": { + "hmrc_surface": "2023-24", + "mapped_build_period": "2024" }, "stage": "hmrc_cgt_gains", - "status": "required_at_build" + "status": "required_at_build", + "superseded_by": { + "reason": "The FRS spine build executes hmrc_cgt_gains_spine, which applies the same HMRC Table 3 amounts redraw directly in source_stages.json before calibration.", + "source_manifest": "source_stages.json", + "source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", + "stage": "hmrc_cgt_gains_spine" + } }, "hmrc_cgt_gains_spine": { "base_candidate_sha256": "f17306ccb2aad7ff0130be3589b560afb2e2a12a943570911cd0c77f07934833", @@ -596,7 +596,7 @@ "capital_gains" ], "source_manifest": "source_stages.json", - "source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", + "source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", "source_vintages": { "hmrc_surface": "2023-24", "mapped_build_period": "2024" @@ -610,7 +610,7 @@ "base_candidate_tier": "frs", "calibration_permitted": false, "canonical_source_manifest": "source_stages.json", - "canonical_source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", + "canonical_source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", "effective_mass_requirements": { "charitable_investment_gifts": { "mass_share_denominator": "all_person_effective_mass", @@ -669,7 +669,7 @@ "OTHERINC" ] }, - "reviewed_fence_ids": [ + "reviewed_fence_ids": [ "frs_epb_source_absent", "frs_exps_source_absent", "frs_taxterm_source_absent", @@ -679,15 +679,9 @@ "frs_srp_regular_code5_subset", "full_frs_tei_band_unavailable" ], - "source_manifest": "hmrc_income_source_stages.json", - "source_manifest_sha256": "c0341af7166ae3a85a3c1164e7d9e880c4b4aec122f1a8fa90c73b46c596e1ea", - "superseded_by": { - "reason": "The FRS spine build executes hmrc_spi_income_spine, which supersedes the June retained-leaves/hmrc_spi_income pair inside source_stages.json.", - "source_manifest": "source_stages.json", - "source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", - "stage": "hmrc_spi_income_spine" - }, - "source_vintages": { + "source_manifest": "hmrc_income_source_stages.json", + "source_manifest_sha256": "c0341af7166ae3a85a3c1164e7d9e880c4b4aec122f1a8fa90c73b46c596e1ea", + "source_vintages": { "hmrc_surface": "2023-24", "mapped_build_period": "2024", "period_mapping": "latest_published_tax_year", @@ -695,7 +689,13 @@ }, "spi_prior_national_household_mass_share": 0.5, "stage": "hmrc_spi_income", - "status": "required_at_build" + "status": "required_at_build", + "superseded_by": { + "reason": "The FRS spine build executes hmrc_spi_income_spine, which supersedes the June retained-leaves/hmrc_spi_income pair inside source_stages.json.", + "source_manifest": "source_stages.json", + "source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", + "stage": "hmrc_spi_income_spine" + } }, "lcfs_consumption": { "base_candidate_sha256": "f17306ccb2aad7ff0130be3589b560afb2e2a12a943570911cd0c77f07934833", @@ -727,7 +727,7 @@ "required_mass_change_reason": "E5 source-stage transform preserves household rows and typed household weights; total household mass is conserved.", "rewrites": [], "source_manifest": "source_stages.json", - "source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", + "source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", "source_vintages": { "source": "UK Data Service SN 9468 Living Costs and Food Survey 2023-24 household/person tabs, NEED 2023 headline energy tables, Ofgem Q2 2026 unit rates, and WAS round-8 bridge donor.", "survey": "Living Costs and Food Survey 2023-24" @@ -748,7 +748,7 @@ "property_wealth" ], "source_manifest": "source_stages.json", - "source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", + "source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", "source_vintages": { "source": "MHCLG dwellings and ONS UK House Price Index December 2025 regional average prices.", "survey": "Public regional property reference" @@ -772,7 +772,7 @@ "employee_pension_contributions" ], "source_manifest": "source_stages.json", - "source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", + "source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", "source_vintages": { "source": "HMRC, Salary sacrifice reform for pension contributions effective from 6 April 2029", "survey": "Family Resources Survey 2024-25 salary-sacrifice respondents and HMRC salary-sacrifice reform analysis" @@ -794,7 +794,7 @@ "student_loan_plan" ], "source_manifest": "source_stages.json", - "source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", + "source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", "source_vintages": { "source": "Explore Education Statistics Table 6a, Higher education total", "survey": "Family Resources Survey 2024-25 and Student Loans Company borrower forecasts for England" @@ -812,6 +812,7 @@ "owned_land", "property_wealth", "corporate_wealth", + "private_pension_wealth", "gross_financial_wealth", "net_financial_wealth", "main_residence_value", @@ -826,7 +827,7 @@ "required_mass_change_reason": "E5 source-stage transform preserves household rows and typed household weights; total household mass is conserved.", "rewrites": [], "source_manifest": "source_stages.json", - "source_manifest_sha256": "84d1c1c85f0172a9a396ef69d445ee33cf1340f62ff73c8653c399af40087bf4", + "source_manifest_sha256": "b04f5c9af88d5c6e11e687daa1c7eb382058e3ed4f637e1075887f4f7cc043e3", "source_vintages": { "source": "Office for National Statistics Wealth and Assets Survey, UK Data Service SN 7215, DOI 10.5255/UKDA-SN-7215-20; local licensed 2006-22 household tab.", "survey": "Wealth and Assets Survey round 8" diff --git a/packages/microcosm-build/src/microcosm/build/uk/source_stages.json b/packages/microcosm-build/src/microcosm/build/uk/source_stages.json index 2741736236..7d206dca19 100644 --- a/packages/microcosm-build/src/microcosm/build/uk/source_stages.json +++ b/packages/microcosm-build/src/microcosm/build/uk/source_stages.json @@ -907,7 +907,8 @@ "weight": "R8xshhwgt" }, "derived": { - "corporate_wealth_excl_isa": "(totalpenr8_aggr - dvvaldbt_scaper8_aggr) + DVFESHARESR8_aggr + DVFShUKVR8_aggr + DVFCollVR8_aggr", + "private_pension_wealth": "totalpenr8_aggr - dvvaldbt_scaper8_aggr", + "corporate_wealth_excl_isa": "DVFESHARESR8_aggr + DVFShUKVR8_aggr + DVFCollVR8_aggr", "stocks_and_shares_isa": "DVIISAVR8_aggr", "cash_isa": "DVCISAVR8_aggr", "student_loan_balance": "Tot_LosR8_aggr - Tot_los_exc_SLCR8_aggr", @@ -960,6 +961,7 @@ "chain_order": [ "owned_land", "property_wealth", + "private_pension_wealth", "corporate_wealth_excl_isa", "stocks_and_shares_isa", "corporate_wealth", @@ -1008,6 +1010,7 @@ "owned_land", "property_wealth", "corporate_wealth", + "private_pension_wealth", "gross_financial_wealth", "net_financial_wealth", "main_residence_value", @@ -1023,6 +1026,7 @@ "owned_land", "property_wealth", "corporate_wealth", + "private_pension_wealth", "gross_financial_wealth", "main_residence_value", "other_residential_property_value", @@ -1033,7 +1037,7 @@ "stocks_and_shares_isa", "student_loan_balance" ], - "notes": "Ports incumbent WAS round-8 wealth imputation with signed E5 differences: exact lower-case column matching replaces the fuzzy r/w fallback; cash ISA uses DVCISAVR8_aggr and stocks-and-shares ISA uses DVIISAVR8_aggr; corporate_wealth folds stocks-and-shares ISA after drawing corporate_wealth_excl_isa; recipient Northern Ireland regions are mapped to Wales for prediction only; student_loan_balance is allocated by household id rather than the incumbent positional off-by-one. Engine predictors materialize at their native entity and person/benunit values are summed to household, reproducing the incumbent map_to=household semantics; region is one-hot encoded jointly across donor and recipient (the incumbent's dummy encoding), with unmapped donor GOR codes becoming all-zero dummy rows. The WAS and FRS predictor definitions are not fully like-for-like and are ported as-is; raw WAS missing values are blanket-filled with zero; UKDS negative sentinel codes (-9/-8/-7/-6) are recoded to zero for the nonnegative-domain columns the licensed audit found carrying them (vcarnr8: 2 rows; HBedRmR8: 95.8 percent - the bedrooms question is effectively unasked in the WAS household file, predictor-quality revisit registered on microcosm#145) - a signed difference vs the incumbent, which trains on raw sentinels; DVPriRntR8's -9 is structural not-applicable so the is_renting mapping is unchanged; genuinely negative domains are never recoded." + "notes": "Ports incumbent WAS round-8 wealth imputation with signed E5 differences: exact lower-case column matching replaces the fuzzy r/w fallback; cash ISA uses DVCISAVR8_aggr and stocks-and-shares ISA uses DVIISAVR8_aggr; private pension wealth (WAS total private pension wealth less the current defined-benefit component; DB-type components at the SCAPE rate, money-purchase components at reported fund values) is drawn as its own output private_pension_wealth and no longer folded into corporate_wealth, which keeps only the share-like holdings - pension rights are disregarded capital in every means test (UC Regs 2013 Sch 10 para 10 and the HB/JSA/ESA/IS/SPC parallels; uk-data#452) - and the downstream chain segment conditions on both components; each chain segment starts its own fit and draw RNG streams from a child seed derived deterministically from the declared seed (start_chain respawns from the model seed on every call, so one shared model coupled the k-th target of every segment); corporate_wealth folds stocks-and-shares ISA after drawing corporate_wealth_excl_isa; recipient Northern Ireland regions are mapped to Wales for prediction only; student_loan_balance is allocated by household id rather than the incumbent positional off-by-one. Engine predictors materialize at their native entity and person/benunit values are summed to household, reproducing the incumbent map_to=household semantics; region is one-hot encoded jointly across donor and recipient (the incumbent's dummy encoding), with unmapped donor GOR codes becoming all-zero dummy rows. The WAS and FRS predictor definitions are not fully like-for-like and are ported as-is; raw WAS missing values are blanket-filled with zero; UKDS negative sentinel codes (-9/-8/-7/-6) are recoded to zero for the nonnegative-domain columns the licensed audit found carrying them (vcarnr8: 2 rows; HBedRmR8: 95.8 percent - the bedrooms question is effectively unasked in the WAS household file, predictor-quality revisit registered on microcosm#145) - a signed difference vs the incumbent, which trains on raw sentinels; DVPriRntR8's -9 is structural not-applicable so the is_renting mapping is unchanged; genuinely negative domains are never recoded." }, { "stage": "regional_property_uprating", diff --git a/packages/microcosm-build/src/microcosm/build/uk/spec/sources.yaml b/packages/microcosm-build/src/microcosm/build/uk/spec/sources.yaml index d30a94190b..c7054e4f5a 100644 --- a/packages/microcosm-build/src/microcosm/build/uk/spec/sources.yaml +++ b/packages/microcosm-build/src/microcosm/build/uk/spec/sources.yaml @@ -724,7 +724,8 @@ stages: num_vehicles: vcarnr8 weight: R8xshhwgt derived: - corporate_wealth_excl_isa: (totalpenr8_aggr - dvvaldbt_scaper8_aggr) + DVFESHARESR8_aggr + DVFShUKVR8_aggr + DVFCollVR8_aggr + private_pension_wealth: totalpenr8_aggr - dvvaldbt_scaper8_aggr + corporate_wealth_excl_isa: DVFESHARESR8_aggr + DVFShUKVR8_aggr + DVFCollVR8_aggr stocks_and_shares_isa: DVIISAVR8_aggr cash_isa: DVCISAVR8_aggr student_loan_balance: Tot_LosR8_aggr - Tot_los_exc_SLCR8_aggr @@ -767,6 +768,7 @@ stages: chain_order: - owned_land - property_wealth + - private_pension_wealth - corporate_wealth_excl_isa - stocks_and_shares_isa - corporate_wealth @@ -803,6 +805,7 @@ stages: - owned_land - property_wealth - corporate_wealth + - private_pension_wealth - gross_financial_wealth - net_financial_wealth - main_residence_value @@ -817,6 +820,7 @@ stages: - owned_land - property_wealth - corporate_wealth + - private_pension_wealth - gross_financial_wealth - main_residence_value - other_residential_property_value @@ -826,7 +830,7 @@ stages: - cash_isa - stocks_and_shares_isa - student_loan_balance - notes: 'Ports incumbent WAS round-8 wealth imputation with signed E5 differences: exact lower-case column matching replaces the fuzzy r/w fallback; cash ISA uses DVCISAVR8_aggr and stocks-and-shares ISA uses DVIISAVR8_aggr; corporate_wealth folds stocks-and-shares ISA after drawing corporate_wealth_excl_isa; recipient Northern Ireland regions are mapped to Wales for prediction only; student_loan_balance is allocated by household id rather than the incumbent positional off-by-one. Engine predictors materialize at their native entity and person/benunit values are summed to household, reproducing the incumbent map_to=household semantics; region is one-hot encoded jointly across donor and recipient (the incumbent''s dummy encoding), with unmapped donor GOR codes becoming all-zero dummy rows. The WAS and FRS predictor definitions are not fully like-for-like and are ported as-is; raw WAS missing values are blanket-filled with zero; UKDS negative sentinel codes (-9/-8/-7/-6) are recoded to zero + notes: 'Ports incumbent WAS round-8 wealth imputation with signed E5 differences: exact lower-case column matching replaces the fuzzy r/w fallback; cash ISA uses DVCISAVR8_aggr and stocks-and-shares ISA uses DVIISAVR8_aggr; private pension wealth (WAS total private pension wealth less the current defined-benefit component; DB-type components at the SCAPE rate, money-purchase components at reported fund values) is drawn as its own output private_pension_wealth and no longer folded into corporate_wealth, which keeps only the share-like holdings - pension rights are disregarded capital in every means test (UC Regs 2013 Sch 10 para 10 and the HB/JSA/ESA/IS/SPC parallels; uk-data#452) - and the downstream chain segment conditions on both components; each chain segment starts its own fit and draw RNG streams from a child seed derived deterministically from the declared seed (start_chain respawns from the model seed on every call, so one shared model coupled the k-th target of every segment); corporate_wealth folds stocks-and-shares ISA after drawing corporate_wealth_excl_isa; recipient Northern Ireland regions are mapped to Wales for prediction only; student_loan_balance is allocated by household id rather than the incumbent positional off-by-one. Engine predictors materialize at their native entity and person/benunit values are summed to household, reproducing the incumbent map_to=household semantics; region is one-hot encoded jointly across donor and recipient (the incumbent''s dummy encoding), with unmapped donor GOR codes becoming all-zero dummy rows. The WAS and FRS predictor definitions are not fully like-for-like and are ported as-is; raw WAS missing values are blanket-filled with zero; UKDS negative sentinel codes (-9/-8/-7/-6) are recoded to zero for the nonnegative-domain columns the licensed audit found carrying them (vcarnr8: 2 rows; HBedRmR8: 95.8 percent - the bedrooms question is effectively unasked in the WAS household file, predictor-quality revisit registered on microcosm#145) - a signed difference vs the incumbent, which trains on raw sentinels; DVPriRntR8''s -9 is structural not-applicable so the is_renting mapping is unchanged; genuinely negative domains are never recoded.' - stage: regional_property_uprating survey: Public regional property reference diff --git a/packages/microcosm-build/src/microcosm/build/uk/was_wealth_support_bounds.json b/packages/microcosm-build/src/microcosm/build/uk/was_wealth_support_bounds.json index 0d9bce9bd4..1cc3475e7b 100644 --- a/packages/microcosm-build/src/microcosm/build/uk/was_wealth_support_bounds.json +++ b/packages/microcosm-build/src/microcosm/build/uk/was_wealth_support_bounds.json @@ -22,6 +22,10 @@ 0.0, 30000000 ], + "private_pension_wealth": [ + 0.0, + 8000000 + ], "gross_financial_wealth": [ 0.0, 30000000 diff --git a/packages/microcosm-build/src/microcosm/build/uk_runtime/terminal_gates.py b/packages/microcosm-build/src/microcosm/build/uk_runtime/terminal_gates.py index 625d96028e..1e4660caf8 100644 --- a/packages/microcosm-build/src/microcosm/build/uk_runtime/terminal_gates.py +++ b/packages/microcosm-build/src/microcosm/build/uk_runtime/terminal_gates.py @@ -178,6 +178,7 @@ def __post_init__(self) -> None: "household.msoa_code", "household.num_vehicles", "household.oa_code", + "household.private_pension_wealth", "household.property_purchased", "household.rail_usage", "household.region_code_oa", diff --git a/packages/microcosm-build/src/microcosm/build/uk_runtime/was_wealth.py b/packages/microcosm-build/src/microcosm/build/uk_runtime/was_wealth.py index 27a1b8d038..1a3879f7f9 100644 --- a/packages/microcosm-build/src/microcosm/build/uk_runtime/was_wealth.py +++ b/packages/microcosm-build/src/microcosm/build/uk_runtime/was_wealth.py @@ -59,6 +59,7 @@ "owned_land", "property_wealth", "corporate_wealth", + "private_pension_wealth", "gross_financial_wealth", "net_financial_wealth", "main_residence_value", @@ -283,10 +284,20 @@ def clean_was_household_table(raw: pd.DataFrame) -> pd.DataFrame: values = cleaned[column] cleaned[column] = values.where(~values.isin(_SENTINEL_CODES), 0) cleaned["is_renting"] = cleaned["private_rent_code"] == 1 + # Private pension wealth other than current-employment defined-benefit + # entitlements (WAS total private pension wealth less DVValDBT_SCAPE; + # defined-benefit-type components are valued at the SCAPE discount rate, + # money-purchase components are reported fund values): DC pots, AVCs, + # current personal pensions, retained DB/DC rights, pensions in payment + # and pensions expected from a former spouse or partner. + # The incumbent folds this into corporate_wealth, where the means-tested + # capital tests count it; pension rights are disregarded capital (UC Regs + # 2013 Sch 10 para 10 and the parallel HB/JSA/ESA/IS/SPC paragraphs), so + # the stage emits it as its own column and keeps corporate_wealth to the + # share-like holdings (uk-data#452). + cleaned["private_pension_wealth"] = cleaned["pensions"] - cleaned["db_pensions"] cleaned["corporate_wealth_excl_isa"] = ( - cleaned["pensions"] - - cleaned["db_pensions"] - + cleaned["emp_shares_options"] + cleaned["emp_shares_options"] + cleaned["uk_shares"] + cleaned["unit_investment_trusts"] ) @@ -376,6 +387,27 @@ class UKWASWealthImputationResult: draws: pd.DataFrame fit_weight_records: tuple[FitWeightRecord, ...] + #: The per-segment RNG roots derived from the declared stage seed. + segment_seeds: tuple[int, ...] = () + + +def was_wealth_segment_seeds(seed: int, segments: int = 3) -> tuple[int, ...]: + """Derive one independent RNG root per chain segment from the stage seed. + + :meth:`RegimeGatedQRF.start_chain` spawns its fit and draw streams from + the model seed on every call, so one model reused across segments would + restart the same streams each time and couple the k-th target of every + segment (the same quantile and sign-gate uniforms per recipient). On the + licensed donor that coupling collapsed P(shares > 0 | property_wealth = 0) + to 0.011 against 0.055 observed; the production child seeds recover 0.039 + (hold-out receipt re-run with exactly this derivation). The declared stage + seed stays the root and the children are deterministic. + """ + + return tuple( + int(child.generate_state(1, dtype=np.uint32)[0]) + for child in np.random.SeedSequence(int(seed)).spawn(int(segments)) + ) def impute_was_wealth( @@ -392,11 +424,16 @@ def impute_was_wealth( donor_encoded, recipient_encoded, encoded_predictors = encode_qrf_predictor_pair( donor, recipient_predictor_frame ) - model = RegimeGatedQRF(n_estimators=n_estimators, seed=seed) + segment_seeds = was_wealth_segment_seeds(seed) + segment_models = iter( + RegimeGatedQRF(n_estimators=n_estimators, seed=segment_seed) + for segment_seed in segment_seeds + ) raw = pd.DataFrame(index=recipient_encoded.index) fit_records: list[FitWeightRecord] = [] def run_segment(base_predictors: Sequence[str], targets: Sequence[str]) -> None: + model = next(segment_models) state = model.start_chain( donor_encoded, list(base_predictors), @@ -428,18 +465,37 @@ def run_segment(base_predictors: Sequence[str], targets: Sequence[str]) -> None: base = encoded_predictors run_segment(base, ("owned_land", "property_wealth")) donor_encoded["corporate_wealth"] = donor_encoded["corporate_wealth"].astype(float) + donor_encoded["private_pension_wealth"] = donor_encoded[ + "private_pension_wealth" + ].astype(float) recipient_encoded["owned_land"] = raw["owned_land"] recipient_encoded["property_wealth"] = raw["property_wealth"] + # Private pension wealth is drawn first in the position the old folded + # corporate_wealth (84.7% pension by donor mass) occupied; the share-like + # components condition on it, and the fold into corporate_wealth follows. run_segment( (*base, "owned_land", "property_wealth"), - ("corporate_wealth_excl_isa", "stocks_and_shares_isa"), + ( + "private_pension_wealth", + "corporate_wealth_excl_isa", + "stocks_and_shares_isa", + ), ) raw["corporate_wealth"] = ( raw["corporate_wealth_excl_isa"] + raw["stocks_and_shares_isa"] ) + recipient_encoded["private_pension_wealth"] = raw["private_pension_wealth"] recipient_encoded["corporate_wealth"] = raw["corporate_wealth"] + # Downstream targets condition on both components, carrying the + # information the old folded corporate_wealth supplied as one column. run_segment( - (*base, "owned_land", "property_wealth", "corporate_wealth"), + ( + *base, + "owned_land", + "property_wealth", + "private_pension_wealth", + "corporate_wealth", + ), ( "gross_financial_wealth", "net_financial_wealth", @@ -455,6 +511,7 @@ def run_segment(base_predictors: Sequence[str], targets: Sequence[str]) -> None: return UKWASWealthImputationResult( draws=raw.loc[:, UK_WAS_WEALTH_OUTPUT_COLUMNS], fit_weight_records=tuple(fit_records), + segment_seeds=segment_seeds, ) diff --git a/packages/microcosm-build/tests/test_spec_engine_country_bundles.py b/packages/microcosm-build/tests/test_spec_engine_country_bundles.py index 9d028071f2..0adfa3083c 100644 --- a/packages/microcosm-build/tests/test_spec_engine_country_bundles.py +++ b/packages/microcosm-build/tests/test_spec_engine_country_bundles.py @@ -42,7 +42,7 @@ ), ( "uk", - "4793baa59a90930956faf8f5b422ece57da3445c7ed0030622ef62ff35dfb142", + "276fddeb17857f64fa1bbaae6551e52580c4ef352f3aba4da0eabd14ef13a668", { "benunit.benunit_id", "household.household_id", diff --git a/packages/microcosm-build/tests/test_uk_was_wealth.py b/packages/microcosm-build/tests/test_uk_was_wealth.py index 872ae69f30..d1c29df9ca 100644 --- a/packages/microcosm-build/tests/test_uk_was_wealth.py +++ b/packages/microcosm-build/tests/test_uk_was_wealth.py @@ -152,8 +152,11 @@ def test_was_donor_cleaning_arithmetic_and_exact_case_insensitive_columns() -> N assert donor["stocks_and_shares_isa"].tolist() == [5.0, 6.0] assert donor["cash_isa"].tolist() == [7.0, 8.0] - assert donor["corporate_wealth_excl_isa"].tolist() == [73.0, 166.0] - assert donor["corporate_wealth"].tolist() == [78.0, 172.0] + # Private pension wealth (total pensions less current DB) is its own + # output; corporate_wealth keeps only the share-like holdings. + assert donor["private_pension_wealth"].tolist() == [60.0, 150.0] + assert donor["corporate_wealth_excl_isa"].tolist() == [13.0, 16.0] + assert donor["corporate_wealth"].tolist() == [18.0, 22.0] assert donor["student_loan_balance"].tolist() == [5000.0, 2000.0] assert donor["region"].tolist() == ["LONDON", "SCOTLAND"] assert donor["is_renting"].tolist() == [True, False] @@ -394,11 +397,12 @@ def test_was_imputer_uses_checkpointed_chain_segments( import microcosm.fit calls = [] + seeds = [] class FakeQRF: def __init__(self, *, n_estimators, seed): assert n_estimators == 7 - assert seed == 0 + seeds.append(seed) def start_chain(self, donor, predictors, targets, *, weights): assert weights == "weight" @@ -437,11 +441,66 @@ def fit_draw_next( assert result.draws.columns.tolist() == list(UK_WAS_WEALTH_OUTPUT_COLUMNS) assert calls[0][1] == ("owned_land", "property_wealth") - assert calls[1][1] == ("corporate_wealth_excl_isa", "stocks_and_shares_isa") + assert calls[1][1] == ( + "private_pension_wealth", + "corporate_wealth_excl_isa", + "stocks_and_shares_isa", + ) + assert "private_pension_wealth" in calls[2][0] assert "corporate_wealth" in calls[2][0] + assert "private_pension_wealth" not in calls[1][0] assert calls[2][1][-1] == "cash_isa" fitted_targets = [name for _, targets in calls for name in targets] assert [record.fit_name for record in result.fit_weight_records] == [ f"uk_was_2018_20_wealth:{target}" for target in fitted_targets ] assert {record.weight_kind for record in result.fit_weight_records} == {"explicit"} + # One independent RNG root per segment, derived from the declared seed. + assert seeds == list(module.was_wealth_segment_seeds(0)) + assert len(set(seeds)) == 3 + assert result.segment_seeds == tuple(seeds) + + +def test_private_pension_wealth_split_preserves_the_old_corporate_wealth_identity() -> ( + None +): + """The pension component plus the new corporate_wealth reproduces the + pre-split corporate_wealth row for row, and the pension component is + exactly total pensions less current defined-benefit wealth.""" + raw = _raw_was() + donor = clean_was_household_table(raw) + + old_corporate_wealth = ( + raw["totalpenr8_aggr"] + - raw["dvvaldbt_scaper8_aggr"] + + raw["DVFESHARESR8_aggr"] + + raw["DVFShUKVR8_aggr"] + + raw["DVFCollVR8_aggr"] + + raw["DVIISAVR8_aggR"] + ) + assert ( + donor["corporate_wealth"] + donor["private_pension_wealth"] + ).tolist() == old_corporate_wealth.tolist() + assert "private_pension_wealth" in UK_WAS_WEALTH_OUTPUT_COLUMNS + assert ( + UK_WAS_WEALTH_OUTPUT_COLUMNS.index("private_pension_wealth") + == UK_WAS_WEALTH_OUTPUT_COLUMNS.index("corporate_wealth") + 1 + ) + + +def test_segment_seeds_are_distinct_and_deterministic() -> None: + """Each chain segment starts its own fit/draw streams (start_chain respawns + from the model seed on every call), so the roots must differ and derive + deterministically from the declared stage seed.""" + import microcosm.build.uk_runtime.was_wealth as module + + seeds = module.was_wealth_segment_seeds(0) + + # Golden pin: the production roots for the declared stage seed 0. Moving + # them is a spec-visible RNG change, never an accident. + assert seeds == (3757552657, 673228719, 3241444873) + assert len(seeds) == 3 + assert len(set(seeds)) == 3 + assert seeds == module.was_wealth_segment_seeds(0) + assert seeds != module.was_wealth_segment_seeds(1) + assert all(isinstance(seed, int) for seed in seeds) diff --git a/packages/microcosm-data/src/microcosm/data/contract.py b/packages/microcosm-data/src/microcosm/data/contract.py index c423461c50..4d765a463f 100644 --- a/packages/microcosm-data/src/microcosm/data/contract.py +++ b/packages/microcosm-data/src/microcosm/data/contract.py @@ -375,13 +375,13 @@ # fingerprint derives from the manifest digest. Editing the spec moves all # three here in the same reviewed change. _UK_GATE_BATTERY_POLICY_SHA256 = ( - "5459347c9077b2acd5970a62d818e3ddd063d86d6c3dbce4d32dcacec3bdc414" + "955d56d8a6ce608615b863a51c5040bdf3b5db16afb92cf1035979c6f55719b8" ) _UK_GATE_BATTERY_GATES_MANIFEST_SHA256 = ( - "f68e10d8ff6654b7fc707da0508ea063b0f3c96b8a813b7098298727d43b9d6d" + "7a8b261a2a9dd87e3a57579c55469d769deaa0002f6ee006370629ab0af8b4e0" ) _UK_GATE_BATTERY_SPEC_FINGERPRINT = ( - "2a4a8b18d024f80782b375539c87d57006592d64470553a4da5a378791254faa" + "e4c4a8fc302f79379e2e2ae75962428781c6cb3c0c31c825fd698240bfe6c89e" ) #: Spec entry id -> the legacy gate name whose observable detail checks #: apply unchanged (the battery re-keys the report by entry id; the gate @@ -607,10 +607,10 @@ }, "release_cut": { "gates_manifest_sha256": ( - "18f07f40eead198a4436de7a43d4c0b13b9f2a9335bc84d4ab6b6a2ed75e597e" + "2c698843042584af8976196f92b2baf097beca93b846fca74671a1cc3cc879aa" ), "policy_sha256": ( - "b2a6446da0ffc53c0a894618795d918163368a266ef01a2a3609587085162115" + "e73dcbcaa07e34d23c8cf40b251f19fd00d9d2b85202647ca0ea13cb1021769a" ), }, } diff --git a/packages/microcosm-data/tests/test_contract.py b/packages/microcosm-data/tests/test_contract.py index 4f4910ebbb..27a4579119 100644 --- a/packages/microcosm-data/tests/test_contract.py +++ b/packages/microcosm-data/tests/test_contract.py @@ -135,13 +135,13 @@ def _trusted_terminal_gate_signing_key(monkeypatch) -> None: UK_GATE_BATTERY_PRODUCER = "microcosm.build.gate_battery" UK_GATE_BATTERY_SIGNING_KEY_ENV = "MICROCOSM_UK_TERMINAL_GATE_SIGNING_KEY" UK_GATE_BATTERY_POLICY_SHA256 = ( - "5459347c9077b2acd5970a62d818e3ddd063d86d6c3dbce4d32dcacec3bdc414" + "955d56d8a6ce608615b863a51c5040bdf3b5db16afb92cf1035979c6f55719b8" ) UK_GATE_BATTERY_GATES_MANIFEST_SHA256 = ( - "f68e10d8ff6654b7fc707da0508ea063b0f3c96b8a813b7098298727d43b9d6d" + "7a8b261a2a9dd87e3a57579c55469d769deaa0002f6ee006370629ab0af8b4e0" ) UK_GATE_BATTERY_SPEC_FINGERPRINT = ( - "2a4a8b18d024f80782b375539c87d57006592d64470553a4da5a378791254faa" + "e4c4a8fc302f79379e2e2ae75962428781c6cb3c0c31c825fd698240bfe6c89e" ) UK_GATE_BATTERY_DEGENERATE_EVIDENCE_SHA256 = ( "d0d024043132fa07c378c393dbe2b24fe99bf19e876bcc39997d2c80cc9bd4f6"