Skip to content

fix: state pension respects recorded amount for new-SP cohort#60

Open
vahid-ahmadi wants to merge 1 commit into
mainfrom
vahid/state-pension-recorded
Open

fix: state pension respects recorded amount for new-SP cohort#60
vahid-ahmadi wants to merge 1 commit into
mainfrom
vahid/state-pension-recorded

Conversation

@vahid-ahmadi
Copy link
Copy Markdown
Contributor

Summary

Closes #59. The state-pension calculation was ignoring the recorded person.state_pension amount for anyone in the new-SP cohort (post-2016 SP claimants — currently anyone aged 66–74 in fiscal year 2025/26). It always returned the full new_state_pension_weekly × 52, over-stating SP for partial-year / partial-record claimants and breaking parity with the Python engine on every synthetic pensioner.

Surface from the parity harness

PR #53's parity harness directly surfaced this — the pensioner_couple synthetic scenario was £946 off:

-- pensioner_couple --                                       BEFORE
  state_pension     rust=23,946  py=23,000  diff=946  *      
  household_net_income           diff=905  *

Two pensioners with state_pension: 11_500 each → input total £23,000. Rust ignored those values and returned 2 × £230.25 × 52 = £23,946.

After this fix:

-- pensioner_couple --                                       AFTER
  state_pension     rust=23,000  py=23,000  diff=0
  household_net_income           diff=-41

The £946 state-pension gap is gone. Net-income diff drops from £905 to £41 (the residual is the same base divergence visible in every scenario).

Fix

Mirror the existing old-SP scaling pattern. If person.state_pension > 0, pass through the recorded amount scaled by (new_state_pension_weekly / baseline_new_sp_weekly) for reform correctness; else fall back to new_state_pension_weekly × 52.

This requires plumbing baseline_new_sp_weekly through Simulation, calculate_benunit, calculate_state_pension, and person_state_pension, parallel to the existing baseline_old_sp_weekly.

Why both baseline_old_sp_weekly and baseline_new_sp_weekly?

A reform that changes the new-SP rate should scale recorded amounts proportionally — someone with a £11,500 baseline SP should still pay/receive the right share when the rate moves. Passing baseline_new_sp_weekly separately from the (potentially reformed) params.state_pension.new_state_pension_weekly lets person_state_pension compute the ratio correctly.

What's included

  • person_state_pension and calculate_state_pension updated to take a second baseline_new_sp_weekly argument (same shape as baseline_old_sp_weekly)
  • Simulation::new and Simulation::new_with_baseline_sp updated to capture and store baseline_new_sp_weekly
  • All call sites updated (16 in tests, 2 in main.rs / simulation.rs, 4 in labour_supply.rs)
  • 3 new Rust unit tests (recorded-amount preserved, fallback to param, scaling under reform)
  • Changelog fragment under changelog.d/fixed/

Verified locally

  • cargo test: 165 passing (162 + 3 new)
  • pytest interfaces/python/tests: 87 passing
  • python -m policyengine_uk_compiled.yaml_tests tests/policy: 29/29
  • Parity harness: pensioner_couple state-pension diff £946 → £0 (and net-income diff £905 → £41)
  • The pre-existing param_state_pension_new_weekly test still passes (it uses state_pension = 0, hitting the fallback branch unchanged)

Stacking

vahid/state-pension-recordedvahid/council-tax-spd (#58) ← vahid/dla-aa-from-flags (#57) ← vahid/pip-from-flags (#56) ← vahid/lbtt-ltt (#55) ← vahid/yaml-test-harness (#54) ← vahid/parity-harness (#53) ← vahid/from-situation (#52). Eight-deep stack.

Test plan

  • CI passes (cargo + pytest + parity smoke + YAML cases)
  • Parity harness shows pensioner_couple state_pension matches Python (£23,000 = £23,000)
  • Reforming new_state_pension_weekly from £230.25 to £250 scales recorded amounts proportionally (£11,500 → £12,484.80)

🤖 Generated with Claude Code

Mirrors the existing old-SP scaling pattern for the new-SP cohort:
- If `person.state_pension > 0`: pass through, scaled by
  `(new_state_pension_weekly / baseline_new_sp_weekly)` for reform
  correctness
- Else: fall back to `new_state_pension_weekly × 52`

Previously the new-SP branch always returned the full parameter rate
× 52, ignoring any recorded amount. This over-stated SP for partial-
year claimants and broke parity for the pensioner_couple synthetic
scenario in PR #53's parity harness (£946 diff).

Implementation:
- Plumb `baseline_new_sp_weekly` through `Simulation`,
  `calculate_benunit`, `calculate_state_pension`, and
  `person_state_pension`, parallel to the existing
  `baseline_old_sp_weekly` field
- 3 new Rust unit tests (recorded-amount preserved, fallback to param
  when no record, recorded amount scales under reform)

Parity-harness impact (synthetic pensioner_couple scenario):
  state_pension     rust=23,000 py=23,000 diff=£0       (was £946)
  household_net_income           diff=£-41              (was £905)

Stacked on #58. Closes #59 (filed today as a follow-up to PR #53).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vahid-ahmadi vahid-ahmadi force-pushed the vahid/council-tax-spd branch from 2097c87 to 974dc21 Compare May 29, 2026 09:17
@vahid-ahmadi vahid-ahmadi force-pushed the vahid/state-pension-recorded branch from 89b4655 to 10c89d3 Compare May 29, 2026 09:17
@vahid-ahmadi vahid-ahmadi changed the base branch from vahid/council-tax-spd to main May 29, 2026 09:20
@vahid-ahmadi
Copy link
Copy Markdown
Contributor Author

Rebased onto main — ready to merge.

Now sits directly on main (was the top of the SP stack) and is mergeable independently. The fix correctly makes the new-SP cohort respect the recorded state_pension amount, scaled by the reform ratio over the baseline new-SP rate — mirroring the existing old-SP pattern, with a clean fallback to rate × 52 when nothing is recorded; the old-SP path is unchanged. The baseline_new_sp_weekly plumbing through Simulation/calculate_benunit/labour_supply is complete. Verified after rebase: builds clean, full suite (141 tests) passes including the new preserved-amount / fallback / reform-scaling cases. Rebased with no conflicts.

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.

State pension: respect recorded amount for 'new SP' cohort (mirror old-SP scaling)

1 participant