Skip to content

Working paper: Public Pension Indexation Under Uncertainty: Fiscal and Distributional Effects (/paper route) - #8

Draft
MaxGhenis wants to merge 58 commits into
mainfrom
paper
Draft

MaxGhenis wants to merge 58 commits into
mainfrom
paper

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Current state (16 Oct 2026)

Title: Public Pension Indexation Under Uncertainty: Fiscal and Distributional Effects (Vahid Ahmadi, Max Ghenis). Draft; not for merge until Max and Vahid sign off.

  • Figures (16 Oct): every figure is now Plotly Express in a PolicyEngine template built from the dashboard (Inter type, light grid, legend under the plot, teal logo) with the dashboard's colours, exported as vector PDF. Authors: Vahid Ahmadi first, then Max Ghenis.
  • Rewritten from scratch on the published pre-Budget release (main: Pre-Budget certified release: rebuild on policyengine 6.2.6, model-conditional expected value, dashboard #30, Merge main into model-v2 (clears #24's conflict) #31, model-v2: certified bundle, engine port and guarded rehearsal #24). Every figure is read from data/results.json. Keywords: pension indexation; stochastic microsimulation; triple lock; United Kingdom; fiscal cost; distributional analysis.
  • PDF on the uk-ai-study LaTeX template (paper/latex/, built by paper/render.sh → build.py, Tectonic), with figures and tables restyled on uk-ai-study's figure style and the dashboard palette. 26 pages.
  • Reviewed in two rounds by a referee, a public-economics professor, a UK pensions-policy expert and a copy/figure editor, then fact-checked (180 claims against results.json and sources; 8 fixed). Final referee verdict: accept as a working paper; no blocking issues.
  • Headline figures (2039-40): expected saving £11.1bn in State Pension spending, £7.9bn after tax and benefits (UK); other calibrations and model versions £8.7bn–£12.0bn, mostly less; £0.9bn on the OBR central forecast; £10.3bn on the OBR long-term triple-lock assumption; Great Britain £10.8bn (£12.8bn scaled for survey coverage) against DWP's £15bn; about 27% of households with less income; pensioner poverty after housing costs up to 0.8 points higher on a typical path. Provisional until the 28 October 2026 Budget.
  • Checks: tests/test_paper.py 50 passed; full suite 1,779 passed, 2 skipped; scripts/check_assumptions.py passes.

Open items

  • data/results.json (and the dashboard copy) carry a paper_rule_arithmetic block written by scripts/paper_rule_arithmetic.py (rule arithmetic on the regenerated draws, no simulation). Any rebuild of the results, including the post-Budget binding run, drops it: rerun the script after the rebuild (paper/render.sh notes this; paperdata.py refuses to load without it).
  • Deferred to the next full rebuild, because they change src/ hashes: the unnormalised estimator in expected_value.reweighted() (the paper uses the corrected one in paperdata.py) and the editorial bracket in src/triple_lock/dwp.py's definition (the paper no longer quotes it; a test enforces this).
  • The HTML manuscript is now built with pandoc (not Quarto); the /paper wrapper is at revision 6.

Summary

This adds a working paper, The Burnham plan and the State Pension triple lock, by Max Ghenis and Vahid Ahmadi. It is a Quarto manuscript that builds the answer up in the dashboard's six steps. It is served at /uk/triple-lock/paper/ as a shelled wrapper around a versioned iframe, following the PolicyBench /paper pattern.

Stacked on #7, which stops the results file publishing one survey household's weight. The paper renders from the results rebuilt there (revision 406ee7a; numbers unchanged). Merge #7 first; this PR's diff then shrinks to the paper commits.

What the paper says (every figure from data/results.json)

  • On the OBR's central forecast the plan saves £0.66bn in State Pension spending in 2039-40 (£0.43bn net). Earnings lead almost every year after the switch, so the only difference comes from the floor years 2030–31.
  • The expected saving over 50,000 calibrated paths, 198 of them run in full, is £8.41bn gross (Monte Carlo SE £0.04bn) and £5.39bn net (SE £0.04bn) in 2039-40. Other calibrations give £6.6bn to £10.7bn gross. DWP's figure is £15bn, on one path through Pensim3, for Great Britain.
  • About 20% of households lose. A pensioner at the Pension Credit guarantee loses nothing, because Pension Credit and a lower tax bill replace the loss; one with a £15,000 private pension bears 80%.

Structure

  1. How the triple lock works: the 2011–2026 inputs, what set each rise, the cumulative index, the plan's rule quoting DWP, what the plan would have paid from 2012, and the past-years check.
  2. The OBR's central forecast: the path and its sources, both rules, and why the saving is small.
  3. Another path: the random draw, with the middle and 90th-percentile paths for context; the monthly VAR and the drift shift.
  4. One pensioner: four example households, with the share of the loss each bears, the Winter Fuel Payment threshold and the council tax reduction gap.
  5. Everyone: gross and net by year, the gross-to-net account, households losing, quintiles, poverty, and the single-record concentration check (contribution and share only).
  6. Every path: the expected saving ± MC SE; strata and estimator; calibration choice (backtest and past-years check) and sensitivities; Enhanced FRS vs Microcosm (paired) and coverage against DWP; DWP's £15bn; the OBR's +0.6pp premium against the paths' 0.55pp.
  7. Method and limitations, with an appendix on the forecast-distribution backtest (CRPS, energy and variogram scores).

Files

  • paper/index.qmd: the manuscript. paper/paperdata.py computes every number from data/results.json (and the DWP text in data/raw/). paper/figures.py draws the figures.
  • paper/references.bib: 26 entries, each fetched and checked on 30 September 2026. paper/SOURCES.md records what each one confirmed, including every quoted passage. I read these directly: the BBC live page, the OBR determinants spreadsheet (its "Triple lock" note), SPC Regulations 2002 reg. 17(10), the Social Security (Up-rating of Benefits) Act 2021 s. 1 and the 2011 Up-rating Order. The rest went through a research lane with a verification log. Only the words the BBC page still quotes as the Prime Minister's ("adjust"; "it will hold its value relative to earnings over time") are quoted as his.
  • paper/render.sh: renders HTML and PDF in one pass and syncs them to dashboard/public/paper/web/.
  • dashboard/public/paper/index.html: the wrapper, revision r1-20260930. Also changed: dashboard/proxy.js (/paper → /paper/); next.config.js (keeps the trailing slash, rewrites /paper/ to the wrapper); eslint ignores public/paper/**; the dashboard footer links to the paper.
  • tests/test_paper.py (33 tests). It checks:
    • the wrapper: version lockstep, sandbox, relative URLs, and that its figures match the results;
    • that the committed render comes from the current results;
    • that every citation resolves and every bib entry is cited;
    • that the prose types no result number (a lint over the manuscript text);
    • sentence-case headings, banned words and no probability language;
    • that no survey identifier or weight reaches the paper;
    • property tests for the number formatting.

Review

Two independent Opus reviews ran on the first draft: an adversarial numbers-and-claims audit, and a check of brief coverage, method and prose. A third, second-round review then checked each earlier finding against the revision and found new ones; those are fixed too. The only finding declined is rerunning the model to raise the Housing Benefit pension-age allowance with earnings, which is disclosed as a limitation instead. Among the fixes:

  • The past-years check is now correctly described as the uncalibrated model.
  • The choice of calibration is explained by effective sample size, not bias alone.
  • DWP is quoted rather than paraphrased as a definition.
  • The random path's position (2nd percentile) is flagged in steps 4 and 5.
  • The MC SE is scoped to path sampling.
  • Pension Credit's after-tax income test is cited to SPC Regulations 2002 reg. 17(10), which settled a reviewer's doubt: the model matches the law there.
  • The council tax reduction take-up claim is corrected (see below).
  • The step between an earnings-linked guarantee and a CPI-linked Housing Benefit allowance is disclosed.
  • Microcosm is compared on its own 40-path subsample.

Checks

  • CI green on the head commit (Pipeline tests, Dashboard lint/test/build, Vercel)
  • pytest -q locally: paper and results tests 63 passed; full suite 150 passed before stacking on Publish no survey household's weight: drop max_household_weight from coverage #7
  • dashboard: bun run test 47 passed, bun run lint 0 errors (2 existing warnings), bun run build OK
  • next start locally: /uk/triple-lock/paper returns 308 to /paper/, which returns 200 (the wrapper); web/index.html, web/index.pdf, the CSS and every figure return 200.

[NEEDS CITATION] markers left in the paper

None. The first draft had two: the April 2022 suspension and April 2011's rise. Both now cite legislation I verified: the Social Security (Up-rating of Benefits) Act 2021 s. 1, and SI 2011/821. The claim that April 2011 "followed RPI" was dropped because the Order doesn't state it; the paper now says only that that year's up-rating order set a larger rise than the rule gives.

Things in the results that look wrong (not changed here)

  • Council tax reduction take-up. METHOD.md and method_limitations say Housing Benefit and council tax reduction respond only for existing claimants.
    • That holds for Housing Benefit (housing_benefit_eligible requires housing_benefit_reported).
    • It does not hold for council tax reduction: would_claim_council_tax_reduction = claims_all_entitled_benefits | reported_ctr, and the Enhanced FRS supplies neither as an input.
    • The paper prints the file's limitations with this one corrected in place.
  • Resolution Foundation benchmark. data/benchmarks.csv describes it as "higher than under a smoothed earnings link since 2012". The RF page says the £12.6bn is the OBR's estimate against earnings-linked uprating "this year". The paper words it from the source.
  • Pension-age Housing Benefit in the Enhanced FRS is £13.4bn against DWP's £7.3bn, and Pension Credit claims are 1.82m against 1.31m. Both inflate the offsets.
  • Guarantee against Housing Benefit allowance. The runs raise the Pension Credit guarantee with earnings, while policyengine-uk raises the pension-age Housing Benefit allowance with CPI. On high-earnings paths the guarantee overtakes the allowance, so crossing onto guarantee credit jumps Housing Benefit to the maximum. On the 90th-percentile path the example council renter gains £873 a year from a £1,552 pension cut. This is the likely source of the single records that move net figures by up to £2.6bn.

🤖 Generated with Claude Code

MaxGhenis and others added 24 commits September 29, 2026 13:01
Adds a Trajectories tab comparing the Burnham plan with the triple lock
on a few macro paths and on past years. Every fiscal and household figure
is a full PolicyEngine UK run: the path's CPI and earnings replace the
model's economic assumptions before the data load, so benefit rates,
thresholds and incomes move with it.

- ts_monthly: monthly VAR of the CPI index and AWE that builds September
  CPI, May-July AWE and calendar measures from the same simulated months,
  so the statutory inputs' reversals (which drive the ratchet) appear.
- ts_methods / ts_backtest: annual VAR variants, entropy tilting to the
  OBR path, proper and dependence-sensitive scores, and chronological
  backtests against the statutory and calendar outturns.
- trajectories: path selection, full-model runs in isolated processes,
  past-year counterfactuals from 2012, largest-household decomposition,
  provenance and source hashes.
- Dashboard tab, helpers and tests; docs/TRAJECTORIES.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… from provenance)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Salvaged uncommitted work from the Opus fix lane, which hit its five-hour
and seven-day limits mid-task. It covers most of the PR #5 review:

- model_horizon: extends policyengine-uk's frozen builders (lagged CPI and
  earnings, the triple lock, private pension uprating, indices, fiscal
  years) to 2042 in memory, with upstream hash checks.
- The trajectory horizon runs to 2039-40.
- Past years keep the data-year denominator at actual law (H1).
- The lag order is chosen on a common sample, and the t-copula df is
  bounded.
- The backtest reports both treatments of April 2022.
- Plus fail-closed dashboard checks, fast-check properties, stricter
  provenance and new committed-file tests.

Committed-file tests fail until the results are regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to 2039-40, and a calibrated expected value

- engine.py: every model run (a path, a past-years counterfactual, dataset coverage) in its own process, cached by input
- central.py: EFO to 2030, OBR long-term determinants to 2039, EFO quarterly proxies for the statutory inputs to 2030
- expected_value.py: monthly-model draws shifted to the OBR means by the smoothest drift path, dynamics tilts as sensitivities, an expected-value backtest, a stratified sample of full runs
- two rules only (triple lock, Burnham plan); the scaled Monte Carlo (uncertainty.py, var_check.py) removed

Work in progress: pipeline, trajectories, dashboard and tests follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s, DWP comparators, tests and dashboard on one results file

- pipeline.py orchestrates one results.json: central run, expected value, trajectories, coverage, benchmarks
- trajectories: central, a random path and the middle and 90th-percentile paths; past years; the triple lock's own record
- households.py: example pensioners on each path (State Pension, income tax, Pension Credit, Housing Benefit, council tax reduction)
- dwp.py: DWP's 2026-27 spending and caseloads, and its uprating analysis
- tests for rules, methods, the expected value, the model mechanisms and the committed results
- dashboard: Budget impact, Who's affected, Trajectories, Past years, Method

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ioner, everyone, every path), Winter Fuel Payment in the accounts, docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the survey year, State Pension age 67 from 2028, earnings-linked Pension Credit guarantee (s150A), CPI-linked additional pension, inputs at published precision

- policyengine-uk moved a cohort a year from the basic to the new State Pension on static survey ages while paying its additional pension on the basic basis, counting part of it twice (about 6-8% of flat-rate spending by 2039-40)
- the installed State Pension age stopped at 66; the survey's 66-year-olds were paid from 2028
- the Pension Credit guarantee followed CPI, below the s150A earnings minimum
- the additional pension followed the model's triple lock, not CPI as in law
- rounding the triple lock to nearest but the plan's top-up upward made the plan cost more on about 0.6% of draws; the rules now round the inputs to 0.1 point as published
- employer NI incidence is checked in every year; the household examples get the same guarantee and a staleness guard

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…test prose, complete recomputation tests

- limitations: the Pension Credit passport to Housing Benefit (not 'eligible for Housing Benefit'), take-up only for existing Housing Benefit and council tax reduction claimants, April 2027's benefit uprating, statutory means off the OBR's quarterly figures before the switch
- expected value: per-path outputs for every year and weight ratios, so every estimate and sensitivity recomputes from the file; effective runs for the sensitivities; the single-record diagnostic; switch rates over consecutive years only
- pipeline: revision and dirty flag rechecked at the end; the build's own outputs never make the tree dirty; pandas and scipy in provenance; --sensitivity-workers
- dashboard: backtest prose generated from the file; '% lower' against the triple lock; tick digits from the step; no '-0.0'; ordinals; the plan's rounding and take-up caveats; step 6 heading and pointers
- tests: applied flat rates, fixed inputs, household accounting on every published path, the real sample design, benchmark and DWP parser tests, drift shift on earnings
- stale results files from the old pipeline removed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ning engine fixes (np rounding in rate sources, pandas in the job key, fail-fast jobs, slot locks, applied-flat-rate check)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… record, Method tab on the fixed inputs, CPI years in step 2, effective runs for sensitivities, pinned install for CI, engine helper and reader tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…te wording

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion line

- data/results.json and the dashboard copy rebuilt from b8d41db (benchmark note reworded; numbers unchanged).
- Next's SWC drops the space after a {value} when the wrapped text that follows contains an HTML entity
  ("200paths", "2030the"). Five places now write the space as {" "}, and jsxSpacing.test.js compiles every
  component with SWC with and without entities so a new case fails the tests.
- The Method tab no longer repeats the replication line the footer already shows.
- Step 4: the kept-share card reads "None" when the pensioner keeps nothing; the explainer says Pension Credit
  counts income after tax and is not itself taxed.

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

The example pensioners on the Pension Credit guarantee came out slightly ahead under the plan. Pension Credit and
the lower tax bill replace the whole loss; the gain is policyengine-uk's council tax reduction, which tapers on
income after tax without counting Pension Credit and does not disregard a guarantee credit recipient's income as
SI 2012/2885 Sch 1 para 13 requires. Step 4 no longer credits the rise to Pension Credit and names the gap; the
limitations and METHOD.md record it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st household

FRS records are licensed (UK Data Service End User Licence), and the results file and dashboard were publishing the
largest record's household id, weight, income change and benefit amounts, and each year's record id and weight.

- pipeline.redact_records keeps only contribution_bn, share_of_income_change and income_change_excluding_bn
  (largest_household) and contribution_bn and share_of_income_change (concentration_by_year) wherever they sit.
  gross_contribution_bn goes too: with a known State Pension change it would give back the weight. It runs in the
  pipeline, so the engine cache is untouched.
- test_no_survey_record_is_published walks the whole file; a Hypothesis property checks the redaction on arbitrary
  nesting.
- The dashboard's flags report the contribution and share only, and say why.
- RESULTS_SCHEMA.md documents the fields (and the expected-value path records, which were stale).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(numbers unchanged)

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

coverage.datasets.*.max_household_weight was the largest Enhanced FRS
household weight, one survey record's weight, which the licence rules out
publishing. The engine still computes it (changing engine.py would rerun
every job), but the results no longer carry it. Tests check that no key
anywhere holds a household weight.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…unchanged)

Every model job came from the cache; the only changes are the two removed
coverage.datasets.*.max_household_weight fields and the provenance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quarto manuscript (paper/index.qmd) following the dashboard's six steps:
the triple lock since 2011 and the plan's rule, the OBR's central forecast,
a random path, example pensioners, the whole population, and the expected
saving across every path; method, limitations and the forecast-distribution
backtest in an appendix. Every number is computed from data/results.json by
paper/paperdata.py; figures by paper/figures.py. references.bib holds only
sources fetched and checked on 30 September 2026.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Past-years check described as the uncalibrated model; calibration choice
explained by effective paths, not bias alone; DWP's wording quoted rather
than paraphrased as a definition; the random path's low position flagged in
steps 4 and 5; Monte Carlo SE scoped to path sampling, with the calibration
range beside it; Pension Credit's after-tax income test cited to the
regulations; council tax reduction take-up corrected; the guarantee versus
Housing Benefit allowance uprating step disclosed; Microcosm compared on its
own subsample; figures recoloured with legends off the data; no hand-typed
result numbers left in captions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dashboard/public/paper/index.html is the shelled wrapper (site header,
paper header, action row, a versioned sandboxed iframe of the manuscript,
footer); paper/render.sh renders HTML and PDF in one pass and syncs them to
dashboard/public/paper/web/. next.config.js keeps /paper/'s trailing slash
and proxy.js sends /paper to /paper/. The dashboard footer links to the
paper. tests/test_paper.py checks the wrapper's versioned links and figures,
that the committed render is from the current results, that every citation
resolves, that the prose types no result numbers, sentence-case headings,
banned words, and that no survey record identifier or weight reaches the
paper; property tests cover the number formatting.

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>
@vercel

vercel Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
uk-triple-lock Ready Ready Preview Oct 16, 2026 9:06am UTC

Request Review

Cites the Social Security (Up-rating of Benefits) Act 2021 for April 2022's
suspension and the 2011 Up-rating Order for April 2011's rise, replacing both
[NEEDS CITATION] markers. Corrects the limitations in place (council tax
reduction take-up, single records' likely cause, DWP's description) instead of
appending contradicting bullets; scopes the past-years check to the model
before any shift; caveats the calibration range in the summary; closes the
gross-to-net arithmetic; quotes DWP and the OBR only where their words are
verified; adds paper/SOURCES.md, the record of what each source confirmed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits September 30, 2026 04:24
Publish no survey household's weight: drop max_household_weight from coverage
…ach drawn path sits

- data/benchmarks.csv: every figure_text is now a verbatim quote (or quoted
  table cells) checked against the live source on 30 September 2026, and each
  comparison says what the source compares. Resolution Foundation: the £12.6bn
  is the OBR's estimate against earnings-linked uprating "this year"; the £9bn
  net figure is RF's. OBR July 2025: the £5.2bn is the cost under the initial
  2012 assumptions. OBR long-term determinants: the 0.6-point note sits under
  the 2025 heading; the 2026 column gives 0.56 points. like_for_like unchanged.
- docs/sources.md: the BBC live page rolled on to 30 September under a new
  headline; the 29 September posts that carry every quoted word ("adjust",
  "prices", "it will hold its value relative to earnings over time") are
  listed with permalinks, with the 15:07 BST archived copy. The OBR 2025 and
  RF entries are corrected as above. README and dashboard link the post.
- trajectories.py: each drawn path's selection records where its 2039-40
  gap sits among the primary calibration's 50,000 draws
  (gap_percentile_2039, larger_gap_pct_2039, draws_compared); documented in
  docs/RESULTS_SCHEMA.md. Not an engine file: no model job changes.
- Step 3 says it: "A draw at the 2nd percentile of the model's 50,000 paths:
  98% of them open a bigger gap by 2039-40, and so save more on the State
  Pension", generated from the file.
- Tests: property tests of the position against the weighted CDF; a results
  test that recomputes it from the expected value's draws and primary
  weights; a results test that the final-year gross saving rises with the gap
  in every full run (what "save more" rests on); dashboard render, reader and
  wording tests.

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

Copy link
Copy Markdown

Program review

Base repository: PolicyEngine/uk-triple-lock
PR number: 8
Reviewed head SHA: c9b192c
Merge base SHA: 3d43e24
Mode: full
Scope: full audit of main 12ada9a (tree-identical to PR base), including model, dashboard, outputs and tests; plus PR #8 paper, route, render and data references
Source manifest: retained with the local review artifacts
Review status: PARTIAL

Source documents

The DWP policy publication was checked live. Two original OBR workbooks were byte-authenticated; the OBR long-term determinants and DWP benchmark workbooks were inspected as committed copies, without publisher-byte authentication. The full local report preserves their SHA-256 manifest and provenance.

Critical

None.

Should Address

A1 — SHOULD ADDRESS — MAIN model; PR8 headline inherits conditional net estimate (OPEN)

Location: src/triple_lock/engine.py:695-714, docs/METHOD.md:33, paper/index.qmd:82,415-421,604-641. Trigger: Interpret the £5.392bn expected 2039–40 net saving or the 20.2% households-losing estimate as nationally calibrated impacts. Expected: The means-tested offsets and household impacts should be robust to reasonable baselines, or the figures should be labelled prominently as conditional on the selected survey/model. Observed: Committed data/results.json has Enhanced FRS pension-age Housing Benefit £13.448bn in 2026–27 versus £7.269bn in the committed DWP workbook (+85%); Pension Credit caseload 1.821m versus 1.312m (+39%). Microcosm gives £3.019bn pension-age Housing Benefit and 1.258m Pension Credit claims. Its paired-path 2039–40 net estimate is £5.864bn versus £5.392bn on the primary dataset, a £0.472bn difference, against a primary path-sampling SE of £0.036bn. The primary net estimate includes £0.965bn extra Pension Credit, £0.136bn extra Housing Benefit, and £1.864bn less income tax, so at least one of the materially misaligned baselines bears directly on the reported offset. The paper describes these discrepancies, static rents, take-up gating, and a possible single-record cliff; the new limitations prose at paper/index.qmd:769-792 makes this clearer, but disclosure does not establish calibration. The DWP figure is GB and includes overseas spending in its flat-rate comparator while the model is UK; the paper flags this, so the differences above are diagnostics, not a direct proof of wrong policy effects. Impact: The net and household headline precision exceeds demonstrated baseline robustness; the fiscal gross figure is better anchored mechanically than the offsets. Show net/household sensitivity beside the headline and investigate the Housing Benefit/Pension Credit population and claim logic before treating the net point as an external costing. Evidence: data/results.json coverage.rows, expected_value.estimates, expected_value.paired_difference; src/triple_lock/dwp.py:22-44,65-89; DWP's State Pension uprating explicitly covers GB, direct AME and a dynamic population model. The DWP benchmark workbook is a committed copy; publisher byte identity is unverified.

Coordinator assessment: Code review separately traces the inherited Housing Benefit claimant gate and modelled council-tax-reduction response in src/triple_lock/engine.py:420-548; the committed example exposes the latter. This supports the need for an offset sensitivity, while the direction of combined bias remains unquantified.

A2 — SHOULD ADDRESS — MAIN uncertainty model; PR8 interpretation (OPEN)

Location: src/triple_lock/ts_monthly.py:106-139,191-225,351-380, src/triple_lock/expected_value.py:91-107,268-348,403-450, paper/index.qmd:82,502-531,553-581,764-804. Trigger: Read the 50,000 paths and £8.411bn gross / £5.392bn net “expected saving” as a well-calibrated predictive joint distribution. Expected: The stated uncertainty should include or bound uncertainty in the macro process, OBR long-term mean path, and data/model response, especially because the policy payoff depends on CPI/earnings reversals and tails. Observed: The primary uses one BIC-selected VAR(1) with 297 fitted residual pairs, residual bootstrap shocks, and a deterministic smooth drift to OBR calendar means. Only 200 sampled paths (198 unique) get full model runs; 40 get paired Microcosm runs. The quoted £0.042bn gross and £0.036bn net SEs measure this sampling only. The older statutory backtest failure has improved but still gives only 1/6 realised gaps inside the primary monthly-shift middle-80% band when April 2022 uses published earnings, versus 4/6 under the legally suspended earnings leg. The paper correctly notes six overlapping origins, dependence on the April-2022 treatment, historical revision, and that no method supports a reliable 80% predictive interval. Its dynamics-tilt sensitivities on the same paths span £6.61bn–£10.66bn gross, with the highest row's net SE about £3.40bn because effective full runs fall to 44. Impact: The numerical “expected” value remains conditional on one macro law and one OBR mean path; the backtests do not support a tightly calibrated predictive expectation of the reform's impact. Carry that conditionality into the abstract/dashboard headline and avoid using the tiny path-sampling SE as the apparent total uncertainty. Consider a distinct model-form/mean-path sensitivity or an explicitly bounded scenario analysis. Evidence: Committed data/results.json expected_value.draws.model, strata, estimates, sensitivities, trajectories.backtest.statutory; paper appendix. These are diagnostics, not proof that an alternative model would give a particular answer.

A3 — Fiscal and distributional estimates hold the pensioner cohort and pension type fixed through 2039 (OPEN)

[MAIN; P2 modeling validation gap, open]. src/triple_lock/engine.py:283-305 pins each person's survey-year state_pension_type, and :449-455 applies it to every run/year. The committed coverage has 8.262m BASIC and 3.873m NEW type people, with survey ages top-coded at 80; the same type mix persists to 2039 while survey weights change. dashboard/src/components/SummaryTab.jsx:221-226 acknowledges the limitation. The cost of a basic versus new flat-rate cut and its household incidence differ, so the £8.41bn gross and £5.39bn net expected savings are estimates for this fixed-composition population, not a projected 2039 pensioner cohort. Comparing with DWP's dynamic £15bn is therefore not like for like. The published results give no ageing/type-mix sensitivity to show how much this changes either number. This is a material uncertainty limitation, not proof that the arithmetic on the frozen sample is wrong.

Suggestions

S1 — The embedded paper disables the scripts its render uses (OPEN)

[PR8; P3 presentation defect]. dashboard/public/paper/index.html:136-139 sets sandbox="allow-same-origin allow-popups allow-popups-to-escape-sandbox" on the iframe, excluding allow-scripts. The rendered manuscript loads Quarto lightbox, anchor, tooltip and tab scripts at dashboard/public/paper/web/index.html:51-62 and initializes the figure lightbox at :2469; figures link to PNGs with class="lightbox" (e.g. :154). Inside the embedded paper, clicking a figure falls back to navigating the iframe to the raw PNG instead of opening its lightbox, and script-driven document controls cannot function. The standalone HTML link avoids the sandbox. Decide whether to allow trusted local scripts in the embed or render the manuscript without script-dependent controls; verify the route in a browser. This does not change model numbers.

Coordinator assessment: The missing allow-scripts token and script-dependent controls are confirmed from the committed files; the interactive effect should be checked in a live browser before treating this as a functional breakage.

Evidence Gaps

  • The private Enhanced FRS/Microcosm microdata and pinned PolicyEngine bundle were unavailable, so the 198 full population runs, offsets and high-weight record could not be independently regenerated.
  • The official OBR long-term-determinants and DWP benchmark workbook bytes could not be authenticated against the publisher from this environment; committed copies were inspected with their provenance stated.
  • The local Python suite could not collect because scipy and hypothesis were absent, and the dashboard build lacked node_modules; GitHub CI success at the reviewed SHA is recorded separately.
  • The paper iframe interaction was inferred from markup and not confirmed in a live browser.
  • The LaTeX builder added at PR Working paper: Public Pension Indexation Under Uncertainty: Fiscal and Distributional Effects (/paper route) #8 head c9b192c was inspected but could not be executed locally: pandoc and tectonic were unavailable. Its output is not the dashboard's currently committed PDF.

Notes

Validation Summary

At PR #8 head c9b192c, GitHub Dashboard, Pipeline and Vercel checks: SUCCESS. Local read-only checks at 200553e: AST parse of 30 Python files, results JSON copy and 32 paper assets validated; a 100,000-path synthetic uprating diagnostic found zero Burnham rates/levels above triple lock. The 200553e..c9b192c delta was inspected in a detached snapshot: paper prose and a new LaTeX builder only, with unchanged model/results data. A bounded local pytest attempt could not collect due to missing scipy and hypothesis; the new LaTeX build, full population rerun and dashboard build were NOT RUN (missing dependencies/private bundle). Five cached/committed source items passed manifest byte integrity; only two OBR workbooks were publisher-authenticated. Live DWP page and relevant legislation were checked by policy reviewer.

Timing

setup seconds: 103.00s; scope seconds: 269.00s; parallel review seconds: 933.00s; policy role seconds: 857.00s; code role seconds: 933.00s; adjudication seconds: 175.00s; consolidation cleanup seconds: 574.00s; elapsed seconds: 1639.00s

Review Severity

COMMENT. Open findings: 0 critical, 3 should address, 1 suggestions.

Conflicts were one-sided: results.json and tests/test_model.py keep the paper
branch's versions (main had not changed them); the dashboard components take
main's new layout. The paper link returns under the dashboard's intro.

Also, since the dashboard no longer follows the six steps:
- the paper's introduction no longer says the dashboard follows its six steps;
- the embedded manuscript's iframe allows its own scripts (figure lightbox,
  anchors, tooltips), issue #9 S1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
vahid-ahmadi and others added 3 commits October 15, 2026 18:34
# Conflicts:
#	dashboard/public/data/results.json
#	data/results.json
#	tests/test_model.py
… FRS 1.58.0)

Every placeholder from the model-v2 draft is filled from data/results.json:
expected saving £11.1bn gross / £7.9bn net in 2039-40 over 50,000 paths
(160 run in full), provisional under the screen's dry run (4 of 6 final
years, 80% of annual checks) until the 28 October 2026 Budget forecast.

- Sections follow the dashboard: what the plan saves, how the triple lock
  works, the OBR central forecast, other paths, the whole population,
  example pensioners, method and limitations.
- New single-scenarios table (OBR central, OBR long-term path, earnings
  ±0.5 points) and the middle 80% of paths, both computed as the dashboard
  does; survey data against DWP for Great Britain; screen score table.
- No Microcosm results and no single-record figures in the paper.
- Neutral wording (no 'cut', 'losing' without its comparison, 'ratchet').
- PDF sections number 1, 2, 3 (shift-heading-level-by: -1).
- LaTeX build: abstract, conclusion and section map updated.
- test_paper: the guarantee pensioner comes out even in the model too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
vahid-ahmadi and others added 8 commits October 16, 2026 08:51
The published PDF (dashboard/public/paper/web/index.pdf) is now the LaTeX
build in the uk-ai-study layout (paper/latex/main.tex, built by
paper/latex/build.py with tectonic), not Quarto's own PDF. render.sh builds
it after the Quarto render and copies it over out/index.pdf before syncing.
Bump the wrapper's ?v= so browsers fetch the new PDF.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new manuscript in the dashboard's order: the two rules and where the saving
comes from (with the 2011-2026 history and the earlier-start replay); what the
plan would save (expected saving and its spread, why the OBR central forecast
saves little, single scenarios, weighting to past volatility, other costings);
one path in detail; households (gross to net, deciles, poverty, example
pensioners on the typical path); method; limitations; conclusion. Every number
is computed from data/results.json. Figures use the dashboard palette (CPI amber,
triple lock darkest teal, Burnham plan mid teal) and pension gaps per year. Plain
words in the main text; no second dataset, no single-record figures. The LaTeX
build follows the manuscript's own order and conclusion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- paper/plotstyle.py: the uk-ai-study figure look (serif type at 10/9 pt,
  light grid under the data, no top/right spines, frameless legends centred
  below, no title on single panels) with the dashboard SERIES colours; figures
  drawn at their printed size for the 6.3 in text width.
- paper/figures.py: every figure re-implemented on the template, units in axis
  labels, one shared legend under multi-panel figures, fiscal-year ticks,
  value labels on decile and scenario bars, shared y scale for the example
  pensioners.
- paper/latex/build.py: vector PDF figures placed at their own width; tables
  in the uk-ai-study layout (short caption above, units in a second header
  row, rule above a bold total, grouped first column, note below).
- paper/index.qmd: table headers carry their units, e.g. "Gross (£bn)".
- Rebuilt PDF.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Responds to the referee, economics, policy and editorial reviews:
model uncertainty leads (all twelve reweightings via the exact
final-year ready-reckoner, £8.7-12.0bn), DWP reconciliation (GB and
coverage-scaled figures, OBR long-term scenario), DWP-definition
anchor and section 150A, paid vs replayed history (2011 RPI),
signed example-pensioner shares and threshold explanations, stated
pass rule and power of the past-forecast test, poverty on three
paths, real-terms and cumulative figures, title from the qmd, hatched
plan bars, and float placement. HTML render not regenerated (no Quarto).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Rule arithmetic on every path (scripts/paper_rule_arithmetic.py, no
  simulation): exact gross saving by year, the three other passing model
  versions costed, the +/-0.5pt earnings scenarios costed exactly, model
  path dynamics against history. Written to results.json as
  paper_rule_arithmetic and checked by paperdata on load.
- Reweighted figures from the full runs now use a post-stratified ratio
  estimator (the unnormalised one lost 12-43% of the probability mass);
  net reweighted figures come from each run's own net saving.
- DWP is quoted only in its own words; the 2029-30 anchor is stated as our
  reading. Headline framed against the model range (8.7-12.0bn, mostly
  below 11.1bn); out-of-sample check reported correctly.
- Pass-rule history, Jensen argument, earnings-scenario explanation,
  coverage attribution, tax/guarantee/take-up conventions corrected.
- build.py now writes the HTML manuscript in the same pass as the PDF
  (no Quarto), plus table spanners, cell citations, caption cross-refs,
  plainnat with URLs; figures and appendix layout tidied; wrapper bumped
  to revision 5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Abstract: full runs are 160 of the 50,000 paths, not all of them.
- Method: 158 paths are drawn across the ten groups (plus two zero-gap checks).
- Cite the BBC 14:54 post for the 'at least prices or 2.5%' paraphrase.
- DWP's model: describe what it projects in DWP's own words.
- Neutral wording with an explicit baseline for single pensioners and example (d).
- Read package and data versions from results.json provenance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Title 'Public Pension Indexation Under Uncertainty: Fiscal and Distributional
Effects' (no subtitle) in the manuscript, PDF title page and metadata, and the
/paper wrapper (revision 6). The abstract opens with the method (stochastic
microsimulation); keywords updated; JEL codes kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vahid-ahmadi vahid-ahmadi changed the title Working paper: the Burnham plan and the triple lock in six steps, with a /paper route Working paper: Public Pension Indexation Under Uncertainty: Fiscal and Distributional Effects (/paper route) Oct 16, 2026
vahid-ahmadi and others added 3 commits October 16, 2026 11:04
Every figure is now a Plotly Express figure in a PolicyEngine template built
from the dashboard (Inter type, gray-500 ticks, light grid, legend under the
plot, teal logo) with the dashboard's SERIES colours, exported with kaleido
as vector PDF at print width (PNG at 3x for the web page). Captions no longer
mention hatching.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Title page, manuscript front matter, HTML metadata, PDF author metadata, the
/paper wrapper and its test. Each author keeps their own affiliation/email
footnote; the acknowledgement stays on the title.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…or order

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

This branch was successfully deployed

1 active deployment
Preview — 30b44d6d Deployed Oct 16, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants