Repository navigation
Policy experiments: past UK measures on the modern population - #210
Conversation
Run historical fiscal-event measures as parameter changes on modern law, on an experiment population, and screen each result against the OBR's costing for crashes, inert constructions, sign errors and implausible magnitudes (cos d1817). Experiments are labelled modern_population_sensitivity, live in their own namespace and are never replays: the historical registries keep refusing replays. First constructions: 17 experiments for Autumn Budget 2021, Spring Statement 2022 and Autumn Statement 2022. The OBR's Historical official forecasts database supplies the scaling bases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Modal probe recorded each engine package's RECORD digest, the packaged bundle manifest, the runtime release bundle and the dataset's years (2024-2032). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Events without a registry read their OBR rows from the inventory. Add the three fuel-duty freezes (forgone RPI uprating from the OBR forecast of the year), Spring Budget 2021's allowance and threshold freeze and Spending Review 2020's pension credit uprating. An experiment can name the years it runs, so a day-weighted parameter keeps whole-fiscal-year windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… group A model swap runs a newer policyengine-uk on the same data cut, to catch current bugs before a cut is built for it and to separate model changes from data. Mirrors now compare only the head groups both experiments map. The summary collects findings, mirror checks and each comparable head's modern base against the OBR's newest forecast. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An event whose costings end before the population's first year compares an experiment's named years with one stated costing year, scaled from that year's event forecast to the run year's base, and records the comparison year on every row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… duty in cash Fuel duty counterfactuals take the 2022 5p cut in cash: 57.95p x (1 + forgone RPI) - 5p. Budget 2018's early allowance and threshold rises, its work-allowance increase and fuel freeze, Autumn Budget 2017's first-time buyers' relief and fuel freeze, and Budget 2017's withdrawn Class 4 rise run on the population's years, carried forward from each event's last costing year. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
36 experiment-years, no investigate-level anomaly; every scaled ratio between 0.5 and 2.3. The six construction_noop notes are diagnosed: superseded on modern law, outside the measure's years, or (SDLT 2024-25) the Growth Plan relief missing from pe-uk 2.132.6, fixed in 2.141.8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… swap Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…2015 and Summer Budget 2015 The two-child limit's removal (sign only: its yield grows until 2035), the additional-dwellings surcharge's original 3pp, the £1,000 trading and property allowances, and the 2016 and 2017 fuel-duty freezes, each carried forward from the event's last costing year. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…per pair Both clean: no anomaly, scaled ratios 0.54 to 1.46. The Health and Social Care Levy and its reversal return exact negatives in every year and head group across separate containers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d Budget 2015 VAT at 17.5%, the child benefit freeze and the high-income charge, the 45p additional rate, the marriage allowance and the personal savings allowance, carried forward from each event's last costing year. A head group may carry a PolicyEngine leg the OBR books under another group's head (the charge is income tax in PolicyEngine, benefit spending in the OBR's costing). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 experiment-years, no anomaly; both welfare rows move Universal Credit in the OBR's direction (scaled ratios 0.57 to 1.40). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… events Offsetting mapped heads hid Budget 2017's Class 4 rise from the magnitude check (mapped share 35% net, 69% by head). It is now flagged, and diagnosed: the OBR's costing carries an incorporation response; the NICs leg alone is 1.27 times the OBR's NICs row. Autumn Budget 2017 and Spring Budget 2021 on the interim pair are clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On pe-uk 2.143.0 the interim cut buys no additional dwellings: from 2.134.1 the purchase price is a dataset input (#2238's fix), which the cut built for 2.132.6 does not supply, so the surcharge reversal is inert and SDLT falls from £15.0bn to £8.1bn. Microcosm imputes it from #1308. A model swap is valid only where the data carries the newer model's inputs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p's input gaps Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…10, Budget 2010 All clean but Budget 2012's 45p rate, flagged 39 times the OBR's costing and diagnosed: the OBR's figure is net of a taxable-income response that offsets almost all of the static cost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ult integrity - Constructions state the current law they are written against (assumes_modern); an engine whose processed value differs gets modern_law_differs (investigate). On pe-uk 2.132.6 that catches the additional-dwellings surcharge reversal removing 1pp, not 3pp, and the dividend higher-rate threshold at 37,500 in the SB2021 and B2018 freezes. - SS2022's primary threshold without the alignment follows CPI to April 2025, then the hold; both experiments are sign only. Budget 2018's work allowance keeps the April 2019 uprating. The 30-hour childcare construction zeroes the age-4 bracket too. - findings() refuses a run on another spec; adopt() checks every download before copying any; every committed FINDINGS and SUMMARY regenerates byte for byte in a test; no output path may enter a replay root of any checkout; a test checks the two engines disagree on a touched parameter's current law only where the construction says so. - Evidence files for the Marriage Allowance reproduction, the dataset token's access and the missing gross-receipts columns. - Reruns on both populations for SS2022, B2018, SB2015 and AS2015, and the certified pair's SB2021. AS2022 and the swap's SB2021 are still running. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Autumn Statement 2022 rerun on both populations, and Spring Budget 2021 on the model swap. The certified pair's figures are unchanged. The additional-rate threshold gets modern_law_differs on 2.132.6 in 2024-25 and 2025-26 (dividend band at 150,000), diagnosed with the SB2021 freeze. - A test checks that wherever the model swap and the certified pair differ on a parameter a construction sets that year, the construction declares the law it assumes or sets the parameter outright. 248 parameter-years, 21 differ: 16 declared and 5 set outright, each listed with its reason. - Findings log and design doc: final ratios, the check, invariants 7 and 8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…diagnosed Answers the second review (Opus 5.5 at 767a695): - B2: every reform path, in every year an experiment can run, either states the current law it is written against (assumes_modern) or is set outright with a reason (set_outright, new). validate_spec enforces it, so modern_law_differs fires on any population, not only a model swap. The builder fills assumes_modern from CURRENT_LAW (204 path-years over 50 paths, which pe-uk 2.132.6 and 2.143.0 share), cross-checked against its own State Pension, guarantee and work-allowance constants. - B1: the model swap's seven undiagnosed changes are diagnosed: VAT grossing (pe-uk#1996, 2.135.7), the savings starting-rate taper (35f992c6a, #1799, 2.139.2), relieved pension contributions in adjusted net income (1dbc88172, 2.134.8) and the Growth Plan first-time-buyer limits (4044d6ccf). Synthetic households for the two income tax ones in docs/uk_replay/evidence/policy_experiments/swap_mechanisms.py. - N1 containment compares replay-root segments case-insensitively; N3 the lower profits limit is 52 x the weekly threshold; N2 the SS2022 hold is a stated convention; N6 lint; N7 the work-allowance comment's arithmetic. The 17 events whose specs changed are being rerun on both populations, so their committed findings do not regenerate until the reruns are adopted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Every committed run is on the current spec again: the 17 events whose specs gained declared current law were rerun on Modal on the certified pair and the model swap and adopted. Effects are unchanged apart from Spring Statement 2022's lower profits limit (52 x the weekly threshold). Both summaries have no undiagnosed row. - Findings log: every model-swap change with its cause; the VAT and savings allowance ratios on 2.143.0; unbacked lag claims cut. - Design doc: the current-law rule as validate_spec enforces it. - Evidence: column_check.py (names only) and the swap-mechanism outputs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…old guard - The two Growth Plan SDLT constructions set all three legs of the Stamp Duty Land Tax (Temporary Relief) Act 2023 s. 1: the main scale, the additional-dwellings first band to £250,000 (s. 1(3)) and the lease-rent nil-rate band to £250,000 (s. 1(4)). Keeping the relief merges the additional-dwellings bands at the current-law 5% rate, stated in the experiment's own assumes_modern. Synthetic checks on 2.143.0 and 2.132.6 in the evidence folder. - validate_spec refuses two brackets of one scale at the same threshold: policyengine adds the rates of brackets that share a threshold, which is how a first attempt here taxed every additional dwelling above £250,000 at 13%. - Citations: the Temporary Relief Act 2023, Finance Act 2023 s. 6(4) and s. 7(3)-(4). Containment treats a cased spelling of the checkout as the repository; a crash keeps modern_law_differs; lint. - Marriage Allowance: fixed upstream in pe-uk 2.148.2 (#2553), confirmed on the synthetic couples. The label claim is narrowed to what each file carries; the declaration count is 204 over 144 distinct path-years. Autumn Statement 2022 is being rerun on both populations, so its committed findings do not regenerate until the rerun is adopted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lief - Both populations rerun and adopted. Keeping the relief from 2025-26 now returns +£1.34bn on 2.143.0 (scaled ratios 1.33, 0.87, 0.80). On 2.132.6 it is flagged modern_law_differs: the construction merges the additional-dwellings bands at current law's 5%, which that engine lacks. - Diagnoses: that flag, and the model-swap change it causes (the additional-dwellings leg; nil on the swap, whose cut has no additional-dwelling purchases). Both summaries have no undiagnosed row. - Findings log: the corrected ratio row and the swap change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- test_a_scaled_path_runs_at_its_modern_value_times_its_factor compared changes_modern_law with factor != 1.0; production uses a 1e-9 relative tolerance, and Hypothesis found factor 1.0000000000000002. The property now applies the same tolerance. - The shared-threshold guard's scope is stated where it is enforced, tested and documented: brackets a reform sets. A collision with a bracket it leaves alone needs the engine's schedule. - Findings log: the declaration count at each round (204 over 144 path-years and 50 parameters then; 207 over 147 and 51 now). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merged by squash at head Reviews. Six rounds, each answered before the next:
The reports are in Gates. Upstream from this work:
Next. The pre-Budget Microcosm cut runs as a new population when it is published. It will carry the fixed engine, so it gives the national Marriage Allowance rerun and the additional-dwellings legs that the interim cut's input gap hides. 🤖 Generated with Claude Code |
WIP. Basis: cos d1817 (Max, 21 Oct 2026): run historical UK Budgets on the newest Microcosm population as policy experiments to catch model bugs.
These are policy experiments on the modern population, not replays, and not PolicyEngine's costing of any past event. The certified replay path is untouched:
compute_uk_event.refuse_unexecutablestill refuses every historical registry.Design note
Full design:
docs/uk_replay/POLICY_EXPERIMENTS.md. Running findings:docs/uk_replay/EXPERIMENTS.md. Evidence for the claims below:docs/uk_replay/evidence/policy_experiments/.Registry flag. None is added; the historical registries are not edited. An experiment spec
data/uk/experiments/<event>.json(kinduk_modern_population_policy_experiments) binds its registry by SHA-256 and reads its source rows for the OBR side. Theexecution: requires_event_populationblock keeps refusing replays.What an experiment is. One registry measure restated as a parameter change on modern law, as either
reversal_on_modern_law(modern law contains the measure, so restore the pre-measure policy and keep later changes) orforward_on_modern_law(modern law lacks it, so apply it). The replay's orientation rule applies: positive means a gain to the Exchequer.Output location.
results/uk/experiments/<population>/<event>/: per experiment-year artifacts,MODERN_LAW_<year>.json,EXPERIMENT_MANIFEST.json,FINDINGS.json,FINDINGS.md. Nothing is written underresults/uk/events,results/uk/replaysor the variant index, and a property test checks that for any output path.Populations are not variants.
data/uk/experiments/populations.json(statusexperiment_population, idsexp-…) has the variant's identity shape, so the replay's environment checks apply. It is never added toreplay_variants.jsonand never sealed. A test checks that no replay reader (the DB build, the namespace scan, the results-PR builder, workflows) names the experiment namespace.Labels. Every experiment-year artifact,
EXPERIMENT_MANIFEST.json,FINDINGS.jsonandSUMMARY.jsoncarrieslabel: modern_population_sensitivity(PLAN.md's name for this question),is_replay: falseand "Policy experiment on the modern population: not a replay or a costing of ." Specs carry the label and the sentence;MODERN_LAWfiles carry the label andis_replay;RUN_LOGfiles are timings and carry none. All of them live only under the experiment namespaces.Runs. Each run reuses
compute_uk_event.run_sim: the policyengine.py managed microsimulation, the dataset SHA-256 before and after, the dataset year set, and every touched parameter read back from the simulation's processed tree. Runs execute on Modal (pipeline/modal_uk_experiment.py) with network blocked, holding one of the replay's two shared simulation slots.Which constructions count as anomalous (
classify, a pure function):crashreform_not_appliedconstruction_noopinertsign_mismatchmagnitude_outside_10x/_3xobr_zero_pe_movesmirror_mismatchmodern_law_differsassumes_modern)Magnitude flags apply only when
magnitude_checkisscaled, the expected value is at least £100m, and the mapped heads hold at least half the OBR's costing.OBR scaling. For each head group,
scale = PE modern baseline / OBR event-vintage forecast of that head, where PE's variables cover the whole head (income_taxwith Income tax, the four NICs classes with NICs,capital_gains_taxwith CGT). Otherwisescale = OBR newest forecast / OBR event-vintage forecast. The forecasts are rows of the OBR Historical official forecasts database (Spring 2026), extracted with cell references intodata/uk/experiments/obr_head_forecasts.jsonand cross-checked value for value against the March 2025 edition that the vintage locks name. The scaling is a bug screen, not a vintage comparison, which is why the bands are wide and every flag needs a diagnosis.Status (23 Oct, 14:00Z)
Bank. 43 experiments across 19 events, from Budget 2010 to Autumn Statement 2022, built by
pipeline/uk_experiment_specs.pywith every counterfactual's arithmetic and source.carried_from_fy).scale: each named parameter's own processed modern value times a factor, so every later uprating of the engine it runs on is kept.assumes_modern), or is set outright with a reason (set_outright);validate_specrefuses anything else. An engine whose processed value differs getsmodern_law_differs, on any population.Populations.
certified_pair: policyengine.py 6.10.0 with policyengine-uk 2.132.6 on the interim Microcosm cut, probed on Modal (data years 2024–2032).model_swap: pe-uk 2.143.0 on the same cut. Against the certified pair on the same data, it separates model changes from data. It is valid only where the data carries the newer model's inputs: three variables 2.132.6 computed are dataset inputs by 2.143.0, and the interim cut has none of them (model_swap_input_gaps).Findings (full log in
docs/uk_replay/EXPERIMENTS.md):modern_law_differson the Budget 2018 and Spring Budget 2021 freezes and the additional-rate threshold;inerton the swap until the pre-Budget cut (microcosm#1308).construction_noopnotes on measures modern law supersedes.Answers to the first review (Opus 5.5, at
bb445279;~/reviews/uk-policy-experiments/review-out-opus.md): the four construction findings are corrected and rerun,modern_law_differsadded,findingsrefuses a run on another spec,adoptchecks before copying, committed findings and summaries regenerate byte for byte in a test, containment refuses any checkout's replay roots, and the PR's claims have evidence files. The SS2022 primary-threshold path has more than one defensible reading; the choice is logged and both experiments are sign only.Answers to the second review (Opus 5.5, at
767a695e;~/reviews/uk-policy-experiments/review-out-opus-r2.md):validate_specenforces it, so the detector covers the pre-Budget pair too.Answers to the third review (GPT-6.1 Sol, at
575f4cc8;~/reviews/uk-policy-experiments/review-out-r3.md):validate_specnow refuses shared thresholds among the brackets a reform sets; a collision with a bracket the reform leaves alone needs the engine's schedule and shows in the effect.modern_law_differs, lint, the narrowed label claim, and the declaration count (now 207 declarations over 147 distinct path-years and 51 parameters).Reruns. All 17 events whose specs changed in round 2 were rerun on both populations and adopted. Autumn Statement 2022 was rerun again for the SDLT correction and adopted. Every flag on either population and every model-swap change is diagnosed, and the experiment tests pass (130). Every committed run is on the current spec, every flag on either population and every model-swap change is diagnosed, and the experiment tests pass (127).
Results pipeline.
PE_UK_PRIVATE_HF_READ_TOKENreadspopulace-uk-private: a scratch workflow's HEAD requests got 302 on the Microcosm file and 200 on its release manifest, against 401 without a token (run link in the evidence folder).47fe63f4,dfc9d655), and 6.10.1's first attempt (201e753c) also endedrun_artifact_failed, though its run job left no log to say where. Run the UK replay's year workers that fit the runner's memory, and log memory around each event #218 measures, on Modal, 10.2 GiB for one worker over the whole of AB2025 and 18.5 GiB for two on Microcosm, against 9.3 GiB for two on the enhanced FRS, so two Microcosm workers exceed the 16 GB hosted runner. It carries the fix and the retries.Invariants (property-tested,
tests/test_uk_policy_experiment.py)math.fsum) sum of its head groups.Decimal.classifyis pure and invariant under joint negation and joint positive scaling above the floors. A ratio further from 1 than a flagged one is also flagged.crash,inertandconstruction_noopexclude the sign and magnitude codes.FINDINGSandSUMMARYfile regenerates byte for byte from the committed artifacts, andfindingsrefuses a run on another spec.Plan
bb445279; its findings answered767a695e); its findings answered575f4cc8); its findings answered9b15c476and378b8496); their findings answeredaxiom: n/a: replay and experiment tooling; no policy encoding.
🤖 Generated with Claude Code