Calculation catalog › Optimizer and comparisons

Scenario comparison cell

For each compared scenario metric, the baseline plan's value, the proposal plan's value and the change (proposal minus baseline, null when either side has no comparable value); the metric's own family says what the value is.

Field: Scenario comparison cell (baseline, proposal, change) Unit: US dollars Basis: nominal dollars Computed by: Engine Status: Complete chain

Appears in: scenarios-page (Baseline, Proposal and Change columns of every MetricTable (formatMetricValue, formatScenarioDelta) and of the annual ledger comparison)

Calculation records

  • Scenario nullable scalar comparison Composition record, justified by derivation

    Purpose: Compare a nullable metric without turning absence into zero.

    Statement: scenarios/scalarComparison.ts#compareNullableScalars preserves nullable baseline and proposal values and publishes proposal minus baseline, through compareScalars, only when both exist, otherwise publishing a null delta; a negative zero is published as 0 and a non-finite value is refused with a RangeError. The scenario comparison's nullable cells (its depletion year, the spending-capacity figures and the annual rows where only one horizon has the year) and the Compare page's depletion age use it; the candidate evaluation's deltas (decisions/evaluateCandidate.ts) are plain subtractions and do not. Units: the metric's own. No stated rounding.

    Formula: delta = proposal - baseline when baseline != null and proposal != null, else null

    • baseline: Baseline metric value or absence (unit: the metric's own unit) (domain: finite number or null)
    • proposal: Proposal metric value or absence (unit: the metric's own unit) (domain: finite number or null)

    Timing: one comparison cell

    Rounding: none stated

    Limits:

    • Comparability is conjunctive: absence is not numerical zero, so coercing a null operand to zero would present a comparison that does not exist. The evidence asserts it directly and through the exported compareScenarioPlans on the metric whose comparison cell is the nullable one (the headline depletion year).

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

    Independent review: grok

    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: scenario-nullable-scalar-comparison.md Mutation receipt: scenario-nullable-scalar-comparison.mutation.md

    Evidence tests: comparisonCells.evidence.test.ts

    Implemented in: scalarComparison.ts; comparison.ts

    Is the number of: scenario-comparison-cell

  • Scenario scalar comparison Composition record, justified by derivation

    Purpose: Publish a scenario metric as baseline, proposal and a signed same-unit delta.

    Statement: scenarios/scalarComparison.ts#compareScalars publishes a finite numeric metric's baseline, proposal and delta in that metric's own units, with delta = proposal - baseline; a negative zero is published as 0 on every member, and a non-finite operand or difference is refused with a RangeError. The scenario comparison, the Compare page's headlines and money-lasts comparison (scenarios/planHeadlines.ts, projection/moneyLasts.ts#compareMoneyLasts), the relocation rows' lifetime delta, the claim-change estate gain, compareMonteCarloSuccessRates and stochasticDeltas publish their differences through it; the candidate evaluation's estate, net-worth, lifetime-tax and money-lasts deltas (decisions/evaluateCandidate.ts, read by InsightImpact and ExactLedgerValidation) are plain subtractions and do not. No stated rounding.

    Formula: delta = proposal - baseline

    • baseline: Baseline metric value (unit: the metric's own unit) (domain: finite)
    • proposal: Proposal metric value (unit: the metric's own unit) (domain: finite)

    Timing: one comparison cell

    Rounding: none stated

    Limits:

    • The signed identity is fixed: baseline and proposal are preserved rather than reordered by magnitude, and the delta stays in the metric's own unit rather than becoming a relative change. The evidence asserts it directly and through the exported compareScenarioPlans on a headline money metric.

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

    Independent review: grok

    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: scenario-scalar-comparison.md Mutation receipt: scenario-scalar-comparison.mutation.md

    Evidence tests: comparisonCells.evidence.test.ts

    Implemented in: scalarComparison.ts; comparison.ts

    Is the number of: scenario-comparison-cell

Records that feed this number

  • Projection summary ending after tax estate Composition record, justified by derivation

    Purpose: Net the horizon estate of charity carve-outs and assumed heir income tax.

    Statement: projection/compare.ts#summarizeProjection computes nominal ending after-tax estate as horizon net worth minus the amount passing to charity and minus the total assumed heir income tax on inherited pre-tax balances: endingAfterTaxEstate = endingNetWorth - endingEstateToCharity - endingEstateHeirTax.

    Formula: endingAfterTaxEstate = endingNetWorth - endingEstateToCharity - endingEstateHeirTax

    • endingNetWorth: Horizon net worth (unit: nominal USD) (domain: finite)
    • endingEstateToCharity: Amount passing to charity, untaxed (unit: nominal USD) (domain: nonnegative)
    • endingEstateHeirTax: Assumed heir income tax on inherited pre-tax balances (unit: nominal USD) (domain: nonnegative)

    Timing: projection horizon

    Rounding: none stated

    Limits:

    • Charity carve-outs pass untaxed rather than remaining in the heirs' estate, so they are subtracted here even though they are also reported separately in endingEstateToCharity; with no charity destination the identity collapses to net worth minus heir tax. Ignoring charity, adding heir tax instead of subtracting it, or subtracting charity a second time through the heir tax each name a different quantity. The figure is assumed terminal exposure at the horizon, not a death-year return.

    Provenance: derived by codex, implemented by claude.

    Independent review: cursor

    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: projection-summary-ending-after-tax-estate.md Mutation receipt: projection-summary-ending-after-tax-estate.mutation.md

    Evidence tests: compareSummary.evidence.test.ts

    Implemented in: compare.ts

    Is the number of: projection-summary-ending-after-tax-estate

    Feeds: swr-rule-result-ending-after-tax-estate, scenario-comparison-cell, insight-spending-headroom-rough-annual

  • Projection summary ending investable Composition record, justified by derivation

    Purpose: Republish the investable balance at the projection horizon.

    Statement: projection/compare.ts#summarizeProjection republishes ProjectionResult.endingInvestable, the nominal-dollar investable balance at the projection horizon, without stated rounding.

    Formula: summary.endingInvestable = result.endingInvestable

    • result.endingInvestable: Whole-run investable endpoint (unit: nominal USD) (domain: finite)

    Timing: projection horizon

    Rounding: none stated

    Limits:

    • A selection, not an aggregation: it takes the whole-run endpoint rather than summing annual rows or selecting the largest annual balance. The valid domain is any completed ProjectionResult with a finite ending value.

    Provenance: derived by codex, implemented by claude.

    Independent review: cursor

    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: projection-summary-ending-investable.md Mutation receipt: projection-summary-ending-investable.mutation.md

    Evidence tests: compareSummary.evidence.test.ts

    Implemented in: compare.ts

    Is the number of: projection-summary-ending-investable

    Feeds: scenario-comparison-cell

  • Projection summary ending net worth Composition record, justified by derivation

    Purpose: Republish net worth at the projection horizon.

    Statement: projection/compare.ts#summarizeProjection republishes ProjectionResult.endingNetWorth, the nominal-dollar net worth at the projection horizon, without stated rounding.

    Formula: summary.endingNetWorth = result.endingNetWorth

    • result.endingNetWorth: Whole-run net-worth endpoint (unit: nominal USD) (domain: finite)

    Timing: projection horizon

    Rounding: none stated

    Limits:

    • The result type already defines annual net worth as investable plus property, insurance cash value and ladder value, less debt and non-recourse-capped HECM loans, so the summary must not rebuild that ledger identity; substituting ending investable, or adding it to the composed net worth, double-counts assets.

    Provenance: derived by codex, implemented by claude.

    Independent review: cursor

    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: projection-summary-ending-net-worth.md Mutation receipt: projection-summary-ending-net-worth.mutation.md

    Evidence tests: compareSummary.evidence.test.ts

    Implemented in: compare.ts

    Is the number of: projection-summary-ending-net-worth

    Feeds: projection-summary-ending-after-tax-estate, scenario-comparison-cell

  • Lifetime Roth conversions: sum of annual traditional-to-Roth movement Composition record, justified by derivation

    Purpose: Total nominal dollars moved traditional-to-Roth across every projection year.

    Statement: lifetimeRothConversions = sum over all projection years of YearResult.rothConversion. Each annual field is dollars moved traditional-to-Roth in that year, so the lifetime figure is the additive identity over all and only the projection rows. Units: nominal dollars. Rounding: none.

    Formula: lifetime = sum_y rothConversion_y

    • rothConversion_y: Nominal dollars converted traditional-to-Roth in year y (unit: usd) (domain: finite)
    • lifetime: Sum of those annual amounts over the projection (unit: usd) (domain: finite)

    Timing: once per projection, over every published year including zeros

    Rounding: none

    Limits:

    • Zero-conversion years remain in the sum; shortening the horizon to drop a zero year also drops later years
    • The evidence constructs the three stated YearResult.rothConversion rows rather than running a full simulation

    Provenance: derived by codex, implemented by grok.

    Independent review: cursor

    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: projection-summary-lifetime-roth-conversions.md Mutation receipt: projection-summary-lifetime-roth-conversions.mutation.md

    Evidence tests: compare.evidence.test.ts

    Implemented in: compare.ts

    Is the number of: projection-summary-lifetime-roth-conversions

    Feeds: scenario-comparison-cell

  • Projection summary lifetime taxes and penalties Composition record, justified by derivation

    Purpose: Sum annual tax and annual penalties over the whole projection.

    Statement: projection/compare.ts#summarizeProjection sums nominal YearResult.tax + YearResult.penalties across every projection year to publish lifetime taxes and penalties, with no stated rounding.

    Formula: L = sum over years y of (tax_y + penalties_y)

    • tax_y: Year y published tax (unit: nominal USD) (domain: nonnegative)
    • penalties_y: Year y published penalties, separate from tax (unit: nominal USD) (domain: nonnegative)

    Timing: every projection year

    Rounding: none stated

    Limits:

    • Penalties are a separate ledger field from tax, so summing taxes only, or adding penalties in the final year alone, understates the published figure. The valid domain is a finite projection-year sequence with finite annual dollar fields.

    Provenance: derived by codex, implemented by claude.

    Independent review: cursor

    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: projection-summary-lifetime-taxes-and-penalties.md Mutation receipt: projection-summary-lifetime-taxes-and-penalties.mutation.md

    Evidence tests: compareSummary.evidence.test.ts

    Implemented in: compare.ts

    Is the number of: projection-summary-lifetime-taxes-and-penalties

    Feeds: swr-rule-result-lifetime-taxes-and-penalties, scenario-comparison-cell

  • Relocation lifetime state local tax Composition record, justified by derivation

    Purpose: Total one candidate row's recorded annual state and local income tax.

    Statement: projection/relocation.ts#compareRelocationCandidates sums a row's recorded nominal per-year state-plus-local income-tax lines over the complete projection to publish that candidate's lifetime state and local tax, with no stated rounding.

    Formula: lifetimeStateLocalTax = sum over years y of stateTaxByYear[y].tax

    • stateTaxByYear[y].tax: Recorded state plus local income tax for year y (unit: nominal USD) (domain: nonnegative)

    Timing: every recorded projection year of the row

    Rounding: none stated

    Limits:

    • Scope is income tax only: federal tax, property tax, sales tax and cost of living are outside this quantity, the annual lines are nominal and are never discounted, and dropping the split or baseline year understates the total. The worksheet's per-year lines are supplied by a deterministic state-tax calculator injected at the createStateTaxCalculator seam, so the recording, the row assembly and the sum are the real code path while the per-state tax data itself is not exercised here. The published total is the driver sum over every recorded line, and the per-year series is that recording restricted to the run horizon; the two agree when every recorded line lies inside the horizon, which the test’s scenario satisfies and which no known code path violates.

    Provenance: derived by codex, implemented by claude.

    Independent review: cursor

    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: relocation-lifetime-state-local-tax.md Mutation receipt: relocation-lifetime-state-local-tax.mutation.md

    Evidence tests: relocation.evidence.test.ts

    Implemented in: relocation.ts

    Is the number of: relocation-lifetime-state-local-tax

    Feeds: insight-state-relocation-lifetime-state-tax-savings, scenario-comparison-cell

  • Scenario comparison: years in an IRMAA surcharge tier Composition record, justified by derivation

    Purpose: How many projection years sit strictly above IRMAA tier 0, compared proposal-minus-baseline.

    Statement: For each side, surchargeTierYears = count of projection years whose published irmaaTier is strictly greater than 0 (tier 0 is the standard premium; tiers 1–5 are surcharge tiers). The comparison is the integer triple (baseline count, proposal count, proposal − baseline). Units: year counts. Rounding: none (exact integers).

    Formula: count = sum_y 1[irmaaTier_y > 0]; delta = count_proposal − count_baseline

    • irmaaTier_y: Published IRMAA tier classification for year y (unit: tier) (domain: integer 0..5)
    • count: Number of surcharge-tier years on one side (unit: years) (domain: integer >= 0)

    Timing: one count per side over that side's projection years

    Rounding: none

    Limits:

    • Medicare premium dollars and the maximum tier are different outputs; this record counts years only
    • The evidence constructs the stated irmaaTier rows rather than running two full simulations

    Provenance: derived by codex, implemented by grok.

    Independent review: cursor

    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: scenario-irmaa-surcharge-tier-years.md Mutation receipt: scenario-irmaa-surcharge-tier-years.mutation.md

    Evidence tests: comparison.evidence.test.ts

    Implemented in: comparison.ts

    Is the number of: scenario-irmaa-surcharge-tier-years

    Feeds: scenario-comparison-cell

  • Scenario lifetime tax and penalties Composition record, justified by derivation

    Purpose: Total each scenario side's annual tax and annual penalties as two separate channels.

    Statement: scenarios/comparison.ts#compareScenarioPlans separately sums nominal annual YearResult.tax and YearResult.penalties for each scenario side, then places each baseline/proposal total in its comparison cell without mixing the two channels.

    Formula: lifetimeTax_s = sum over years y of tax_{s,y}; lifetimePenalties_s = sum over years y of penalties_{s,y}

    • tax_{s,y}: Side s, year y published tax (unit: nominal USD) (domain: nonnegative)
    • penalties_{s,y}: Side s, year y published penalties (unit: nominal USD) (domain: nonnegative)

    Timing: every projection year of each side

    Rounding: none stated

    Limits:

    • Penalties are excluded from tax by the result-type contract, so folding them into the tax channel changes both totals, and the delta belongs to the separate comparison-cell identity rather than to these side totals. The worksheet's annual tax and penalty rows for both sides are supplied as ProjectionResult literals at the simulate seam; the aggregation and the comparison assembly are the real functions.

    Provenance: derived by codex, implemented by claude.

    Independent review: cursor

    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: scenario-lifetime-tax-and-penalties.md Mutation receipt: scenario-lifetime-tax-and-penalties.mutation.md

    Evidence tests: comparisonCells.evidence.test.ts

    Implemented in: comparison.ts

    Is the number of: scenario-lifetime-tax, scenario-lifetime-penalties

    Feeds: scenario-comparison-cell

Engine source: engine/src/scenarios/comparison.ts (ScalarComparison / NullableScalarComparison (scalar, nullableScalar))

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.