Compute the State Pension triple lock from its statutory inputs - #1939
Conversation
The April uprating now uses September CPI (ONS D7G7) and May-July AWE total pay growth as used in each review (ONS KAC3, per the Up-rating Order explanatory memoranda), not calendar-year OBR growth. The inputs live in gov.economic_assumptions.statutory_uprating_inputs with ONS outturn, then forecasts: calendar-year growth plus the OBR's gap between its statutory-basis and calendar-year forecasts (September CPI from receipts Table 3.19, Q3 CPI and Q2 earnings from economy Tables 1.7 and 1.6), then calendar-year growth after the EFO horizon. Macro scenarios that edit calendar-year growth move them; scenarios can also set them directly. The rule reproduces every published uprating from April 2012 to April 2026. April 2011 (RPI) is the only override; April 2022 is include_earnings false. The rates run to the end of the economic assumptions. The active toggle now means the s150A statutory minimum, and an optional earnings-path guarantee models plans that keep the pension in line with earnings over time. The OBR importer regenerates the forecast gaps. April 2027 rises by 3.9% (published May-July 2026 earnings) rather than 3.4%. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Measure each forecast gap from the calendar-year growth stored in yoy_growth.yaml, so the baseline inputs equal the OBR's statutory-basis figures (May-July 2027 earnings was 2.5% after rounding, OBR 2.4%). - Importer: render and check every file before writing any; write gaps in fixed point (3e-05 loaded as a string); put gaps next to --yaml-path; require the receipts tables unless gaps are skipped; add --gaps-only. - Accept numpy scalars in the rounding helpers. - Docs: a double lock is the floor at 0; active=false ignores the floor, the include flags and the one-year 2021 and 2022 changes to s150A; calendar-year edits need year:YYYY-01-01:1 keys; levers work through Scenario, not reform dictionaries; the baseline has no triple-lock ratchet premium (OBR: 0.56pp); earnings are as used, not first published. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mutation testing of the first commit left 18 mutants alive. New tests kill them: an independent table of the inputs each review used (typos in non-binding years), exact OBR gaps, zero inputs and zero overrides kept as values, an override under the earnings-path guarantee, levers read on 30 April of the year they name, input rounding, the horizon following the shorter calendar series, the missing-input guard, no_economic_assumptions zeroing the gaps, half-up ties, and the receipts header row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Reviewed: approve with nits. Driving the April rise from September CPI, May–July AWE total pay and 2.5%, with the inputs rounded to 0.1pp before the max, is the right structure. From the stored inputs I recomputed every April from 2012 to 2026 and got the published rate each time; only 2011 needs an override.
Verified: I read s.150A on legislation.gov.uk. It is an earnings floor with no cut, the 2020 and 2021 Acts modify it for one year each, and the AWE and CPI series are policy rather than statute. The new and changed tests passed (95/95) and CI is green. |
Independent review (Astra gate, round 1) found that an OS error while writing the second or third file left yoy_growth.yaml and one gap file on the new forecast and the other on the old. Each file is now staged beside its target and moved into place; any failure restores the files already moved and removes the staged copies. Tests fail the move at each of the three files, and fail the staging step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Independent review (gate round 2) found that restoring an original with write_text truncates the target first, so a persistent I/O fault during rollback could leave a file empty with no copy of the original. Each existing target is now copied to a backup beside it before anything moves, and rollback restores it with an atomic move. If a restore fails, its backup is kept and named in the error. A new test fails a move and the first restore and checks no file is emptied and the original survives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Label the September 2026 earnings estimate and derived pension rates as provisional, describe the exact compatibility changes, and document that projected weekly State Pension amounts are not rounded to 5p. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@vahid-ahmadi, addressed your 1 October review in c45f23f2a, following merge 054d68de.
Triple-lock mechanics are unchanged. Format/lint passed; 79 rule/outturn/importer tests and one statutory-input case passed in fresh runs. The combined run was interrupted after 86 passes under extreme memory pressure; remaining statutory-input, DWP YAML and parameter/metadata/code-health checks are pending because the sandbox denies the required swap-usage reading. |
|
Merged at
|
Summary
The State Pension triple lock is now computed from the statutory inputs the uprating review uses, not from calendar-year OBR growth:
The April uprating in year Y uses year Y-1's inputs, each rounded to 0.1pp as published.
add_triple_locktakes the highest of the included elements and the floor.Why:
Statutory inputs
New node
gov.economic_assumptions.statutory_uprating_inputs. Each input is keyed to its observation month and haspreserve_calendar_dates.cpi_septemberawe_total_pay_may_julyNotes:
forecast_gap/), measured from the calendar-year growth stored inyoy_growth.yaml. A scenario that editsyoy_growth.obr.consumer_price_indexor.average_earningstherefore moves the inputs one for one. A value set directly on an input replaces the forecast for that year only; the null after the outturn makesParameter.update's restore entry a null, so nothing leaks into later years.import_obr_forecasts.pyregenerates the gaps from the same EFO (--receipts-file/--receipts-urlfor September CPI;--gaps-onlyafter editingyoy_growth.yamlby hand). It renders and checks every file before writing any.Rule
outturn.yamlis April 2011, when the basic State Pension rose by September 2010 RPI (4.6%) during the switch to CPI (EM to SI 2011/821, para 7.4). April 2022 isinclude_earningsfalse, citing the Social Security (Up-rating of Benefits) Act 2021 s1, and needs no override. The include flags now start in 2011, when the triple guarantee first applied.activehad no effect before. False now means no triple lock: the statutory minimum from the review under SSAA 1992 s150A as it stands, i.e. earnings growth, and nothing when earnings fall. The floor, the include flags and the one-year 2020 and 2021 Act modifications then do not apply.earnings_path_guarantee(off under current law). Each year it applies, the pension also rises enough to stay on an earnings path started from its level in the year before the guarantee first applies, rounded up to 0.1pp. Withinclude_earningsfalse from 2030, that is one reading of the September 2026 plan; the speech gave no formula. It is the same rule asburnham_2030in uk-triple-lock'srules.py.policyengine_uk/utils/create_triple_lock.py, a stale script that printed a calendar-year triple lock on import. Nothing imported it.Scenario(parameter_changes=...), which rebuilds the rates. Areform=dictionary edits parameters after processing, so it changes the parameter but not the rates. That is pre-existing and verified; the docs and the lever metadata now say so.Baseline before and after
The table records historical runs before the merge of current main, using the Enhanced FRS 2024-25 (sha256 e433e532): the then-current
origin/main(44240bd) and this branch (f5629fd). These aggregate results describe those original commits and are not validation of the current merged branch; no new microsimulation was run for this review follow-up. Every figure below came from those runs; nothing is scaled or interpolated. "Net fiscal cost" is the fall ingov_balance. The scripts and outputs are on the author's machine and are not available in this repository.Provisional figures: the April 2027 uprating and all 2027-28 onward after/change/net-cost pension levels and aggregate impacts depend on the 15 September 2026 first estimate of May-July earnings (3.9%), including £250.71 a week and the £0.47bn net fiscal cost in 2027-28; those figures remain provisional until the October labour market release (20 October), the vintage used by the uprating review, and the qualification applies to the 2027 uprating and the subsequent level and impact comparisons below.
Provisional higher level from 2027 (historical runs). The only rate that changes in 2027-2034 is April 2027, 3.4% to 3.9%. The pension stays about 0.48% higher through 2034, so spending rises £0.68bn in 2027-28 and £0.91bn in 2034-35. About a fifth of the gross cost comes back through income tax, and a little through Pension Credit.
Provisional 2028-29 net figure (historical runs). The aggregate net fiscal cost is £0.20bn. It is depressed by a Housing Benefit cliff in a very small number of survey records in the base run; treat the dip as sampling noise, not a policy effect. The neighbouring years are £0.47bn and £0.51bn.
Means-test offsets on the merged head. Since these runs,
mainhas counted State Pension as Universal Credit unearned income (Count State Pension as Universal Credit unearned income #1943) and changed Housing Benefit (Cap the eligible rent at the LHA before the Housing Benefit taper #1926), Pension Credit (Count Carer's Allowance and Carer Support Payment as Pension Credit income #1952, Let a dataset supply each benefit unit's own Pension Credit capital #2018, Replace generic child and adult flags with each programme's legal definitions #1896) and Council Tax Reduction (Disregard guarantee credit recipients' income and capital in pensioner council tax reduction #1909). The gross State Pension figures are unaffected, but the net column and the share recovered through income tax and means-tested benefits may differ on the merged head. Re-run on currentmain(real runs on 2 October 2026:main3c48247e, which includes Replace generic child and adult flags with each programme's legal definitions #1896, againstmainmerged with this headc45f23f2; published Enhanced FRS 2024-25 1.57.4, sha25603fe15e4…; £m a year):About 7.2m households gain in 2027. Nothing changes in 2026. The gross State Pension change passes £1bn in 2035-36, so the merge is queued for Max's decision (d854) under his merge-on-gates rule.
2035 onwards. The generated uprating rate follows the long-run earnings path (3.8%) instead of carrying 2034's 3.7% forward.
Invariants and tests
Stated invariants, each with tests:
Triple lock. The rate is at least each included element and the floor, and equals one of them. It never falls when an element rises. With the triple lock off, it is max(earnings, 0). (Hypothesis)
Earnings-path guarantee.
All four are Hypothesis tests.
Differential against uk-triple-lock
rules.rates_matrix('burnham_2030'). An independent transcription agrees on random paths. (Hypothesis)Differential against published figures.
Forecasts.
Scenarios.
Scenarioapplied before the data load moves the April 2028 rate, and the new State Pension a pensioner is paid in a small-dataset microsimulation.activefalse follows earnings.no_economic_assumptionszeroes the forecast inputs and leaves only the floor.Importer.
--yaml-path.Review. Five independent lenses reviewed the first commit: rule correctness, data and claims against primary sources, mutation testing, integration, and design and docs. Each finding then went to a skeptic.
Not in this PR (follow-ups)
reform=dictionaries don't rebuild derived parameters (pre-existing).cpi_septemberinput to fix that.engine.path_followingreproduction check,config.pyparameter readers and horizon compatibility guards when bumping the policyengine-uk pin. Consumers must use the observation-year statutory inputs, the following April's generated uprating rate, and the include flags; published inputs take precedence over forecast gaps.yoy_growth.triple_lockis the generated April rate, not a same-year calendar-growth maximum: each prior-year input is rounded to 0.1pp before applying the rule, with forecast gaps only for unpublished inputs (CPI observation years 2026–2030 and earnings observation years 2027–2030), and April 2027 earnings currently using the provisional May-July 2026 first estimate.triple_lock.outturnis null from 2012; generated rates continue through April 2074 and follow prior-year rounded earnings after 2034 under the stored baseline. The provisional State Pension levels are about 0.48% above the previous baseline in 2027–2034; later rates also change. Existing uprating indices still stop at 2039, as noted separately above.axiom: TheAxiomFoundation/rulespec-uk#366 queued (SSAA 1992 s150A earnings review, with the 2020 c.23 and 2021 c.32 one-year modifications; s150A is not yet in the Axiom corpus). The in-force 2026-27 rates are unchanged and already encoded-correct: uk:regulations/uksi/2026/148/article/6 (241.30) and article/4 (184.90), each with companion tests.
🤖 Generated with Claude Code