Bug
person_state_pension in src/variables/benefits.rs ignores the recorded person.state_pension value for the new SP cohort (those whose age is below basic_sp_min_age) and always returns the full parameter rate × 52.
The basic SP cohort branch correctly uses scaled-recorded-amount: it returns person.state_pension × (reform_rate / baseline_rate) if recorded, falling back to the parameter rate × 52 only when nothing is recorded.
The new SP branch should follow the same pattern.
Symptom (already measurable)
The parity harness in PR #53 shows a £946 divergence on the pensioner_couple synthetic scenario:
-- pensioner_couple --
state_pension rust=23,946 py=23,000 diff=946
Both pensioners have recorded state_pension: 11500 (totalling £23,000). Rust ignores those values and returns 2 × new_state_pension_weekly × 52 = 2 × £230.25 × 52 = £23,946. Python uses the recorded values. £946 = 2 × (£11,973 - £11,500).
Fix
Mirror the existing old-SP pattern, so that:
- If
person.state_pension > 0: return that amount, scaled by (new_state_pension_weekly / baseline_new_sp_weekly) for reform-correctness
- If
person.state_pension == 0: return new_state_pension_weekly × 52
This requires plumbing baseline_new_sp_weekly through Simulation::new and person_state_pension, parallel to the existing baseline_old_sp_weekly field.
Acceptance
- Parity harness shows £0 state-pension diff on
pensioner_couple (or matches Python's recorded-amount semantics)
- Existing
param_state_pension_new_weekly test still passes (it uses state_pension = 0 so reform behaviour unchanged on that path)
- A new unit test asserts that when
state_pension > 0, the recorded amount is returned (×1.0 in baseline) regardless of the new-SP parameter rate
Effort
Small — ~15 LOC + 2-3 unit tests + the YAML parity case.
Surfaces from
PR #53 parity harness — exactly the kind of regression the harness was designed to catch.
Bug
person_state_pensioninsrc/variables/benefits.rsignores the recordedperson.state_pensionvalue for the new SP cohort (those whose age is belowbasic_sp_min_age) and always returns the full parameter rate × 52.The basic SP cohort branch correctly uses scaled-recorded-amount: it returns
person.state_pension × (reform_rate / baseline_rate)if recorded, falling back to the parameter rate × 52 only when nothing is recorded.The new SP branch should follow the same pattern.
Symptom (already measurable)
The parity harness in PR #53 shows a £946 divergence on the
pensioner_couplesynthetic scenario:Both pensioners have recorded
state_pension: 11500(totalling £23,000). Rust ignores those values and returns2 × new_state_pension_weekly × 52 = 2 × £230.25 × 52 = £23,946. Python uses the recorded values. £946 = 2 × (£11,973 - £11,500).Fix
Mirror the existing old-SP pattern, so that:
person.state_pension > 0: return that amount, scaled by(new_state_pension_weekly / baseline_new_sp_weekly)for reform-correctnessperson.state_pension == 0: returnnew_state_pension_weekly × 52This requires plumbing
baseline_new_sp_weeklythroughSimulation::newandperson_state_pension, parallel to the existingbaseline_old_sp_weeklyfield.Acceptance
pensioner_couple(or matches Python's recorded-amount semantics)param_state_pension_new_weeklytest still passes (it usesstate_pension = 0so reform behaviour unchanged on that path)state_pension > 0, the recorded amount is returned (×1.0 in baseline) regardless of the new-SP parameter rateEffort
Small — ~15 LOC + 2-3 unit tests + the YAML parity case.
Surfaces from
PR #53 parity harness — exactly the kind of regression the harness was designed to catch.