Calculation catalog › Optimizer and comparisons

Compare plan deltas

Differences between two plans in last funded year, depletion age and deterministic success (100 or 0 by depletion), bounded when only one plan runs its full horizon and published as no number when both do.

Field: Plan B minus Plan A: money lasts, ages and deterministic success Unit: years Basis: no dollar basis Computed by: UI-native Status: Complete chain

Appears in: compare-page (Money lasts, Depletion age and Deterministic success rows (delta column))

Calculation records

  • Compare plans: how long the money lasts, deterministic success and depletion age Composition record, justified by derivation

    Purpose: Compare two plans on the last fully funded year, the single-path success reading and the older person's age in the depletion year, named.

    Statement: scenarios/planHeadlines.ts#comparePlanHeadlines publishes moneyLasts through projection/moneyLasts.ts#compareMoneyLasts: each side's projection/moneyLasts.ts#moneyLasts (owner decision R15: the last funded year L = D − 1, or E when the projection never depletes) and delta = L_p − L_b, with bound atLeast when only the proposal runs its full horizon (a lower bound), atMost when only the baseline does (an upper bound) and null when both deplete (exact); when both run their full horizons the bound is bothFull and the delta is null, because neither exhaustion year is known. It publishes deterministicSuccessPct as compareScalars of 100 for a side that never depletes and 0 otherwise, and depletionAge as compareNullableScalars of D minus the birth year of the side's person that model/peopleOrder.ts#canonicalFirstPerson puts first (the older; a tie to the sex order, then the id), whoever that plan lists first, null on a side that never depletes, with depletionAgePersonId naming that person on each side (decision D-PEOPLE-ORDER); the Compare page labels the row with their names. When both sides publish an age and the two people differ by name or date of birth, the delta is null and depletionAgeDeltaWithheld is differentPeople, since two people's ages have no difference to publish (the independent review's L1); the page shows both ages. The depletion year is the household's; the age is that one person's. A depleting side whose published person has no birth date in YYYY-MM-DD form is refused with a RangeError. A plan depleting in 2046 (its older person born 1962) against one that never depletes and ends in 2049 reads 2045 against 2049: a difference of 4 years, bound atLeast, and ages 84 and none. Units: calendar years; percentage points; years of age. Rounding: none.

    Formula: L_s = D_s − 1 when D_s is not null, else E_s; delta = L_p − L_b unless both D are null; success_s = 100 when D_s is null, else 0; age_s = D_s − birthYear_s

    • D_s: The side's first year short of money (unit: calendar year) (domain: integer in [start year, E_s], or null)
    • E_s: The side's last projection year (unit: calendar year) (domain: integer)
    • birthYear_s: Birth year of the side's person the canonical order puts first (the older) (unit: calendar year) (domain: integer)

    Timing: once per pair of projections that share a start year

    Rounding: none

    Limits:

    • The age is one person's, the older on each side, named on the page; when the two plans' older people are different people, the row names both, shows both ages and publishes no difference.
    • The age is the calendar age in the first short year (the year minus the birth year), not the exact age when the money runs out.
    • A bound with a zero difference (at least the same) is a correct bound.
    • The deterministic success reading is 100 or 0 on the one deterministic path; it is not a probability.

    Provenance: derived by claude, implemented by claude-subagent.

    Independent review: codex

    Gates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass

    Worksheet: compare-plan-deltas.md Mutation receipt: compare-plan-deltas.mutation.md

    Evidence tests: planHeadlines.evidence.test.ts

    Implemented in: planHeadlines.ts; moneyLasts.ts; scalarComparison.ts; peopleOrder.ts

    Is the number of: compare-plan-deltas

Records that feed this number

  • Money lasts: the last funded year and the years short of the plan's end Formula record, justified by derivation

    Purpose: One convention for how long the money lasts: the last fully funded year, and how many plan years the money falls short of the plan's end.

    Statement: projection/moneyLasts.ts#moneyLasts publishes lastFundedYear = depletionYear - 1, or endYear when the projection never depletes, and yearsShortOfPlanEnd = endYear - lastFundedYear: 0 when it never depletes and endYear - depletionYear + 1 when it does, so a plan short only in its final year reads 1. A depletion year outside the projection's years is refused. The decision and optimizer money-lasts deltas are differences of lastFundedYear. Units: calendar year; count of years. Rounding: none.

    Formula: L = D - 1 when D is not null, else E; N = E - L

    • D: Depletion year: first year with a funding shortfall above half a cent after any HECM backstop (unit: calendar year) (domain: integer in [startYear, E], or null)
    • E: The plan's inclusive last year (unit: calendar year) (domain: integer)
    • L: Last fully funded year (unit: calendar year) (domain: startYear - 1 when the first year is short)
    • N: Years short of the plan's end (unit: years) (domain: integer, 0 when D is null)

    Timing: the ledger's annual rows

    Rounding: none

    Limits:

    • A distance, not a count of shortfall years: a year after the depletion year can be funded again if income resumes
    • Deterministic ledger only; the Monte Carlo pages have their own depletion distribution
    • Owner decision R15 (2026-09-25): the Results sentence used to read endYear - depletionYear years before the end (0 for a plan short only in its final year), the KPI bar and the printed report named the depletion year as the year the money lasts until, and the decision code counted a non-depleting plan as lasting through endYear + 1. Every surface now names the last funded year and counts endYear - lastFundedYear. The decision and optimizer deltas did not move: both conventions shift every result by the same one year

    Provenance: derived by claude, implemented by claude-subagent.

    Independent review: codex

    Gates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass

    Worksheet: display-years-before-plan-end.md Mutation receipt: display-years-before-plan-end.mutation.md

    Evidence tests: moneyLasts.evidence.test.ts

    Implemented in: moneyLasts.ts; evaluateCandidate.ts

    Is the number of: display-years-before-plan-end, longevity-last-funded-year

    Feeds: compare-plan-deltas

Transformations

  • scenarios/planHeadlines.ts#comparePlanHeadlines: moneyLasts through projection/moneyLasts.ts#compareMoneyLasts (delta = lastFundedYear(proposal) − lastFundedYear(baseline), bound atLeast or atMost when only one plan runs its full horizon, delta null with bound bothFull when both do); deterministicSuccessPct = compareScalars(100 or 0 by depletion); depletionAge = compareNullableScalars(depletionYear − birth year of each side's model/peopleOrder.ts#canonicalFirstPerson, the older, whoever is listed first), with depletionAgePersonId naming that person, refused when a depleting side's person has no birth date.

The evidence links on this page (each record's worksheet, mutation receipt, evidence tests and implementation, and the source links) are pinned to engine commit 2624dbdba9fd, committed 2026-10-11. The live calculation index and output-family census links point at the repository's main branch, so you can check this snapshot against them.