Conversation
The State Pension age parameters held one age per year and stayed at 66 from 2020, so the model never applied the Pensions Act 2014 s.26 rise to 67 for people born on or after 6 April 1960. From 2026-27 every 66-year-old counted as over State Pension age. Encode Pensions Act 1995 Sch 4 para 1 by date of birth, row for row: age_by_birth_date (the age, in months) and day_by_birth_date (the day, where the statute sets one), for women and for men born on or after 6 December 1953, plus rule (1) for men born earlier. A person attains State Pension age on the later of the two. months_since_last_birthday places each date of birth within the year of age, measured at 6 October, the middle of the fiscal year. A fractional age is the exact age; single households use the middle of the year of age; representative microdata spreads each single year of age and sex evenly by weight, so the weighted share over State Pension age is the statutory share (three quarters of 66-year-olds in 2026-27, a quarter in 2027-28, none after). state_pension_age is now the person's own State Pension age, and is_SP_age, the basic/new State Pension split and the Savings Credit age test (SPCA 2002 s.3(1)(a), including its age-65 limb) all follow it. No additional State Pension is paid below State Pension age. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A birth instant within a day is the day starting at or after it, so a person's legal age on 6 October is their age and each age is attained at the commencement of the anniversary (Family Law Reform Act 1969 s.9(1)). The time-of-day carry onto the anniversary is gone. - One helper gives exact age in months (float64), capping months since the last birthday a few minutes short of 12 so it never rounds onto the next birthday. Hypothesis found that edge. - Birthdays are spread over the year in any simulation built from data (Simulation.built_from_dataset), not whenever weights exceed a million, so a constituency or local authority filtered from the data is still spread, and filter_dataset carries each person's place into an extract. - Rewrite the old Savings Credit cases from dates of birth, keep one that tests the state_pension_age override, and test the attainment day exactly in the day-level differential test. - male/born_before is a YYYYMMDD number like the scales; labels no longer hardcode the date; the NI reference notes its numbering; the removed fragment and docs say how to reform the timetable now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 30, 2026
Constituency and local-authority simulations get no UC deductions or private school attendance
#1902
Open
This was referenced Sep 30, 2026
MaxGhenis
added a commit
that referenced
this pull request
Sep 30, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 30, 2026
Open
MaxGhenis
added a commit
that referenced
this pull request
Sep 30, 2026
The two boundary cases test how primary and secondary Class 1 split at state pension age, not how that age is derived. Setting is_SP_age as an input keeps them valid under #1899, which derives it from date of birth (a 66-year-old in 2026-27 is then usually over it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 30, 2026
Open
Open
Collaborator
|
Reviewed: approve with nits. Working out State Pension age from date of birth (encoded row by row, with the 6 October status date and the weighted spread of birthdays within each year of age) matches the statute and gives the right population shares.
Verified: all 77 rows in the test fixture match the current Sch 4 Part I text on legislation.gov.uk (rules 1–10, tables 1–4, rule 7A). I checked the shares of 66-year-olds over State Pension age by hand: 75% in 2026-27 (born up to 5 July 1960), 25% in 2027-28 (born up to 5 January 1961), and none after that. |
This was referenced Oct 1, 2026
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.
Summary
PolicyEngine UK held one State Pension age per year, 66 from 2020 (
gov/dwp/state_pension/age/male.yamlandfemale.yaml). So it never applied the Pensions Act 2014 s.26 rise to 67 for people born on or after 6 April 1960. From 2026-27, every 66-year-old counted as over State Pension age, which moves State Pension, Pension Credit, Universal Credit, National Insurance and the HBAI pensioner group.The law sets State Pension age by date of birth: Pensions Act 1995 Sch 4 para 1, as amended by the Pensions Acts 2007, 2011 and 2014. This PR encodes that timetable row for row and works out each person's State Pension age from their date of birth.
How the model represents it
Parameters (
gov/dwp/state_pension/age/). The old per-yearmaleandfemaleages are replaced by:age_by_birth_dateday_by_birth_datemale/age,male/born_beforeThe two scales cover women, and men born on or after 6 December 1953, which is also the Pension Credit qualifying age for everyone (SPCA 2002 s.1(6)). A person attains State Pension age on the later of the two. Every row cites its rule and table and quotes the statutory period.
Variables:
months_since_last_birthday(new). Months since the person's last birthday on 6 October, the middle of the fiscal year. Together withageit places the date of birth. A birth instant within a day is taken as the day starting at or after it, so the person's legal age on 6 October isage, and each age is attained at the commencement of the anniversary (Family Law Reform Act 1969 s.9(1)).ageis read as the exact age.Simulation.built_from_dataset, so also a region or constituency filtered from the data) spread each single year of age and sex evenly over the year. Records are ordered bysplitmix64_uniform(person_id, salt=2)(theuc_deduction_random_drawprecedent) and placed by their share of the group's weight (stratified_uniform), so the weighted share over State Pension age matches the statute to within one record's weight, not merely in expectation. Independent hashed draws would have a standard deviation of about 50k people a year at age 66.filter_datasetcarries each person's value into a household it extracts, so an extracted household keeps the status it had in the full simulation.state_pension_age: now the person's own State Pension age, from their date of birth.months_since_state_pension_age(new): months between attaining State Pension age and 6 October (negative before).is_SP_age: attained by 6 October, so over State Pension age for most of the year.state_pension_type: BASIC if the person reached State Pension age before 6 April 2016 (Pensions Act 2014 s.1(2)), else NEW. Main approximated this from integer ages.meets_savings_credit_age_requirement: reached State Pension age before 6 April 2016 and has attained 65 (SPCA 2002 s.3(1)(a)). Main used the current State Pension age as if it were each cohort's own. Because ages are held fixed across years, that sent Savings Credit to £0 in 2030-31 and excluded men who reached it at 65 in 2015 (e.g. a man aged 75 in 2025). The newsavings_credit/minimum_ageparameter (65) adds the statutory age limb.additional_state_pension: zero below State Pension age. Main kept paying additional State Pension and protected payment to people moved below it.Docs:
docs/book/programs/gov/dwp/state-pension.mdhas a new State Pension age section.Invariants (stated and tested)
test_state_pension_age.py::TestParametersMatchStatuteparses the verbatim statute text (fixture from legislation.gov.uk, cross-checked against a second independent parse: 77/77 rows agree). It rebuilds both scales and the male rule from the text and requires exact equality. It also checks that the statutory periods are contiguous.datetime, sharing no code with the model, covers births every third day from 1945 to 1984, both sides of every statutory boundary, both sexes and 13 fiscal years (2015-20, 2025-28, 2044-46). For each,is_SP_age,state_pension_typeand the Savings Credit age test agree exactly, and the attainment day agrees to within a day. Status is only ever read on the 6th, where the grid is exact.is_SP_age⇔months_since_state_pension_age≥ 0.stratified_uniform(Hypothesis): values lie in [0, 1), are deterministic, don't depend on record order, and the weighted CDF within each stratum is uniform to within half the largest weight.filter_datasetextract keeps each person's value and status.Boundary YAML tests:
state_pension_age.yaml(25 cases, at every rule change, expected values from the day-level reading) andis_SP_age.yaml(15 phase-in cases). The phase-in cases cover mixed-age couples (UC, not Pension Credit), State Pension of zero below State Pension age, and Class 1 NI liability.state_pension_type.yamlandmeets_savings_credit_age_requirement.yamlgain tests at the 6 April 2016 boundaries.Microsimulation impact
Enhanced FRS 2024-25 (policyengine-uk-data-private 1.57.4, snapshot ace89433). Every figure is from a full microsimulation run of each version: origin/main 34e10d5, this branch at c46893e, and the same code with table 3 and rule 8 held at 66 (the counterfactual that isolates the rise). Scripts and CSVs are kept with the session's review notes.
The rise from 66 to 67 (this branch minus the same code with table 3 and rule 8 held at 66)
Other corrections (exact birth cohorts, the Savings Credit age test, no additional State Pension below State Pension age)
Whole PR (this branch minus origin/main)
Levels on this branch
Caveats:
ni_class_1_employerstopping at State Pension age. Charge employer NI on employees over state pension age #1895 fixes that; employers keep paying after the employee reaches it.is_hbai_pensioner=is_hbai_adult & is_SP_agein Replace generic child and adult flags with each programme's legal definitions #1896, which inherits this fix.additional_state_pension, which splits the reported amount by its data-year type. With ages held fixed across years, a record's data-year type can differ from its period type, so basic/new plus additional can overlap or leave a gap. Switching to the period type would remove about £1bn (2025-26) rising to about £7bn (2030-31) of overlap already on main. That needs its own review, so it's a separate follow-up, not part of this PR.Removed parameters
gov.dwp.state_pension.age.maleand.femaleare removed. The towncrier fragment isremoved(a minor release), following this repo's practice for removed parameters (2.28.1 removed Pension Credit parameters as a patch); downstream packages pin exact versions (policyengine.py pinspolicyengine-uk==2.102.3), so upgrading is always explicit. The fragment says how to express a State Pension age reform now. Known readers outside this repo:packages/microcosm-build/src/microcosm/build/uk_runtime/frs_take_up.pyreadsages.male(instant)andages.female(instant)to bound the UC take-up population. Migration is tracked in microcosm#1069.src/nics_exemption/pipeline.pyreads them forSPA_MALEandSPA_FEMALE. Its working-age bounds need a migration follow-up.src/triple_lock/config.py,STATE_PENSION_AGE_CHANGES, applies 2028–2042 overrides to both removed scalar paths. uk-triple-lock#21 tracks replacing those overrides with the built-in cohort timetable or an explicit cohort-based reform.axiom_oracles/bridges/efrs_uk.pycompares PolicyEngine's wholeagewithstate_pension_age. axiom-oracles#576 prepares the migration and waits for this PR's release; the statutory specification follow-up is rulespec-uk#369.Readers need cohort-specific eligibility from
is_SP_ageorstate_pension_age; reform writers need the birth-date timetable parameters. These paths were checked against the downstream source during this review.axiom: uk/statute/ukpga/1995/26/schedule/4/paragraph/1 TheAxiomFoundation/rulespec-uk#369 queued
🤖 Generated with Claude Code