Repository navigation
Conversation
The part of a reported State Pension above the type's full rate (additional pension for the basic State Pension, protected payments for the new) was uprated by the full rate's own triple-lock rise. Each Up-rating Order raises it by September CPI instead (SSAA 1992 ss.150(1)(c), 151 and 151A; SI 2026/148 arts 4(3) and 6(3)(b): 3.8% against 4.8% for the full rates; SI 2025/295: 1.7% against 4.1%). Uprate it by gov.economic_assumptions.indices.september_cpi_uprating from the data year, read from the simulation's own parameters, so a reform to the full rates or the triple lock no longer moves it and a price scenario does. Fixes #1941. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 11, 2026
…rms as a reform CI found two test faults (run 38161065391): the differential against main's formula compared the model's float32 arrays with a float64 reference at rtol 1e-12, which fails for any reported amount float32 cannot hold exactly (e.g. £169.00 a week, aged 98); and a Scenario cannot take the reform dictionary's "2026-01-01.2100-12-31" period, so that case passes it as a reform. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Oct 11, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in the docs At 5% September CPI the triple lock also rose 5%, so the scenario test passed on main. At 1%, below the triple lock's 2.5% floor, the add-on rises 1% and the full rates at least 2.5%. The docs record the change in the model's State Pension against DWP's forecast, from full runs on the certified dataset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep the September CPI uprating in additional_state_pension: main's np.divide guard (00c07d6) was on the full-rate ratio this branch replaces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Oct 12, 2026
…branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: GPT-6 <noreply@openai.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1941
Law
Additional pensions and protected payments follow prices separately from the full State Pension rates. SI 2026/148 article 4(3) directs the covered additional pensions to be “increased by 3.8 per cent.”, with a final relevant year before 2025–26. Article 6(3)(b) covers amounts “at the transitional rate which exceed the full rate” and applies the same 3.8%. The full rates rise about 4.8%: £176.45→£184.90 and £230.25→£241.30 (articles 4(2) and 6(1)).
The equivalent 2025 article 4(3) and article 6(3)(b) direct the add-ons to be “increased by 1.7 per cent.”, against about 4.1% for the full rates. The made HTML and XML were read for both Orders; the current 2025
/data.xmlomits these provisions, so the operative text was checked at/made/data.xml.SSAA 1992 sections 150–151 provide the prices review and percentage increase for additional pensions; section 151A covers the transitional excess. These duties prescribe an increase when prices rise, rather than a cut when they fall. The 2016 Order explanatory memorandum, paragraph 4.2 and footnote 2, confirms no increase under section 150 when September 2015 CPI was −0.1%. Section 150A sets an earnings minimum for the full rates; it does not itself legislate all three triple-lock elements.
Change
additional_state_pensionuprates the reported excess above the period type's legislated data-year full rate byI(period) / I(data_year), whereIis the simulation's owngov.economic_assumptions.indices.september_cpi_uprating. CPI or macro scenarios can therefore move the add-on. The baseline data-year split from #2122 remains; basic and new flat-rate components retain their full-rate ratios.Reforms to the full rates or the triple lock no longer move the add-on because in law it follows prices: SI 2026/148 articles 4(3) and 6(3)(b). This fixes the model to the statute; it is not a methodology choice and needs no separate decision. This intentionally changes the #2122 full-rate-reform expectations, including a rate of zero.
gov.contrib.abolish_state_pensionstill removes the entire pension.The existing September CPI builder was read, including its docstring: published September CPI is rounded to 0.1 percentage points and floored at zero. The test reference hard-codes the statutory rises for 2011–2026 and independently rounds CPI inputs only for forecast years; it never calls the production index. Those historical percentages were checked against the Orders, including zero in April 2016. The model rounds each compounded index to five decimal places, so ratio/reference checks allow
1e-5rounding tolerance.Files:
policyengine_uk/variables/gov/dwp/additional_state_pension.py:50(baseline split),:79(simulation CPI),:82(index ratio);policyengine_uk/tests/test_state_pension_components.py:101(Order reference),:275and:306(dataset regressions);docs/book/programs/gov/dwp/state-pension.md(Components, Uprating and fixes);changelog.d/1941.fixed.md.Tests and fail-on-main evidence
The two statutory regressions build 2024–25 data: BASIC, £200/week, has 2026–27 additional pension
(200 − 169.50) × 52 × 1.017 × 1.038 = £1,674.254556; NEW, £240/week, has protected payment(240 − 221.20) × 52 × 1.017 × 1.038 = £1,031.9995296. Separate growth cases check each year's CPI rise. Situation simulations use the period as data year, so dataset-built cases are necessary.At the previous continuation head
2fd0c7a8be714487be8895ffa248b600d48ba8e0, CI run 38200686793 passed: 3,058 YAML tests; 5,125 Python tests, 67 skipped and 2 expected failures; zero failures, across three shards with four workers each. The shard gate verified all 5,194 collected Python tests ran exactly once. Lint, documentation build and smoke installs on Python 3.11–3.14 passed. The latest continuation commit changes documentation only. The older15bb98d44CI had only the known dataframe round-trip failure, fixed by the merged #2349.The recovered local YAML log records 298 cases passed in 737.73 seconds across 24 State Pension/downstream files at
4b9264f19; the exact old invocation was not preserved. Completed full CI at the formula head2fd0c7a8covers the other State Pension readers and the DWP folders, subject to its reported dataset-access skips. Focuseduv run --active --no-sync pytest policyengine_uk/tests/test_state_pension_components.py -n 4 -vv --tb=shortat that head passed 31/31 in 1,140.63 seconds, with no skips or failures. All four Hypothesis tests ran ten deterministic examples each. The final documentation-only head has identical formula/test source.At
4a6b736a09076e303f3e646c85f5709805c93de4, documentation build, lint, Python 3.11–3.14 smoke installs, full YAML and Python shards 1/3 and 3/3 pass. Python shard 2/3 reported results for all 1,791 collected cases (1,765 passed, 25 skipped, one expected failure, zero failed), including explicit passes for all 14 State Pension cases in that shard, then received a runner shutdown before its final summary/artifact upload. The log says “The runner has received a shutdown signal” and “The operation was canceled.” Its job status is failed and the overall gate is incomplete; this is not a model assertion failure. The underlying shutdown cause is unverified. No source change or CI retry was made for the interruption.Fail-on-main: an isolated clone at the merged main
8019c0ccea83f414d6b44a90c2c64fe5fc9b5ee8, with only the updated component test file copied in, produced 23 expected assertion failures and 8 passing controls in 1,843.53 seconds, exit 1. Both statutory pension cases, both annual-growth cases, all four properties and the changed add-on reform expectations fail. There were no import/fixture failures. The eight passing controls exercise unaffected behavior. Command:PYTHONPATH=<main clone> uv run --active --no-sync pytest policyengine_uk/tests/test_state_pension_components.py -n 0 -vv --tb=short -o usefixtures=cloned_uk_tax_benefit_system. This existing repository fixture loads model code once and clones independent state; no conftest/model source was edited. A shared clone replaced the linked worktree because linked-worktree metadata is outside the sandbox's writable roots. These old-main property violations are intended evidence of the statutory defect, not new failures on the fix.Formatting/lint:
uv run --active --no-sync ruff formatandruff checkonadditional_state_pension.pyandtest_state_pension_components.py: two files unchanged, all checks passed. The touched-file equivalent was used because workspace-only scratch clones make repository-widemake formattraverse duplicate test trees.Invariants and mutation evidence
Four Hypothesis tests, ten deterministic examples each, cover:
All four properties were also reduced to one initial BASIC record (male, age 90, three months since birthday, £200/week in 2024): 4 expected failures on main in 7.33 seconds; 4 passes on head in 6.98 seconds. The monotonicity case adds the required paired record reporting £1/week more; the reform case sets both full rates to zero, undated. Main's deflated accounting result is £10,437.43275 against a £10,400 report, and its zero-rate reform removes £1,586 of data-year add-on; the fix preserves both identities. These are synthetic counterexamples, not national figures.
The saved mutation run had 12/12 unmutated checks pass and 5/5 mutants killed: restore full-rate uprating; read CPI from baseline instead of the scenario; remove the floor; use the prior-year index; split at the reformed data-year full rate. This evidence predates the main merges; its formula source is identical to the merged formula head. A fresh scratch-tree confirmation passed three unmutated statutory/scenario controls, then killed both restored full-rate uprating (one intended assertion failure) and baseline-CPI reading (one intended assertion failure). The source was restored and the scratch clone removed. Each command used
uv run --active --no-sync pytest <selected nodeids> -n 0 -vv --tb=short -o usefixtures=cloned_uk_tax_benefit_system; no model file was mutated in the actual head.Impact on the certified pair
Not measured on the current base: sandbox access blocks the required machine-wide heavy queue. Base is the exact merged main
8019c0ccea83f414d6b44a90c2c64fe5fc9b5ee8; final head is4a6b736a09076e303f3e646c85f5709805c93de4. The interrupted launch preparation originally targeted2fd0c7a8be714487be8895ffa248b600d48ba8e0; the final commit changes documentation only, and the prepared retry script is pinned to the final SHA. Dataset:enhanced_frs_2024_25, uk-data1.58.0,hf://policyengine/policyengine-uk-data-private/enhanced_frs_2024_25.h5@1.58.0, 128,369,110 bytes, verified SHA-2565ba399ea5ab9a6c7d4e9aaf6c623080beb34d9ad5f330f6244dbad72b8d960b6.The unchanged required heavy runner first failed its sandbox-denied
os.nice(0)query. A read-onlyos.getpriorityadapter confirmed actual niceness zero while preserving the shared queue, locks, thread caps and 20 GB ceiling. The runner then failed creating its queue ticket withoperation not permittedunder~/reviews/test-runner-memory-2026-10-02/heavy.queue/; it was stopped, with no simulation child started. Runtime and all 2024–2030 totals, changed-person counts, BHC/AHC poverty rates and decile impacts remain unverified. No stale result or extrapolated figure is presented as current evidence.When the full-rate rise exceeds prices, this correction lowers projected State Pension and widens the shortfall against the OBR/DWP table in the docs. The current national size is unverified. The unsupported stale quantified paragraph was removed from the docs; their forecast table was not rerun. Impact scripts are prepared to copy/hash the certified dataset privately per tree and produce weighted aggregates, all requested poverty rates and deciles with primary/complementary small-cell suppression. A synthetic disclosure check passed.
Continuation and overlaps
Already done: formula, tests, docs/changelog, five mutation checks, both main merges, and Axiom queue issue. Added here: recovery/audit of the pushed continuation, statutory/skill/overlap verification, current test evidence, precise statutory documentation and removal of stale impact figures, hardened disclosure reporting, and this full PR write-up. No
uv.lockis in the PR diff.additional_state_pension.py, the component tests and UC State Pension properties. Preserve the statutory CPI and baseline split when that stack is rebased.Axiom parity
rulespec-uk#457 is open, labelled
pe-parity, and dispatch-ready: module pathsuk/regulations/uksi/2026/148/article/{4,6}.yaml, corpus citations, verbatim law, required outputs, pasteablereview_finding, and companion cases derived from the same Orders. Live modules/corpus still include only the flat-rate provisions. Re-ingestion/release/rebind of the missing corpus paragraphs is the recorded blocker; no RuleSpec was handwritten or billed encoder dispatched.axiom: TheAxiomFoundation/rulespec-uk#457 queued
Not verified
Current national impact for 2024–2030 and numerical movement against OBR/DWP forecasts; completed latest CI gate/artifacts after runner shutdown; the underlying shutdown cause; local broad-suite rerun after the final merge (full CI passed at the formula head, with its reported dataset-access skips). The documentation build passed in CI after the final clarification.
SKILLS_BLOCKED: model-development runtime loader/catalog unavailable; its complete source and relevant references were read, and the canonical repository guidance was used as the brief's explicit fallback. The historical local YAML and five-mutant shell invocations were not preserved; complete results/source were recovered, and current focused/mutation commands were recorded.This PR remains a draft.
🤖 Generated with Claude Code
Reproducer:
policyengine_uk/tests/test_state_pension_components.py::test_protected_payment_above_new_state_pension_follows_period_type,policyengine_uk/tests/test_state_pension_components.py::test_unchanged_type_splits_at_its_own_flat_rate,policyengine_uk/tests/test_state_pension_components.py::test_new_in_data_year_and_basic_in_period_is_paid_in_full,policyengine_uk/tests/test_state_pension_components.py::test_additional_pension_rises_with_september_cpi_not_the_basic_rate,policyengine_uk/tests/test_state_pension_components.py::test_protected_payment_rises_with_september_cpi_not_the_new_rate,policyengine_uk/tests/test_state_pension_components.py::test_add_on_grows_each_year_by_that_years_september_cpi_rise