← Back to RetIQ · Features · Manual · Security

RetIQ Financial Validation Report

Independent verification of all financial calculations against authoritative sources

100.0%

pass rate

4137
Total Tests
4137
Passed
0
Failed
253
Categories

Methodology

Every financial calculation in RetIQ's projection engine is tested against authoritative sources: IRS publications and revenue procedures, Social Security Administration actuarial tables, CMS Medicare data, HHS Federal Poverty Level guidelines, ACA Premium Tax Credit tables, and enacted legislation (SECURE 2.0, OBBB Act, ARPA/IRA enhanced subsidies). Tests use exact bracket boundaries, known-answer scenarios from IRS worksheets, and complete lookup tables verified entry-by-entry. Tolerance is $1 for rounding unless otherwise noted.

Authoritative Sources

Federal Income Tax Brackets (2026)

Internal Revenue Service
Source →

Standard Deduction & Senior Additions (2026)

Internal Revenue Service
Source →

Social Security Benefit Taxation (IRS Section 86)

Internal Revenue Service
Source →

Social Security PIA & Benefit Calculation (2026)

Social Security Administration
Source →

Required Minimum Distributions

Internal Revenue Service
Source →

Long-Term Capital Gains Tax Rates

Internal Revenue Service
Source →

Medicare IRMAA Surcharges (2026)

Centers for Medicare & Medicaid Services
Source →

Retirement Contribution Limits (2026)

Internal Revenue Service
Source →

ACA Premium Tax Credit (2026 coverage)

IRS / HHS / CMS
Source →

Pension Enhancements (v2.0)

RetIQ Engine

Test Results by Category

Federal Tax (MFJ) 10/10

Formula
Taxable Income (TI) = Gross Income − Standard Deduction
Tax = Σ [ min(TI, bracket_top) − bracket_bottom ] × rate
    for each bracket where TI > bracket_bottom
IRC §1 • IRS Rev. Proc. 2025-32, §4.01 • OBBB Act P.L. 119-21 §101 • IRS source →
Authority: Internal Revenue Service
Documents:
• IRS Revenue Procedure 2025-32, Section 4.01 — Tax Rate Tables
• One Big Beautiful Bill Act (P.L. 119-21), signed July 4, 2025 — Permanent extension of TCJA brackets
Method: Seven marginal tax brackets verified at exact boundary amounts for both MFJ and Single filing status. Cumulative tax computed manually at each bracket top and compared against engine output.
TestExpectedActualResult
Top of 10% bracket ($24,800)2,4802,480✓
Top of 12% bracket ($100,800)11,60011,600✓
Top of 22% bracket ($211,400)35,93235,932✓
Top of 24% bracket ($403,550)82,04882,048✓
Top of 32% bracket ($512,450)116,896116,896✓
Top of 35% bracket ($768,700)206,584206,584✓
In 37% bracket ($1,000,000)292,165292,165✓
Zero income00✓
Mid-bracket $50,0005,5045,504✓
Mid-bracket $150,00022,42422,424✓

Federal Tax (Single) 6/6

Formula
Taxable Income (TI) = Gross Income − Standard Deduction
Tax = Σ [ min(TI, bracket_top) − bracket_bottom ] × rate
    for each bracket where TI > bracket_bottom
IRC §1 • IRS Rev. Proc. 2025-32, §4.01 • OBBB Act P.L. 119-21 §101 • IRS source →
Authority: Internal Revenue Service
Documents:
• IRS Revenue Procedure 2025-32, Section 4.01 — Tax Rate Tables
• One Big Beautiful Bill Act (P.L. 119-21), signed July 4, 2025 — Permanent extension of TCJA brackets
Method: Seven marginal tax brackets verified at exact boundary amounts for both MFJ and Single filing status. Cumulative tax computed manually at each bracket top and compared against engine output.
TestExpectedActualResult
Top of 10% bracket ($12,400)1,2401,240✓
Top of 12% bracket ($50,400)5,8005,800✓
Top of 22% bracket ($105,700)17,96617,966✓
Top of 24% bracket ($201,775)41,02441,024✓
Mid-bracket $75,00011,21211,212✓
$200,000 income40,59840,598✓

Standard Deduction 8/8

Formula
Deduction = Base + Senior Addition + OBBB Senior Deduction
OBBB = min($6,000/person, $6,000 − 0.06 × max(0, MAGI − threshold))
Threshold: $150,000 MFJ / $75,000 Single • Phases out at 6% per $1,000
IRC §63(c) • IRS Rev. Proc. 2024-40, §3.01 • OBBB Act P.L. 119-21 §102 • IRS source →
Authority: Internal Revenue Service
Documents:
• IRS Revenue Procedure 2024-40, Section 3.01 — Standard Deduction
• One Big Beautiful Bill Act (P.L. 119-21) Section 102 — Additional Senior Deduction
• IRS Fact Sheet FS-2025-03 — Tax deductions for working Americans and seniors
Method: Base standard deduction ($31,500 MFJ / $15,750 Single), existing senior additions ($1,600/$2,000), and new OBBB senior bonus deduction ($6,000/person with 6% phaseout above $150K MFJ / $75K Single).
TestExpectedActualResult
MFJ base deduction32,20032,200✓
Single base deduction16,10016,100✓
Senior additional — MFJ (per person)1,6501,650✓
Senior additional — Single2,0502,050✓
OBBB Senior Deduction: $6,000 per person6,0006,000✓
OBBB phaseout threshold — MFJ150,000150,000✓
OBBB phaseout threshold — Single75,00075,000✓
OBBB phaseout rate: 6% ($60 per $1,000 over threshold)0.060.06✓

OBBB Sunset 5/5

TestExpectedActualResult
In-window: MFJ both 65+ with $80K pension gets $12K OBBB3,4043,404✓
Post-sunset 2029: MFJ both 65+ loses $12K OBBB deduction4,8444,844✓
Sunset delta = $12K × 12% bracket = $1,4401,4401,440✓
In-window: Single 65 with $100K MAGI gets partial OBBB ($4.5K)11,72911,729✓
Post-sunset: Single 65 with $100K MAGI loses the partial $4.5K990990✓

OBBB itemizer (O-1) 8/8

TestExpectedActualResult
Single 66 itemizing gets OBBB on top of itemized: fedTax = 21,03821,03821,038✓
Single 66 itemizing: isItemizing = 111✓
Single 66 itemizing: itemizedTotal = 30,00030,00030,000✓
Single 66: itemized 20,000 (between base and base+OBBB) itemizes: fedTax 23,43823,43823,438✓
Single 66: itemized 20,000 → isItemizing = 111✓
Regression: standard path fedTax unchanged (23,882)23,88223,882✓
MFJ both 66 itemizing: isItemizing = 1 (buggy engine kept standard)11✓
MFJ both 66 itemizing: fedTax = 23,30423,30423,304✓

Senior Std Ded 5/5

TestExpectedActualResult
Bug-reveal: MFJ primary 60 / spouse 67 in-window gets spouse-only seniors4,3224,322✓
Bug-reveal: MFJ primary 60 / spouse 67 post-sunset 2030 gets §63(f) only5,0425,042✓
Regression: MFJ both 65+ in-window unchanged3,4043,404✓
Regression: MFJ both <65 unchanged (no senior, no OBBB)5,2405,240✓
Regression: Single 65+ in-window with phase-out unchanged11,72911,729✓

OBBB Smart Fill (F-166) 3/3

TestExpectedActualResult
In-window MFJ with capGains: Smart Fill produces consistent year output11✓
Below-threshold MFJ regression: capGains=0 leaves Smart Fill unchanged11✓
Above-cliff MFJ: capGains pushes AGI past $250K → OBBB fully phased out11✓

F-167 OBBB window 3/3

TestExpectedActualResult
Year-rows for 2028 and 2029 present in projection11✓
fedTax > 0 in 2028 (plan exercises tax path)11✓
fedTax 2028→2029 delta = $6,000 × 12% bracket = $720 (OBBB sunset boundary)720720✓

Phase C §199A 9/9

TestExpectedActualResult
qbiDeduction is state-independent (sanity, equals $15,367 hand-derived)15,36715,367✓
PA non-conformer + $100K SE: stateTax = $2,853 (gross-base × 3.07%)2,8532,853✓
CA non-conformer + $100K SE: stateTax = $5,296 (gross-base through CA brackets)5,2965,296✓
NJ non-conformer + $100K SE: stateTax = $3,794 (gross-base through NJ brackets)3,7943,794✓
NY AGI-state gross-base + $100K SE: stateTax = $4,915 (§199A absent below AGI line)4,9154,915✓
ID conformer + $100K SE: stateTax = $4,111 (§199A flows through, flat 5.3%)4,1114,111✓
IA conformer + $100K SE: stateTax = $2,948 (§199A flows through, flat 3.8%)2,9482,948✓
ND conformer + $100K SE: stateTax = $567 (§199A flows through, ND brackets)567567✓
CO conformer + $100K SE (below $500K threshold): stateTax = $3,413 (§199A flows through, flat 4.4%)3,4133,413✓

State Tax 93/93

Formula
State Tax = Σ [ min(TI_state, bracket_top) − bracket_bottom ] × rate (same marginal structure as federal)
TI_state = Federal AGI − state-specific exemptions (SS, pension, retirement income) subject to age/income gates
State DOR publications (50 states + DC) • IRC §86 state conformity rules • State-specific retirement income exclusions
Sources: State DOR publications — progressive brackets, SS exemptions with age/income gates, retirement income exclusions. IRC §86 state conformity.
TestExpectedActualResult
CO: above $1M MFJ AGI — §199A subtraction suppressed (F-197)52,80052,800✓
Florida — no income tax00✓
Texas — no income tax00✓
California — progressive brackets on $100K3,2463,246✓
New York — progressive brackets on $100K (2026 rates)5,0685,068✓
Maryland — progressive brackets only on $100K (local tax tested separately)4,6984,698✓
Illinois — flat 4.95% on $100K4,9504,950✓
Pennsylvania — flat 3.07% on $100K3,0703,070✓
Nevada — no income tax00✓
Washington — no income tax00✓
No state selected — zero tax00✓
MD: $100K MFJ with 3.2% local rate7,8987,898✓
MD: local rate 0 = state-only (backward compat)4,6984,698✓
MD: $100K MFJ age 65 with local + retirement exclusion11✓
FL: mdLocalTaxRate ignored for non-MD state00✓
FL: SS+IRA income, no tax00✓
TX: $200K income, no tax00✓
WA: any income, no tax00✓
NH: IRA distribution, no tax00✓
none: federal only, no state tax00✓
IL: SS+IRA both fully exempt00✓
IL: earned income taxable (4.95%)4,9504,950✓
IL: mixed — wages taxable, IRA exempt2,4752,475✓
IL: $100K Roth conversion fully exempt00✓
IL: $50K TAD fully exempt00✓
IL: mixed wages + conversion (only conversion exempt)4,9504,950✓
PA: $40K Roth conversion exempt at 60+00✓
PA: $40K conversion taxable under 601,2281,228✓
NY: conversion competes with pension for $20K cap3,9883,988✓
PA: IRA exempt age 60+00✓
PA: IRA taxable age 58 (3.07%)1,5351,535✓
PA: SS always exempt (all ages)00✓
CA: MFJ $80K — progressive brackets2,0462,046✓
CA: SS exempt, IRA taxable804804✓
CA: single filer pays more than MFJ same income11✓
NY: MFJ $100K with $20K pension exclusion3,9883,988✓
NY: SS exempt from state tax11✓
KS: SS exempt under $75K AGI11✓
KS: SS taxable above $75K AGI11✓
CT: SS exempt under $150K MFJ AGI11✓
RI: SS exempt under $114,900 AGI11✓
Invalid state code returns $000✓
Zero income returns $000✓
PA age gate: $40K IRA age 58 taxable1,2281,228✓
PA age gate: $40K IRA age 60 exempt00✓
NC: flat 3.99% on $100K3,9903,990✓
IN: flat 2.95% on $100K2,9502,950✓
KY: flat 3.5% on $100K3,5003,500✓
MS: flat 4.0% on $100K4,0004,000✓
ID: flat 5.3% on $100K5,3005,300✓
GA: flat 5.19% on $100K5,1905,190✓
CO: flat 4.4% on $100K4,4004,400✓
UT: flat 4.5% on $100K4,5004,500✓
OH: $100K MFJ — 2.75% above $26,0502,0342,034✓
OH: $20K income — below $26,050 threshold, $0 tax00✓
MT: $60K MFJ — all at 4.7% (below $95K threshold)2,8202,820✓
MT: $120K MFJ — split bracket5,8785,878✓
MT: single $60K pays more than MFJ $60K (lower threshold)11✓
NE: $100K MFJ — three brackets (top rate 4.55%)4,2844,284✓
OK: $20K MFJ — four-bracket structure (HB2764)471471✓
SC: $100K MFJ — 2026 brackets (top rate 6.0%)5,3445,344✓
F-052: MD MFJ $100K, mdLocalTaxRate=3.30 adds $3,3007,9987,998✓
F-052: MD MFJ $100K, mdLocalTaxRate=3.00 (Montgomery)7,6987,698✓
F-052: MD MFJ $100K, mdLocalTaxRate=0 yields bracket-only result4,6984,698✓
F-052: VA $100K MFJ passthrough (no localRate field)5,4935,493✓
F-052: MD MFJ $30K low income, mdLocalTaxRate=3.302,3632,363✓
F-052: MD MFJ $250K high income, mdLocalTaxRate=3.3020,57320,573✓
F-055: NJ MFJ $80K gross, $40K pension, age 65 — Tier 1 (full)630630✓
F-055: NJ MFJ $110K gross, $40K pension, age 65 — Tier 2 (50%)1,2951,295✓
F-055: NJ MFJ $130K gross, $40K pension, age 65 — Tier 3 (25%)3,0263,026✓
F-055: NJ MFJ $160K gross, $40K pension, age 65 — Cliff (0%)6,1506,150✓
F-055: NJ Single $80K gross, $40K pension, age 65 — Tier 1 Single ($75K max)718718✓
F-055: NJ Single $130K gross, $40K pension, age 65 — Tier 3 Single (25% × $75K)4,9604,960✓
F-055: NJ MFJ age 60 (under 62) — exclusion ineligible1,6451,645✓
F-055: NJ MFJ age 62 just-eligible — full exclusion at $80K630630✓
F-055: NJ MFJ exactly $100K gross — Tier 1 boundary (100%)1,0501,050✓
F-055: NJ MFJ exactly $150K gross — Tier 3 boundary (25%, not cliff)4,1314,131✓
Local rate adds flat charge on state base (NC 3.99% + 1.5% local)5,4905,490✓
localTaxRate default 0 is a no-op (NC flat 3.99% baseline)3,9903,990✓
MD ignores generic localTaxRate (no double-count with mdLocalTaxRate)11✓
Local rate respects exemption base (PA all-retirement at 60+ is $0)00✓
Override replaces the bracket model (NC 5% on gross)5,0005,000✓
Override bypasses exemptions and the zero-base early return (PA retiree)2,0002,000✓
Override (state) and local rate stack (NC 5% + 1% local)6,0006,000✓
Override null is a no-op (NC bracket model unchanged)3,9903,990✓
OH earned-base: retiree with no wages, local rate is a no-op1,4841,484✓
OH earned-base: local rate applies to wages (worker)1,6591,659✓
Untagged state stays broad-base (NC retiree, contrast with OH)3,9923,992✓
PA earned-base: local rate applies to wages (worker under 60)2,4422,442✓
AL: DB pension fully exempt, SS exempt — no tax00✓
AL: $12K@65 withdrawal exclusion applies while DB pension stays exempt320320✓
AL: under 65 gets NO withdrawal exclusion920920✓
AL: QLAC payout is not DB pension — competes for the $12K bucket220220✓

SS Taxation (MFJ) 6/6

Formula
Provisional Income (PI) = AGI + Tax-Exempt Interest + 0.5 × SS Gross
If PI ≤ $32,000 MFJ ($25,000 Single): 0% of SS taxable
If PI between thresholds: min(0.50 × (PI − lower), 0.50 × SS Gross)
If PI > $44,000 MFJ ($34,000 Single): min(0.85 × (PI − upper) + 0.50 × $12,000, 0.85 × SS Gross)
IRC §86 • IRS Pub. 915, Worksheet 1 • IRS Pub. 915 →
Authority: Internal Revenue Service
Documents:
• IRC Section 86 — Social Security and Tier 1 Railroad Retirement Benefits
• IRS Publication 915 (2024) — Social Security and Equivalent Railroad Retirement Benefits
• IRS Publication 915, Worksheet 1 — Figuring Your Taxable Benefits
Method: Three-tier taxation: 0% when combined income below base threshold ($32K MFJ / $25K Single), up to 50% between thresholds, up to 85% above upper threshold ($44K MFJ / $34K Single). Verified with hand calculations matching Pub 915 Worksheet 1.
TestExpectedActualResult
Below base threshold: $20K SS, $10K other → 0% taxable00✓
At base threshold: $24K SS, $20K other → 0% taxable00✓
Between thresholds: $30K SS, $25K other → partial4,0004,000✓
Above upper: $30K SS, $60K other → 85% rule25,50025,500✓
Very high income: $40K SS, $200K other → 85% cap34,00034,000✓
Zero SS benefit → zero taxable00✓

SS Taxation (Single) 3/3

Formula
Provisional Income (PI) = AGI + Tax-Exempt Interest + 0.5 × SS Gross
If PI ≤ $32,000 MFJ ($25,000 Single): 0% of SS taxable
If PI between thresholds: min(0.50 × (PI − lower), 0.50 × SS Gross)
If PI > $44,000 MFJ ($34,000 Single): min(0.85 × (PI − upper) + 0.50 × $12,000, 0.85 × SS Gross)
IRC §86 • IRS Pub. 915, Worksheet 1 • IRS Pub. 915 →
Authority: Internal Revenue Service
Documents:
• IRC Section 86 — Social Security and Tier 1 Railroad Retirement Benefits
• IRS Publication 915 (2024) — Social Security and Equivalent Railroad Retirement Benefits
• IRS Publication 915, Worksheet 1 — Figuring Your Taxable Benefits
Method: Three-tier taxation: 0% when combined income below base threshold ($32K MFJ / $25K Single), up to 50% between thresholds, up to 85% above upper threshold ($44K MFJ / $34K Single). Verified with hand calculations matching Pub 915 Worksheet 1.
TestExpectedActualResult
Below base: $20K SS, $10K other → 0%00✓
Between thresholds: $24K SS, $20K other → partial3,5003,500✓
Above upper: $30K SS, $40K other → 85% rule22,35022,350✓

SS PIA 4/4

Formula
PIA = 90% × min(AIME, $1,226) + 32% × min(AIME − $1,226, $6,165) + 15% × max(0, AIME − $7,391)
AIME = Average Indexed Monthly Earnings (capped at wage base: $176,100 for 2025)
42 U.S.C. §415 • SSA OACT 2025 Bend Points • SSA POMS RS 00605.900 • SSA source →
Authority: Social Security Administration
Documents:
• SSA 2025 Bend Points — Office of the Chief Actuary (OACT)
• SSA POMS RS 00615.003 — Full Retirement Age
• SSA POMS RS 00615.301-304 — Early/Late Claiming Adjustments
• SSA 2025 Contribution and Benefit Base — $176,100
Method: PIA bend points ($1,226 / $7,391 for 2025), replacement rates (90%/32%/15%), wage base cap, FRA table for all birth years 1937-1960+, early claiming reduction (5/9% per month first 36, 5/12% beyond), and delayed retirement credits (8%/year capped at 70).
TestExpectedActualResult
Low earner AIME=$1,000 → PIA=$900900900✓
Mid earner AIME=$5,000 → PIA≈$2,3462,345.82,345✓
Max earner AIME=$15,375 → PIA≈$4,3694,369.44,369✓
Income above wage cap ($200K) capped at $184,5004,3694,369✓

FRA 16/16

Formula (Full Retirement Age by birth year)
1943-1954: FRA = 66
1955-1959: FRA = 66 + (birthYear - 1954) * 2 months
1960+: FRA = 67
42 U.S.C. §416(l) • SSA full retirement age table
Sources: 42 U.S.C. §416(l) (definition of full retirement age); SSA full retirement age table by year of birth. Applies to both retirement benefits and the earnings test.
TestExpectedActualResult
Born 1937 → FRA 65y 0m780780✓
Born 1938 → FRA 65y 2m782782✓
Born 1939 → FRA 65y 4m784784✓
Born 1940 → FRA 65y 6m786786✓
Born 1941 → FRA 65y 8m788788✓
Born 1942 → FRA 65y 10m790790✓
Born 1943 → FRA 66y 0m792792✓
Born 1954 → FRA 66y 0m792792✓
Born 1955 → FRA 66y 2m794794✓
Born 1956 → FRA 66y 4m796796✓
Born 1957 → FRA 66y 6m798798✓
Born 1958 → FRA 66y 8m800800✓
Born 1959 → FRA 66y 10m802802✓
Born 1960+ → FRA 67y 0m804804✓
Born 1975 → FRA 67y 0m804804✓
Born 1990 → FRA 67y 0m804804✓

SS Claiming 7/7

Formula (claiming-age adjustments to PIA)
Before FRA: benefit reduced 5/9% per month for first 36 months, then 5/12% per month beyond
After FRA: benefit increased 2/3% per month (8%/year) of delayed retirement credits
Delayed retirement credits stop accruing at age 70.
42 U.S.C. §402(q) (reduction) • §402(w) (delayed credits) • SSA POMS RS 00615
Sources: 42 U.S.C. §402(q) (early-claiming reduction); §402(w) (delayed retirement credits); SSA POMS RS 00615. Engine applies the appropriate adjustment based on claim age relative to FRA.
TestExpectedActualResult
Claim at FRA (67) → 100% of PIA2,0002,000✓
Claim at 62 (60 months early) → 70% of PIA1,4001,400✓
Claim at 64 (36 months early) → 80% of PIA1,6001,600✓
Claim at 65 (24 months early) → ~86.7% of PIA1,7331,733✓
Claim at 70 (36 months late) → 124% of PIA2,4802,480✓
Claim at 68 (12 months late) → 108% of PIA2,1602,160✓
Claim at 72 → same as 70 (DRC capped)2,4802,480✓

SS Reverse 5/5

TestExpectedActualResult
PIA $3000 → claim 70 → reverse recovers ~$30003,0003,000✓
PIA $2500 → claim 62 → reverse recovers ~$25002,5002,500✓
PIA $2000 → claim 67 (FRA) → reverse recovers $2000 exactly2,0002,000✓
PIA $4000 → claim 65 → reverse recovers ~$40004,0003,999✓
Zero current monthly returns 000✓

Divorced-Spouse SS 11/11

TestExpectedActualResult
Eligible: 10-year marriage, divorced 5 years11✓
Not eligible: marriage 8 years00✓
Not eligible: divorced 1 year, ex not filed00✓
Eligible: divorced 1 year but ex has filed11✓
Not eligible: no ex PIA entered00✓
At FRA: 50% of ex PIA $2,000 = $12,000/yr floor12,00012,000✓
Claim at 62 (60 months early): 50% floor x 65%7,8007,800✓
Claim at 70: same as FRA (no delayed credits)12,00012,000✓
Own benefit above the floor → no top-up00✓
Single filer: divorced floor lifts SS to $12,00012,00012,000✓
Own benefit (income $8K) is below the $12,000 floor11✓

SS COLA Accumulation 8/8

TestExpectedActualResult
Manual PIA claim at FRA: 5yr pre-claim COLA accumulates39,74739,747✓
Manual PIA claim at 70: 8yr COLA + 24% DRC52,30352,303✓
Already collecting: year 0 = entered (no COLA back-application)30,00030,000✓
Already collecting: year 5 = entered × 1.02^533,12233,122✓
estimateSS path: 5yr COLA ratio matches 1.02^511,04111,041✓
Couple manual PIAs: combined y.ss reflects both pre-claim COLAs59,62059,620✓
Spouse estimateSS path: 5yr COLA ratio matches 1.02^511,04111,041✓
Cohort-stable manual PIA (birthYear 1980): same COLA accumulation as 1964 cohort39,74739,747✓

RMD Table 34/34

Formula
RMD = Prior Year-End Account Balance ÷ Distribution Period (age-based divisor)
Divisors from IRS Uniform Lifetime Table III: age 72 → 27.4, age 80 → 20.2, age 90 → 12.2 …
IRC §401(a)(9) • IRS Pub. 590-B, Table III (Uniform Lifetime Table) • SECURE 2.0 Act §107 • IRS Pub. 590-B →
Authority: Internal Revenue Service
Documents:
• IRS Publication 590-B — Distributions from IRAs
• IRS Uniform Lifetime Table (Table III) — updated effective 2022
• SECURE 2.0 Act of 2022, Section 107 — RMD Start Age Changes
Method: All 34 divisors from the Uniform Lifetime Table (ages 72-105) verified to exact decimal. SECURE 2.0 RMD start ages confirmed: 72 (born <=1950), 73 (1951-1959), 75 (1960+).
TestExpectedActualResult
Age 72 divisor = 27.427.427.4✓
Age 73 divisor = 26.526.526.5✓
Age 74 divisor = 25.525.525.5✓
Age 75 divisor = 24.624.624.6✓
Age 76 divisor = 23.723.723.7✓
Age 77 divisor = 22.922.922.9✓
Age 78 divisor = 222222✓
Age 79 divisor = 21.121.121.1✓
Age 80 divisor = 20.220.220.2✓
Age 81 divisor = 19.419.419.4✓
Age 82 divisor = 18.518.518.5✓
Age 83 divisor = 17.717.717.7✓
Age 84 divisor = 16.816.816.8✓
Age 85 divisor = 161616✓
Age 86 divisor = 15.215.215.2✓
Age 87 divisor = 14.414.414.4✓
Age 88 divisor = 13.713.713.7✓
Age 89 divisor = 12.912.912.9✓
Age 90 divisor = 12.212.212.2✓
Age 91 divisor = 11.511.511.5✓
Age 92 divisor = 10.810.810.8✓
Age 93 divisor = 10.110.110.1✓
Age 94 divisor = 9.59.59.5✓
Age 95 divisor = 8.98.98.9✓
Age 96 divisor = 8.48.48.4✓
Age 97 divisor = 7.87.87.8✓
Age 98 divisor = 7.37.37.3✓
Age 99 divisor = 6.86.86.8✓
Age 100 divisor = 6.46.46.4✓
Age 101 divisor = 666✓
Age 102 divisor = 5.65.65.6✓
Age 103 divisor = 5.25.25.2✓
Age 104 divisor = 4.94.94.9✓
Age 105 divisor = 4.64.64.6✓

RMD Calc 4/4

Formula
RMD = Balance(Dec 31, prior year) ÷ Distribution Period(current age)
Example: $1,000,000 ÷ 26.5 (age 73) = $37,736
IRC §401(a)(9) • IRS Pub. 590-B, Table III • IRS Pub. 590-B →
Authority: Internal Revenue Service
Documents:
• IRS Publication 590-B — Distributions from IRAs
• IRS Uniform Lifetime Table (Table III) — updated effective 2022
• SECURE 2.0 Act of 2022, Section 107 — RMD Start Age Changes
Method: All 34 divisors from the Uniform Lifetime Table (ages 72-105) verified to exact decimal. SECURE 2.0 RMD start ages confirmed: 72 (born <=1950), 73 (1951-1959), 75 (1960+).
TestExpectedActualResult
Age 73, $1M balance → $37,73637,73637,736✓
Age 80, $1M balance → $49,50549,50549,505✓
Age 90, $1M balance → $81,96781,96781,967✓
Age 100, $500K balance → $78,12578,12578,125✓

RMD Joint Life 13/13

Formula (Joint Life and Last Survivor Expectancy)
Eligibility: ownerAge − spouseAge > 10 AND spouse is sole beneficiary (re-evaluated yearly)
RMD = Prior Year-End Balance ÷ JOINT_LIFE_TABLE[ownerAge][spouseAge]
Fallback: if ineligible (gap ≤ 10, flag off, or spouse deceased), use Uniform Lifetime (Table III)
IRC §401(a)(9) • IRS Pub 590-B Appendix B, Table II • Treas. Reg. §1.401(a)(9)-9(d) • IRS Pub 590-B PDF →
Authority: Internal Revenue Service
Documents:
• IRS Publication 590-B, Appendix B, Table II — Joint and Last Survivor Life Expectancy
• Treasury Regulation §1.401(a)(9)-9(d) — post-2022 divisor tables
• IRC §401(a)(9) — RMD rules
Method: 5 anchor divisors from Pub 590-B Table II verified to exact decimal, including the IRS worked example (owner 75 / spouse 64 = 25.3) and the user-reported 24-year-gap scenario (75 / 51 = 35.8). Boundary cases at exactly 10 years and 11 years verified. Eligibility helper tested for sole-beneficiary flag, spouse age null/zero, and spouse older than owner. 3,234 cells total in the engine; owner ages 72–120, spouse ages 20 through (owner − 11).
TestExpectedActualResult
Table II: owner 75 / spouse 64 = 25.3 (IRS worked example)25.325.3✓
Table II: owner 73 / spouse 62 = 27.2 (11-yr gap boundary)27.227.2✓
Table II: owner 75 / spouse 51 = 35.8 (24-yr gap)35.835.8✓
Table II: owner 100 / spouse 89 = 6.8 (oldest at 11-yr gap)6.86.8✓
Table II: owner 120 / spouse 109 = 2.0 (table minimum)22✓
calcRMD uses Table II for 75/51 + sole beneficiary flag27,93327,933✓
Exactly 10-year gap falls back to Uniform Lifetime40,65040,650✓
Flag off → Uniform Lifetime even at 24-yr gap40,65040,650✓
Missing Table II cell falls back to Uniform Lifetime40,65040,650✓
usesJointLifeTable(75, 51, true) → true (24-yr gap + flag)11✓
usesJointLifeTable(75, 65, true) → false (exactly 10-yr gap)00✓
usesJointLifeTable(75, 51, false) → false (flag off)00✓
usesJointLifeTable(75, 80, true) → false (spouse older)00✓

RMD Start 5/5

Formula
RMD Start Age = 72 (born ≤ 1950) | 73 (born 1951–1959) | 75 (born ≥ 1960)
SECURE Act of 2019 §114 (age 72) • SECURE 2.0 Act of 2022 §107 (ages 73 and 75) • IRS source →
Authority: Internal Revenue Service
Documents:
• IRS Publication 590-B — Distributions from IRAs
• IRS Uniform Lifetime Table (Table III) — updated effective 2022
• SECURE 2.0 Act of 2022, Section 107 — RMD Start Age Changes
Method: All 34 divisors from the Uniform Lifetime Table (ages 72-105) verified to exact decimal. SECURE 2.0 RMD start ages confirmed: 72 (born <=1950), 73 (1951-1959), 75 (1960+).
TestExpectedActualResult
Born 1950 → RMD starts at 727272✓
Born 1951 → RMD starts at 737373✓
Born 1959 → RMD starts at 737373✓
Born 1960 → RMD starts at 757575✓
Born 1980 → RMD starts at 757575✓

SEPP 4/4

TestExpectedActualResult
Single Life divisor at age 50 = 36.2 (Pub 590-B Table I)36.236.2✓
Single Life divisor at age 60 = 27.127.127.1✓
RMD method: $1,000,000 at age 50 → $27,624 (balance / 36.2)27,62427,624✓
Amortization at 5% over 36.2 yrs: 1e6·r/(1−(1+r)^−n) = 60,31260,31260,312✓

NIIT 1/1

Formula (Net Investment Income Tax)
NIIT = 3.8% × min(net_investment_income, MAGI − threshold)
Thresholds (not inflation-adjusted): ,000 MFJ / ,000 Single
Investment income includes capital gains, dividends, interest, royalties, rental.
IRC §1411 • IRS Form 8960
Sources: IRC §1411 (NIIT); IRS Form 8960 (computation). Thresholds are fixed by statute (not indexed). Engine applies NIIT on top of ordinary + CG tax when MAGI exceeds threshold.
TestExpectedActualResult
Tax rate = 3.8%11✓

IRMAA MAGI 2/2

TestExpectedActualResult
AGI $100K + tax-exempt $5K = $105K105,000105,000✓
AGI $200K + zero tax-exempt = $200K (regression baseline)200,000200,000✓

ACA MAGI 3/3

TestExpectedActualResult
AGI $40K + ss $30K + ssTaxable $22K → $48K48,00048,000✓
AGI $80K + zero SS → $80K (regression baseline)80,00080,000✓
F-161 hand-computed: AGI $55,350 + ss $30K + ssTaxable $15,350 → ACA MAGI $70K70,00070,000✓

NIIT MAGI 2/2

TestExpectedActualResult
AGI $300K + foreign earned $0 → $300K300,000300,000✓
AGI $250K + foreign earned $50K → $300K300,000300,000✓

F-160 AGI 1/1

TestExpectedActualResult
Couple $200K W2 + $30K 401(k) deferral → year-0 AGI ≈ $170K170,000170,000✓

F-161 storage 1/1

TestExpectedActualResult
Year output stores ssTaxable < ss for SS-collecting couple11✓

ACA F-301 basis 6/6

TestExpectedActualResult
Year 64: acaMagiBasis = current-year MAGI 100,000 (conversion AGI)100,000100,000✓
Year 64: basis 100,000 equals current-year MAGI 100,00011✓
Year 64: basis over 400% FPL (2-person, 2026) → subsidy $0 (cliff)00✓
Year 65: acaMagiBasis = current-year MAGI 0 (no income)00✓
Year 65: basis 0 matches current-year MAGI 0 (no divergence)11✓
Year 65: basis 0 → below 100% FPL → subsidy $0 (not eligible)00✓

ACA F-303 transition 5/5

TestExpectedActualResult
Year 64: FPL% is the UNWEIGHTED ratio 278, not 278 × 11/12 = 255278278✓
Year 64: reported FPL% differs from the phase-weighted value (278 vs 255)11✓
Year 64: subsidy is phase-weighted (Form 8962 proration): 18,842×11/12 + 7,275×1/12 = 17,87817,87817,878✓
Year 64: net premium phase-weighted = 5,5775,5775,577✓
Year 64: basis = full-year MAGI 60,000 (not reduced by coverage fraction)60,00060,000✓

ACA F-304 cliff 4/4

TestExpectedActualResult
Year 62: smartFill keeps ACA MAGI basis at or below the 395% FPL ceiling 83,54211✓
Year 62: subsidy retained (basis under cliff → credit > 0)11✓
Year 62: conversion fills exactly up to the 395% FPL ceiling (83,542), stopping short of the 12% bracket top83,54283,542✓
Year 62: acaFPLPercent is the unweighted annual ratio (F-303 honored) and at/below 395%11✓

LTC medical deduction (F-305) 4/4

TestExpectedActualResult
Age 85 itemizing: medical deduction (100,000 − 7.5% × 81,581 ≈ 93,881) zeroes federal tax00✓
Age 85 NOT itemizing: no medical deduction → federal tax > 0 (5,083)11✓
Age 85 itemizing: isItemizing true (medical deduction beats standard)11✓
Care-year ltcCost is present (100,000) and deductible-eligible11✓

F-300 Medicare medical deduction 6/6

TestExpectedActualResult
Age 70 itemizing: itemized total = OOP 40,000 + Part B 2,435 − 7.5%×150,000 = 31,18531,18531,185✓
Age 70 itemizing: fed tax on 150,000 − (31,185 itemized + 1,500 OBBB) = 20,75420,75420,754✓
IRMAA case: single tier 4 (N−2 MAGI 205,001) premiums 2,435 + 6,355 = 8,790; itemized = 40,000 + 8,790 − 11,250 = 37,54037,54037,540✓
IRMAA case: fed tax on 150,000 − (37,540 itemized + 1,500 OBBB) = 19,22819,22819,228✓
Defaults-off gate: itemized disabled → no medical deduction (itemizedTotal 0)00✓
Defaults-off gate: standard deduction only → fed tax on 150,000 − 19,650 = 23,88223,88223,882✓

IRMAA Headroom 5/5

TestExpectedActualResult
Base (N−2 MAGI 0, single): lookback basis = 000✓
Base (single tier 0): headroom to tier 1 = 109,000 (2026 single threshold)109,000109,000✓
Single tier 4 (N−2 MAGI 205,001): lookback basis = 205,001205,001205,001✓
Single tier 4 → headroom to top tier = 500,000 − 205,001 = 294,999294,999294,999✓
Top tier (single, N−2 MAGI 600,000): no higher tier → headroom 000✓

F-307 Enrollment 6/6

TestExpectedActualResult
Age 65 working: no Part B premium (enrollment delayed to 67)00✓
Age 66 working: no Part B premium00✓
Age 67: Part B base premium $2,435 (creditable coverage → no penalty)2,4352,435✓
Age 66: HSA contribution still allowed (stop 6 months before age-67 Part A)11✓
Age 67: HSA contribution stopped at the retroactive Part A bound00✓
Age 67 without creditable coverage: 2-year delay → 1.20 × $2,435 = $2,9222,9222,922✓

F-306 SSA-44 8/8

TestExpectedActualResult
Without relief, age 65 IRMAA = tier 3 surcharge $4,620 (from the age-63 working return)4,6204,620✓
Without relief, age 65 tier index = 333✓
With relief, age 65 IRMAA = $0 (current-year MAGI below the $109k tier-1 line)00✓
With relief, age 65 tier index = 000✓
With relief, age 66 IRMAA = $0 (second coverage year)00✓
With relief, age 65 lookback basis is the current-year MAGI (< $109,000), not the age-63 return11✓
Gate: still working at 65 (retires 67) → relief does NOT apply; age-65 IRMAA stays $4,6204,6204,620✓
Gate: retired at 60 (lookback already post-stoppage) → relief is a no-op vs the same plan unadjusted00✓

F-306b 2/2

TestExpectedActualResult
Without relief, age 65 household IRMAA = tier-2 surcharge × 2 enrollees = $5,7705,7705,770✓
Spouse-anchored relief fires at age 65 even though the primary retired long ago → IRMAA $000✓

F-306c 3/3

TestExpectedActualResult
Without relief, age 65 survivor IRMAA = tier 2 surcharge $2,885 (joint lookback, single year)2,8852,885✓
Death-of-spouse relief: age 65 IRMAA $0 (survivor current-year income below tier 1)00✓
Death-of-spouse relief: age 66 IRMAA $0 (second coverage year)00✓

F-306d 3/3

TestExpectedActualResult
Without hold-harmless, age-66 base Part B = $2,435 × 1.05 = $2,5572,5572,557✓
Hold-harmless with 0% SS COLA holds age-66 base Part B at $2,4352,4352,435✓
Hold-harmless does not change the first Medicare year (no prior-year increase)2,4352,435✓

Part D Base 3/3

TestExpectedActualResult
Default (no base premium entered) → partDBase = 000✓
$50/month entered → partDBase = $600/yr600600✓
Base premium flows into expenses (expenses + $600 vs default)600600✓

Tax Brackets 6/6

TestExpectedActualResult
2026 (year-0) MFJ 10% top unchanged: $24,80024,80024,800✓
2030 MFJ 10% top: $24,800 × 1.03^4 floored = $27,90027,90027,900✓
2036 MFJ 22% top (10yr): $211,400 × 1.03^10 floored284,100284,100✓
Top bracket (Infinity) preserved unchanged11✓
Shared-boundary contract: bracket[N+1].min === bracket[N].max post-index11✓
inflation=0 invariance: brackets unchanged at year >0201,775201,775✓

Indexed Brackets 1/1

TestExpectedActualResult
indexDollarAmount(32200, 2026, 2036, 3) == 4325043,25043,250✓

Std Deduction 2/2

TestExpectedActualResult
2026 MFJ year-0: $32,20032,20032,200✓
2036 MFJ: $32,200 × 1.03^10 floored43,25043,250✓

Senior Std Deduction 1/1

TestExpectedActualResult
2030 MFJ per-spouse: $1,650 × 1.03^4 floored1,8501,850✓

OBBB Senior Deduction 4/4

TestExpectedActualResult
2025 base year: $6,000 (year-zero, in-window)6,0006,000✓
2027 (in-window): $6,000 fixed (no indexing)6,0006,000✓
2029 (post-sunset): returns 000✓
2024 (pre-window): returns 000✓

OBBB Phase-out Threshold 1/1

TestExpectedActualResult
2027 MFJ: $150,000 fixed (no indexing)150,000150,000✓

CG Breakpoint 2/2

TestExpectedActualResult
2030 MFJ 0% threshold: $98,900 × 1.03^4 floored111,300111,300✓
2036 Single 0% threshold: $49,450 × 1.03^10 floored66,45066,450✓

QBI Phase-out 2/2

TestExpectedActualResult
2030 MFJ start: $403,500 × 1.03^4 floored454,100454,100✓
2036 MFJ end: $553,500 × 1.03^10 floored743,850743,850✓

Indexing Year-Zero 2/2

TestExpectedActualResult
taxBracketsFor at base year returns same .max as REGS211,400211,400✓
stdDeductionFor at base year preserves raw value32,20032,200✓

Indexing Integration 2/2

TestExpectedActualResult
2036 MFJ 22% bracket top > 2026 22% bracket top11✓
2036 MFJ std deduction > 2026 std deduction11✓

IRMAA Indexing 16/16

TestExpectedActualResult
2026 (year-0) tier 1 MFJ unchanged: $218,000218,000218,000✓
2026 (year-0) tier 5 (top) MFJ unchanged: $750,000750,000750,000✓
inflation=0 returns identical tiers across years274,000274,000✓
2030 tier 1 MFJ: $218,000 × 1.03^4 round-to-1000245,000245,000✓
2036 tier 1 MFJ: $218,000 × 1.03^10 round-to-1000293,000293,000✓
2036 tier 4 MFJ: $410,000 × 1.03^10 round-to-1000551,000551,000✓
2030 tier 1 Single: $109,000 × 1.03^4 round-to-1000123,000123,000✓
2027 top tier MFJ stays at $750,000 (frozen pre-2028)750,000750,000✓
2028 top tier MFJ stays at $750,000 (year-zero for top tier)750,000750,000✓
2030 top tier MFJ: $750,000 × 1.03^2 round-to-1000796,000796,000✓
2036 top tier MFJ: $750,000 × 1.03^8 round-to-1000950,000950,000✓
Tier 0 (magi: 0) baseline preserved across years00✓
Tier 1 partB surcharge preserved across years3,409.23,409.2✓
$1,000 round-to-nearest: tier 2 MFJ at 2028291,000291,000✓
calcIRMAA at 2030 with $230K MFJ uses indexed tier 1 ($245K)2,4352,435✓
calcIRMAA at 2030 inflation=0 uses 2026 thresholds3,4093,409✓

IRMAA (MFJ) 12/12

Formula
Surcharge = f(MAGI from 2 years prior, filing status) → 6-tier lookup table
Total Part B Premium = $185.00/mo (base) + IRMAA Part B surcharge
Total Part D Premium = Plan premium + IRMAA Part D surcharge
42 U.S.C. §1395r • CMS 2025 IRMAA Tables • CMS Pub. 100-02, Ch. 15 §40 • CMS source →
Authority: Centers for Medicare & Medicaid Services
Documents:
• CMS 2025 Medicare Parts B and D Income-Related Monthly Adjustment Amounts
• CMS Standard Part B Premium: $185.00/month (2025)
Method: All six IRMAA tiers verified for both MFJ and Single filing status. Part B premiums, Part D surcharges, and MAGI thresholds at each tier. Two-year MAGI lookback implemented.
TestExpectedActualResult
Tier 0: below $218K — Part B annual2,4352,435✓
Tier 0: below $218K — Part D annual00✓
Tier 1: $218K–$274K — Part B annual3,4093,409✓
Tier 1: $218K–$274K — Part D annual174174✓
Tier 2: $274K–$342K — Part B annual4,8704,870✓
Tier 2: $274K–$342K — Part D annual450450✓
Tier 3: $342K–$410K — Part B annual6,3306,330✓
Tier 3: $342K–$410K — Part D annual725725✓
Tier 4: $410K–$750K — Part B annual7,7907,790✓
Tier 4: $410K–$750K — Part D annual1,0001,000✓
Tier 5: above $750K — Part B annual8,2798,279✓
Tier 5: above $750K — Part D annual1,0921,092✓

IRMAA (Single) 3/3

Formula
Surcharge = f(MAGI from 2 years prior, filing status) → 6-tier lookup table
Total Part B Premium = $185.00/mo (base) + IRMAA Part B surcharge
Total Part D Premium = Plan premium + IRMAA Part D surcharge
42 U.S.C. §1395r • CMS 2025 IRMAA Tables • CMS Pub. 100-02, Ch. 15 §40 • CMS source →
Authority: Centers for Medicare & Medicaid Services
Documents:
• CMS 2025 Medicare Parts B and D Income-Related Monthly Adjustment Amounts
• CMS Standard Part B Premium: $185.00/month (2025)
Method: All six IRMAA tiers verified for both MFJ and Single filing status. Part B premiums, Part D surcharges, and MAGI thresholds at each tier. Two-year MAGI lookback implemented.
TestExpectedActualResult
Tier 0: below $109K — Part B annual2,4352,435✓
Tier 1: $109K–$137K — Part B annual3,4093,409✓
Tier 5: above $500K — Part B annual8,2798,279✓

IRMAA 1/1

Formula
Surcharge = f(MAGI from 2 years prior, filing status) → 6-tier lookup table
Total Part B Premium = $185.00/mo (base) + IRMAA Part B surcharge
Total Part D Premium = Plan premium + IRMAA Part D surcharge
42 U.S.C. §1395r • CMS 2025 IRMAA Tables • CMS Pub. 100-02, Ch. 15 §40 • CMS source →
Authority: Centers for Medicare & Medicaid Services
Documents:
• CMS 2025 Medicare Parts B and D Income-Related Monthly Adjustment Amounts
• CMS Standard Part B Premium: $185.00/month (2025)
Method: All six IRMAA tiers verified for both MFJ and Single filing status. Part B premiums, Part D surcharges, and MAGI thresholds at each tier. Two-year MAGI lookback implemented.
TestExpectedActualResult
Base Part B premium = $202.90/month ($2,434.80/year)2,4352,435✓

Year Object 10/10

TestExpectedActualResult
Couple plan: spAge at year 0 = configured spouseAge6262✓
Couple plan: spAge increments by year offset6767✓
Couple plan: primaryDead/spouseDead false while both alive11✓
Single plan: spAge is null11✓
Spouse-dies plan: spAge numeric while spouse alive (primary 72)6969✓
Spouse-dies plan: spAge null after spouse death (primary 75)11✓
Spouse-dies plan: primaryDead stays false post spouse death11✓
Primary-dies plan: primaryDead true post primary death11✓
Primary-dies plan: spAge still numeric (survivor age) post primary death7272✓
Primary-dies plan: spouseDead stays false while spouse alive11✓

IRMAA Per-Enrollee 3/3

TestExpectedActualResult
Couple both 65+ in MFJ Tier 1 → IRMAA = 2× per-person surcharge2,2962,296✓
Couple, only primary 65+ → IRMAA = 1× per-person surcharge1,1481,148✓
Single filer 65+ in single Tier 1 → IRMAA = 1× per-person surcharge1,1481,148✓

Medicare/IRMAA Inflation 5/5

TestExpectedActualResult
Post-fix: y.medicareCost + y.irmaa equals authority total at yr=38,2968,296✓
y.irmaa grows at exactly medicareInflation% nominal year-over-year (same tier)00✓
y.medicareCost grows at exactly medicareInflation% nominal year-over-year01✓
Tier 0: y.medicareCost = base × enrollees × (1+m)^yr, y.irmaa = 05,6385,638✓
Tier 0: y.irmaa = 0 (no surcharge below MFJ Tier 1 threshold)00✓

Medicare cash flow 3/3

TestExpectedActualResult
Couple both 65+: y.expenses includes Medicare cost (post-65 > pre-65 expenses)11✓
Single 65+: y.expenses includes 1× Medicare cost11✓
Pre-65: medicareCost is 0 (no Medicare in expenses)00✓

Medicare OOP 9/9

TestExpectedActualResult
Absent medicareOOP → oopHealthcare = 0 (off by default)00✓
enabled:false → oopHealthcare = 0 (inert)00✓
Couple both alive 65+: oopHealthcare = round(9000 * 1.05^10)14,66014,660✓
OOP charged to expenses (delta = round(9000 * 1.05^10))14,66014,660✓
Pre-Medicare (age 63): oopHealthcare = 000✓
Custom inflation 3% honored (overrides medicareInflation 5%)12,09512,095✓
Survivor (spouse died): oopHealthcare = round(5000 * 1.05^10)8,1448,144✓
Survivor single amount < both-alive household amount (drop occurred)11✓
Single plan: uses annualAmount, ignores annualAmountSingle8,1448,144✓

Medicare OOP 213 7/7

TestExpectedActualResult
OOP feeds §213 medical: itemizedTotal rises by round(9000 * 1.05^10)14,66014,660✓
Fixture itemizes (SALT 40k + medical > standard) so §213 is active11✓
§213 feed does not change MAGI (deduction is below AGI)00✓
§213 feed does not change Medicare cost (IRMAA is MAGI-driven)00✓
Itemizing year: OOP §213 deduction lowers federal tax11✓
Pre-Medicare (age 63): no OOP in §213 (itemizedTotal unchanged)00✓
Survivor: §213 medical picks up the single-survivor OOP = round(5000 * 1.05^10)8,1448,144✓

Pre-Medicare OOP 13/13

TestExpectedActualResult
Absent preMedicareOOP -> preMedicareOOP = 0 (off by default)00✓
enabled:false -> preMedicareOOP = 0 (inert)00✓
Gap year 63: preMedicareOOP = round(9000 * 1.05^3)10,41910,419✓
OOP charged to expenses (delta = round(9000 * 1.05^3))10,41910,419✓
Post-Medicare (age 70): preMedicareOOP = 0 (handoff to medicareOOP)00✓
Still working at 63 (retireAge 70): preMedicareOOP = 000✓
Custom inflation 6.5% honored = round(9000 * 1.065^3)10,87210,872✓
Survivor (spouse died): preMedicareOOP = round(5000 * 1.05^3)5,7885,788✓
Survivor single amount < both-alive household amount (drop occurred)11✓
Single plan: uses annualAmount, ignores annualAmountSingle5,7885,788✓
Gap year 63: preMedicareOOP > 0 and oopHealthcare == 011✓
Medicare year 70: oopHealthcare > 0 and preMedicareOOP == 011✓
No double-count: no year has both pre- and post-Medicare OOP nonzero00✓

Pre-Medicare OOP 213 5/5

TestExpectedActualResult
OOP feeds 213 medical: itemizedTotal rises by round(9000 * 1.05^3)10,41910,419✓
Fixture itemizes (SALT 40k + medical > standard) so 213 is active11✓
213 feed does not change MAGI (deduction is below AGI)00✓
Itemizing year: OOP 213 deduction lowers federal tax11✓
Survivor: 213 medical picks up the single-survivor OOP = round(5000 * 1.05^3)5,7885,788✓

F-047 3/3

TestExpectedActualResult
Default Medicare inflation 5%/yr (no param)3,9663,966✓
Custom Medicare inflation 3%/yr honored3,2723,272✓
0% Medicare inflation honored (?? not ||)2,4352,435✓

F-165 IRMAA 5/5

TestExpectedActualResult
Single, priorYearMinus2MAGI=$200K → year 0 surcharge matches Tier 3 per-enrollee from REGS4,6204,620✓
Single, priorYearMinus2MAGI=$200K → year 0 surcharge > 0 (proxy was 0)11✓
Single, priorYearMinus2MAGI=0 → year 0 falls to proxy (no surcharge)11✓
MFJ, priorYearMinus1MAGI alone → year 0 unaffected (no priorYearMinus2 override)11✓
MFJ both 65+: year 1 surcharge = 2 × per-enrollee Tier surcharge2,2962,296✓

F-188 IRMAA 5/5

TestExpectedActualResult
Single retired, $150K active stream → year 0 surcharge > 0 (was 0 pre-fix)11✓
Single retired, $150K active stream → year 0 surcharge matches REGS Tier lookup2,8852,885✓
Single retired, no incomeStreams → year 0 IRMAA = 0 (Tier 0)11✓
Single retired, $150K stream startAge=70 (inactive at currentAge=65) → year 0 surcharge = 011✓
F-165 override precedence — priorYearMinus2MAGI=$50K beats $150K stream proxy11✓

Contrib Limits 5/5

Formula (2025 contribution limits)
401(k)/403(b)/457(b) employee deferral: ,500 + ,500 catch-up (50+) + ,250 super catch-up (60-63)
IRA (Traditional/Roth): ,000 + ,000 catch-up (50+)
HSA: ,300 self / ,550 family + ,000 catch-up (55+)
IRC §402(g), §414(v), §219, §223 • SECURE 2.0 §109 (super catch-up) • IRS Notice 2024-80
Sources: IRC §402(g) (401(k)); §414(v) (age 50 catch-up); SECURE 2.0 §109 (ages 60-63 enhanced catch-up); §219 (IRA); §223 (HSA); IRS Notice 2024-80 (2025 limits). Engine enforces per-account limits during the contribution phase.
TestExpectedActualResult
401(k) elective deferral: $24,50024,50024,500✓
IRA contribution limit: $7,5007,5007,500✓
401(k) catch-up (50–59, 64+): $8,0008,0008,000✓
SECURE 2.0 enhanced catch-up (60–63): $11,25011,25011,250✓
IRA catch-up (50+): $1,1001,1001,100✓

QCD 1/1

Formula (Qualified Charitable Distribution)
QCD = direct transfer from IRA to qualified charity, up to ,000/year (2025)
Counts toward RMD; excluded from AGI/MAGI (vs. itemized charitable deduction which reduces taxable income but keeps MAGI intact).
Eligible at age 70½; inflation-adjusted starting 2024.
IRC §408(d)(8) • SECURE 2.0 Act §307 (indexing) • IRS Rev. Proc. 2024-40
Sources: IRC §408(d)(8) (QCD exclusion); SECURE 2.0 Act §307 (indexed starting 2024); IRS Rev. Proc. 2024-40 (2025 limit K). Engine: QCD reduces RMD-driven taxable withdrawals and MAGI in the same year.
TestExpectedActualResult
Annual limit: $111,000 (2026)111,000111,000✓

Integration 26/26

TestExpectedActualResult
Projection covers 46 years (age 50–95)4646✓
Net worth stays positive (no bust)11✓
Pre-retirement savings accumulate11✓
RMDs start at age 75 (born 1976)7575✓
Tax computed for retirement year11✓
SS benefits start at claim age 676767✓
Account balances sum to net worth4,736,2304,736,230✓
Higher income → higher lifetime tax11✓
Roth conversion increases tax in conversion year11✓
Roth-first order depletes Roth faster11✓
California adds state tax vs no-state11✓
primaryEarned field present in year output11✓
spouseEarned field present in year output11✓
primaryEarned + spouseEarned === earned (invariant)180,000180,000✓
FICA field present in pre-retirement year11✓
Pre-retirement FICA ≈ 7.65% of gross wages (below SS cap)9,1809,180✓
Retirement-year FICA = 0 (no wages)00✓
Non-working spouse: total SS = primary own + spousal floor ($54K)54,00054,000✓
Low-earning spouse ($700/mo own): total SS top-up to floor ($43,200/yr)43,20043,200✓
High-earning spouse ($1,200/mo own > floor): no spousal top-up, total $38,40038,40038,400✓
Spousal benefit = $0 before primary files (primary age 67, files at 70)00✓
Spousal benefit activates when primary files at 7059,64059,640✓
spousalTopUp field exists in projection year output11✓
spousalTopUp value is correct ($18,000 for non-working spouse)18,00018,000✓
No spousal benefit when spouse not enabled24,00024,000✓
Spousal benefit stable year-over-year with zero COLA11✓

Return Rate Independence 5/5

TestExpectedActualResult
netWorth identical when all accounts have explicit rates1,128,1901,128,190✓
preTax identical across nominalReturn values443,001443,001✓
roth identical across nominalReturn values685,189685,189✓
taxable identical across nominalReturn values00✓
accounts grow at their explicit rates (sanity)11✓

MC Return Independence 3/3

TestExpectedActualResult
MC netWorth identical despite nominalReturn 5 vs 15744,144744,144✓
MC preTax identical despite nominalReturn 5 vs 15393,293393,293✓
MC results differ from deterministic (sanity)11✓

Edge Case 6/6

TestExpectedActualResult
RMD on $0 balance = $000✓
RMD at age 110 (beyond table) uses fallback11✓
SS estimate with $0 income → $0 benefit00✓
calcTax on negative income → $011✓
IRMAA at exact $218K MFJ threshold → Tier 110✓
State tax with invalid code → $000✓

SS Earnings Test 13/13

Formula (retirement earnings test)
Before FRA year: $1 withheld per $2 earned above annual limit ($23,400 in 2025)
Year of FRA (months before): $1 withheld per $3 earned above higher limit ($62,160 in 2025)
Starting FRA month: no withholding regardless of earnings.
42 U.S.C. §403 • SSA POMS RS 02501.021 • SSA 2025 exempt amounts
Sources: 42 U.S.C. §403 (earnings test); SSA POMS RS 02501.021 (application); SSA 2025 exempt amounts ($23,400 / $62,160). Engine: earnings test logic in src/engine/08-ss-engine.js applies withholding during pre-FRA claiming years.
TestExpectedActualResult
Under FRA: $20K earnings (below $24,480 limit) → $000✓
Under FRA: $50K earnings → $12,760 withheld12,76012,760✓
Under FRA: $100K earnings, $16,800 benefit → capped at $16,80016,80016,800✓
At FRA (age 67, born 1960+): $200K earnings → $000✓
After FRA (age 70): $200K earnings → $000✓
Zero earnings → $000✓
FRA year (66.5): $70K earnings → $1,6131,6131,613✓
SSDI: exempt from earnings test00✓
Zero SS benefit → $000✓
Earnings at exact $24,480 limit → $000✓
Inflation indexing: 2030 under-FRA limit10,90010,900✓
Integration: age 62 (working + SS) has withholding11✓
Integration: age 67 (FRA, retired) no withholding00✓

F-205 ARF 5/5

TestExpectedActualResult
No withholding control: claim 64, 0 months → $16001,6001,600✓
Amy's example: claim 64, 18 months withheld → $18001,8001,800✓
Partial credit: claim 64, 12 months → $17331,7331,733✓
Over-credit cap: claim 64, 50 months → $2000 (capped)2,0002,000✓
Beyond-36: claim 62, 12 months → $15001,5001,500✓

F-205 ARF Integration 3/3

TestExpectedActualResult
Single claim-64: age 64 ssWithheld = 1920019,20019,200✓
Single claim-64: age 67 ss = 24000 (post-ARF restored)24,00024,000✓
Already-collecting: age 67 ss = 18000 (no ARF jump)18,00018,000✓

Wage Base Indexing 6/6

TestExpectedActualResult
2026 baseline → $184,500 unchanged184,500184,500✓
2030 (4yr forward) → $212,400212,400212,400✓
2036 (10yr forward) → $262,800262,800262,800✓
2046 (20yr forward) → $374,400374,400374,400✓
$150-tie rounding → rounds UP per §430(b)(2)248,100248,100✓
FICA at 2026 → identical to pre-fix (year-zero invariance)11,43911,439✓

ACA FPL 5/5

Formula
FPL(size) = $15,650 + $5,500 × (size − 1) for household size ≥ 2
FPL% = MAGI ÷ FPL(household size) × 100
42 U.S.C. §9902(2) • HHS 2025 Poverty Guidelines (48 contiguous states + DC) • HHS source →
Authority: U.S. Department of Health and Human Services
Documents:
• HHS 2025 Federal Poverty Level Guidelines — 48 contiguous states + DC
Method: Base FPL for household sizes 1–4 verified against published guidelines ($15,650 / $21,150 / $26,650 / $32,150). Size 5+ uses incremental $5,500 per additional person. Annual 2% FPL inflation applied for projection years.
TestExpectedActualResult
Household of 1 base FPL = $15,65015,65015,650✓
Household of 2 base FPL = $21,15021,15021,150✓
Household of 3 base FPL = $26,65026,65026,650✓
Household of 4 base FPL = $32,15032,15032,150✓
Household of 5 base FPL = $37,65037,65037,650✓

ACA Subsidy 13/13

Formula
Max Contribution = MAGI × Applicable Percentage (AP)
PTC = max(0, Benchmark Premium − Max Contribution)
AP = f(FPL%) via 6-tier table; interpolated within tiers. No cliff through 2025 (ARPA/IRA enhanced rules)
IRC §36B • 26 CFR §1.36B-3 • IRS Rev. Proc. 2024-35 (enhanced 2025) • IRS Rev. Proc. 2025-25 (original 2026+) • IRS source →
Authority: Internal Revenue Service
Documents:
• IRS Rev. Proc. 2024-35 — Enhanced Applicable Percentage Table (2021–2025)
• IRS Rev. Proc. 2025-25 — Original ACA Applicable Percentages (2026+)
• ARPA/IRA — Enhanced PTC removes 400% FPL cliff through 2025
• ACA Section 36B — Premium Tax Credit eligibility (100–400% FPL under original rules)
Method: Applicable percentage verified at each FPL tier boundary under both enhanced (2025) and original (2026+) rules. Linear interpolation within tiers confirmed. Subsidy = benchmark premium minus expected contribution (MAGI × applicable %). Cliff behavior at 400% FPL tested for both rule sets.
TestExpectedActualResult
150% FPL enhanced (2025) → 0% contribution, full subsidy00✓
200% FPL enhanced (2025) → 2% applicable22✓
300% FPL enhanced (2025) → 6% applicable66✓
400% FPL enhanced (2025) → 8.5% applicable8.58.5✓
500% FPL enhanced (2025) → still eligible (no cliff)11✓
Below 100% FPL → not eligible for subsidy00✓
400%+ FPL original (2026) → no subsidy (cliff)00✓
300% FPL original (2026) → 9.96% applicable9.969.96✓
130% FPL original (2026) → 2.10% applicable2.12.1✓
Full subsidy at 150% FPL enhanced = benchmark premium20,00020,000✓
175% FPL enhanced → 1.0% applicable (interpolation)11✓
Couple at $44k expenses, 2026 original rules → positive subsidy11✓
Couple at $44k expenses, 2026 original rules → FPL ≈ 208%208208✓

ACA SLCSP 2/2

Formula (Estimator)
SLCSP ≈ $7,500 (age-40 base) × 1.04^(age − 40) × 1.05^(years from 2025)
Couple ≈ 1.9× single. Users can override with actual local marketplace quotes.
CMS Marketplace benchmark premium data • Planning estimate only — actual premiums vary by state and plan • CMS source →
Authority: RetIQ Estimator (national average)
Documents:
• CMS Marketplace average benchmark premium data
Method: Second Lowest Cost Silver Plan (SLCSP) estimated from national average ($7,500 base at age 40) with 4% per year age adjustment and 5% annual medical inflation. Couple rate ~1.9× single. Values are planning estimates; users can override with actual local quotes.
TestExpectedActualResult
Age 55 single base → ~$9,0009,0009,000✓
Age 60 couple > age 55 single11✓

ACA Actual Plan 8/8

TestExpectedActualResult
No actual plan arg → netPremium = benchmark-based (regression)3,8073,807✓
actualPlanPremium = 0 → same as benchmark (sentinel)3,8073,807✓
Gold plan above benchmark → net = plan − full credit5,8075,807✓
Bronze plan below credit → net premium floored at $000✓
Bronze plan below credit → subsidy capped at plan premium7,0007,000✓
Over-cliff ineligible with actual plan → net = actual plan7,0007,000✓
Over-cliff ineligible, no actual plan → net = benchmark12,00012,000✓
Below-100%-FPL ineligible with actual plan → net = actual plan7,0007,000✓

Pension 15/15

Formula
Annual Pension = Base × (1 + COLA)^years_since_start × taxable_pct (for tax purposes)
Survivor Benefit = Pension × survivor_fraction (50% / 75% / 100%) after primary death
IRC §72 (annuity taxation) • Pension COLA and survivor election terms set by plan document — no single federal statute
Authority: RetIQ Engine v2.0
Features:
• Per-pension COLA (independent rate per pension)
• Survivor benefits: single life, joint & 50/75/100%
• Taxable percentage (military disability, after-tax contributions)
Method: Verify pension income starts at correct age, COLA compounds correctly, survivor benefits pay the correct fraction after primary death, taxable percentage reduces tax liability appropriately. Mortality scenarios test single-life (pension stops) vs joint-and-survivor (pension continues at reduced rate).
TestExpectedActualResult
Pension starts at age 65 with $24,000/yr24,00024,000✓
Pension COLA: $24K at 2% after 5yr ≈ $26,49726,49726,498✓
Taxable 100%: pensionTaxable = pension24,00024,000✓
Taxable 60%: pensionTaxable ≈ 60% of pension14,40014,400✓
Taxable 0%: pensionTaxable = 0 (fully exempt)00✓
Single-life: pension = 0 after pensioner dies00✓
Joint & 50%: survivor gets 50% of pension12,00012,000✓
Joint & 75%: survivor gets 75% of pension18,00018,000✓
Joint & 100%: survivor gets 100% of pension24,00024,000✓
J&50% + 60% taxable: survivor taxable = 50%*60%*24000 = 72007,2007,200✓
Migration shim: legacy pension scalar produces correct income18,00018,000✓
Two primary pensions sum: 12000 + 18000 = 3000030,00030,000✓
Staggered start ages: only first pension active at age 6612,00012,000✓
Staggered start ages: both active at age 6730,00030,000✓
Mixed-owner two pensions: primary 12000 + spouse 8000 = 2000020,00020,000✓

Pension Partial Year 11/11

TestExpectedActualResult
Integer startAge=65, cola=0 → full $24K at age 65 (regression)24,00024,000✓
Integer startAge=65, cola=0 → full $24K at age 70 (regression)24,00024,000✓
startAge=65.5 → 50% prorated ($12K) at age 6512,00012,000✓
startAge=65.5 → full $24K at age 6624,00024,000✓
startAge=65.25 → 75% prorated ($18K) at age 6518,00018,000✓
startAge=65.75 → 25% prorated ($6K) at age 656,0006,000✓
startAge=65.5 → $0 at age 64 (before activation)00✓
Lifetime total: fractional pension = integer total - half-year12,00012,000✓
Spouse startAge=65.5 → 50% prorated ($6K) at spouse age 656,0006,000✓
COLA compounds with fractional penYrs (age 70, startAge 65.5, 2% COLA)26,23726,237✓
taxablePercent applies post-proration (60% of $12K = $7,200)7,2007,200✓

Additional Income 21/21

TestExpectedActualResult
Other income shows in projection at age 6530,00030,000✓
$30K additional income increases lifetime tax11✓
Tax increase is meaningful (>$10K lifetime)11✓
Default window: other income at retirement age30,00030,000✓
Default window: other income at end age30,00030,000✓
Late start (70): no other income at 6500✓
Late start (70): no other income at 6900✓
Late start (70): other income begins at 7030,00030,000✓
Early end (72): other income at 7230,00030,000✓
Early end (72): no other income at 7300✓
Pre-retirement start (60): other income while working30,00030,000✓
Window end (75): last year of other income30,00030,000✓
After window (76): no other income00✓
$100K additional income raises IRMAA surcharges11✓
FillBracket: $50K other income reduces Roth conversion amount10✓
FillBracket: conversion reduced by ~$50K with other income10✓
SmartFill: $50K other income reduces Roth conversion10✓
TargetMAGI: $50K other income reduces Roth conversion10✓
Stuck state: empty incomeStreams + legacy additionalIncome → other = 000✓
Legacy plan: incomeStreams undefined + additionalIncome → other = 50005,0005,000✓
Populated incomeStreams: additionalIncome ignored, other = stream amount12,00012,000✓

Life Insurance 12/12

Formula
Death Benefit: excluded from gross income; deposited to brokerage at death_age
Term: benefit paid only if death_age < expires_age. Permanent: benefit paid at any death_age.
Premiums: deducted from cash flow while policy is active; stop at death or term expiry.
IRC §101(a) (death benefit exclusion from gross income) • IRC §101 →
Sources: IRC §101 (death benefit exclusion from gross income); engine logic: premium collection, term expiry, death benefit payout to taxable account
TestExpectedActualResult
Term premiums active before expiry (age 74)2,4002,400✓
Term premiums stop at expiresAge (age 75)00✓
Term: no benefit if insured outlives term (death 80, expires 75)00✓
Term: benefit pays when death before expiry (death 72, expires 75)500,000500,000✓
Permanent: premiums active before death (age 84)5,0005,000✓
Permanent: premiums stop at death (age 85)00✓
Permanent: benefit pays at death age 85500,000500,000✓
Death benefit does NOT inflate MAGI by benefit amount (IRC §101)11✓
Death benefit increases brokerage balance11✓
No benefit when no death age configured00✓
Multiple policies: both pay out at death750,000750,000✓
Spouse policy: benefit triggers at spouse death300,000300,000✓

REGS 13/13

Schema validation
REGS = { version, federal, ss, irmaa, aca, retirement_accounts }
federal.brackets_mfj.length === 7, federal.brackets_single.length === 7
irmaa.brackets_mfj.length === 6, aca.pct_enhanced.length === 6
Engine regulatory constants structure (v4.0 schema)
Sources: Engine regulatory constants structure (src/engine/07-regulatory.js). Validates that the aliases (TAX_BRACKETS_MFJ, IRMAA_BRACKETS_MFJ, ACA_PCT_ENHANCED) reference the same underlying REGS objects, preventing silent schema drift after KV updates.
TestExpectedActualResult
REGS object exists11✓
REGS.version is 202511✓
REGS has 5 data sections55✓
federal.brackets_mfj has 7 brackets77✓
federal.brackets_single has 7 brackets77✓
irmaa.brackets_mfj has 6 tiers66✓
aca.pct_enhanced has 6 tiers66✓
TAX_BRACKETS_MFJ === REGS.federal.brackets_mfj11✓
IRMAA_BRACKETS_MFJ === REGS.irmaa.brackets_mfj11✓
ACA_PCT_ENHANCED === REGS.aca.pct_enhanced11✓
SS_WAGE_BASE matches REGS184,500184,500✓
CONTRIB_401K matches REGS24,50024,500✓
STD_DEDUCTION matches REGS32,20032,200✓

HSA 26/26

Formula
Contribution limit (2025): $4,300 self / $8,550 family + $1,000 catch-up (55+)
Qualified medical withdrawals: tax-free. Non-medical withdrawals after 65: ordinary income (no penalty).
Contributions: above-the-line deduction, reduce AGI. Growth: tax-free.
IRC §223 • IRS Rev. Proc. 2024-25 • IRS Pub 969
Sources: IRC §223 (HSA rules); IRS Rev. Proc. 2024-25 (2025 limits $4,300/$8,550); IRS Pub 969 (HSA treatment, medical withdrawal fraction, age 65 rule). Engine: src/engine/09-engine.js HSA contribution/growth/withdrawal logic.
TestExpectedActualResult
HSA contribution reduces federal taxable income11✓
HSA contribution does NOT reduce CA state income (added back + earnings taxed)11✓
HSA contribution DOES reduce OR state income11✓
HSA balance grows at configured rate tax-free53,00053,000✓
HSA medical withdrawal at age 60: tax-free00✓
HSA non-medical withdrawal at age 60: taxed + 20% penalty10✓
HSA withdrawal at age 67 (100% medical): tax-free00✓
HSA medical withdrawal at 67: zero MAGI impact00✓
HSA withdrawal at age 67 (50% medical): 50% taxed16,21816,224✓
no receipts and no modeled medical: the whole HSA draw is taxable40,86640,866✓
no receipts and no modeled medical: nothing is qualified00✓
pre-65 non-qualified HSA draw carries the 20% additional tax8,1738,173✓
prior receipts cover the draw: fully qualified30,00030,000✓
prior receipts cover the draw: no taxable income and no penalty11✓
cap disabled: fraction-only legacy behavior (full draw qualified)30,00030,000✓
post-65: non-qualified draw is income with no additional tax11✓
one-time non-medical HSA expense, no receipts: fully taxable (capacity 0)11✓
one-time non-medical HSA expense, receipts cover: fully qualified11✓
one-time MEDICAL HSA expense, no receipts: still qualified00✓
HSA: no RMD generated at any age00✓
Contribution limits enforced: self, age 56 = $5,400 max5,4005,400✓
Contribution limits enforced: family, age 58 = $9,750 max9,7509,750✓
Contributions stop at hsaContributionStopAge11✓
SSDI early Medicare (age 62) stops HSA contributions11✓
ACA MAGI reduced by HSA contribution11✓
Net worth includes HSA balance195,000195,000✓

HSA-65 spouse 3/3

TestExpectedActualResult
Age 60 (spouse 66 on Medicare): contribution = family limit 9,750, NOT 0 and NOT > limit9,7509,750✓
Age 64: family-limit funding continues through the younger spouse (9,750)9,7509,750✓
Age 65 (younger spouse now Medicare-eligible): contribution = 000✓

ACA Medicare spouse 3/3

TestExpectedActualResult
Age 62 (spouse 66 on Medicare): FPL% = 355 (2-person tax family), not 479 (wrong 1-person)355355✓
Age 62: MAGI basis = 75,000 (2-person pricing)75,00075,000✓
Age 62: subsidy retained (75,000 < 400% × 21,150 = 84,600 cliff)11✓

HSA-65 marketplace 5/5

TestExpectedActualResult
Age 65 (spouse 62, ACA): contribution = self limit 5,400, not family 9,750 and not 05,4005,400✓
Age 65: conversion fills the 395% FPL cliff ceiling (AGI = 83,542), NOT collapsed to 083,54283,542✓
Age 65: rothConv = 88,942 (cliff ceiling minus negative pre-conversion MAGI)88,94288,942✓
Age 65: subsidy retained (83,542 < 400% × 21,150 = 84,600 cliff)11✓
Age 68 (spouse 65): contribution stops (both Medicare-entitled)00✓

SS Claiming Optimizer 5/5

TestExpectedActualResult
Returns all 81 claim-age paths (9 primary × 9 spouse)8181✓
Top path = both claim at 70 (delay dominates DRC over long horizon)11✓
(70,70) lifetime SS = 1,740,9601,740,9601,740,960✓
(62,62) lifetime SS = 1,285,200 (early claim −30%)1,285,2001,285,200✓
(70,69) lifetime SS = 1,718,880 (spouse DRC 16% at 69)1,718,8801,718,880✓

SS Optimizer SSDI 7/7

TestExpectedActualResult
Primary SSDI, no spouse: 1 fixed path (not 9)11✓
Primary SSDI fixed path: age 68, flagged primarySSDI11✓
Primary SSDI, spouse not: 9 paths (not 81)99✓
Primary SSDI, spouse not: primary age constant 68 across all paths11✓
Spouse SSDI, primary not: 9 paths, spouse age constant 6811✓
Both on SSDI: 1 path (no choice for either)11✓
Control: no SSDI returns all 81 paths, flags false11✓

Medicare Transition 2/2

TestExpectedActualResult
birthMonth=6 → healthcare ≈ full × 6/12 in transition year6,0006,000✓
birthMonth=6 → medicareCost ≈ full × 6/12 in transition year1,2181,218✓

Catch-Up Static 5/5

TestExpectedActualResult
HSA self year 5, age 60, 3% inflation → $6,1016,1016,101✓
HSA family year 9, age 64, 3% inflation → $12,41712,41712,417✓
HSA year-zero invariance: self age 56 = $5,400 (regression of Test 10)5,4005,400✓
IRA cumulative roth at year 5 (age 50 vs 44, 3% inflation) → delta $6,6006,6006,600✓
IRA year-zero invariance: roth[0] delta = $1,100 (age 50 vs 44)1,1001,100✓

Inherited IRA 16/16

Formula (post-2019 SECURE 10-year rule)
Forced annual distribution = remaining_balance ÷ years_remaining_in_window
10-year clock starts year of original owner's death; balance must reach zero by year 10.
Excluded from Roth conversion and Tax-Aware Drawdown. Traditional: ordinary income. Roth: tax-free.
SECURE Act 2019 • SECURE 2.0 Act §401 • IRS Pub 590-B
Sources: SECURE Act 2019 — 10-year distribution requirement for non-spouse beneficiaries; IRS Pub 590-B (distribution rules); SECURE 2.0 §401 (final regulations 2024). Engine: pooled post-2019 distribution logic in src/engine/09-engine.js.
TestExpectedActualResult
$0 balance returns null (truthy check)11✓
$1M at 0% return, 22% bracket → net ~$780K780,000780,000✓
Balance fully depleted after 10 years00✓
Year 1 dist at 0% return = balance/10100,000100,000✓
Higher bracket (24%) yields less net than 22%11✓
7% growth: total distributed > initial balance11✓
Post-2019: $100K, 10 yrs left → $10,00010,00010,000✓
Post-2019: $100K, 1 yr left → full balance100,000100,000✓
Post-2019: $0 balance → $000✓
Post-2019: $250K, 5 yrs left → $50,00050,00050,000✓
Pre-2020 stretch: age 50 yr 1, $1M → $27,62427,624.30927,624✓
Pre-2020 stretch: age 50 yr 5 (divisor 32.2), $1M → $31,05631,055.90131,056✓
Pre-2020 stretch: age 65 yr 1, $500K → $21,83421,834.06121,834✓
Pre-2020 stretch: divisor floor at 1.0 (divisor would go negative)500,000500,000✓
Distribution never exceeds balance1,0001,000✓
Unknown regime returns 000✓

Inherited Acct 19/19

Formula (inherited account integration with plan)
Forced distributions flow through ordinary income → SS taxation, MAGI, IRMAA, NIIT calculations
Balance continues to grow at nominal return between distributions.
Explicitly excluded from Roth conversion math (not eligible for conversion under SECURE).
SECURE Act • IRC §86 (SS taxation) • 42 U.S.C. §1395r (IRMAA)
Sources: SECURE Act (10-year rule); IRC §86 (SS taxation interaction); 42 U.S.C. §1395r (IRMAA MAGI). Engine: inherited distributions integrated with tax and MAGI pipelines in src/engine/09-engine.js.
TestExpectedActualResult
Year 0: regular pretax balance = 500K (untouched)500,000500,000✓
Year 0: inherited pretax = 450K after first distribution450,000450,000✓
Year 0: inherited distribution = 50K50,00050,000✓
Year 9 (age 69): inherited balance = 0 (fully distributed)00✓
Year 10 (age 70): inherited dist = 0 (nothing left)00✓
Net worth = sum of all buckets including inherited1,266,1801,266,180✓
Inherited pretax distributions increase lifetime taxes11✓
Inherited distributions preserve pretax (less drawdown needed)11✓
Roth conversion pulls from pretax only (not inherited)460,000460,000✓
Inherited balance unchanged by Roth conversion (still 450K)450,000450,000✓
No standard RMD when only inherited pretax exists00✓
Inherited Roth has lower lifetime tax than inherited traditional11✓
Inherited Roth depleted by year 900✓
Future inh: no distribution before inheritance (year=CY)00✓
Future inh: first share in year of death (year=IY) = balance/11 = 100K100,000100,000✓
Future inh: second-year share at IY+1 = 100K100,000100,000✓
Future inh: deadline (IY+10) — final 100K share drains balance to 011✓
Future inh: no distribution after deadline (DL+1)00✓
Future inh: total distributed == starting balance (1,100,000)1,100,0001,100,000✓

Inherited Stretch 8/8

Formula (pre-2020 grandfathered stretch + EDB exceptions)
RMD = balance ÷ Single Life Expectancy divisor (IRS Pub 590-B Table I)
Subtract-1 method: divisoryear_n = initial_divisor − (n − 1)
Four regimes: pre-2020 stretch, EDB-disabled, EDB-chronically-ill, EDB-not-more-than-10-yr-younger
SECURE Act §401(a)(9)(H)(ii) (EDB exceptions) • IRC §72(m)(7) • IRC §7702B(c)(2) • IRS Pub 590-B Table I
Sources: SECURE Act §401(a)(9)(H)(ii) (Eligible Designated Beneficiary exceptions to 10-year rule); IRC §72(m)(7) (disabled); IRC §7702B(c)(2) (chronically ill); IRS Pub 590-B Table I (Single Life Expectancy divisors). Engine: per-account stretch IRA logic in src/engine/09-engine.js.
TestExpectedActualResult
Yr 1: age 55, $500K → ~$15,82315,82315,823✓
Stretch balance adds to netWorth yr 111✓
Inherited Roth stretch: tax-free distributions never raise tax11✓
Traditional stretch yr 1 inheritedDist recorded12,65812,658✓
No regime field: v5.4 pooled behavior preserved9,0919,091✓
Mixed stretch + post-2019: both distribute29,25229,252✓
Stretch balance decreases year-over-year at 0% return11✓
EDB Disabled: yr 1 uses stretch math (100K / 31.6 age 55)3,1653,165✓

Inherited Per-Account 3/3

TestExpectedActualResult
Two post-2019 with different inheritedYear: independent schedules25,75825,758✓
Earlier-inherited account depletes first11✓
Per-account distributionTiming respected (Roth)9,0919,091✓

CG Harvesting 6/6

Formula (0% capital gains harvesting)
If ordinary_income + projected_gains ≤ 0% CG threshold (2025: $96,700 MFJ / $48,350 single):
  harvest = min(remaining_room, unrealized_gains)
Basis steps up by harvested amount. IRMAA-aware: capped at next Medicare surcharge threshold.
IRC §1(h) (CG brackets) • IRS Rev. Proc. 2024-40 (2025 thresholds)
Sources: IRC §1(h) (preferential capital gains brackets); IRS Rev. Proc. 2024-40 (2025 0% CG threshold). Engine: CG harvesting logic in src/engine/09-engine.js, IRMAA-aware via same logic as Roth Smart Fill.
TestExpectedActualResult
Disabled: zero harvested11✓
Enabled with basis: harvests in low-income year11✓
No unrealized gains: zero harvested10✓
Harvested amount ≤ unrealized gains11✓
SS-feedback: harvesting raises fedTax via SS provisional income11✓
SS-feedback: Fed Brkt not stale-dash in taxable harvest years00✓

F-217 MAGI Components 3/3

TestExpectedActualResult
earnedFederalTaxable exposed on year-row11✓
taxableWd exposed on year-row11✓
taxableRothConv exposed on year-row11✓

TLH 4/4

Formula (tax-loss harvesting)
Realized losses first offset realized capital gains dollar-for-dollar.
Excess losses offset up to $3,000/year of ordinary income (IRC §1211(b)).
Remaining unused losses carry forward indefinitely (IRC §1212(b)).
IRC §1211 • IRC §1212 • IRS Schedule D
Sources: IRC §1211 (loss limitation); IRC §1212(b) (capital loss carryover); IRS Schedule D (Form 1040). Engine: TLH offset and carry-forward in src/engine/09-engine.js.
TestExpectedActualResult
Disabled: zero offset11✓
$5K losses: offset applied11✓
$3K ordinary offset when minimal CG11✓
Carry-forward accumulates across years11✓

Itemized Deductions 4/4

Formula (itemized vs standard)
Itemized total = SALT (OBBB cap: $40K 2025-2029 phasing down above $500K MAGI, $10K from 2030) + medical above 7.5% AGI + non-QCD charity (≤60% AGI)
Engine uses max(standard + senior + OBBB, itemized) each year.
Senior additions and OBBB senior deduction apply to the standard deduction only.
IRC §63 • IRC §164(b)(6) as amended by OBBB Act P.L. 119-21 • IRC §213 (medical) • IRC §170 (charity) • OBBB Act §102
Sources: IRC §63 (standard vs itemized); IRC §164(b)(6) as amended by OBBB Act P.L. 119-21 (2025); IRC §213 (medical, 7.5% AGI floor); IRC §170 (charitable, 60% AGI cap). Engine: deduction comparison logic in src/engine/09-engine.js.
TestExpectedActualResult
Disabled: uses standard deduction11✓
SALT $10K + charity $25K beats standard for single filer11✓
Low SALT, no charity: standard wins for MFJ senior11✓
Medical 7.5% AGI floor applied correctly11✓

SALT Cap (OBBB) 6/6

Formula (OBBB SALT cap, year-aware)
2025 base cap = $40,000 (MFJ/Single). Grows 1% per year 2026-2029.
Phase-down: cap reduced by 30% of MAGI excess above $500,000 threshold (also grows 1%/yr).
Floor: cap cannot drop below $10,000 in any year. Reverts to $10,000 flat in 2030+.
OBBB Act P.L. 119-21 (2025) amending IRC §164(b)(6)
Sources: One Big Beautiful Bill Act, P.L. 119-21, enacted July 4, 2025, amending IRC §164(b)(6). Effective 2025-2029 with annual growth, phase-down above $500K MAGI, floor $10K; reverts to TCJA $10K baseline in 2030. Engine: saltCapFor(year, magi) helper in src/engine/07-regulatory.js, called from three sites in src/engine/09-engine.js (main itemized flow, Smart Fill bracket-room calc, and Tax-Aware Drawdown scenario).
TestExpectedActualResult
2025 base cap $40,000 MFJ/Single40,00040,000✓
2026 cap with 1% growth: $40,40040,40040,400✓
2030 reversion to $10,000 TCJA baseline10,00010,000✓
2025 phase-down at MAGI $550K: cap $25,00025,00025,000✓
2025 phase-down floor at MAGI $700K: cap $10,00010,00010,000✓
2026 phase-down threshold also grows: MAGI $505K → full cap40,40040,400✓

DAF Bunching 4/4

Formula (charitable bunching)
Bunch N years of charitable giving into one year (typically 2–3).
In bunch year: itemized deduction dominates standard; claim full bunched amount.
In off years: standard deduction applies; charitable = 0. Total giving unchanged.
IRC §170 (charitable contributions) • DAF regulations (IRS Notice 2006-109)
Sources: IRC §170 (charitable contribution deduction); IRS Notice 2006-109 (DAF rules); IRC §63 (standard vs itemized comparison). Engine: bunching logic in src/engine/09-engine.js routes charitable giving by year.
TestExpectedActualResult
Disabled: charity same every year11✓
Every-2yr: bunch year has 2x charity11✓
Every-2yr: off year has zero charity11✓
Lifetime charity total unchanged vs no bunching11✓

QLAC 21/21

Formula (Qualified Longevity Annuity Contract)
Purchase from pre-tax account; excluded from RMD base until payments begin.
Max purchase (2026): $210,000 (SECURE 2.0 §202; IRS Notice 2025-67).
Payments begin by age 85; taxed as ordinary income when received.
SECURE 2.0 Act §202 • Treas. Reg. §1.401(a)(9)-6
Sources: SECURE 2.0 Act §202 ($200K cap effective 2024, inflation-adjusted to $210K for 2026 per IRS Notice 2025-67); Treas. Reg. §1.401(a)(9)-6 (QLAC exclusion from RMD base). Engine: QLAC purchase and payment logic in src/engine/09-engine.js.
TestExpectedActualResult
Disabled: no QLAC payout or balance11✓
Purchase reduces pre-tax balance at purchase age11✓
RMD lower with QLAC than without11✓
Payout starts at payoutStartAge as ordinary income11✓
2026 QLAC limit: $210,000210,000210,000✓
Engine caps purchase at QLAC_LIMIT when user requests more210,000210,000✓
Unfunded QLAC pays nothing (no pre-tax pool at purchase age)00✓
Balance-constrained purchase caps the premium at the available pre-tax balance100,000100,000✓
Balance-constrained purchase scales the annuity to the premium actually paid9,0009,000✓
Pre-existing contract (purchase age before plan start) still pays15,00015,000✓
Report Card offers no QLAC when the pre-tax pool is empty at the purchase age00✓
Guard precondition: the fixture really does drain the pool before age 7000✓
Dual disabled: per-owner balances both 011✓
Primary-only QLAC: primary balance = 200K, spouse balance = 0200,000200,000✓
Primary-only QLAC: spouse balance unchanged00✓
Dual QLAC: per-owner balances both = 200K at purchase age400,000400,000✓
Dual QLAC: qlacBalance backward-compat = primary + spouse00✓
Dual QLAC: netWorth includes both owners QLAC balance1,614,9241,614,924✓
Spouse-only QLAC: payout active when spouse reaches payoutStartAge11✓
F-289: spouse-only QLAC pension equals no-QLAC pension at payout age00✓
F-289: spouse-only QLAC pensionTaxable equals no-QLAC pensionTaxable at payout age00✓

F-273 3/3

TestExpectedActualResult
Pre-59.5 pre-tax discretionary draws incur 10% early withdrawal penalty11✓
72(t) penalty = 10% of discretionary pre-tax draw (within rounding)00✓
Post-59.5 pre-tax draws incur no 72(t) penalty11✓

F-288 5/5

TestExpectedActualResult
MA gov pension < MA private pension at same income11✓
NY gov pension < NY private pension at same income11✓
HI gov pension < HI private pension at same income11✓
OR gov pension (within $6,250 cap) < OR private pension at same income11✓
OR cap binding: $12K gov > $6K gov (excess above $6,250 taxed)10✓

Retire Year 11/11

TestExpectedActualResult
Not-passed birthday, retireMonth before birthMonth: partial in yr060,00060,000✓
Not-passed birthday, retireMonth before birthMonth: zero in yr100✓
Passed birthday, retireMonth after birthMonth: partial in yr060,00060,000✓
Passed birthday, retireMonth after birthMonth: zero in yr100✓
Passed birthday, future retireMonth before birthMonth: full in yr5120,000120,000✓
Passed birthday, future retireMonth before birthMonth: partial in yr610,00010,000✓
Passed birthday, future retireMonth before birthMonth: zero in yr700✓
Not-passed birthday, retireMonth after birthMonth: partial in yr0100,000100,000✓
Not-passed birthday, retireMonth after birthMonth: zero in yr100✓
Spouse not-passed birthday, retireMonth before birthMonth: spouse partial in yr060,00060,000✓
Spouse not-passed birthday, retireMonth before birthMonth: spouse zero in yr100✓

Roth Conversion Gate 12/12

Formula
preTax = preTaxConvertible + Σ(preTaxGatedTranches[].balance)
Conversion cap = preTaxConvertible   // never the full preTax pool
At year start: for each tranche, if age ≥ unlockAge then convertible ←← tranche (and tranche removed)
Unlock age (primary frame): primaryRetires→retirementAge; spouseRetires→spouseRetirementAge + (currentAge - spouseAge); specificAge→conversionGateAge
Engine: src/engine/09-engine.js (v5.7). Per-account field: account.conversionGate. Spouse-gated coerces to always when spouseEnabled is false.
Sources: Engine logic in src/engine/09-engine.js — split-pool model ensures employer plans without in-service in-plan Roth rollover (IRR) are excluded from conversions until the account's gate opens. RMDs, QCDs, QLAC, and regular withdrawals continue to act on the full pre-tax pool (reducing gated tranches proportionally) — the gate restricts conversions only. Backward compatible: absent conversionGate defaults to 'always'.
TestExpectedActualResult
Before primary retires: conversion caps at IRA (convertible) balance only50,00050,000✓
After convertible pool drained, conversions stop even with gated balance remaining00✓
At primaryRetires age, gated 401k unlocks and permits conversion again11✓
Without gate, conversions continue at age 62 from full pretax pool100,000100,000✓
Absent conversionGate field = always convertible (backward compat)100,000100,000✓
Spouse-gated account: still locked when spouse < spouseRetirementAge00✓
Spouse-gated account: unlocks when spouse reaches spouseRetirementAge11✓
spouseRetires gate with spouseEnabled=false coerces to always-convertible50,00050,000✓
specificAge gate: still locked before the specified age00✓
specificAge gate: unlocks at the specified primary age11✓
Account identity holds with gated tranches in pool00✓
RMDs draw proportionally from full pool including gated tranches11✓

F-215 Mixed-Retirement Spouse Deferral 5/5

TestExpectedActualResult
Fix path ordinary AGI (agi - capGains) matches gate path at Y3 (spouse age 60, working)145,250145,251✓
rothConv identical across paths — LTCG does not crowd out an ordinary bracket fill71,16971,169✓
F-249a: the non-retired shortfall sale realizes gain (capGains_B > capGains_A)11✓
Fix path earned matches gate path earned at Y397,63197,631✓
Pre-retirement year Y1: both paths produce same AGI (gate runs in both)73,65573,655✓

Non-Retired Conversion-Sale Gain (F-239) 5/5

TestExpectedActualResult
A: conversionSaleGain === round(sold x 0.40) (taxable src)1,0481,048✓
B: capGains === conversionSaleGain (same-year fold)1,0481,048✓
D: cgTax === 0 (IRC §1(h)(1)(B) 0% LTCG rate)00✓
C1: cash-overflow conversionSaleGain === round(sold x 0.40)648648✓
C1b: cash-overflow conversionSaleGain > 011✓

Dual-Owner RMD 9/9

TestExpectedActualResult
Spouse RMD fires at spAge 73 (projection yr 4)11✓
Primary RMD = 0 at primary age 69 (below RMD start)00✓
Total rmd = rmdSpouse when only spouse at RMD age22,48722,487✓
Spouse RMD amount ≈ balance ÷ 26.5 at age 7322,48722,487✓
Primary RMD fires at primary age 75 (projection yr 10)11✓
Spouse RMD still firing at spAge 79 (yr 10)11✓
Total rmd = rmdPrimary + rmdSpouse when both at RMD age61,81461,814✓
Invariant: preTax = preTaxPrimary + preTaxSpouse every year11✓
Opt-out: no owner:spouse account → preTaxSpouse stays 011✓

Dual-Owner Growth 2/2

TestExpectedActualResult
Primary bucket grows at 5% (per-owner rate)638,141638,141✓
Spouse bucket grows at 7% (per-owner rate)701,276701,276✓

Dual-Owner Rollover 2/2

TestExpectedActualResult
Primary death → spouse absorbs pool (preTaxPrimary = 0)00✓
Spouse death → primary absorbs pool (preTaxSpouse = 0)00✓

Debt Amortization 11/11

TestExpectedActualResult
Year 1 interest: $200K @ 6% / $1,199.10/mo11,93311,933✓
Year 1 principal: $200K @ 6% / $1,199.10/mo2,4562,456✓
Year 1 ending balance: $200K @ 6% / $1,199.10/mo197,544197,544✓
Year 1 total paid: 12 × $1,199.10/mo14,38914,389✓
$200K @ 6% / $1,199.10/mo pays off in 30 years3031✓
Zero-rate loan: $12K @ 0% / $1,000/mo — no interest00✓
Zero-rate loan: $12K @ 0% / $1,000/mo — paid off in year 111✓
Zero balance: no payment, no interest00✓
Payment < monthly interest: balance unchanged200,000200,000✓
Engine integration: year-1 interest matches helper11,93311,933✓
Engine integration: year-1 payments matches helper14,38914,389✓

Relocation Scenario 7/7

TestExpectedActualResult
No relocation params: CA tax non-zero pre-retirement (age 60)11✓
Move at 70: pre-move year (age 65) uses original state11✓
Move at 70: post-move year (age 75) uses target state00✓
Move at 70: cutover year (age 70 itself) uses target state00✓
Move at currentAge: target state for entire projection00✓
Move beyond endAge: original state for entire projection11✓
Pre-retirement move at 55: working year (age 60) uses target state00✓

Multi-relocation 6/6

TestExpectedActualResult
Age 69 stays in the origin state11✓
Age 70 switches to the first destination11✓
Age 79 stays in the first destination11✓
Age 80 switches to the second destination11✓
Age 85 stays in the second destination11✓
CA → WA → CA ends in CA11✓

Roth Multi-Phase 6/6

TestExpectedActualResult
Empty phases array: identical to absent phases (regression)68,43268,432✓
Single phase = single-target (full-window 12% phase)1,2351,235✓
Two-phase: 24% phase produces larger conv than 12% phase11✓
Gap year (age 68): zero conversion00✓
Phase boundary: endAge is inclusive (age 67 is in phase 1)11✓
Outer rc.startAge gates phases: age 60 outside outer window → 000✓

Account Canon 13/13

TestExpectedActualResult
Legacy type:cash → type:taxable11✓
Legacy type:cash → subtype:cash11✓
Legacy type:cash preserves balance65,00065,000✓
Legacy type:cash preserves returnRate4.54.5✓
Canonical taxable (no subtype) unchanged11✓
Type pretax passes through unchanged11✓
Type roth passes through unchanged11✓
Type hsa passes through unchanged11✓
Type inherited-pretax passes through unchanged11✓
Type inherited-roth passes through unchanged11✓
canonicalizeAccount is idempotent11✓
Legacy type:cash without returnRate → default 444✓
Legacy cash account → engine cash bucket > 0 in year 011✓

Optimizer Blend 8/8

TestExpectedActualResult
Pre-tax overridden to nominalReturn77✓
Roth overridden to nominalReturn77✓
Taxable-investment overridden77✓
Cash subtype keeps its rate4.54.5✓
HSA overridden to nominalReturn77✓
Input array not mutated55✓
blendedAccountsForOptimizer is idempotent77✓
Integration: real Roth 10% > blended Roth 7% by year 3011✓

Divergence 14/14

TestExpectedActualResult
pre-tax 5% vs Roth 9% → shouldWarn:true11✓
pre-tax 5% vs Roth 9% → divergence == 444✓
pre-tax 6% vs Roth 7.5% (1.5pp) → shouldWarn:false00✓
Exactly 2.0pp → shouldWarn:true11✓
Roth < $10K → shouldWarn:false00✓
No Roth → shouldWarn:false00✓
Multi-account pre-tax: weighted avg 5.4%5.45.4✓
Multi-account divergence 2.6pp → shouldWarn:true11✓
Equal pre-tax/Roth but brokerage +2pp → shouldWarn:true11✓
maxSpread reflects the taxable bucket (2.0pp)22✓
legacy divergence stays pre-tax-vs-Roth (0.0 when equal)00✓
cash is excluded from the taxable bucket (3% cash does not widen the spread)77✓
Roth 8% vs brokerage 5% → shouldWarn:true (conversions flattered)11✓
maxSpread 3.0pp when the Roth out-earns the brokerage33✓

Tax Payment v6.8 11/11

TestExpectedActualResult
Single retiree FL: tax > 0 in year 111✓
Single retiree FL: withdrawal exceeds raw expense gap11✓
Couple MFJ GA: state tax > 0 with $50K taxable pension11✓
Couple MFJ GA: withdrawal covers fed+state tax bill11✓
Stable-income retiree: iteration converges from year 1+ (warm-start)11✓
Year record exposes taxConvergenceFlag as boolean11✓
NIIT triggers for high-MAGI single via iteration11✓
SS taxation propagates through iteration (couple MFJ)11✓
wdOrder honored: roth untouched when earlier sources suffice11✓
Pre-retirement: taxConvergenceFlag stays false (no iteration)11✓
Pre-retirement: withdrawal stays 0 (no fillGap path)11✓

Roth 5-year 10/10

TestExpectedActualResult
Age 58 (yr 3 post-conv): $5K Roth wd → ~$500 penalty (iter cascade adds slight overshoot)530556✓
Age 60: Roth wd → no penalty (age >59.5 exemption)00✓
Age 61: Roth wd → no penalty (age + 5-yr both clear)00✓
Two conversions, $15K wd at age 54 → penalty oldest-first ≈ $1500-$17001,6001,668✓
Age 56 ($5K wd, 1yr post-conv) → ~$500 penalty530555✓
Age 60 ($10K wd, 5yr post-conv but >59.5) → no penalty00✓
No conversions, age 55 Roth wd → no penalty (empty tranches)00✓
F-249b: one-time Roth expense penalty enters the SAME-year fedTax2,0002,000✓
F-249b: penalty recorded on the expense-year row2,0002,000✓
F-249b: nothing deferred into the following year00✓

F-252 3/3

TestExpectedActualResult
Year 1: pooled blend and per-account growth agree (they must)212,000212,000✓
Year 2: accounts grow at their OWN rates, not a frozen blend224,800224,800✓
No distribution contaminates the growth measurement00✓

Inherited Roth Backload 7/7

TestExpectedActualResult
Default even-spread: year 2026 has non-zero inherited Roth dist11✓
Back-load: zero distribution in 2026 (year 4 of 10)11✓
Back-load: zero distribution in 2032 (year 9 of 10)11✓
Back-load: full balance distributed in deadline year 203311✓
Back-load total distributions > even-spread total (extra compounding)11✓
Back-load: inherited Roth balance = 0 after year 1011✓
Back-load: lifetime federal tax not increased by inherited Roth timing11✓

Brokerage Dividend Yield 9/9

TestExpectedActualResult
4% × $1M brokerage = $40K dividend income (year 0)40,00040,000✓
$40K brokerage QD reflected in MAGI11✓
Ordinary dividend yield produces higher fed tax than qualified11✓
Account without dividendYield: zero dividend income00✓
Multi-account: dividends sum correctly40,00040,000✓
Mixed qualified + ordinary: brokerageDividendIncome sums both40,00040,000✓
Untouched brokerage: dividend income grows with balance over 10 years11✓
Drained brokerage: dividend income shrinks as balance is withdrawn11✓
No brokerage accounts: zero div income, no NaN11✓

QD Pipeline Regression 8/8

TestExpectedActualResult
$50K QD stream raises lifetime fed tax11✓
$50K QD stream reflected in MAGI11✓
High-MAGI single filer: NIIT > 0 on QD11✓
$30K QD alone < $48,350 single 0% LTCG threshold → $0 fed tax00✓
F-037: high-MAGI single with non-QD stream pays NIIT > 011✓
F-037: nonqualifiedDividends field exposed in year output250,000250,000✓
F-037: high-MAGI single with ordinary brokerage div pays NIIT > 011✓
F-037: $250K QD and $250K non-QD produce equal NIIT (same NII base)2,9212,921✓

Monte Carlo Envelopes 6/6

TestExpectedActualResult
p10/median/p90 returned and equal-length11✓
p10 <= median <= p90 at EVERY age (the fan-chart property)11✓
Envelope entry has age/year/retired/netWorth fields11✓
Final-year p10 < final-year p90 (non-degenerate)11✓
Final-year envelope spread > year-0 spread (variance grows)11✓
successRate is integer 0-10011✓

yearEndAge 6/6

TestExpectedActualResult
born March 1961, projYear 2026 → year-end age 656565✓
born December 1961, projYear 2026 → year-end age 656565✓
born January 1962, projYear 2026 → year-end age 646464✓
born March 1961, projYear 2025 → year-end age 646464✓
born March 1961, projYear 2030 → year-end age 696969✓
born 1961, projYear 1961 → age 0 (newborn)00✓

OBBB senior deduction 6/6

TestExpectedActualResult
F-213: obbbSeniorDeductionFor returns 6000 in 20256,0006,000✓
F-213: obbbSeniorDeductionFor returns 6000 in 20266,0006,000✓
F-213: obbbSeniorDeductionFor returns 6000 in 20276,0006,000✓
F-213: obbbSeniorDeductionFor returns 6000 in 20286,0006,000✓
F-213: obbbSeniorDeductionFor returns 0 in 2024 (pre-window)00✓
F-213: obbbSeniorDeductionFor returns 0 in 2029 (post-sunset)00✓

OBBB phase-out threshold 4/4

TestExpectedActualResult
F-213: obbbPhaseoutThresholdFor MFJ returns 150000 in 2025150,000150,000✓
F-213: obbbPhaseoutThresholdFor MFJ returns 150000 in 2026150,000150,000✓
F-213: obbbPhaseoutThresholdFor MFJ returns 150000 in 2027150,000150,000✓
F-213: obbbPhaseoutThresholdFor MFJ returns 150000 in 2028150,000150,000✓

F-214 401(k) AGI 4/4

TestExpectedActualResult
Trad-only: AGI = $100k+$80k − $23.5k(Trad) − $15k(Trad) = $141,500141,500141,500✓
Roth-only: AGI = $100k+$80k = $180,000 (no Box-1 reduction)180,000180,000✓
Mixed P-Roth/S-Trad: AGI = $100k + ($80k − $15k) = $165,000165,000165,000✓
Mixed P-Trad/S-Roth: AGI = ($100k − $23.5k) + $80k = $156,500156,500156,500✓

v6.47 RMD 17/17

TestExpectedActualResult
Legacy default: rmd > 0 in age-74 row11✓
Legacy default: rmdReinvested > 0 (cash funds gap, RMD reinvests)11✓
Legacy default: withdrawalForSpending + rmdReinvested === withdrawal127,565127,565✓
Legacy default: rmdReinvested ≈ rmd (entire RMD reinvested when cash funds gap)78,43178,431✓
spendFirst Case A (RMD < gap): rmdReinvested === 000✓
spendFirst Case A: withdrawalForSpending + rmdReinvested === withdrawal42,43542,435✓
spendFirst Case A: withdrawalForSpending >= rmd (RMD fully went to spending)11✓
spendFirst Case B (RMD > gap): rmdReinvested > 0 AND < rmd11✓
spendFirst Case B: withdrawalForSpending + rmdReinvested === withdrawal78,43178,431✓
spendFirst Case B: rmdReinvested === rmd − (gap-from-rmd)29,29729,297✓
cash dest: cash[cash-dest] − cash[brok-dest] === rmdReinvested11✓
cash dest: taxableInv[brok-dest] − taxableInv[cash-dest] === rmdReinvested11✓
cash dest: total cash + brokerage equals brokerage-dest equivalent11✓
invariant: withdrawalForSpending + rmdReinvested === withdrawal in EVERY year11✓
invariant: no violating year in projection (loop diagnostic)-1-1✓
migration equivalence: missing rmdHandling === explicit legacy default (withdrawal[0])127,565127,565✓
migration equivalence: missing rmdHandling === explicit legacy default (rmdReinvested[0])78,43178,431✓

Per-Spouse HC 16/16

TestExpectedActualResult
ACA primary older: healthcare > 0 at primary age 67 (spouse 62)11✓
ACA primary older: healthcarePhase = split at primary 6711✓
ACA primary older: healthcarePhase = couple at primary 6311✓
ACA primary older: healthcare = 0 at primary 71 (both on Medicare)00✓
ACA primary older: enrollees = 2 at primary 63, 1 at primary 6711✓
ACA split benchmark < couple benchmark11✓
ACA spouse older: healthcare > 0 at primary 62 (split phase)11✓
ACA spouse older: healthcarePhase = couple at primary 5811✓
Same-age couple regression: healthcare = 0 at 66 (both on Medicare at 65)00✓
Single filer regression: healthcare = 0 at 6600✓
SSDI primary: healthcare > 0 at primary 60 (spouse still pre-Medicare)11✓
SSDI primary: healthcarePhase = split at primary 6011✓
Retiree plan split: cost at 67 < cost at 63 (single < couple)11✓
Retiree plan fallback ratio: split cost ≈ annualCost × 0.5 × inflation9,8509,850✓
spouse_plan: primary on Medicare + spouse still working → $000✓
survivor: healthcare active for younger spouse before their Medicare11✓

F-225 per-source W/D 204/204

TestExpectedActualResult
Clarks age 64: per-source sum == withdrawal00✓
Clarks age 64: wdPretaxRMD == rmd - qcd00✓
Clarks age 65: per-source sum == withdrawal19,80719,807✓
Clarks age 65: wdPretaxRMD == rmd - qcd00✓
Clarks age 66: per-source sum == withdrawal21,17421,174✓
Clarks age 66: wdPretaxRMD == rmd - qcd00✓
Clarks age 67: per-source sum == withdrawal59,94659,946✓
Clarks age 67: wdPretaxRMD == rmd - qcd00✓
Clarks age 68: per-source sum == withdrawal65,16665,166✓
Clarks age 68: wdPretaxRMD == rmd - qcd00✓
Clarks age 69: per-source sum == withdrawal42,53842,538✓
Clarks age 69: wdPretaxRMD == rmd - qcd00✓
Clarks age 70: per-source sum == withdrawal44,85744,857✓
Clarks age 70: wdPretaxRMD == rmd - qcd00✓
Clarks age 71: per-source sum == withdrawal47,25447,254✓
Clarks age 71: wdPretaxRMD == rmd - qcd00✓
Clarks age 72: per-source sum == withdrawal49,75849,758✓
Clarks age 72: wdPretaxRMD == rmd - qcd00✓
Clarks age 73: per-source sum == withdrawal52,33752,337✓
Clarks age 73: wdPretaxRMD == rmd - qcd00✓
Clarks age 74: per-source sum == withdrawal55,04055,040✓
Clarks age 74: wdPretaxRMD == rmd - qcd00✓
Clarks age 75: per-source sum == withdrawal57,84857,848✓
Clarks age 75: wdPretaxRMD == rmd - qcd52,21852,218✓
Clarks age 76: per-source sum == withdrawal60,75060,750✓
Clarks age 76: wdPretaxRMD == rmd - qcd55,38455,384✓
Clarks age 77: per-source sum == withdrawal63,78763,787✓
Clarks age 77: wdPretaxRMD == rmd - qcd58,49258,492✓
Clarks age 78: per-source sum == withdrawal66,93666,936✓
Clarks age 78: wdPretaxRMD == rmd - qcd62,04562,045✓
Clarks age 79: per-source sum == withdrawal70,20170,201✓
Clarks age 79: wdPretaxRMD == rmd - qcd65,82565,825✓
Clarks age 80: per-source sum == withdrawal73,60873,608✓
Clarks age 80: wdPretaxRMD == rmd - qcd69,85369,853✓
Clarks age 81: per-source sum == withdrawal76,85676,856✓
Clarks age 81: wdPretaxRMD == rmd - qcd73,76573,765✓
Clarks age 82: per-source sum == withdrawal80,18280,182✓
Clarks age 82: wdPretaxRMD == rmd - qcd78,32378,323✓
Clarks age 83: per-source sum == withdrawal83,64983,649✓
Clarks age 83: wdPretaxRMD == rmd - qcd82,74682,746✓
Clarks age 84: per-source sum == withdrawal87,95487,954✓
Clarks age 84: wdPretaxRMD == rmd - qcd87,95487,954✓
Clarks age 85: per-source sum == withdrawal93,60593,605✓
Clarks age 85: wdPretaxRMD == rmd - qcd92,93492,934✓
Clarks age 86: per-source sum == withdrawal100,700100,700✓
Clarks age 86: wdPretaxRMD == rmd - qcd98,13198,131✓
Clarks age 87: per-source sum == withdrawal109,386109,386✓
Clarks age 87: wdPretaxRMD == rmd - qcd103,542103,542✓
Clarks age 88: per-source sum == withdrawal119,022119,022✓
Clarks age 88: wdPretaxRMD == rmd - qcd108,364108,364✓
Clarks age 89: per-source sum == withdrawal130,576130,576✓
Clarks age 89: wdPretaxRMD == rmd - qcd114,151114,151✓
Clarks age 90: per-source sum == withdrawal143,101143,101✓
Clarks age 90: wdPretaxRMD == rmd - qcd119,139119,139✓
Clarks age 91: per-source sum == withdrawal156,635156,635✓
Clarks age 91: wdPretaxRMD == rmd - qcd124,153124,153✓
Clarks age 92: per-source sum == withdrawal171,075171,075✓
Clarks age 92: wdPretaxRMD == rmd - qcd129,153129,153✓
Clarks age 93: per-source sum == withdrawal186,250186,250✓
Clarks age 93: wdPretaxRMD == rmd - qcd134,089134,089✓
Clarks age 94: per-source sum == withdrawal200,453200,453✓
Clarks age 94: wdPretaxRMD == rmd - qcd137,434137,434✓
Clarks age 95: per-source sum == withdrawal213,238213,238✓
Clarks age 95: wdPretaxRMD == rmd - qcd140,446140,446✓
Lees age 59: per-source sum == withdrawal00✓
Lees age 59: wdPretaxRMD == rmd - qcd00✓
Lees age 60: per-source sum == withdrawal00✓
Lees age 60: wdPretaxRMD == rmd - qcd00✓
Lees age 61: per-source sum == withdrawal00✓
Lees age 61: wdPretaxRMD == rmd - qcd00✓
Lees age 62: per-source sum == withdrawal21,42321,423✓
Lees age 62: wdPretaxRMD == rmd - qcd00✓
Lees age 63: per-source sum == withdrawal23,71123,711✓
Lees age 63: wdPretaxRMD == rmd - qcd00✓
Lees age 64: per-source sum == withdrawal117,063117,063✓
Lees age 64: wdPretaxRMD == rmd - qcd00✓
Lees age 65: per-source sum == withdrawal122,172122,172✓
Lees age 65: wdPretaxRMD == rmd - qcd00✓
Lees age 66: per-source sum == withdrawal119,680119,680✓
Lees age 66: wdPretaxRMD == rmd - qcd00✓
Lees age 67: per-source sum == withdrawal124,833124,833✓
Lees age 67: wdPretaxRMD == rmd - qcd00✓
Lees age 68: per-source sum == withdrawal140,079140,079✓
Lees age 68: wdPretaxRMD == rmd - qcd00✓
Lees age 69: per-source sum == withdrawal111,107111,107✓
Lees age 69: wdPretaxRMD == rmd - qcd00✓
Lees age 70: per-source sum == withdrawal46,23246,232✓
Lees age 70: wdPretaxRMD == rmd - qcd00✓
Lees age 71: per-source sum == withdrawal46,31246,312✓
Lees age 71: wdPretaxRMD == rmd - qcd00✓
Lees age 72: per-source sum == withdrawal49,19849,198✓
Lees age 72: wdPretaxRMD == rmd - qcd00✓
Lees age 73: per-source sum == withdrawal52,20652,206✓
Lees age 73: wdPretaxRMD == rmd - qcd00✓
Lees age 74: per-source sum == withdrawal55,39655,396✓
Lees age 74: wdPretaxRMD == rmd - qcd00✓
Lees age 75: per-source sum == withdrawal67,69067,690✓
Lees age 75: wdPretaxRMD == rmd - qcd67,69067,690✓
Lees age 76: per-source sum == withdrawal78,46078,460✓
Lees age 76: wdPretaxRMD == rmd - qcd72,12372,123✓
Lees age 77: per-source sum == withdrawal120,454120,454✓
Lees age 77: wdPretaxRMD == rmd - qcd107,606107,606✓
Lees age 78: per-source sum == withdrawal160,399160,399✓
Lees age 78: wdPretaxRMD == rmd - qcd114,627114,627✓
Lees age 79: per-source sum == withdrawal204,928204,928✓
Lees age 79: wdPretaxRMD == rmd - qcd121,927121,927✓
Lees age 80: per-source sum == withdrawal227,314227,314✓
Lees age 80: wdPretaxRMD == rmd - qcd129,832129,832✓
Lees age 81: per-source sum == withdrawal241,389241,389✓
Lees age 81: wdPretaxRMD == rmd - qcd137,711137,711✓
Lees age 82: per-source sum == withdrawal256,976256,976✓
Lees age 82: wdPretaxRMD == rmd - qcd146,574146,574✓
Lees age 83: per-source sum == withdrawal272,004272,004✓
Lees age 83: wdPretaxRMD == rmd - qcd155,107155,107✓
Lees age 84: per-source sum == withdrawal288,797288,797✓
Lees age 84: wdPretaxRMD == rmd - qcd165,003165,003✓
Lees age 85: per-source sum == withdrawal305,059305,059✓
Lees age 85: wdPretaxRMD == rmd - qcd174,407174,407✓
Lees age 86: per-source sum == withdrawal322,462322,462✓
Lees age 86: wdPretaxRMD == rmd - qcd184,551184,551✓
Lees age 87: per-source sum == withdrawal340,170340,170✓
Lees age 87: wdPretaxRMD == rmd - qcd194,807194,807✓
Lees age 88: per-source sum == withdrawal357,141357,141✓
Lees age 88: wdPretaxRMD == rmd - qcd204,410204,410✓
Lees age 89: per-source sum == withdrawal375,948375,948✓
Lees age 89: wdPretaxRMD == rmd - qcd215,432215,432✓
Lees age 90: per-source sum == withdrawal392,871392,871✓
Lees age 90: wdPretaxRMD == rmd - qcd225,026225,026✓
Lees age 91: per-source sum == withdrawal410,884410,884✓
Lees age 91: wdPretaxRMD == rmd - qcd235,248235,248✓
Lees age 92: per-source sum == withdrawal428,538428,538✓
Lees age 92: wdPretaxRMD == rmd - qcd244,962244,962✓
Lees age 93: per-source sum == withdrawal446,310446,310✓
Lees age 93: wdPretaxRMD == rmd - qcd254,607254,607✓
Lees age 94: per-source sum == withdrawal461,510461,510✓
Lees age 94: wdPretaxRMD == rmd - qcd262,123262,123✓
Lees age 95: per-source sum == withdrawal476,270476,270✓
Lees age 95: wdPretaxRMD == rmd - qcd269,155269,155✓
Washingtons age 63: per-source sum == withdrawal00✓
Washingtons age 63: wdPretaxRMD == rmd - qcd00✓
Washingtons age 64: per-source sum == withdrawal00✓
Washingtons age 64: wdPretaxRMD == rmd - qcd00✓
Washingtons age 65: per-source sum == withdrawal12,73012,730✓
Washingtons age 65: wdPretaxRMD == rmd - qcd00✓
Washingtons age 66: per-source sum == withdrawal16,22916,229✓
Washingtons age 66: wdPretaxRMD == rmd - qcd00✓
Washingtons age 67: per-source sum == withdrawal84,63084,630✓
Washingtons age 67: wdPretaxRMD == rmd - qcd00✓
Washingtons age 68: per-source sum == withdrawal93,09093,090✓
Washingtons age 68: wdPretaxRMD == rmd - qcd00✓
Washingtons age 69: per-source sum == withdrawal73,20973,209✓
Washingtons age 69: wdPretaxRMD == rmd - qcd00✓
Washingtons age 70: per-source sum == withdrawal27,71627,716✓
Washingtons age 70: wdPretaxRMD == rmd - qcd00✓
Washingtons age 71: per-source sum == withdrawal29,11529,115✓
Washingtons age 71: wdPretaxRMD == rmd - qcd00✓
Washingtons age 72: per-source sum == withdrawal30,56630,566✓
Washingtons age 72: wdPretaxRMD == rmd - qcd00✓
Washingtons age 73: per-source sum == withdrawal20,91320,913✓
Washingtons age 73: wdPretaxRMD == rmd - qcd00✓
Washingtons age 74: per-source sum == withdrawal11,15611,156✓
Washingtons age 74: wdPretaxRMD == rmd - qcd00✓
Washingtons age 75: per-source sum == withdrawal105,137105,137✓
Washingtons age 75: wdPretaxRMD == rmd - qcd90,40890,408✓
Washingtons age 76: per-source sum == withdrawal113,660113,660✓
Washingtons age 76: wdPretaxRMD == rmd - qcd96,32996,329✓
Washingtons age 77: per-source sum == withdrawal122,170122,170✓
Washingtons age 77: wdPretaxRMD == rmd - qcd102,172102,172✓
Washingtons age 78: per-source sum == withdrawal131,785131,785✓
Washingtons age 78: wdPretaxRMD == rmd - qcd108,827108,827✓
Washingtons age 79: per-source sum == withdrawal142,014142,014✓
Washingtons age 79: wdPretaxRMD == rmd - qcd115,893115,893✓
Washingtons age 80: per-source sum == withdrawal152,946152,946✓
Washingtons age 80: wdPretaxRMD == rmd - qcd123,391123,391✓
Washingtons age 81: per-source sum == withdrawal164,042164,042✓
Washingtons age 81: wdPretaxRMD == rmd - qcd130,667130,667✓
Washingtons age 82: per-source sum == withdrawal491,909491,909✓
Washingtons age 82: wdPretaxRMD == rmd - qcd139,059139,059✓
Washingtons age 83: per-source sum == withdrawal519,533519,533✓
Washingtons age 83: wdPretaxRMD == rmd - qcd147,111147,111✓
Washingtons age 84: per-source sum == withdrawal556,991556,991✓
Washingtons age 84: wdPretaxRMD == rmd - qcd156,472156,472✓
Washingtons age 85: per-source sum == withdrawal485,456485,456✓
Washingtons age 85: wdPretaxRMD == rmd - qcd165,332165,332✓
Washingtons age 86: per-source sum == withdrawal238,161238,161✓
Washingtons age 86: wdPretaxRMD == rmd - qcd174,578174,578✓
Washingtons age 87: per-source sum == withdrawal270,173270,173✓
Washingtons age 87: wdPretaxRMD == rmd - qcd184,204184,204✓
Washingtons age 88: per-source sum == withdrawal275,317275,317✓
Washingtons age 88: wdPretaxRMD == rmd - qcd192,782192,782✓
Washingtons age 89: per-source sum == withdrawal291,222291,222✓
Washingtons age 89: wdPretaxRMD == rmd - qcd203,079203,079✓
Washingtons age 90: per-source sum == withdrawal305,375305,375✓
Washingtons age 90: wdPretaxRMD == rmd - qcd211,951211,951✓
Washingtons age 91: per-source sum == withdrawal319,724319,724✓
Washingtons age 91: wdPretaxRMD == rmd - qcd220,872220,872✓
Washingtons age 92: per-source sum == withdrawal334,175334,175✓
Washingtons age 92: wdPretaxRMD == rmd - qcd229,768229,768✓
Washingtons age 93: per-source sum == withdrawal348,576348,576✓
Washingtons age 93: wdPretaxRMD == rmd - qcd238,549238,549✓
Washingtons age 94: per-source sum == withdrawal359,454359,454✓
Washingtons age 94: wdPretaxRMD == rmd - qcd244,500244,500✓
Washingtons age 95: per-source sum == withdrawal369,712369,712✓
Washingtons age 95: wdPretaxRMD == rmd - qcd249,857249,857✓

Roth Optimizer Hang 3/3

TestExpectedActualResult
Already-retired plan: projection length matches age range2929✓
Past startAge (62) for 67-year-old: engine does not throw00✓
Long conversion window (67-95, $1M pretax): non-zero total conversions11✓

F-219 T1-T6 6/6

TestExpectedActualResult
T1: primary transition, spouse on ACA, other source22,33522,335✓
T2: primary transition, spouse on Medicare via SSDI3,1913,191✓
T3: spouse transition, primary still pre-Medicare23,42823,428✓
T4: spouse transition, primary on Medicare (Srini age 71)8,5528,552✓
T5 (regression): single transition unchanged8,6828,682✓
T6 (regression): both transition same year, other source14,35814,358✓

Brief 1B 8/8

TestExpectedActualResult
Test A: mixed over-routing — total = 55000 (no over-allocation)55,00055,000✓
Test B: routed-routed clamp — savings conserved at 3000030,00030,000✓
Test B: routed-routed clamp — spouse pre-tax ≈ 1230812,30812,308✓
Test C: no-shortfall — savings === 70000 (savRatio=1 guard)70,00070,000✓
Test C: no-shortfall — spouse pre-tax === 16000 (returns/balance guard)16,00016,000✓
Test C: no-shortfall — brokerage === 31000 (returns/balance guard)31,00031,000✓
Test D: no-limits block — savings conserved at 4000040,00040,000✓
Test D: no-limits block — spouse pre-tax ≈ 1727317,27317,273✓

Release 2 7/7

TestExpectedActualResult
Percent mode: 10% of $100K income → $10,000 deferral (year 0)10,00010,000✓
Percent mode tracks income: $10,000 deferral at year 0 (growth ×1)10,00010,000✓
Percent mode tracks income: cumulative preTax $31,525 at year 231,52531,525✓
Percent 10% of $100K ≡ dollar $10,000 — preTax identical every year77✓
Percent mode §402(g) clamp: 90% of $200K clamps identically to dollar $180K24,50024,500✓
Percent mode §402(g) clamp: clamped deferral is below the raw $180,000 want11✓
Spouse percent mode: 10% of $60K spouse income ≡ dollar $6,000 (own income, not household)77✓

Birth-Month Retirement 10/10

TestExpectedActualResult
Flag false is byte-identical to flag absent (migration safety)4141✓
retireMonth < birthMonth shifts the retirement year +1 (born March, retire January)4141✓
January birth produces no shift (retireMonth 0 < birthMonth 0 is false)4141✓
retireMonth on or after birthMonth produces no shift (born March, retire June)4141✓
Flag on: the age-65 row is still a working year (not retired)00✓
Flag on: the age-66 row is retired11✓
Flag off: the age-65 row is retired (bare age-counter, unchanged behavior)11✓
Partial-year proration rides the shifted year (equivalence)4141✓
Shifted partial year: age-66 earned is 3/12 of the age-65 full year120,000120,000✓
Spouse shift mirrors the primary path (spouse born July, retire January)4141✓

One-Time Expense 5/5

TestExpectedActualResult
TC1: single event at age 60 surfaces as oneTimeExpense=3000030,00030,000✓
TC1: another in-range row (age 70) has oneTimeExpense=000✓
TC2: two events at age 70 sum to oneTimeExpense=3500035,00035,000✓
TC3: empty oneTimeExpenses produces oneTimeExpense=0 on every row00✓
TC4: one-time expense does not leak into y.expenses in the spend year92,74292,742✓

Cliff-Aware Fill 10/10

TestExpectedActualResult
ACA cap binds — smartFill and cliffFill both cap below the 24% bracket fill (F-304)11✓
ACA cap binds — cliffFill still converts > 011✓
ACA cap inert post-Medicare — equals smartFill11✓
ACA cap inert under enhanced rules — equals smartFill11✓
ACA cap inert with non-ACA coverage — equals smartFill11✓
NIIT cap binds — cliffFill converts strictly less than smartFill11✓
NIIT off — equals smartFill11✓
Caps OFF — conversion fills past the ACA ceiling (equals plain bracket fill)11✓
Caps OFF — smartFill equals plain bracket fill on the same bracket11✓
IRMAA cap OFF — conversion crosses the IRMAA tier (more than with the cap on)11✓

Optimizer ACA Inert 4/4

TestExpectedActualResult
Non-ACA baseline: total acaSubsidy = 0 (baseACA = 0)00✓
Non-ACA + conversions: candidate total acaSubsidy = 000✓
Non-ACA + cliffFill: total acaSubsidy = 000✓
Non-ACA cliffFill ties smartFill on all score-relevant totals11✓

Single survivor leak 11/11

TestExpectedActualResult
Single + primaryLifeExpectancy: final balance unchanged (no spouse to lose)750,000750,000✓
Single + primaryLifeExpectancy: year-68 expenses unchanged (no 30% survivor cut)00✓
Single + spouseLifeExpectancy: final balance unchanged (no spouse exists)750,000750,000✓
Couple + spouseLifeExpectancy: year-70 expenses DROP vs both-alive (survivor cut fires)11✓
Couple + spouseLifeExpectancy: year-70 row is single after spouse death11✓
Stale single (spouseAge + spouseIncome) heals to clean single after migration750,000750,000✓
Migrate: legacy couple (spouseAge>0, no flag) -> spouseEnabled true11✓
Migrate: legacy couple keeps spouseAge (not zeroed)6565✓
Migrate: disabled spouse -> spouseAge zeroed00✓
Migrate: disabled spouse -> spouseLifeExpectancy cleared00✓
Migrate: idempotent — spouseAge stays 0 on a second pass00✓

Conversion Tax Funding (F-237) 53/53

TestExpectedActualResult
Well-funded: no fallback00✓
Well-funded: total tax = 38203,8203,820✓
Well-funded: net worth falls by exactly the tax796,180796,180✓
Well-funded: year 0 tax iteration converges00✓
Drained brokerage: fallback is the whole bill3,8203,820✓
Drained brokerage: net worth funds the tax exactly once697,180697,180✓
Drained brokerage: brokerage fully consumed00✓
Drained brokerage: roth carries the shortfall247,180247,180✓
Drained brokerage: year 0 tax iteration converges00✓
Partial cover: fallback is the uncovered slice2,3202,320✓
Partial cover: net worth funds the tax exactly once697,680697,680✓
Partial cover: designated cash fully consumed00✓
Partial cover: brokerage overflow fully consumed00✓
Partial cover: roth carries the 2,320 shortfall247,680247,680✓
Legacy 'none': funded by neither route (load-bearing else)00✓
Legacy 'none': tax is still computed3,8203,820✓
Legacy 'none': net worth unchanged (no account charged)701,000701,000✓
Legacy 'none': brokerage untouched1,0001,000✓
'ira' withholding: clamp unreachable, no fallback00✓
'ira' withholding: roth carries the full tax246,180246,180✓
'ira' withholding: net worth funds the tax exactly once696,180696,180✓
Invariant: NW drop == tax (Test 1 well-funded)3,8203,820✓
Invariant: NW drop == tax (Test 2 drained)3,8203,820✓
Invariant: NW drop == tax (Test 3 partial)3,8203,820✓
Invariant: NW drop == tax (Test 5 ira)3,8203,820✓
I1 Clarks: conversionTaxFallback >= 0 every year11✓
I2 Clarks: conversionTaxFallback <= conversionTax every year11✓
I3 Clarks: conversionTax 0 ⟹ fallback 0 every year11✓
I1 Lees: conversionTaxFallback >= 0 every year11✓
I2 Lees: conversionTaxFallback <= conversionTax every year11✓
I3 Lees: conversionTax 0 ⟹ fallback 0 every year11✓
I1 Washingtons: conversionTaxFallback >= 0 every year11✓
I2 Washingtons: conversionTaxFallback <= conversionTax every year11✓
I3 Washingtons: conversionTax 0 ⟹ fallback 0 every year11✓
I5 Clarks: conversions disabled ⟹ convTax 0 and fallback 0 every year11✓
I5 Washingtons: conversions disabled ⟹ convTax 0 and fallback 0 every year11✓
I4 'none' plan: source 'none' ⟹ fallback 0 every year11✓
P1 Lees idx3: rothConv > 0 (window open, ages 62-65)11✓
P1 Lees idx4: rothConv > 0 (window open, ages 62-65)11✓
P1 Lees idx5: rothConv > 0 (window open, ages 62-65)11✓
P1 Lees idx6: rothConv > 0 (window open, ages 62-65)11✓
P2 Lees idx2: conversionTax === 0 (outside the ages 62-65 window)00✓
P2 Lees idx7: conversionTax === 0 (outside the ages 62-65 window)00✓
P2 Lees idx8: conversionTax === 0 (outside the ages 62-65 window)00✓
P2 Lees idx11: conversionTax === 0 (outside the ages 62-65 window)00✓
P3 Lees idx3: conversionTaxFallback === 0 (brokerage covers the bill)00✓
P3 Lees idx4: conversionTaxFallback === 0 (brokerage covers the bill)00✓
P3 Lees idx5: conversionTaxFallback === 0 (brokerage covers the bill)00✓
P3 Lees idx6: conversionTaxFallback === 0 (brokerage covers the bill)00✓
P4 Lees idx3: wdRoth === 0 (Roth never raided in conversion years)00✓
P4 Lees idx4: wdRoth === 0 (Roth never raided in conversion years)00✓
P4 Lees idx5: wdRoth === 0 (Roth never raided in conversion years)00✓
P4 Lees idx6: wdRoth === 0 (Roth never raided in conversion years)00✓

Strategy Report Window Parity (F-238) 8/8

TestExpectedActualResult
T1 guard: RMD-75 plan produces at least one Roth Strategy alternative11✓
T2 customer case: Smart Fill Roth 22% endAge === 74 (earliestRmdAge - 1, RMD 75)7474✓
T3 category parity: Smart Fill Roth 22% endAge === Tax-Aware Drawdown 22% endAge11✓
T4 stacked legs: stack rothConversion.endAge === taxAwareDrawdown.endAge11✓
T5 dual-owner: Smart Fill Roth 22% endAge === 72 (min(75,73) - 1)7272✓
T6 age-73 cohort: Smart Fill Roth 22% endAge === 727272✓
T7 RMD-75 no-op: Smart Fill Roth 22% endAge === 74 (locks the RMD-75 no-op guarantee)7474✓
T8 documented removal: age-73 cohort past the window loses the Roth Strategy category11✓

OTE Age Norm 6/6

TestExpectedActualResult
Decimal expense age 65.9 fires in the age-65 row85,00085,000✓
currentAge 65.9: projection spans ages 65..72 (8 rows)88✓
currentAge 65.9: first row age is integer 656565✓
currentAge 65.9 + expense age 66 fires at row age 6685,00085,000✓
Integer ages: expense at 66 fires at 66 (path unchanged)85,00085,000✓
spouseAge 62.9: year-0 spAge is integer 626262✓

Cash Event Age Norm 5/5

TestExpectedActualResult
Decimal cash-event age 60.9 fires in the age-60 row200,000200,000✓
Decimal cash-event age 60.9 does NOT fire in the age-61 row00✓
Integer cash-event age 60 fires at age 60 (path unchanged)200,000200,000✓
Decimal QLAC purchaseAge 72.9 buys at age 72 ($100K leaves pre-tax)400,000400,000✓
Integer QLAC purchaseAge 72 buys at 72 (path unchanged)400,000400,000✓

F-212 Year-End Age 4/4

TestExpectedActualResult
yearEnd 50 at loop age 49 grants the $8,000 catch-up ($32,500 deferral)32,50032,500✓
yearEnd 49 at loop age 49 grants base only ($24,500)24,50024,500✓
yearEnd 60 at loop age 59 grants the SECURE 2.0 super-catch-up ($35,750)35,75035,750✓
yearEnd 59 at loop age 59 grants the standard catch-up ($32,500)32,50032,500✓

OTE Cascade 10/10

TestExpectedActualResult
Pretax one-time expense: 50,000 enters MAGI as ordinary income50,00050,000✓
Cash shortfall cascades to brokerage: capGains 32,50032,50032,500✓
Cascade drains brokerage by the overflow: taxableInv 235,000235,000235,000✓
Fully-cascaded expense reports funded 85,000 / unfunded 011✓
HSA non-medical one-time at 60: MAGI 12,00012,00012,000✓
HSA non-medical one-time at 60: fedTax 2,400 (20% additional tax)2,4002,400✓
HSA MEDICAL one-time at 60: qualified, MAGI 0 and fedTax 011✓
Exhausted portfolio: funded 50,00050,00050,000✓
Exhausted portfolio: unfunded 35,00035,00035,000✓
Inherited-pretax one-time draw: MAGI delta 20,000 vs twin20,00020,000✓

OTE Dollar Basis 7/7

TestExpectedActualResult
inflate=true: 50,000 today-basis funds 67,196 at +10yr, 3%67,19667,196✓
flag absent: amount stays fixed future 50,00050,00050,000✓
inflate=true in year 0: uninflated 50,00050,00050,000✓
inflate=true at 0% inflation: 50,00050,00050,000✓
twin inflate=false: funded 50,00050,00050,000✓
twin inflate=true: funded 67,196 through the cascade67,19667,196✓
capGains twin delta 8,598 (extra brokerage overflow x 0.5)8,5988,597✓

Muni Exempt 6/6

TestExpectedActualResult
Fed-exempt: $50K muni interest → federal tax $000✓
taxExemptInterest accumulator = $50K (yield 5% × $1M)50,00050,000✓
Not routed to `other` (not fed-taxable ordinary income)00✓
Not aliased into qualifiedDividends (LTCG stacking / NII)00✓
agi stays $0 (exempt interest is an add-back to MAGIs, never AGI)00✓
magi display = AGI + tax-exempt interest = $50K50,00050,000✓

Muni SS add-back 6/6

TestExpectedActualResult
Manual SS $2,000/mo claimed at 63 → $18,000/yr18,00018,000✓
Exempt interest counts toward SS: 85% taxable ($15,300) with $50K muni15,30015,300✓
Control (no dividends): SS taxable $000✓
§86 equivalence: exempt interest moves SS tax exactly like ordinary income15,30015,300✓
Exempt interest NOT federally taxed: fedTax $000✓
Ordinary-dividend control: fedTax $5,6565,6565,656✓

Muni NIIT 2/2

TestExpectedActualResult
Exempt interest excluded from NII: niitTax $0 at $250K pension00✓
Same yield as qualified dividends → NIIT $1,900 (control)1,9001,900✓

Muni State 2/2

TestExpectedActualResult
CA resident + CA-issued muni: state tax $000✓
CA resident + NY-issued muni: state tax = CA tax on $50K = $1,6231,6231,623✓

Muni Relocation 3/3

TestExpectedActualResult
CA muni while CA resident (age 69): state-taxable slice $000✓
Same CA muni after move to VA (age 70): entire interest becomes state-taxable50,00050,000✓
State tax appears only after the move11✓

Muni Streams 3/3

TestExpectedActualResult
tax_exempt stream → taxExemptInterest $40K40,00040,000✓
tax_exempt stream is not ordinary income (other = 0)00✓
tax_exempt stream: federal tax $000✓

Muni BackwardCompat 1/1

TestExpectedActualResult
Plan without new fields ≡ plan with explicit defaults11✓

Muni Trap6 2/2

TestExpectedActualResult
Withdrawal gains still realized: capGains identical for exempt vs qualified flag26,34326,343✓
Gains are NOT zeroed for exempt accounts (capGains > 0)11✓

Muni ACA year-0 2/2

TestExpectedActualResult
Year-0 ACA subsidy identical for exempt vs ordinary $50K interest11✓
Year-0 ACA MAGI = AGI + tax-exempt interest ($50K), not $011✓

Fed Bracket 7/7

TestExpectedActualResult
Year-0 fedMarginalBracket = 12% (taxable 33,900 in the 12% bracket)1212✓
fedTax = $3,820 on taxable 33,9003,8203,820✓
Effective federal rate = fedTax / AGI = 3,820 / 50,000 = 7.6%7.67.6✓
marginalTaxRate(50,000, single, 2026) = 0.120.120.12✓
marginalTaxRate(60,000, single, 2026) = 0.220.220.22✓
marginalTaxRate(250,000, MFJ, 2026) = 0.240.240.24✓
marginalTaxRate(0, single, 2026) = 0 (brackets keyed "over X")00✓

IRMAA Ceiling 17/17

TestExpectedActualResult
MFJ 2026: auto @$100K caps at the $218K tier-1 start (top of tier 0)218,000218,000✓
MFJ 2026: ceilingTier 0 @$100K = $218K (no surcharge band)218,000218,000✓
MFJ 2026: ceilingTier 1 @$100K = $274K (top of tier 1)274,000274,000✓
MFJ 2026: ceilingTier 2 @$100K = $342K (top of tier 2)342,000342,000✓
MFJ 2026: ceilingTier 4 @$100K = $750K (top of tier 4)750,000750,000✓
MFJ 2026: ceilingTier 5 (top tier) = no ceiling11✓
Single 2026: ceilingTier 0 @$60K = $109K; ceilingTier 1 = $137K246,000246,000✓
MFJ 2026 auto @$230K (in tier 1) caps at $274K — fills current tier274,000274,000✓
MFJ 2026 auto @$218K exact threshold caps at $274K (strict >)274,000274,000✓
MFJ 2026 auto @$800K (above top tier) = no ceiling11✓
MFJ 2027 @2.5%: ceilingTier 1 = $281K (indexed from $274K, $1K rounding)281,000281,000✓
MFJ 2027 @2.5%: top tier frozen at $750K (indexes only from 2028)750,000750,000✓
Projection MFJ: auto (no ceilingTier) converts exactly $218K218,000218,000✓
Projection MFJ: ceilingTier 0 converts exactly $218K (no surcharge)218,000218,000✓
Projection MFJ: ceilingTier 1 converts exactly $274K (through tier 1)274,000274,000✓
Projection MFJ: ceilingTier 5 (top) equals IRMAA-unchecked conversion11✓
Projection MFJ: IRMAA-unchecked fills the 24% bracket (conversion $435,750 → taxable $403,550)435,750435,750✓

Rollover into Pre-Tax 7/7

TestExpectedActualResult
Rollover at 62: pre-tax balance = baseline + $200,000700,000700,000✓
Rollover at 62: federal tax identical to no-event baseline00✓
Rollover at 62: AGI identical to no-event baseline00✓
Control: cash-destination $200K at 62 IS taxed (fedTax > baseline)11✓
Control: cash-destination deposits to cash, not pre-tax500,000500,000✓
RMD year: age-73 RMD identical with vs. without $100K rollover30,18930,189✓
RMD year: pre-tax after rollover = baseline + $100K at 73869,811869,811✓

Estate Tax (federal) 7/7

TestExpectedActualResult
Single, $20M → 40% × ($20M − $15M) = $2,000,0002,000,0002,000,000✓
Single, $10M → $0 (below exclusion)00✓
Single exactly at $15M → $000✓
Couple w/ portability, $30M → $000✓
Couple w/ portability, $40M → 40% × ($40M − $30M) = $4,000,0004,000,0004,000,000✓
No portability (married, opt-out), $40M → 40% × ($40M − $15M) = $10,000,00010,000,00010,000,000✓
Flat path unchanged: single $30M, no year → 40% × ($30M − $15M) = $6,000,0006,000,0006,000,000✓

Estate Tax (federal, projected) 4/4

TestExpectedActualResult
Exclusion, no year → flat $15,000,000 (byte-identical)15,000,00015,000,000✓
Exclusion 2046 (20 yrs @ 3%) → $15M × 1.03^20 = $27,090,00027,090,00027,090,000✓
Exclusion 2046, married + portability → 2 × $27,090,000 = $54,180,00054,180,00054,180,000✓
Single $30M, 2046 @ 3% → 40% × ($30M − $27.09M) = $1,164,0001,164,0001,164,000✓

Estate Tax (state) 9/9

TestExpectedActualResult
WA single $4M → 10% of ($4M − $3.0M) = $100,000100,000100,000✓
OR single $1.5M → 10% of ($1.5M − $1.0M) = $50,00050,00050,000✓
VT single $6M → 16% of ($6M − $5M) = $160,000160,000160,000✓
MA single $2.2M → §2011 table($2.14M) − $99,600 = $15,20015,20015,200✓
NY single $8M (above the 105% cliff) → full table = $773,200773,200773,200✓
NY single $7M (below the exclusion) → $000✓
CT single $20M → 12% of ($20M − $15M) = $600,000600,000600,000✓
MD single $6M → 16% of ($6M − $5M) = $160,000 (below the §2011 cap)160,000160,000✓
No-estate-tax state (FL) → $000✓

Inheritance Tax (state) 5/5

TestExpectedActualResult
PA lineal descendant on $1M → 4.5% = $45,00045,00045,000✓
NE lineal on $200K → 1% of ($200K − $100K) = $1,0001,0001,000✓
MD non-exempt heir on $1M → 10% = $100,000100,000100,000✓
MD lineal descendant (child) is exempt → $000✓
Iowa repealed for deaths on/after 2025 → $000✓

Legacy net-to-heirs 9/9

TestExpectedActualResult
PA lineal heir: $1M pre-tax → IRD 22% + PA inheritance 4.5%735,000735,000✓
$1M pre-tax @ 22% heir → $780K net780,000780,000✓
HSA $100K to a NON-spouse heir → taxed at 22%858,000858,000✓
HSA $100K to a SPOUSE → tax-free (becomes their HSA)880,000880,000✓
Taxable brokerage $500K → §1014 step-up, no income tax1,280,0001,280,000✓
Roth $500K → tax-free to heirs1,280,0001,280,000✓
Non-qualified annuity gain is IRD; basis untaxed278,000278,000✓
Qualified annuity $300K is IRD (ordinary income)234,000234,000✓
Estate tax layers on the gross estate ($20M pre-tax, 0% heir, single)18,000,00018,000,000✓

Legacy floor 5/5

TestExpectedActualResult
No legacy goal → spend-to-zero ceiling ≈ $1,000,000 / 5 = $200,000200,000199,821✓
Legacy $310K floor → ceiling ≈ ($1,000,000 − $310,000) / 5 = $138,000138,000137,934✓
Ceiling respects the floor (endNW ≥ $310,000)11✓
Ceiling + $5,000 drops the terminal below the floor (boundary)11✓
Disabled legacy goal is ignored (same ceiling as no goal)199,821199,821✓

Annual Gifting (F-347b) 7/7

TestExpectedActualResult
No gifts, $1M cash, $40K/yr × 5 yrs → terminal $800,000800,000800,000✓
$10K/yr ages 60–62 → $30,000 removed; terminal $770,000770,000770,000✓
Cumulative gifts = $10K × 3 yrs = $30,00030,00030,000✓
giftTotal = $10,000 inside the range (age 61, no inflation)10,00010,000✓
giftTotal = 0 outside the age range (age 63)00✓
Gifts reduce the gross estate (inherited assets)11✓
Disabled by default → no giftTotal rows11✓

Primary Residence (F-347e) 14/14

TestExpectedActualResult
Property tax = 2% × $500,000 = $10,000/yr, inside expenses10,00010,000✓
Owned home held flat at $500,000 every year (0% app)11✓
Home stays out of portfolio net worth: $1M − 5×$10K = $950,000950,000950,000✓
Appreciation: $500,000 × 1.05 = $525,000 (age 60)525,000525,000✓
5 yrs at 5%, compounded on rounded values → $638,142638,142638,142✓
Purchase at 62 funds $300,000 from the portfolio300,000300,000✓
Purchase reduces liquid net worth: $1M − $300K = $700,000700,000700,000✓
Sale at 64 nets $500,000 × (1 − 6%) = $470,000 to brokerage470,000470,000✓
Sale proceeds raise liquid net worth: $1M + $470K = $1,470,0001,470,0001,470,000✓
After the sale the residence is out of the estate ($0)00✓
Residence is estate-includible: gross rises by $500,000500,000500,000✓
Residence adds no heir income tax (IRC §1014 step-up)00✓
Composed residence bucket = $500,000500,000500,000✓
Disabled by default → zero residence fields11✓

Estate & relocation (F-347f) 5/5

TestExpectedActualResult
Staying in MD: state estate tax = 16% × ($6M − $5M) = $160,000160,000160,000✓
Terminal effective state after the move is NH11✓
NH (no estate tax) → state estate tax is $0, not MD's $160,00000✓
NH levies no inheritance tax either → $000✓
Non-relocating plans are unchanged (MD tax still applies)11✓

Home swap (F-347g) 7/7

TestExpectedActualResult
Owns $600K up to the move (age 64) — a planned purchase no longer zeroes it600,000600,000✓
Sells at 65: $600K × (1 − 6%) = $564,000 net to brokerage564,000564,000✓
Buys the replacement at 65 for $700,000700,000700,000✓
Home value from 65 is the new $700,000 (not $600K, not from year 1)700,000700,000✓
Estate at plan end holds the new $700,000 home700,000700,000✓
A replacement with no explicit sale age still sells at the buy age564,000564,000✓
Buy-only (no current home): $0 before 65, buys at 6511✓

Multi-home 6/6

TestExpectedActualResult
Buys the first replacement at 65 for $700,000700,000700,000✓
Holds the $700,000 home through 69700,000700,000✓
Sells it at 70: $700,000 × (1 − 6%) = $658,000 net658,000658,000✓
Buys the second replacement at 70 for $800,000800,000800,000✓
Home value from 70 is the new $800,000800,000800,000✓
Estate at plan end holds the last $800,000 home800,000800,000✓

Legacy reachability 3/3

TestExpectedActualResult
Target above the max achievable → not reachable11✓
Target below the max achievable → reachable11✓
No goal → legacyGap null11✓

Legacy MC 3/3

TestExpectedActualResult
No goal → legacySuccessRate null (unchanged behavior)11✓
Impossible target → legacySuccessRate 000✓
Easy target → legacySuccessRate 100100100✓

HSA estate (F-345) 2/2

TestExpectedActualResult
Married: HSA to a spouse → no heir income tax00✓
Single: HSA to a non-spouse heir → fully taxable at the heir bracket22,00022,000✓

HSA draw (F-345) 1/1

TestExpectedActualResult
Medical% = 0 does not disable the cap (HSA not drained)00✓

HSA capacity (F-345) 2/2

TestExpectedActualResult
ACA/Marketplace premium is NOT HSA-qualified (no tax-free room from it)00✓
COBRA premium IS HSA-qualified (draws tax-free against it)11✓

Drain (FR2) 4/4

TestExpectedActualResult
drain empties the pre-tax balance to $0 by the plan end age00✓
fill 22% leaves a large pre-tax residual (partial ladder)11✓
a full drain costs more lifetime tax than the partial ladder11✓
drain builds a larger terminal Roth than the ladder11✓

maxRoth goal (FR2) 2/2

TestExpectedActualResult
the "Max Tax-Free Roth" goal is recognized (not falling back to net worth)11✓
higher terminal Roth scores higher (goal ordering)11✓

Real Estate rental 35/35

TestExpectedActualResult
gross rent, vacancy, opex, interest, and property tax11✓
taxable rental income = rent − vacancy − opex − interest − property tax3,8003,800✓
cash flow = taxable − principal; mortgage amortizes11✓
rental cash compounds in the portfolio11✓
year 2 amortizes the mortgage11✓
January mid-month depreciation = $200,000 / 27.5 × 11.5/126,9706,970✓
January placement: taxable = $24,000 − $6,97017,03017,030✓
year 2 full-year depreciation = $200,000 / 27.511✓
December mid-month depreciation = $200,000 / 27.5 × 0.5/1211✓
sale proceeds and gain at the sale age11✓
§1250 recapture at 25% and LTCG remainder11✓
a sold property stops producing rent11✓
NIIT applies to net rental income1,9001,900✓
rental value is in the estate, net of the mortgage11✓
zero-gain sale releases the $10,000 suspended loss in full10,00010,000✓
zero-gain sale leaves no suspended-loss carryforward00✓
zero-gain sale produces no capital gain00✓
sale below basis recognizes the $50,000 loss50,00050,000✓
sale below basis leaves no suspended-loss carryforward00✓
$5,000 gain against a $10,000 carry nets a $5,000 ordinary loss5,0005,000✓
the $5,000 net-loss sale reports no capital gain00✓
the $5,000 net-loss sale clears the carry00✓
$20,000 gain less a $10,000 carry leaves $10,000 of gain10,00010,000✓
the remaining $10,000 is long-term capital gain10,00010,000✓
the gain sale clears the carry00✓
MAGI $100,000 allows the full $25,000 loss25,00025,000✓
MAGI $100,000 carries the remaining $15,00015,00015,000✓
MAGI $120,000 reduces the allowance to $15,00015,00015,000✓
MAGI $120,000 carries the remaining $25,00025,00025,000✓
MAGI $150,000 phases the allowance to $000✓
MAGI $150,000 carries the full $40,000 loss40,00040,000✓
a $10,000 loss at MAGI $50,000 is allowed in full10,00010,000✓
a fully allowed loss leaves no carryforward00✓
MFJ uses the same $100,000 phaseout threshold15,00015,000✓
MFJ carries the remaining $25,00025,00025,000✓

FICA 10/10

Authority: IRC §3101(a) (OASDI 6.2%); IRC §3101(b) (Medicare 1.45%); IRC §3101(b)(2) (Additional Medicare 0.9% above household threshold); IRS Pub 15.
Method: Per-person calcFICA on W-2 wages; SS portion capped at annual wage base ($184,500 in 2026); Medicare uncapped; Additional Medicare 0.9% on combined household wages above $250,000 MFJ / $200,000 single.
TestExpectedActualResult
SS portion = wages × 6.2%6,2006,200✓
Medicare portion = wages × 1.45%1,4501,450✓
Total = SS + Medicare, no Addl Med here7,6507,650✓
SS portion at cap = cap × 6.2%11,43911,439✓
Medicare at cap = cap × 1.45% (no cap on Medicare)2,6752,675✓
SS portion caps at wage base × 6.2%11,43911,439✓
Medicare continues on full wage amount3,6253,625✓
Zero wages → all zero00✓
SS portion limited to remaining base5,2395,239✓
Medicare still on full wages2,1752,175✓

Pre-tax 401(k) fix 2/2

Authority: IRC §3121(a) (FICA wage base includes 401(k) deferrals); IRC §62(a)(2) (above-the-line treatment for federal income tax).
Method: Verifies that 401(k) pre-tax deferrals reduce ordinary income for federal tax computation but NOT for the FICA wage base.
TestExpectedActualResult
Having a pre-tax account reduces pre-retirement federal tax11✓
FICA unchanged by pre-tax 401(k) choice9,1809,180✓

Phase resolver 6/6

Method: Income phase resolver tests confirming phase-level overrides take priority for ages they cover, age-outside-phase falls back to base config, per-phase incomeBreakdown propagates to FICA correctly, per-phase selfEmployed propagates to SE tax.
TestExpectedActualResult
No phases → base income at year 0120,000120,000✓
Phase income overrides base during covered years150,000150,000✓
Age outside phases uses base income120,000120,000✓
Phase-level incomeBreakdown reduces FICA base by §125 + payrollHSA10,09810,098✓
Phase SE does not pollute FICA (separate channels)00✓
W-2 + SE in same phase: FICA on W-2 only (unchanged by SE)7,6507,650✓

SE Tax 10/10

Authority: IRC §1401 (OASDI 12.4% / Medicare 2.9%); IRC §1402(a)(12) (92.35% multiplier); IRC §1402(b)(2) (wage base shared with W-2); IRC §1402(b) ($400 SECA-base floor); IRC §164(f) (half-of-SE-tax above-the-line deduction); IRS Schedule SE Instructions; IRS Pub 334.
Method: calcSETax computes SECA base = netSE × 0.9235; SS portion = min(SECA, ssWageBaseRemaining) × 12.4%; Medicare = SECA × 2.9%; halfDeduction = total / 2 reduces AGI.
TestExpectedActualResult
$50K net SE: secaBase = $46,17546,17546,175✓
$50K net SE: SS portion = $5,7265,7265,726✓
$50K net SE: Medicare = $1,3391,3391,339✓
$50K net SE: total = $7,0657,0657,065✓
$50K net SE: halfDeduction ≈ $3,5333,5333,533✓
Negative SE: zero SE tax00✓
Negative SE: zero half-deduction00✓
$400 net SE: under SECA floor → zero SE tax00✓
Wage base sharing: SS portion limited4,2784,278✓
Wage base sharing: Medicare uncapped2,1432,143✓

SE Tax Integration 20/20

Method: End-to-end runProjection() integration tests verifying SE tax flows through engine pipeline: AGI/MAGI propagation via the `other` accumulator, year-output field surface (seTax / seNetEarnings / seHalfDeduction), wage base sharing with W-2 income, business loss handling (negative net SE → no SE tax, AGI reduction only), spouse SE independence.
TestExpectedActualResult
SE tax in year output ≈ $11,30411,30411,304✓
seNetEarnings = $80K80,00080,000✓
seHalfDeduction ≈ $5,6525,6525,652✓
SE loss: zero SE tax00✓
SE loss: seNetEarnings = -$20K-20,000-20,000✓
SE loss: seHalfDeduction = 000✓
SE loss reduces MAGI vs baseline11✓
W-2 + SE: SE tax respects wage base sharing + Addl Medicare6,6366,636✓
Working-age SE: seTax > 011✓
Spouse SE: seTax > 011✓
F-044: $220K SE single → seTax includes ~$29 Addl Medicare28,79928,799✓
F-044: $130K W-2 + $145K SE → SE Addl Medicare via §1401(b)(2)(B)11,21611,216✓
F-044: MFJ combined SE ($80K + $200K) → Addl Medicare on $8,580 excess39,61539,615✓
F-044: $300K SE single → seTax includes $693 Addl Medicare31,60531,605✓
F-044: §164(f) half-deduction is NOT inflated by Addl Medicare15,45615,456✓
F-156: retired primary + spouse SE $300K → seTax includes $243 Addl Medicare31,15531,155✓
F-156: retired primary + spouse SE $200K (below threshold) → no Addl Medicare28,23428,234✓
F-156: retired primary + spouse W-2 $300K → fica reflects W-2 Addl Medicare ($450)16,23916,239✓
F-156: retired primary + spouse W-2 $300K → seTax = 0 (no SE)00✓
F-156: working primary $300K SE → seTax = $31,605 (matches 23d, no double-count)31,60531,605✓

QBI 10/10

Authority: IRC §199A(a)-(d), §199A(i); Treas. Reg. §1.199A-3(b)(1)(vi); IRS Rev. Proc. 2025-32 §4.26 (post-OBBBA §70105).
Method: Per-business 20% deduction with SSTB phaseout, household taxable-income cap (20% × (taxable − net cap gain)), W-2 wage limit (sole-prop assumption: $0), OBBBA $400 floor for ≥$1,000 QBI from a qualified trade or business. 2026 thresholds: $403,500 / $553,500 MFJ; $201,750 / $276,750 single (post-OBBBA $150K / $75K phase-in extension).
TestExpectedActualResult
Below threshold MFJ: full 20% deduction20,00020,000✓
SSTB above upper MFJ: $0 deduction00✓
Non-SSTB above upper MFJ no W-2: $400 floor400400✓
SSTB phase-in midpoint MFJ: W-2=0 → $400 floor400400✓
Taxable income limit MFJ: capped at 20% × (taxable − net capgain)10,00010,000✓
QBI below $1,000: no deduction00✓
Single below threshold: full 20% deduction10,00010,000✓
Single SSTB above upper: $0 deduction00✓
Two-business household MFJ: combined deduction16,00016,000✓
Mixed SSTB/non-SSTB above upper: $400 floor via non-SSTB400400✓

QBI Integration 6/6

Method: End-to-end runProjection() tests verifying QBI deduction flows correctly through engine: per-baseline computation across actual / no-conversion / conversion-only scenarios for accurate Roth conversion delta math, surface in year output (qbiDeduction field), interaction with TLH-ordinary-offset and capital-gains stacking. v6.14.1 added TLH-ordinary-offset symmetry across all three baselines.
TestExpectedActualResult
W-2 + SE: qbiDeduction > 011✓
W-2 + SE $100K: qbiDeduction ≈ 20% × (netSE − halfSE)18,93218,932✓
Pure SE $100K: deduction capped by household taxable income limit11✓
SSTB in phase-in range: deduction reduced vs full11✓
SSTB conversion: conversionTax > 011✓
TLH + QBI: deduction computed (no asymmetry crash)11✓

Solo 401k 17/17

Authority: IRC §401(c) (self-employed plan participation); IRC §401(c)(2)(A)(v) (positive-earned-income gating); IRC §402(g) + §414(v) (elective deferral limit + age-based catch-up); SECURE 2.0 §109 (enhanced catch-up ages 60-63); IRC §415(c) (annual addition cap); IRC §404(a)(8)(C) (20% sole-prop employer limit on net SE − half-SE-tax); SECURE 2.0 §604 (Roth employer profit-share); IRS Notice 2025-67 (2026 contribution limits).
Method: Solo 401(k) employee elective deferral (Traditional or Roth) and employer profit-share for sole prop / SMLLC / partnership entities. §402(g) capacity aggregates with any W-2 401(k) deferral target. §415(c) caps combined employee + employer at $72,000 (2026). Manual override for both employee dollar amount and employer percentage; auto-max default.
TestExpectedActualResult
$100K netSE age 45 auto-max: employee = $24,50024,50024,500✓
$100K netSE age 45 auto-max: employer ≈ $18,58718,58718,587✓
Traditional employee classification24,50024,500✓
Roth employee classification00✓
Age 62 SECURE 2.0 catch-up: employee = $35,75035,75035,750✓
§415(c) binds: employer + employee = annual addition limit72,00072,000✓
§415(c) clamp produces employer = $47,50047,50047,500✓
§402(g) aggregation: Solo limited by W-2 deferral target4,5004,500✓
Auto employer respects §415(c)18,58718,587✓
Roth employee: tradEmployee = 000✓
Roth employee: rothEmployee tracks amount24,50024,500✓
Roth employer: rothEmployerAmt > 018,58718,587✓
Roth employer: tradEmployer = 000✓
Manual deferral $10K respected10,00010,000✓
Manual employer 10% = $9,2949,2949,294✓
Negative SE: zero employee deferral00✓
Negative SE: zero employer profit-share00✓

Solo 401k Integration 9/9

Authority: Treas. Reg. §1.199A-3(b)(1)(vi) (retirement plan deductions reduce QBI); IRS Notice 2024-2 Q&A L9 (Roth employer profit-share treatment).
Method: End-to-end runProjection() integration tests verifying Solo 401(k) flows correctly: contribution amounts surface in year output (solo401kEmployee / solo401kEmployer fields); traditional contributions reduce AGI/MAGI; Roth employee preserves AGI; traditional employee + traditional employer + Roth employer all reduce QBI input; Roth employee preserves QBI input; spousal Solo 401(k) read independently via spouseSelfEmployed.
TestExpectedActualResult
solo401kEmployee surfaces in year output24,50024,500✓
solo401kEmployer ≈ $18,58718,58718,587✓
Traditional Solo reduces MAGI11✓
Roth employee preserves AGI11✓
Traditional Solo: qbiDeduction not larger than without Solo11✓
Roth employee preserves QBI deduction11✓
Roth employer: same QBI input as traditional employer (W-2+SE)11✓
Roth employer: higher MAGI than traditional employer11✓
Spouse Solo 401(k): contributions > 011✓

§402(g) precision 6/6

TestExpectedActualResult
Pool >> target (regression): solo401kEmployee = 000✓
Pool < target, traditional Solo: solo401kEmployee = $9,500 (was $0 pre-fix)9,5009,500✓
Pool < target, Roth Solo: solo401kEmployee = $9,500 (Roth deferrals count vs §402(g) per §402A)9,5009,500✓
Percent W-2 (25% of $98K = $24,500) consumes §402(g): solo401kEmployee = 000✓
Percent W-2 + pool clamp frees §402(g) room: solo401kEmployee = $9,5009,5009,500✓
Spouse percent W-2 (E4): spouse Solo §402(g) capacity consumed, solo401kEmployee = 000✓

QBI loss carry 6/6

TestExpectedActualResult
SE loss year-0: qbiLossCarryforward (carry-in) = 000✓
SE loss year-0: qbiLossCarryforwardNext = $30,000 (loss magnitude)30,00030,000✓
Loss y0 → profit y1: y1 qbiLossCarryforward = $30,000 (carry-in from y0)30,00030,000✓
Loss y0 → profit y1: y1 qbiLossCarryforwardNext = 0 (carry absorbed)00✓
Pre-existing carry $25K + y0 profit $40K: y0 qbiLossCarryforward = $25K (carry-in from params)25,00025,000✓
Pre-existing carry $25K + y0 profit $40K: y0 qbiLossCarryforwardNext = 0 (absorbed)00✓

Solo 401k S-Corp 6/6

TestExpectedActualResult
S-corp $100K salary: employer auto 25% = $25,00025,00025,000✓
S-corp $100K salary: employee auto-max = $24,50024,50024,500✓
S-corp: §415(c) not binding ($49,500 < $72,000)49,50049,500✓
S-corp manual 15%: employer = $15,00015,00015,000✓
S-corp $300K salary: §415(c) binds72,00072,000✓
S-corp §415(c): employer clamped to $47,50047,50047,500✓

S-Corp Integration 4/4

TestExpectedActualResult
S-corp: seTax = 0 (IRC §1402(a)(13))00✓
S-corp: FICA on salary ≈ $7,6507,6507,650✓
S-corp QBI deduction ≈ $10,10010,10010,100✓
S-corp age 62: QBI deduction ≈ $7,8507,8507,850✓

§603 Roth Catch-Up 11/11

TestExpectedActualResult
§603: trad base = $24,50024,50024,500✓
§603: forced Roth catch-up = $8,0008,0008,000✓
§603: total employee unchanged = $32,50032,50032,500✓
§603 + §109: trad base = $24,50024,50024,500✓
§603 + §109: forced Roth super catch-up = $11,25011,25011,250✓
§603 below threshold: all traditional = $32,50032,50032,500✓
§603 below threshold: no forced Roth00✓
§603 sole prop: all traditional = $32,50032,50032,500✓
§603 sole prop: no forced Roth00✓
§603 + already Roth: tradEmployee = 000✓
§603 + already Roth: all Roth = $32,50032,50032,500✓

§199A W-2 Limit 7/7

TestExpectedActualResult
S-corp above upper: W-2 limit not binding ($10,100)10,10010,100✓
Above upper, low W-2: limit binds at $10,00010,00010,000✓
UBIA path wins: 25%W2+2.5%UBIA = $17,50017,50017,500✓
Phase-in non-SSTB: W-2 high, no reduction = $16,00016,00016,000✓
Phase-in non-SSTB low W-2: reduction ≈ $13,91713,91713,917✓
Phase-in SSTB: phased amounts ≈ $5,7075,7075,707✓
Zero W-2 above upper: $400 floor (backward compat)400400✓

SEP-IRA 5/5

TestExpectedActualResult
SEP sole prop: employer 20% of planComp = $18,58718,58718,587✓
SEP sole prop: employee = $0 (no deferral)00✓
SEP S-corp: employer 25% of salary = $25,00025,00025,000✓
SEP S-corp: employee = $000✓
SEP S-corp: QBI deduction ≈ $15,00015,00015,000✓

SIMPLE IRA 23/23

TestExpectedActualResult
$100K netSE age 45: employee = $17,00017,00017,000✓
$100K netSE age 45: employer match = $3,0003,0003,000✓
Traditional employee classification17,00017,000✓
Age 52 catch-up: employee = $21,00021,00021,000✓
Age 52: employer match still = $3,0003,0003,000✓
Age 62 SECURE 2.0 super catch-up: employee = $22,25022,25022,250✓
Non-elective: employer = $2,0002,0002,000✓
Non-elective: employee still auto-maxes = $17,00017,00017,000✓
Manual $10K deferral respected10,00010,000✓
Match mirrors deferral (< 3% cap): $3,0003,0003,000✓
S-corp: employer match based on W-2 salary = $2,4002,4002,400✓
S-corp: employee deferral = $17,00017,00017,000✓
§402(g) aggregation: deferral capped at $4,5004,5004,500✓
Match tracks actual deferral: min($3K, $4.5K) = $3,0003,0003,000✓
Negative SE: zero employee deferral00✓
Negative SE: zero employer contribution00✓
§603: tradEmployee = $17,000 (base)17,00017,000✓
§603: rothEmployee = $4,000 (catch-up forced Roth)4,0004,000✓
§603: total deferral = $21,00021,00021,000✓
Roth employee: tradEmployee = 000✓
Roth employee: rothEmployee = $17,00017,00017,000✓
Employer always traditional: tradEmployer = $3,0003,0003,000✓
Employer rothEmployerAmt = 000✓

SIMPLE IRA Integration 5/5

TestExpectedActualResult
solo401kEmployee = SIMPLE deferral ≈ $17,00017,00017,000✓
solo401kEmployer = match ≈ $3,0003,0003,000✓
Traditional SIMPLE reduces AGI11✓
Non-elective employer ≈ $2,0002,0002,000✓
Traditional SIMPLE reduces QBI deduction11✓

Mega Backdoor Roth 5/5

TestExpectedActualResult
Auto-max: afterTax = $28,91328,91328,913✓
Employee + employer unchanged43,08743,087✓
Manual cap $15K respected15,00015,000✓
§415(c) ceiling: afterTax = $000✓
Disabled: afterTax = 000✓

Mega Backdoor Roth Integration 2/2

TestExpectedActualResult
solo401kAfterTax > 0 in year output11✓
solo401kAfterTax ≈ $28,91328,91328,913✓

Federal MRA 5/5

TestExpectedActualResult
Born 1970 → 57y0m684684✓
Born 1965 → 56y2m674674✓
Born 1959 → 56y0m672672✓
Born 1953 → 56y0m672672✓
Born 1948 → 55y2m662662✓

FERS Annuity 6/6

TestExpectedActualResult
30 yrs @ 1.0% × $100K high-3 = $30,00030,00030,000✓
Age 62 + 30 yrs → 1.1% × $100K = $33,00033,00033,000✓
Age 62 + 19 yrs stays 1.0% = $19,000 (the cliff)19,00019,000✓
Special category 25 yrs: 1.7%×20 + 1.0%×5 = $39,00039,00039,000✓
MRA+10 at 57, 15 yrs: 15,000 × (1 − 60×(5/1200)) = $11,25011,25011,250✓
MRA+10 postponed to 62: no reduction → $15,00015,00015,000✓

CSRS Annuity 2/2

TestExpectedActualResult
30 yrs: 1.5%×5 + 1.75%×5 + 2.0%×20 = 56.25% × $100K = $56,25056,25056,250✓
45 yrs capped at 80% of high-3 = $80,00080,00080,000✓

FERS SRS 3/3

TestExpectedActualResult
28 yrs, age-62 SS $21,600/yr → $15,120/yr ($1,260/mo)15,12015,120✓
20 yrs, $24,000 → $12,000/yr12,00012,000✓
Zero SS estimate → $0 (guard)00✓

Survivor 3/3

TestExpectedActualResult
Full 50% election costs 10%0.10.1✓
Partial 25% election costs 5%0.050.05✓
None costs 0%00✓

Simpl. Method 6/6

TestExpectedActualResult
Age 60 single → 310 payments310310✓
Age 65 single → 260 payments260260✓
Combined ages 120 w/ survivor → 360 payments360360✓
Taxable % = 91.9% (single 60, $50K basis, $24K annuity)91.991.9✓
With survivor (combined 120): 93.1%93.193.1✓
No basis → 100% taxable100100✓

FERS COLA 4/4

TestExpectedActualResult
CPI 1.5% → full 1.5%1.51.5✓
CPI 2.5% → 2.0%22✓
CPI 3.0% → 2.0% (CPI − 1)22✓
CPI 4.0% → 3.0%33✓

Eligibility 3/3

TestExpectedActualResult
FERS age 57 + 30 yrs: MRA+30 qualifies11✓
FERS age 57 + 30 yrs: 60+20 not yet00✓
FERS age 62 + 5 yrs: 62+5 qualifies11✓

Write-through 18/18

TestExpectedActualResult
FERS annuity written: $30,000 gross, $30,000 net (no election)30,00030,000✓
Pension source = government (F-288)11✓
Pension start age 57, survivorBenefit none5757✓
Diet COLA written from 2.5% inflation → 2.0%22✓
SRS stream written: 30/40 × 21,600 = $16,200, endAge 6116,20016,200✓
SRS endAge = 61 (stops before 62nd birthday)6161✓
Annual leave lump sum $25K written as ordinary income25,00025,000✓
SPIA: premium reduces brokerage by $100,000400,000400,000✓
SPIA: pension entry = $6,500/yr (100K × 6.5%)6,5006,500✓
SPIA: life-only (survivorBenefit none), starts at 6511✓
Projection: year-0 pension income = $6,5006,5006,500✓
Lump sum: $250,000 ordinary-income cash event at age 6511✓
Rollover apply: 0% taxable writes destination rollover-pretax11✓
Rollover projection: no ordinary income from the $250K at 65 (fedTax 0)00✓
Rollover projection: pre-tax grows by the full $250K at 65550,000550,000✓
Annuity: $30,000/yr, 2% COLA, starts at 6511✓
Future lumpAge: event dated at age 70 (not currentAge 65)11✓
Future lumpAge: pre-tax untouched at 68, +$250K at 7011✓

Projection 4/4

TestExpectedActualResult
Year 0 pension income = $30,00030,00030,000✓
Year 0 other = SRS $16,200 + annual leave $25,000 = $41,20041,20041,200✓
SRS stops at 62: other = 0 at age 62 (idx 5)00✓
NY state tax unchanged when the gov pension is removed (F-288 exempts it)2,0602,060✓

F2a 4/4

TestExpectedActualResult
FERS diet COLA: flat until 62 (age 61 = $30,000)30,00030,000✓
FERS diet COLA: 2.0% from 62 onward (age 62 = $30,600)30,60030,600✓
FERS diet COLA compounds (age 63 = $31,212)31,21231,212✓
cpiFull: full 2.5% inflation COLA (age 58 = $30,750)30,75030,750✓

F2b 2/2

TestExpectedActualResult
SRS with $40K earned income: reduced by (40,000−24,480)×0.5 = $7,760 → $8,4408,4408,440✓
SRS with no earned income: full $16,200 (control)16,20016,200✓

F2c 3/3

TestExpectedActualResult
Retiree alive: $30K unreduced × (1−0.10) = $27,00027,00027,000✓
Survivor (after death at 60): 50% of UNREDUCED = $15,00015,00015,000✓
Partial 25% election: retiree 28,500, survivor 25% of unreduced = $7,5007,5007,500✓

F2d 3/3

TestExpectedActualResult
Control (no exception): pre-59.5 TSP draws penalized 10%11✓
Separated at 55+ (age55): TSP draws before 59.5 penalty-free11✓
Public-safety (LEO/FF/ATC): draws from 50 penalty-free11✓

PensionCompare 10/10

TestExpectedActualResult
Lump sum PV after 22% tax = $390,000390,000390,000✓
Annuity PV = 23,400 × annuity-due(21, 5%) ≈ $315,000315,000315,016✓
Winner = lump sum (390,000 > 315,000)11✓
Break-even age = 81 (17 after-tax payments × 23,400 = 397,800 ≥ 390,000)8181✓
Winner = annuity when PV of $30K/yr over 31 yrs beats $100K11✓
Rollover (0% taxable): lump-sum PV = full $500,000500,000500,000✓
No annuity payment → winner = lump sum11✓
Lump received at 60 (annuity starts 65): grown 5 yrs @5%497,750497,750✓
Lump received at 70 (annuity starts 65): discounted 5 yrs @5%305,575305,575✓
Lump received at the annuity start age (65) = face $390K390,000390,000✓

SPIA 3/3

TestExpectedActualResult
$200,000 at 6.5% → $13,000/yr13,00013,000✓
$100,000 at 7.0% → $7,000/yr7,0007,000✓
Zero premium → $0 (guard)00✓

Pre-flight 3/3

TestExpectedActualResult
calcSpousalBenefit existsfunctionfunction✓
calcSpousalBenefit has 4 parameters (not old 2-param version)44✓
ssReverseAdjustPIA existstruetrue✓

Spousal SS 13/13

TestExpectedActualResult
Spouse at FRA (67), no own record → 50% of worker PIA12,00012,000✓
Worker PIA = $3,000/mo, spouse at FRA → $18,000/yr floor18,00018,000✓
Spouse own = $9,600/yr (< $12K floor) → top-up = $2,4002,4002,400✓
Spouse own = $600/mo ($7,200/yr) → top-up = $4,8004,8004,800✓
Spouse own = $12,000/yr (= floor) → top-up = $000✓
Spouse own = $13,200/yr (> floor) → top-up = $000✓
Spouse claims at 62 (60mo early), no own record → 32.5% of worker PIA7,8007,800✓
Spouse claims at 65 (24mo early) → ~41.7% of worker PIA annual10,00010,000✓
FRA=66, claims at 62 (48mo early) → 35% of worker PIA8,4008,400✓
Spouse claims at 70 (past FRA) → same as FRA, no DRC increase12,00012,000✓
Spouse claims at 68 (past FRA) → same as FRA12,00012,000✓
Worker PIA = $0 → top-up = $000✓
Spouse own = $0, worker PIA = $0 → $000✓

COLA 2/2

TestExpectedActualResult
Spousal benefit grows with COLA at age 68 vs 6711✓
Spousal top-up at 68 ≈ $18,360 (18,000 × 1.02)18,36018,360✓

UI Wording 4/4

TestExpectedActualResult
"Spousal SS Benefit" Plan Health lever title exists in HTML11✓
Lever says benefit is "already included in your projection"11✓
Lever links to SS tab (inputTab=ss)11✓
Old 2-param calcSpousalBenefit signature is NOT present11✓

Survivor claim-age cap 6/6

TestExpectedActualResult
Primary dies y0, survivor past FRA → survivor benefit applies immediately, not deferred to age 8558,34458,344✓
Primary dies y0, survivor benefit holds across years (not just y0)58,34458,344✓
Spouse dies y0, primary is lower earner → survivor benefit dominates immediately60,00060,000✓
Pre-FRA strategic claim age respected — survivor 62 with claim age 65 → no benefit yet11✓
Pre-FRA strategic claim age respected — survivor benefit starts at chosen age 6511✓
User-set claim age above FRA is capped — no waiting past FRA per RS 00615.30111✓

Reverse round-trip 16/16

TestExpectedActualResult
PIA $1500 claim 62 → adj $1050 → reverse $15001,5001,500✓
PIA $1500 claim 65 → adj $1300 → reverse $15001,5001,500✓
PIA $1500 claim 67 → adj $1500 → reverse $15001,5001,500✓
PIA $1500 claim 68 → adj $1620 → reverse $15001,5001,500✓
PIA $1500 claim 70 → adj $1860 → reverse $15001,5001,500✓
PIA $2500 claim 62 → adj $1750 → reverse $25002,5002,500✓
PIA $2500 claim 65 → adj $2166 → reverse $24992,5002,499✓
PIA $2500 claim 67 → adj $2500 → reverse $25002,5002,500✓
PIA $2500 claim 68 → adj $2700 → reverse $25002,5002,500✓
PIA $2500 claim 70 → adj $3100 → reverse $25002,5002,500✓
PIA $3500 claim 62 → adj $2450 → reverse $35003,5003,500✓
PIA $3500 claim 65 → adj $3033 → reverse $35003,5003,500✓
PIA $3500 claim 67 → adj $3500 → reverse $35003,5003,500✓
PIA $3500 claim 68 → adj $3780 → reverse $35003,5003,500✓
PIA $3500 claim 70 → adj $4340 → reverse $35003,5003,500✓
Zero current monthly returns 000✓

Primary already-collecting 4/4

TestExpectedActualResult
Age 75: ss = current monthly × 12 (COLA 0%)50,40050,400✓
Age 80: ss still 50400 (COLA 0%)50,40050,400✓
Age 75 with COLA 2%: ss = 50400 (zero COLA years)50,40050,400✓
Age 80 with COLA 2%: ss ≈ 50400 × 1.02^5 ≈ 5564855,64855,646✓

Spousal floor 1/1

TestExpectedActualResult
Already-collecting primary supplies recovered PIA for spousal floor54,00054,000✓

Spouse already-collecting 2/2

TestExpectedActualResult
Primary not yet filed, spouse already collecting → ss > 011✓
Spouse benefit constant when primary still not filed (COLA 0%)11✓

Both already-collecting 2/2

TestExpectedActualResult
Year 0 (age 73): combined ss ≈ (3500+1800) × 12 = 6360063,60063,600✓
Five years later (age 78): combined ss ≈ 63600 × 1.02^5 ≈ 7022170,22170,219✓

Backwards compat 1/1

TestExpectedActualResult
Non-working spouse with ssAlreadyCollecting:false → $54K at FRA54,00054,000✓

Clarks Y0 37/37

TestExpectedActualResult
year = 20262,0262,026✓
age = 646464✓
spAge = 626262✓
earned = 182000182,000182,000✓
primaryEarned = 110000110,000110,000✓
spouseEarned = 7200072,00072,000✓
savings = 2800028,00028,000✓
pension = 000✓
ss = 000✓
ssTaxable = 000✓
rmd = 000✓
rmdRequired = falsefalsefalse✓
rothConv = 000✓
hsaContrib = 000✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 000✓
capGains = 000✓
agi = 165200165,200165,200✓
fedTax = 1720217,20217,202✓
stateTax = 000✓
fica = 1392313,92313,923✓
cgTax = 000✓
niitTax = 000✓
qbiDeduction = 000✓
conversionTax = 000✓
tax = 3112531,12531,125✓
medicareCost = 18261,8261,826✓
irmaa = 000✓
irmaaBracket = 000✓
preTax = 873976873,976873,976✓
roth = 167990167,990167,990✓
taxable = 111494111,494111,494✓
netWorth = 11534601,153,4601,153,460✓
retired === nulltruetrue✓

Clarks Y1 37/37

TestExpectedActualResult
year = 20272,0272,027✓
age = 656565✓
spAge = 636363✓
retired = truetruetrue✓
earned = 7344073,44073,440✓
primaryEarned = 000✓
spouseEarned = 7344073,44073,440✓
savings = 000✓
pension = 000✓
ss = 000✓
ssTaxable = 000✓
rmd = 000✓
rmdRequired = falsefalsefalse✓
rothConv = 000✓
hsaContrib = 000✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 1980719,80719,807✓
capGains = 99049,9049,904✓
agi = 7722477,22477,224✓
fedTax = 26722,6722,672✓
stateTax = 000✓
fica = 56185,6185,618✓
cgTax = 000✓
niitTax = 000✓
qbiDeduction = 000✓
conversionTax = 000✓
tax = 82908,2908,290✓
medicareCost = 25572,5572,557✓
irmaa = 000✓
irmaaBracket = 000✓
preTax = 935154935,154935,154✓
roth = 179749179,749179,749✓
taxable = 9810598,10598,105✓
netWorth = 12130081,213,0081,213,008✓

Clarks Y3 37/37

TestExpectedActualResult
year = 20292,0292,029✓
age = 676767✓
spAge = 656565✓
retired = truetruetrue✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
savings = 000✓
pension = 000✓
ss = 3311033,11033,110✓
ssTaxable = 81498,1498,149✓
rmd = 000✓
rmdRequired = falsefalsefalse✓
rothConv = 000✓
hsaContrib = 000✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 5994659,94659,946✓
capGains = 2997329,97329,973✓
agi = 3812238,12238,122✓
fedTax = 000✓
stateTax = 000✓
fica = 000✓
cgTax = 000✓
niitTax = 000✓
qbiDeduction = 000✓
conversionTax = 000✓
tax = 000✓
medicareCost = 56385,6385,638✓
irmaa = 000✓
irmaaBracket = 000✓
preTax = 10706581,070,6581,070,658✓
roth = 205795205,795205,795✓
taxable = 2393623,93623,936✓
netWorth = 13003891,300,3891,300,389✓

Clarks Y5 37/37

TestExpectedActualResult
year = 20312,0312,031✓
age = 696969✓
spAge = 676767✓
retired = truetruetrue✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
savings = 000✓
pension = 000✓
ss = 5962059,62059,620✓
ssTaxable = 3009630,09630,096✓
rmd = 000✓
rmdRequired = falsefalsefalse✓
rothConv = 000✓
hsaContrib = 000✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 4253842,53842,538✓
capGains = 000✓
agi = 7263472,63472,634✓
fedTax = 32103,2103,210✓
stateTax = 000✓
fica = 000✓
cgTax = 000✓
niitTax = 000✓
qbiDeduction = 000✓
conversionTax = 000✓
tax = 32103,2103,210✓
medicareCost = 62156,2156,215✓
irmaa = 000✓
irmaaBracket = 000✓
preTax = 11330761,133,0761,133,076✓
roth = 235615235,615235,615✓
taxable = 000✓
netWorth = 13686911,368,6911,368,691✓

Clarks Y11 37/37

TestExpectedActualResult
year = 20372,0372,037✓
age = 757575✓
spAge = 737373✓
retired = truetruetrue✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
savings = 000✓
pension = 000✓
ss = 6714267,14267,142✓
ssTaxable = 4630646,30646,306✓
rmd = 5221852,21852,218✓
rmdRequired = truetruetrue✓
rothConv = 000✓
hsaContrib = 000✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 5784857,84857,848✓
capGains = 000✓
agi = 104154104,154104,154✓
fedTax = 59265,9265,926✓
stateTax = 000✓
fica = 000✓
cgTax = 000✓
niitTax = 000✓
qbiDeduction = 000✓
conversionTax = 000✓
tax = 59265,9265,926✓
medicareCost = 83298,3298,329✓
irmaa = 000✓
irmaaBracket = 000✓
preTax = 13125961,312,5961,312,596✓
roth = 353594353,594353,594✓
taxable = 000✓
netWorth = 16661901,666,1901,666,190✓

Clarks Y31 37/37

TestExpectedActualResult
year = 20572,0572,057✓
age = 959595✓
spAge = 939393✓
retired = truetruetrue✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
savings = 000✓
pension = 000✓
ss = 9977099,77099,770✓
ssTaxable = 8480584,80584,805✓
rmd = 140446140,446140,446✓
rmdRequired = truetruetrue✓
rothConv = 000✓
hsaContrib = 000✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 213238213,238213,238✓
capGains = 3639636,39636,396✓
agi = 261646261,646261,646✓
fedTax = 1558915,58915,589✓
stateTax = 000✓
fica = 000✓
cgTax = 000✓
niitTax = 443443443✓
qbiDeduction = 000✓
conversionTax = 000✓
tax = 1558915,58915,589✓
medicareCost = 2210022,10022,100✓
irmaa = 000✓
irmaaBracket = 000✓
preTax = 11871881,187,1881,187,188✓
roth = 13682981,368,2981,368,298✓
taxable = 8058480,58480,584✓
netWorth = 26360702,636,0702,636,070✓

Lees Y0 43/43

TestExpectedActualResult
year = 20262,0262,026✓
age = 595959✓
spAge = 575757✓
earned = 267000267,000267,000✓
primaryEarned = 175000175,000175,000✓
spouseEarned = 9200092,00092,000✓
totalInc = 269250269,250269,250✓
savings = 4300043,00043,000✓
hsaContrib = 85508,5508,550✓
cashInterest = 22502,2502,250✓
pension = 000✓
ss = 000✓
ssTaxable = 000✓
rmd = 000✓
rmdRequired = falsefalsefalse✓
rothConv = 000✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 000✓
capGains = 000✓
agi = 245700245,700245,700✓
fedTax = 3643636,43636,436✓
stateTax = 1387013,87013,870✓
fica = 1984819,84819,848✓
cgTax = 000✓
niitTax = 000✓
qbiDeduction = 000✓
conversionTax = 000✓
tax = 7015470,15470,154✓
medicareCost = 000✓
irmaa = 000✓
irmaaBracket = 000✓
acaSubsidy = 000✓
expenses = 8800088,00088,000✓
preTax = 11930501,193,0501,193,050✓
roth = 217745217,745217,745✓
taxable = 324565324,565324,565✓
hsa = 4616346,16346,163✓
netWorth = 17815231,781,5231,781,523✓
retired === nulltruetrue✓
isSingle === nulltruetrue✓

Lees Y3 54/54

TestExpectedActualResult
year = 20292,0292,029✓
age = 626262✓
spAge = 606060✓
earned = 9763197,63197,631✓
primaryEarned = 000✓
spouseEarned = 9763197,63197,631✓
totalInc = 100199100,199100,199✓
savings = 000✓
hsaContrib = 85508,5508,550✓
cashInterest = 16041,6041,604✓
pension = 000✓
ss = 000✓
ssTaxable = 000✓
rmd = 000✓
rmdRequired = falsefalsefalse✓
rothConv = 1297212,97212,972✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 2142321,42321,423✓
withdrawalForSpending = 2142321,42321,423✓
capGains = 000✓
conversionSaleGain = 000✓
agi = 8865788,65788,657✓
fica = 74697,4697,469✓
niitTax = 000✓
qbiDeduction = 000✓
drawdownTax = 000✓
taxAwareDrawdown = 000✓
medicareCost = 000✓
irmaa = 000✓
irmaaBracket = 000✓
healthcare = 88308,8308,830✓
acaSubsidy = 1650816,50816,508✓
acaBenchmark = 2533825,33825,338✓
acaNetPremium = 88308,8308,830✓
acaFPLPercent = 395395395✓
expenses = 104990104,990104,990✓
cash = 3561235,61235,612✓
preTax = 14832071,483,2071,483,207✓
preTaxPrimary = 10616511,061,6511,061,651✓
preTaxSpouse = 421556421,556421,556✓
roth = 336321336,321336,321✓
taxable = 382917382,917382,917✓
taxableInv = 347305347,305347,305✓
hsa = 8383483,83483,834✓
netWorth = 22862792,286,2792,286,279✓
retired === truetruetrue✓
isSingle === nulltruetrue✓
healthcareSource === "aca"truetrue✓
agi === sum of AGI components (IRC 62)truetrue✓
netWorth === preTax + roth + cash + taxableInv + hsatruetrue✓
conversionSaleGain in [0, capGains] (Gate C)truetrue✓

Lees Y5 53/53

TestExpectedActualResult
year = 20312,0312,031✓
age = 646464✓
spAge = 626262✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
totalInc = 481481481✓
savings = 000✓
hsaContrib = 61016,1016,101✓
cashInterest = 000✓
pension = 000✓
ss = 000✓
ssTaxable = 000✓
rmd = 000✓
rmdRequired = falsefalsefalse✓
rothConv = 4577745,77745,777✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 117063117,063117,063✓
withdrawalForSpending = 117063117,063117,063✓
capGains = 5256052,56052,560✓
conversionSaleGain = 252525✓
agi = 9223692,23692,236✓
fica = 000✓
niitTax = 000✓
qbiDeduction = 000✓
drawdownTax = 000✓
taxAwareDrawdown = 000✓
irmaa = 000✓
irmaaBracket = 000✓
healthcare = 91879,1879,187✓
acaSubsidy = 1425414,25414,254✓
acaBenchmark = 2344123,44123,441✓
acaNetPremium = 91879,1879,187✓
acaFPLPercent = 395395395✓
expenses = 112498112,498112,498✓
cash = 000✓
preTax = 16332771,633,2771,633,277✓
preTaxPrimary = 11654431,165,4431,165,443✓
preTaxSpouse = 467834467,834467,834✓
roth = 449900449,900449,900✓
taxable = 287774287,774287,774✓
taxableInv = 287774287,774287,774✓
hsa = 108897108,897108,897✓
netWorth = 24798482,479,8482,479,848✓
retired === truetruetrue✓
isSingle === nulltruetrue✓
healthcareSource === "aca"truetrue✓
agi === sum of AGI components (IRC 62)truetrue✓
netWorth === preTax + roth + cash + taxableInv + hsatruetrue✓
conversionSaleGain in [0, capGains] (Gate C)truetrue✓

Lees Y6 54/54

TestExpectedActualResult
year = 20322,0322,032✓
age = 656565✓
spAge = 636363✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
totalInc = 000✓
savings = 000✓
hsaContrib = 62546,2546,254✓
cashInterest = 000✓
pension = 000✓
ss = 000✓
ssTaxable = 000✓
rmd = 000✓
rmdRequired = falsefalsefalse✓
rothConv = 4088140,88140,881✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 122172122,172122,172✓
withdrawalForSpending = 122172122,172122,172✓
capGains = 5945459,45459,454✓
conversionSaleGain = 000✓
agi = 9408194,08194,081✓
fica = 000✓
niitTax = 000✓
qbiDeduction = 000✓
drawdownTax = 000✓
taxAwareDrawdown = 000✓
medicareCost = 32633,2633,263✓
irmaa = 000✓
irmaaBracket = 000✓
healthcare = 93709,3709,370✓
acaSubsidy = 65506,5506,550✓
acaBenchmark = 1592015,92015,920✓
acaNetPremium = 93709,3709,370✓
acaFPLPercent = 395395395✓
expenses = 117710117,710117,710✓
cash = 000✓
preTax = 17038641,703,8641,703,864✓
preTaxPrimary = 12158111,215,8111,215,811✓
preTaxSpouse = 488053488,053488,053✓
roth = 525135525,135525,135✓
taxable = 183143183,143183,143✓
taxableInv = 183143183,143183,143✓
hsa = 118601118,601118,601✓
netWorth = 25307432,530,7432,530,743✓
retired === truetruetrue✓
isSingle === nulltruetrue✓
healthcareSource === "aca"truetrue✓
agi === sum of AGI components (IRC 62)truetrue✓
netWorth === preTax + roth + cash + taxableInv + hsatruetrue✓
conversionSaleGain in [0, capGains] (Gate C)truetrue✓

Lees Y11 54/54

TestExpectedActualResult
year = 20372,0372,037✓
age = 707070✓
spAge = 686868✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
totalInc = 9053890,53890,538✓
savings = 000✓
hsaContrib = 000✓
cashInterest = 000✓
pension = 000✓
ss = 9053890,53890,538✓
ssTaxable = 3725637,25637,256✓
rmd = 000✓
rmdRequired = falsefalsefalse✓
rothConv = 000✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 4383243,83243,832✓
withdrawalForSpending = 4383243,83243,832✓
capGains = 000✓
conversionSaleGain = 000✓
agi = 7275972,75972,759✓
fica = 000✓
niitTax = 000✓
qbiDeduction = 000✓
drawdownTax = 000✓
taxAwareDrawdown = 000✓
medicareCost = 83298,3298,329✓
irmaa = 000✓
irmaaBracket = 000✓
healthcare = 000✓
acaSubsidy = 000✓
acaBenchmark = 000✓
acaNetPremium = 000✓
acaFPLPercent = 000✓
expenses = 130142130,142130,142✓
cash = 000✓
preTax = 20192552,019,2552,019,255✓
preTaxPrimary = 14408621,440,8621,440,862✓
preTaxSpouse = 578393578,393578,393✓
roth = 736530736,530736,530✓
taxable = 000✓
taxableInv = 000✓
hsa = 117924117,924117,924✓
netWorth = 28737092,873,7092,873,709✓
retired === truetruetrue✓
isSingle === nulltruetrue✓
healthcareSource === nulltruetrue✓
taxConvergenceFlag === truetruetrue✓
agi === sum of AGI components (IRC 62)truetrue✓
netWorth === preTax + roth + cash + taxableInv + hsatruetrue✓

Lees Y16 55/55

TestExpectedActualResult
year = 20422,0422,042✓
age = 757575✓
spAge = 737373✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
totalInc = 9996199,96199,961✓
savings = 000✓
hsaContrib = 000✓
cashInterest = 000✓
pension = 000✓
ss = 9996199,96199,961✓
ssTaxable = 7181271,81271,812✓
rmd = 7144571,44571,445✓
rmdPrimary = 7144571,44571,445✓
rmdSpouse = 000✓
rmdReinvested = 1907619,07619,076✓
rothConv = 000✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 8207682,07682,076✓
withdrawalForSpending = 6300063,00063,000✓
capGains = 000✓
agi = 143257143,257143,257✓
fica = 000✓
niitTax = 000✓
qbiDeduction = 000✓
drawdownTax = 000✓
taxAwareDrawdown = 000✓
medicareCost = 1063110,63110,631✓
irmaa = 000✓
irmaaBracket = 000✓
healthcare = 000✓
acaSubsidy = 000✓
acaBenchmark = 000✓
acaNetPremium = 000✓
acaFPLPercent = 000✓
expenses = 151845151,845151,845✓
cash = 000✓
preTax = 25590512,559,0512,559,051✓
preTaxPrimary = 18041421,804,1421,804,142✓
preTaxSpouse = 754909754,909754,909✓
roth = 10330211,033,0211,033,021✓
taxable = 2041120,41120,411✓
taxableInv = 2041120,41120,411✓
hsa = 100383100,383100,383✓
netWorth = 37128663,712,8663,712,866✓
retired === truetruetrue✓
isSingle === nulltruetrue✓
healthcareSource === nulltruetrue✓
taxConvergenceFlag === falsetruetrue✓
rmdRequired === truetruetrue✓
netWorth === preTax + roth + cash + taxableInv + hsatruetrue✓

Lees Y36 55/55

TestExpectedActualResult
year = 20622,0622,062✓
age = 959595✓
spAge = 939393✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
totalInc = 148536148,536148,536✓
savings = 000✓
hsaContrib = 000✓
cashInterest = 000✓
pension = 000✓
ss = 148536148,536148,536✓
ssTaxable = 126256126,256126,256✓
rmd = 284089284,089284,089✓
rmdPrimary = 197450197,450197,450✓
rmdSpouse = 8663986,63986,639✓
rmdReinvested = 284089284,089284,089✓
rothConv = 000✓
qcd = 000✓
charityTotal = 000✓
isItemizing = 000✓
isBunchYear = falsefalsefalse✓
withdrawal = 495926495,926495,926✓
withdrawalForSpending = 211837211,837211,837✓
capGains = 105919105,919105,919✓
agi = 516263516,263516,263✓
fica = 000✓
niitTax = 40254,0254,025✓
qbiDeduction = 000✓
drawdownTax = 000✓
taxAwareDrawdown = 000✓
medicareCost = 2820628,20628,206✓
irmaa = 000✓
irmaaBracket = 000✓
healthcare = 000✓
acaSubsidy = 000✓
acaBenchmark = 000✓
acaNetPremium = 000✓
acaFPLPercent = 000✓
expenses = 283254283,254283,254✓
cash = 000✓
preTax = 25126512,512,6512,512,651✓
preTaxPrimary = 16690501,669,0501,669,050✓
preTaxSpouse = 843601843,601843,601✓
roth = 39974653,997,4653,997,465✓
taxable = 24490012,449,0012,449,001✓
taxableInv = 24490012,449,0012,449,001✓
hsa = 000✓
netWorth = 89591178,959,1178,959,117✓
retired === truetruetrue✓
isSingle === nulltruetrue✓
healthcareSource === nulltruetrue✓
taxConvergenceFlag === falsetruetrue✓
rmdRequired === truetruetrue✓
netWorth === preTax + roth + cash + taxableInv + hsatruetrue✓

Lees F-241 gates 9/9

TestExpectedActualResult
G1: conversionTaxFallback === 0 (all years)11✓
G2 precond: wdRoth === 0 (all years)11✓
G2: roth continuity recurrence (drift <= 2)11✓
G3: preTax continuity recurrence (drift <= 2)11✓
G4: netWorth === preTax+roth+taxableInv+cash+hsa+inherited (all years)11✓
G6a: roth non-decreasing (never drawn)11✓
G6b: rothConv > 0 iff year in {3,4,5,6} (window 62-65)11✓
G6c: conversionSaleGain > 0 only in the brokerage-funded year (Y5)11✓
G7: smartFill caps ACA-year MAGI at the 395% FPL cliff ceiling (F-304, drift <= 1)11✓

Lees F-240 gain band 1/1

TestExpectedActualResult
sum(conversionSaleGain, Y3-Y6) <= 0.5 * sum(conversionTax, Y3-Y6)truetrue✓

Washingtons Y0 41/41

TestExpectedActualResult
year = 20262,0262,026✓
age = 636363✓
spAge = 616161✓
earned = 208000208,000208,000✓
primaryEarned = 130000130,000130,000✓
spouseEarned = 7800078,00078,000✓
savings = 3200032,00032,000✓
cashInterest = 45004,5004,500✓
cash = 104500104,500104,500✓
agi = 193300193,300193,300✓
fedTax = 2486624,86624,866✓
stateTax = 96659,6659,665✓
fica = 1591215,91215,912✓
tax = 5044350,44350,443✓
debtPayments = 2160021,60021,600✓
debtInterest = 71377,1377,137✓
debtPrincipalPaid = 1446314,46314,463✓
expenses = 103600103,600103,600✓
preTax = 10370441,037,0441,037,044✓
preTaxPrimary = 10370441,037,0441,037,044✓
preTaxSpouse = 000✓
roth = 276060276,060276,060✓
taxableInv = 219136219,136219,136✓
netWorth = 16367401,636,7401,636,740✓
pension = 000✓
ss = 000✓
ssTaxable = 000✓
ssdi = 000✓
rmd = 000✓
rmdPrimary = 000✓
rmdSpouse = 000✓
rmdRequired = falsefalsefalse✓
ltcCost = 000✓
medicareCost = 000✓
irmaa = 000✓
irmaaBracket = 000✓
niitTax = 000✓
retired === nulltruetrue✓
isSingle === nulltruetrue✓
healthcareSource === nulltruetrue✓
taxConvergenceFlag === falsetruetrue✓

Washingtons Y2 42/42

TestExpectedActualResult
year = 20282,0282,028✓
age = 656565✓
spAge = 636363✓
earned = 8115181,15181,151✓
primaryEarned = 000✓
spouseEarned = 8115181,15181,151✓
savings = 000✓
cashInterest = 43414,3414,341✓
cash = 100814100,814100,814✓
agi = 108001108,001108,001✓
fedTax = 74067,4067,406✓
stateTax = 39003,9003,900✓
fica = 62086,2086,208✓
tax = 1751417,51417,514✓
pension = 3000030,00030,000✓
pensionTaxable = 3000030,00030,000✓
ss = 000✓
ssTaxable = 000✓
ssdi = 000✓
rmd = 000✓
rmdPrimary = 000✓
rmdSpouse = 000✓
rmdRequired = falsefalsefalse✓
medicareCost = 26852,6852,685✓
irmaa = 000✓
irmaaBracket = 000✓
ltcCost = 000✓
niitTax = 000✓
debtPayments = 2160021,60021,600✓
debtInterest = 59345,9345,934✓
debtPrincipalPaid = 1566615,66615,666✓
expenses = 111279111,279111,279✓
preTax = 12097331,209,7331,209,733✓
preTaxPrimary = 12097331,209,7331,209,733✓
preTaxSpouse = 000✓
roth = 325403325,403325,403✓
taxableInv = 256494256,494256,494✓
netWorth = 18924441,892,4441,892,444✓
retired === truetruetrue✓
isSingle === nulltruetrue✓
healthcareSource === nulltruetrue✓
taxConvergenceFlag === falsetruetrue✓

Washingtons Y7 46/46

TestExpectedActualResult
year = 20332,0332,033✓
age = 707070✓
spAge = 686868✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
savings = 000✓
cashInterest = 000✓
cash = 000✓
agi = 8581285,81285,812✓
fedTax = 28352,8352,835✓
stateTax = 000✓
fica = 000✓
tax = 28352,8352,835✓
pension = 3312233,12233,122✓
pensionTaxable = 3312233,12233,122✓
ss = 7129271,29271,292✓
ssTaxable = 3883238,83238,832✓
ssdi = 000✓
rmd = 000✓
rmdPrimary = 000✓
rmdSpouse = 000✓
rmdRequired = falsefalsefalse✓
medicareCost = 68536,8536,853✓
irmaa = 000✓
irmaaBracket = 000✓
ltcCost = 000✓
niitTax = 000✓
capGains = 1385813,85813,858✓
cgTax = 000✓
withdrawal = 2771627,71627,716✓
withdrawalForSpending = 2771627,71627,716✓
debtPayments = 2160021,60021,600✓
debtInterest = 24722,4722,472✓
debtPrincipalPaid = 1912819,12819,128✓
expenses = 129303129,303129,303✓
preTax = 16967141,696,7141,696,714✓
preTaxPrimary = 16967141,696,7141,696,714✓
preTaxSpouse = 000✓
roth = 456395456,395456,395✓
taxableInv = 137048137,048137,048✓
netWorth = 22901572,290,1572,290,157✓
retired === truetruetrue✓
isSingle === nulltruetrue✓
healthcareSource === nulltruetrue✓
taxConvergenceFlag === falsetruetrue✓

Washingtons Y9 46/46

TestExpectedActualResult
year = 20352,0352,035✓
age = 727272✓
spAge = 707070✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
savings = 000✓
cashInterest = 000✓
cash = 000✓
agi = 9214992,14992,149✓
fedTax = 30573,0573,057✓
stateTax = 000✓
fica = 000✓
tax = 30573,0573,057✓
pension = 3446134,46134,461✓
pensionTaxable = 3446134,46134,461✓
ss = 7417274,17274,172✓
ssTaxable = 4240542,40542,405✓
ssdi = 000✓
rmd = 000✓
rmdPrimary = 000✓
rmdSpouse = 000✓
rmdRequired = falsefalsefalse✓
medicareCost = 75557,5557,555✓
irmaa = 000✓
irmaaBracket = 000✓
ltcCost = 000✓
niitTax = 000✓
capGains = 1528315,28315,283✓
cgTax = 000✓
withdrawal = 3056630,56630,566✓
withdrawalForSpending = 3056630,56630,566✓
debtPayments = 2160021,60021,600✓
debtInterest = 882882882✓
debtPrincipalPaid = 2071820,71820,718✓
expenses = 136146136,146136,146✓
preTax = 19425671,942,5671,942,567✓
preTaxPrimary = 19425671,942,5671,942,567✓
preTaxSpouse = 000✓
roth = 522527522,527522,527✓
taxableInv = 9086790,86790,867✓
netWorth = 25559612,555,9612,555,961✓
retired === truetruetrue✓
isSingle === nulltruetrue✓
healthcareSource === nulltruetrue✓
taxConvergenceFlag === falsetruetrue✓

Washingtons Y12 47/47

TestExpectedActualResult
year = 20382,0382,038✓
age = 757575✓
spAge = 737373✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
savings = 000✓
cashInterest = 000✓
cash = 000✓
agi = 201247201,247201,247✓
fedTax = 1759217,59217,592✓
stateTax = 42984,2984,298✓
fica = 000✓
tax = 2189021,89021,890✓
pension = 3657036,57036,570✓
pensionTaxable = 3657036,57036,570✓
ss = 7871278,71278,712✓
ssTaxable = 6690566,90566,905✓
ssdi = 000✓
rmd = 9040890,40890,408✓
rmdPrimary = 9040890,40890,408✓
rmdSpouse = 000✓
rmdRequired = truetruetrue✓
rmdReinvested = 9040890,40890,408✓
medicareCost = 87468,7468,746✓
irmaa = 000✓
irmaaBracket = 000✓
ltcCost = 000✓
niitTax = 000✓
capGains = 73647,3647,364✓
cgTax = 11051,1051,105✓
withdrawal = 105137105,137105,137✓
withdrawalForSpending = 1472914,72914,729✓
debtPayments = 000✓
debtInterest = 000✓
debtPrincipalPaid = 000✓
expenses = 108122108,122108,122✓
preTax = 22829922,282,9922,282,992✓
preTaxPrimary = 22829922,282,9922,282,992✓
preTaxSpouse = 000✓
roth = 640118640,118640,118✓
taxableInv = 153901153,901153,901✓
netWorth = 30770113,077,0113,077,011✓
retired === truetruetrue✓
isSingle === nulltruetrue✓
healthcareSource === nulltruetrue✓
taxConvergenceFlag === truetruetrue✓

Washingtons Y32 47/47

TestExpectedActualResult
year = 20582,0582,058✓
age = 959595✓
spAge = 939393✓
earned = 000✓
primaryEarned = 000✓
spouseEarned = 000✓
savings = 000✓
cashInterest = 000✓
cash = 000✓
agi = 463543463,543463,543✓
fedTax = 5274352,74352,743✓
stateTax = 1461214,61214,612✓
fica = 000✓
tax = 6735567,35567,355✓
pension = 5434154,34154,341✓
pensionTaxable = 5434154,34154,341✓
ss = 116962116,962116,962✓
ssTaxable = 9941899,41899,418✓
ssdi = 000✓
rmd = 249857249,857249,857✓
rmdPrimary = 249857249,857249,857✓
rmdSpouse = 000✓
rmdRequired = truetruetrue✓
rmdReinvested = 249857249,857249,857✓
medicareCost = 2320523,20523,205✓
irmaa = 000✓
irmaaBracket = 000✓
ltcCost = 000✓
niitTax = 22772,2772,277✓
capGains = 5992859,92859,928✓
cgTax = 89898,9898,989✓
withdrawal = 369712369,712369,712✓
withdrawalForSpending = 119855119,855119,855✓
debtPayments = 000✓
debtInterest = 000✓
debtPrincipalPaid = 000✓
expenses = 223804223,804223,804✓
preTax = 21120432,112,0432,112,043✓
preTaxPrimary = 21120432,112,0432,112,043✓
preTaxSpouse = 000✓
roth = 24770542,477,0542,477,054✓
taxableInv = 18270261,827,0261,827,026✓
netWorth = 64161236,416,1236,416,123✓
retired === truetruetrue✓
isSingle === nulltruetrue✓
healthcareSource === nulltruetrue✓
taxConvergenceFlag === falsetruetrue✓

Clarks Cruise 178/178

TestExpectedActualResult
projection.length = 323232✓
Y1.year = Y0.year + 12,0272,027✓
Y2.year = Y1.year + 12,0282,028✓
Y3.year = Y2.year + 12,0292,029✓
Y4.year = Y3.year + 12,0302,030✓
Y5.year = Y4.year + 12,0312,031✓
Y6.year = Y5.year + 12,0322,032✓
Y7.year = Y6.year + 12,0332,033✓
Y8.year = Y7.year + 12,0342,034✓
Y9.year = Y8.year + 12,0352,035✓
Y10.year = Y9.year + 12,0362,036✓
Y11.year = Y10.year + 12,0372,037✓
Y12.year = Y11.year + 12,0382,038✓
Y13.year = Y12.year + 12,0392,039✓
Y14.year = Y13.year + 12,0402,040✓
Y15.year = Y14.year + 12,0412,041✓
Y16.year = Y15.year + 12,0422,042✓
Y17.year = Y16.year + 12,0432,043✓
Y18.year = Y17.year + 12,0442,044✓
Y19.year = Y18.year + 12,0452,045✓
Y20.year = Y19.year + 12,0462,046✓
Y21.year = Y20.year + 12,0472,047✓
Y22.year = Y21.year + 12,0482,048✓
Y23.year = Y22.year + 12,0492,049✓
Y24.year = Y23.year + 12,0502,050✓
Y25.year = Y24.year + 12,0512,051✓
Y26.year = Y25.year + 12,0522,052✓
Y27.year = Y26.year + 12,0532,053✓
Y28.year = Y27.year + 12,0542,054✓
Y29.year = Y28.year + 12,0552,055✓
Y30.year = Y29.year + 12,0562,056✓
Y31.year = Y30.year + 12,0572,057✓
Y1.age = Y0.age + 16565✓
Y2.age = Y1.age + 16666✓
Y3.age = Y2.age + 16767✓
Y4.age = Y3.age + 16868✓
Y5.age = Y4.age + 16969✓
Y6.age = Y5.age + 17070✓
Y7.age = Y6.age + 17171✓
Y8.age = Y7.age + 17272✓
Y9.age = Y8.age + 17373✓
Y10.age = Y9.age + 17474✓
Y11.age = Y10.age + 17575✓
Y12.age = Y11.age + 17676✓
Y13.age = Y12.age + 17777✓
Y14.age = Y13.age + 17878✓
Y15.age = Y14.age + 17979✓
Y16.age = Y15.age + 18080✓
Y17.age = Y16.age + 18181✓
Y18.age = Y17.age + 18282✓
Y19.age = Y18.age + 18383✓
Y20.age = Y19.age + 18484✓
Y21.age = Y20.age + 18585✓
Y22.age = Y21.age + 18686✓
Y23.age = Y22.age + 18787✓
Y24.age = Y23.age + 18888✓
Y25.age = Y24.age + 18989✓
Y26.age = Y25.age + 19090✓
Y27.age = Y26.age + 19191✓
Y28.age = Y27.age + 19292✓
Y29.age = Y28.age + 19393✓
Y30.age = Y29.age + 19494✓
Y31.age = Y30.age + 19595✓
Y0 earned = primaryEarned + spouseEarned182,000182,000✓
Y1 earned = primaryEarned + spouseEarned73,44073,440✓
Y2 earned = primaryEarned + spouseEarned74,90974,909✓
Y3 earned = primaryEarned + spouseEarned00✓
Y4 earned = primaryEarned + spouseEarned00✓
Y5 earned = primaryEarned + spouseEarned00✓
Y6 earned = primaryEarned + spouseEarned00✓
Y7 earned = primaryEarned + spouseEarned00✓
Y8 earned = primaryEarned + spouseEarned00✓
Y9 earned = primaryEarned + spouseEarned00✓
Y10 earned = primaryEarned + spouseEarned00✓
Y11 earned = primaryEarned + spouseEarned00✓
Y12 earned = primaryEarned + spouseEarned00✓
Y13 earned = primaryEarned + spouseEarned00✓
Y14 earned = primaryEarned + spouseEarned00✓
Y15 earned = primaryEarned + spouseEarned00✓
Y16 earned = primaryEarned + spouseEarned00✓
Y17 earned = primaryEarned + spouseEarned00✓
Y18 earned = primaryEarned + spouseEarned00✓
Y19 earned = primaryEarned + spouseEarned00✓
Y20 earned = primaryEarned + spouseEarned00✓
Y21 earned = primaryEarned + spouseEarned00✓
Y22 earned = primaryEarned + spouseEarned00✓
Y23 earned = primaryEarned + spouseEarned00✓
Y24 earned = primaryEarned + spouseEarned00✓
Y25 earned = primaryEarned + spouseEarned00✓
Y26 earned = primaryEarned + spouseEarned00✓
Y27 earned = primaryEarned + spouseEarned00✓
Y28 earned = primaryEarned + spouseEarned00✓
Y29 earned = primaryEarned + spouseEarned00✓
Y30 earned = primaryEarned + spouseEarned00✓
Y31 earned = primaryEarned + spouseEarned00✓
Y0 tax = fedTax + stateTax + fica31,12531,125✓
Y1 tax = fedTax + stateTax + fica8,2908,290✓
Y2 tax = fedTax + stateTax + fica7,6327,632✓
Y3 tax = fedTax + stateTax + fica00✓
Y4 tax = fedTax + stateTax + fica2,9822,982✓
Y5 tax = fedTax + stateTax + fica3,2103,210✓
Y6 tax = fedTax + stateTax + fica3,6243,624✓
Y7 tax = fedTax + stateTax + fica4,0444,044✓
Y8 tax = fedTax + stateTax + fica4,4954,495✓
Y9 tax = fedTax + stateTax + fica4,9394,939✓
Y10 tax = fedTax + stateTax + fica5,4245,424✓
Y11 tax = fedTax + stateTax + fica5,9265,926✓
Y12 tax = fedTax + stateTax + fica6,4326,432✓
Y13 tax = fedTax + stateTax + fica6,9816,981✓
Y14 tax = fedTax + stateTax + fica7,5447,544✓
Y15 tax = fedTax + stateTax + fica8,1218,121✓
Y16 tax = fedTax + stateTax + fica8,7378,737✓
Y17 tax = fedTax + stateTax + fica9,0849,084✓
Y18 tax = fedTax + stateTax + fica9,3979,397✓
Y19 tax = fedTax + stateTax + fica9,7359,735✓
Y20 tax = fedTax + stateTax + fica10,15810,158✓
Y21 tax = fedTax + stateTax + fica10,65810,658✓
Y22 tax = fedTax + stateTax + fica11,18711,187✓
Y23 tax = fedTax + stateTax + fica11,73311,733✓
Y24 tax = fedTax + stateTax + fica12,19712,197✓
Y25 tax = fedTax + stateTax + fica12,78112,781✓
Y26 tax = fedTax + stateTax + fica13,26513,265✓
Y27 tax = fedTax + stateTax + fica13,74313,743✓
Y28 tax = fedTax + stateTax + fica14,21614,216✓
Y29 tax = fedTax + stateTax + fica14,67814,678✓
Y30 tax = fedTax + stateTax + fica15,00715,007✓
Y31 tax = fedTax + stateTax + fica15,58915,589✓
Y0 netWorth = sum of buckets1,153,4601,153,460✓
Y1 netWorth = sum of buckets1,213,0081,213,008✓
Y2 netWorth = sum of buckets1,275,2631,275,263✓
Y3 netWorth = sum of buckets1,300,3891,300,389✓
Y4 netWorth = sum of buckets1,321,6891,321,689✓
Y5 netWorth = sum of buckets1,368,6911,368,691✓
Y6 netWorth = sum of buckets1,416,5021,416,502✓
Y7 netWorth = sum of buckets1,465,0951,465,095✓
Y8 netWorth = sum of buckets1,514,4101,514,410✓
Y9 netWorth = sum of buckets1,564,4191,564,419✓
Y10 netWorth = sum of buckets1,615,0351,615,035✓
Y11 netWorth = sum of buckets1,666,1901,666,190✓
Y12 netWorth = sum of buckets1,717,8211,717,821✓
Y13 netWorth = sum of buckets1,769,8161,769,816✓
Y14 netWorth = sum of buckets1,822,0821,822,082✓
Y15 netWorth = sum of buckets1,874,5131,874,513✓
Y16 netWorth = sum of buckets1,926,9681,926,968✓
Y17 netWorth = sum of buckets1,979,6201,979,620✓
Y18 netWorth = sum of buckets2,032,3992,032,399✓
Y19 netWorth = sum of buckets2,085,1612,085,161✓
Y20 netWorth = sum of buckets2,137,6832,137,683✓
Y21 netWorth = sum of buckets2,189,7332,189,733✓
Y22 netWorth = sum of buckets2,241,1092,241,109✓
Y23 netWorth = sum of buckets2,291,6022,291,602✓
Y24 netWorth = sum of buckets2,341,0852,341,085✓
Y25 netWorth = sum of buckets2,389,2072,389,207✓
Y26 netWorth = sum of buckets2,435,8152,435,815✓
Y27 netWorth = sum of buckets2,480,6462,480,646✓
Y28 netWorth = sum of buckets2,523,4012,523,401✓
Y29 netWorth = sum of buckets2,563,7712,563,771✓
Y30 netWorth = sum of buckets2,601,5422,601,542✓
Y31 netWorth = sum of buckets2,636,0702,636,070✓
Y0 retired === nulltruetrue✓
all years Y1+ retired === truetruetrue✓
Y0 earned > 0 (both working)truetrue✓
Y1 earned > 0 (spouse continues working)truetrue✓
Y2 earned > 0 (spouse continues working)truetrue✓
all years Y3+ earned = 0truetrue✓
all years Y0-Y2 ss = 0 (pre-claim)truetrue✓
all years Y3+ ss > 0 (post-primary-claim)truetrue✓
all years Y0-Y10 rmd = 0 (pre-trigger)truetrue✓
all years Y0-Y10 rmdRequired === false (pre-trigger)truetrue✓
all years Y11+ rmdRequired === true (post-trigger)truetrue✓
all years rothConv = 0truetrue✓
all years hsaContrib = 0truetrue✓
all years charityTotal = 0truetrue✓
all years stateTax = 0truetrue✓
all years fedTax >= 0truetrue✓
all years cgTax >= 0truetrue✓
all years niitTax >= 0truetrue✓
roth bucket monotonically non-decreasingtruetrue✓

Lees Cruise 68/68

TestExpectedActualResult
Y1 ss === 0truetrue✓
Y1 ssTaxable === 0truetrue✓
Y2 ss === 0truetrue✓
Y2 ssTaxable === 0truetrue✓
Y4 ss === 0truetrue✓
Y4 ssTaxable === 0truetrue✓
Y6 ss === 0truetrue✓
Y6 ssTaxable === 0truetrue✓
Y7 ss === 0truetrue✓
Y7 ssTaxable === 0truetrue✓
Y8 ss === 0truetrue✓
Y8 ssTaxable === 0truetrue✓
Y9 ss === 0truetrue✓
Y9 ssTaxable === 0truetrue✓
Y12 ss > Y11 sstruetrue✓
Y13 ss > Y12 sstruetrue✓
Y14 ss > Y13 sstruetrue✓
Y15 ss > Y14 sstruetrue✓
Y17 ss > Y16 sstruetrue✓
Y18 ss > Y17 sstruetrue✓
Y19 ss > Y18 sstruetrue✓
Y20 ss > Y19 sstruetrue✓
Y21 ss > Y20 sstruetrue✓
Y22 ss > Y21 sstruetrue✓
Y23 ss > Y22 sstruetrue✓
Y24 ss > Y23 sstruetrue✓
Y25 ss > Y24 sstruetrue✓
Y26 ss > Y25 sstruetrue✓
Y27 ss > Y26 sstruetrue✓
Y28 ss > Y27 sstruetrue✓
Y29 ss > Y28 sstruetrue✓
Y30 ss > Y29 sstruetrue✓
Y31 ss > Y30 sstruetrue✓
Y32 ss > Y31 sstruetrue✓
Y33 ss > Y32 sstruetrue✓
Y34 ss > Y33 sstruetrue✓
Y35 ss > Y34 sstruetrue✓
Y8 medicareCost > Y7 medicareCosttruetrue✓
Y9 medicareCost > Y8 medicareCosttruetrue✓
Y10 medicareCost > Y9 medicareCosttruetrue✓
Y12 medicareCost > Y11 medicareCosttruetrue✓
Y13 medicareCost > Y12 medicareCosttruetrue✓
Y14 medicareCost > Y13 medicareCosttruetrue✓
Y15 medicareCost > Y14 medicareCosttruetrue✓
Y17 medicareCost > Y16 medicareCosttruetrue✓
Y18 medicareCost > Y17 medicareCosttruetrue✓
Y19 medicareCost > Y18 medicareCosttruetrue✓
Y20 medicareCost > Y19 medicareCosttruetrue✓
Y21 medicareCost > Y20 medicareCosttruetrue✓
Y22 medicareCost > Y21 medicareCosttruetrue✓
Y23 medicareCost > Y22 medicareCosttruetrue✓
Y24 medicareCost > Y23 medicareCosttruetrue✓
Y25 medicareCost > Y24 medicareCosttruetrue✓
Y26 medicareCost > Y25 medicareCosttruetrue✓
Y27 medicareCost > Y26 medicareCosttruetrue✓
Y28 medicareCost > Y27 medicareCosttruetrue✓
Y29 medicareCost > Y28 medicareCosttruetrue✓
Y30 medicareCost > Y29 medicareCosttruetrue✓
Y31 medicareCost > Y30 medicareCosttruetrue✓
Y32 medicareCost > Y31 medicareCosttruetrue✓
Y33 medicareCost > Y32 medicareCosttruetrue✓
Y34 medicareCost > Y33 medicareCosttruetrue✓
Y35 medicareCost > Y34 medicareCosttruetrue✓
irmaaBracket === 0 across all yearstruetrue✓
roth balance > 0 across all yearstruetrue✓
age increments by 1 each yeartruetrue✓
spAge increments by 1 each yeartruetrue✓
age[36] === endAge 95truetrue✓

Washingtons Cruise 1154/1154

TestExpectedActualResult
Y0 age === 63truetrue✓
Y0 spAge === 61truetrue✓
Y1 age === 64truetrue✓
Y1 spAge === 62truetrue✓
Y2 age === 65truetrue✓
Y2 spAge === 63truetrue✓
Y3 age === 66truetrue✓
Y3 spAge === 64truetrue✓
Y4 age === 67truetrue✓
Y4 spAge === 65truetrue✓
Y5 age === 68truetrue✓
Y5 spAge === 66truetrue✓
Y6 age === 69truetrue✓
Y6 spAge === 67truetrue✓
Y7 age === 70truetrue✓
Y7 spAge === 68truetrue✓
Y8 age === 71truetrue✓
Y8 spAge === 69truetrue✓
Y9 age === 72truetrue✓
Y9 spAge === 70truetrue✓
Y10 age === 73truetrue✓
Y10 spAge === 71truetrue✓
Y11 age === 74truetrue✓
Y11 spAge === 72truetrue✓
Y12 age === 75truetrue✓
Y12 spAge === 73truetrue✓
Y13 age === 76truetrue✓
Y13 spAge === 74truetrue✓
Y14 age === 77truetrue✓
Y14 spAge === 75truetrue✓
Y15 age === 78truetrue✓
Y15 spAge === 76truetrue✓
Y16 age === 79truetrue✓
Y16 spAge === 77truetrue✓
Y17 age === 80truetrue✓
Y17 spAge === 78truetrue✓
Y18 age === 81truetrue✓
Y18 spAge === 79truetrue✓
Y19 age === 82truetrue✓
Y19 spAge === 80truetrue✓
Y20 age === 83truetrue✓
Y20 spAge === 81truetrue✓
Y21 age === 84truetrue✓
Y21 spAge === 82truetrue✓
Y22 age === 85truetrue✓
Y22 spAge === 83truetrue✓
Y23 age === 86truetrue✓
Y23 spAge === 84truetrue✓
Y24 age === 87truetrue✓
Y24 spAge === 85truetrue✓
Y25 age === 88truetrue✓
Y25 spAge === 86truetrue✓
Y26 age === 89truetrue✓
Y26 spAge === 87truetrue✓
Y27 age === 90truetrue✓
Y27 spAge === 88truetrue✓
Y28 age === 91truetrue✓
Y28 spAge === 89truetrue✓
Y29 age === 92truetrue✓
Y29 spAge === 90truetrue✓
Y30 age === 93truetrue✓
Y30 spAge === 91truetrue✓
Y31 age === 94truetrue✓
Y31 spAge === 92truetrue✓
Y32 age === 95truetrue✓
Y32 spAge === 93truetrue✓
Y0 isSingle === nulltruetrue✓
Y0 primaryDead === falsetruetrue✓
Y0 spouseDead === falsetruetrue✓
Y1 isSingle === nulltruetrue✓
Y1 primaryDead === falsetruetrue✓
Y1 spouseDead === falsetruetrue✓
Y2 isSingle === nulltruetrue✓
Y2 primaryDead === falsetruetrue✓
Y2 spouseDead === falsetruetrue✓
Y3 isSingle === nulltruetrue✓
Y3 primaryDead === falsetruetrue✓
Y3 spouseDead === falsetruetrue✓
Y4 isSingle === nulltruetrue✓
Y4 primaryDead === falsetruetrue✓
Y4 spouseDead === falsetruetrue✓
Y5 isSingle === nulltruetrue✓
Y5 primaryDead === falsetruetrue✓
Y5 spouseDead === falsetruetrue✓
Y6 isSingle === nulltruetrue✓
Y6 primaryDead === falsetruetrue✓
Y6 spouseDead === falsetruetrue✓
Y7 isSingle === nulltruetrue✓
Y7 primaryDead === falsetruetrue✓
Y7 spouseDead === falsetruetrue✓
Y8 isSingle === nulltruetrue✓
Y8 primaryDead === falsetruetrue✓
Y8 spouseDead === falsetruetrue✓
Y9 isSingle === nulltruetrue✓
Y9 primaryDead === falsetruetrue✓
Y9 spouseDead === falsetruetrue✓
Y10 isSingle === nulltruetrue✓
Y10 primaryDead === falsetruetrue✓
Y10 spouseDead === falsetruetrue✓
Y11 isSingle === nulltruetrue✓
Y11 primaryDead === falsetruetrue✓
Y11 spouseDead === falsetruetrue✓
Y12 isSingle === nulltruetrue✓
Y12 primaryDead === falsetruetrue✓
Y12 spouseDead === falsetruetrue✓
Y13 isSingle === nulltruetrue✓
Y13 primaryDead === falsetruetrue✓
Y13 spouseDead === falsetruetrue✓
Y14 isSingle === nulltruetrue✓
Y14 primaryDead === falsetruetrue✓
Y14 spouseDead === falsetruetrue✓
Y15 isSingle === nulltruetrue✓
Y15 primaryDead === falsetruetrue✓
Y15 spouseDead === falsetruetrue✓
Y16 isSingle === nulltruetrue✓
Y16 primaryDead === falsetruetrue✓
Y16 spouseDead === falsetruetrue✓
Y17 isSingle === nulltruetrue✓
Y17 primaryDead === falsetruetrue✓
Y17 spouseDead === falsetruetrue✓
Y18 isSingle === nulltruetrue✓
Y18 primaryDead === falsetruetrue✓
Y18 spouseDead === falsetruetrue✓
Y19 isSingle === nulltruetrue✓
Y19 primaryDead === falsetruetrue✓
Y19 spouseDead === falsetruetrue✓
Y20 isSingle === nulltruetrue✓
Y20 primaryDead === falsetruetrue✓
Y20 spouseDead === falsetruetrue✓
Y21 isSingle === nulltruetrue✓
Y21 primaryDead === falsetruetrue✓
Y21 spouseDead === falsetruetrue✓
Y22 isSingle === nulltruetrue✓
Y22 primaryDead === falsetruetrue✓
Y22 spouseDead === falsetruetrue✓
Y23 isSingle === nulltruetrue✓
Y23 primaryDead === falsetruetrue✓
Y23 spouseDead === falsetruetrue✓
Y24 isSingle === nulltruetrue✓
Y24 primaryDead === falsetruetrue✓
Y24 spouseDead === falsetruetrue✓
Y25 isSingle === nulltruetrue✓
Y25 primaryDead === falsetruetrue✓
Y25 spouseDead === falsetruetrue✓
Y26 isSingle === nulltruetrue✓
Y26 primaryDead === falsetruetrue✓
Y26 spouseDead === falsetruetrue✓
Y27 isSingle === nulltruetrue✓
Y27 primaryDead === falsetruetrue✓
Y27 spouseDead === falsetruetrue✓
Y28 isSingle === nulltruetrue✓
Y28 primaryDead === falsetruetrue✓
Y28 spouseDead === falsetruetrue✓
Y29 isSingle === nulltruetrue✓
Y29 primaryDead === falsetruetrue✓
Y29 spouseDead === falsetruetrue✓
Y30 isSingle === nulltruetrue✓
Y30 primaryDead === falsetruetrue✓
Y30 spouseDead === falsetruetrue✓
Y31 isSingle === nulltruetrue✓
Y31 primaryDead === falsetruetrue✓
Y31 spouseDead === falsetruetrue✓
Y32 isSingle === nulltruetrue✓
Y32 primaryDead === falsetruetrue✓
Y32 spouseDead === falsetruetrue✓
Y0 taxConvergenceFlag === falsetruetrue✓
Y1 taxConvergenceFlag === falsetruetrue✓
Y2 taxConvergenceFlag === falsetruetrue✓
Y3 taxConvergenceFlag === falsetruetrue✓
Y4 taxConvergenceFlag === falsetruetrue✓
Y5 taxConvergenceFlag === falsetruetrue✓
Y6 taxConvergenceFlag === falsetruetrue✓
Y7 taxConvergenceFlag === falsetruetrue✓
Y8 taxConvergenceFlag === falsetruetrue✓
Y9 taxConvergenceFlag === falsetruetrue✓
Y10 taxConvergenceFlag === falsetruetrue✓
Y11 taxConvergenceFlag === falsetruetrue✓
Y12 taxConvergenceFlag === truetruetrue✓
Y13 taxConvergenceFlag === falsetruetrue✓
Y14 taxConvergenceFlag === falsetruetrue✓
Y15 taxConvergenceFlag === falsetruetrue✓
Y16 taxConvergenceFlag === falsetruetrue✓
Y17 taxConvergenceFlag === falsetruetrue✓
Y18 taxConvergenceFlag === falsetruetrue✓
Y19 taxConvergenceFlag === truetruetrue✓
Y20 taxConvergenceFlag === falsetruetrue✓
Y21 taxConvergenceFlag === falsetruetrue✓
Y22 taxConvergenceFlag === falsetruetrue✓
Y23 taxConvergenceFlag === falsetruetrue✓
Y24 taxConvergenceFlag === falsetruetrue✓
Y25 taxConvergenceFlag === falsetruetrue✓
Y26 taxConvergenceFlag === falsetruetrue✓
Y27 taxConvergenceFlag === falsetruetrue✓
Y28 taxConvergenceFlag === falsetruetrue✓
Y29 taxConvergenceFlag === falsetruetrue✓
Y30 taxConvergenceFlag === falsetruetrue✓
Y31 taxConvergenceFlag === falsetruetrue✓
Y32 taxConvergenceFlag === falsetruetrue✓
Y0 primaryEarned > 0truetrue✓
Y0 spouseEarned > 0truetrue✓
Y0 earned > 0truetrue✓
Y0 fica > 0truetrue✓
Y1 primaryEarned > 0truetrue✓
Y1 spouseEarned > 0truetrue✓
Y1 earned > 0truetrue✓
Y1 fica > 0truetrue✓
Y2 primaryEarned === 0truetrue✓
Y2 spouseEarned > 0truetrue✓
Y2 earned > 0truetrue✓
Y2 fica > 0truetrue✓
Y3 primaryEarned === 0truetrue✓
Y3 spouseEarned > 0truetrue✓
Y3 earned > 0truetrue✓
Y3 fica > 0truetrue✓
Y4 primaryEarned === 0truetrue✓
Y4 spouseEarned === 0truetrue✓
Y4 earned === 0truetrue✓
Y4 fica === 0truetrue✓
Y5 primaryEarned === 0truetrue✓
Y5 spouseEarned === 0truetrue✓
Y5 earned === 0truetrue✓
Y5 fica === 0truetrue✓
Y6 primaryEarned === 0truetrue✓
Y6 spouseEarned === 0truetrue✓
Y6 earned === 0truetrue✓
Y6 fica === 0truetrue✓
Y7 primaryEarned === 0truetrue✓
Y7 spouseEarned === 0truetrue✓
Y7 earned === 0truetrue✓
Y7 fica === 0truetrue✓
Y8 primaryEarned === 0truetrue✓
Y8 spouseEarned === 0truetrue✓
Y8 earned === 0truetrue✓
Y8 fica === 0truetrue✓
Y9 primaryEarned === 0truetrue✓
Y9 spouseEarned === 0truetrue✓
Y9 earned === 0truetrue✓
Y9 fica === 0truetrue✓
Y10 primaryEarned === 0truetrue✓
Y10 spouseEarned === 0truetrue✓
Y10 earned === 0truetrue✓
Y10 fica === 0truetrue✓
Y11 primaryEarned === 0truetrue✓
Y11 spouseEarned === 0truetrue✓
Y11 earned === 0truetrue✓
Y11 fica === 0truetrue✓
Y12 primaryEarned === 0truetrue✓
Y12 spouseEarned === 0truetrue✓
Y12 earned === 0truetrue✓
Y12 fica === 0truetrue✓
Y13 primaryEarned === 0truetrue✓
Y13 spouseEarned === 0truetrue✓
Y13 earned === 0truetrue✓
Y13 fica === 0truetrue✓
Y14 primaryEarned === 0truetrue✓
Y14 spouseEarned === 0truetrue✓
Y14 earned === 0truetrue✓
Y14 fica === 0truetrue✓
Y15 primaryEarned === 0truetrue✓
Y15 spouseEarned === 0truetrue✓
Y15 earned === 0truetrue✓
Y15 fica === 0truetrue✓
Y16 primaryEarned === 0truetrue✓
Y16 spouseEarned === 0truetrue✓
Y16 earned === 0truetrue✓
Y16 fica === 0truetrue✓
Y17 primaryEarned === 0truetrue✓
Y17 spouseEarned === 0truetrue✓
Y17 earned === 0truetrue✓
Y17 fica === 0truetrue✓
Y18 primaryEarned === 0truetrue✓
Y18 spouseEarned === 0truetrue✓
Y18 earned === 0truetrue✓
Y18 fica === 0truetrue✓
Y19 primaryEarned === 0truetrue✓
Y19 spouseEarned === 0truetrue✓
Y19 earned === 0truetrue✓
Y19 fica === 0truetrue✓
Y20 primaryEarned === 0truetrue✓
Y20 spouseEarned === 0truetrue✓
Y20 earned === 0truetrue✓
Y20 fica === 0truetrue✓
Y21 primaryEarned === 0truetrue✓
Y21 spouseEarned === 0truetrue✓
Y21 earned === 0truetrue✓
Y21 fica === 0truetrue✓
Y22 primaryEarned === 0truetrue✓
Y22 spouseEarned === 0truetrue✓
Y22 earned === 0truetrue✓
Y22 fica === 0truetrue✓
Y23 primaryEarned === 0truetrue✓
Y23 spouseEarned === 0truetrue✓
Y23 earned === 0truetrue✓
Y23 fica === 0truetrue✓
Y24 primaryEarned === 0truetrue✓
Y24 spouseEarned === 0truetrue✓
Y24 earned === 0truetrue✓
Y24 fica === 0truetrue✓
Y25 primaryEarned === 0truetrue✓
Y25 spouseEarned === 0truetrue✓
Y25 earned === 0truetrue✓
Y25 fica === 0truetrue✓
Y26 primaryEarned === 0truetrue✓
Y26 spouseEarned === 0truetrue✓
Y26 earned === 0truetrue✓
Y26 fica === 0truetrue✓
Y27 primaryEarned === 0truetrue✓
Y27 spouseEarned === 0truetrue✓
Y27 earned === 0truetrue✓
Y27 fica === 0truetrue✓
Y28 primaryEarned === 0truetrue✓
Y28 spouseEarned === 0truetrue✓
Y28 earned === 0truetrue✓
Y28 fica === 0truetrue✓
Y29 primaryEarned === 0truetrue✓
Y29 spouseEarned === 0truetrue✓
Y29 earned === 0truetrue✓
Y29 fica === 0truetrue✓
Y30 primaryEarned === 0truetrue✓
Y30 spouseEarned === 0truetrue✓
Y30 earned === 0truetrue✓
Y30 fica === 0truetrue✓
Y31 primaryEarned === 0truetrue✓
Y31 spouseEarned === 0truetrue✓
Y31 earned === 0truetrue✓
Y31 fica === 0truetrue✓
Y32 primaryEarned === 0truetrue✓
Y32 spouseEarned === 0truetrue✓
Y32 earned === 0truetrue✓
Y32 fica === 0truetrue✓
Y0 retired === nulltruetrue✓
Y1 retired === nulltruetrue✓
Y2 retired === truetruetrue✓
Y3 retired === truetruetrue✓
Y4 retired === truetruetrue✓
Y5 retired === truetruetrue✓
Y6 retired === truetruetrue✓
Y7 retired === truetruetrue✓
Y8 retired === truetruetrue✓
Y9 retired === truetruetrue✓
Y10 retired === truetruetrue✓
Y11 retired === truetruetrue✓
Y12 retired === truetruetrue✓
Y13 retired === truetruetrue✓
Y14 retired === truetruetrue✓
Y15 retired === truetruetrue✓
Y16 retired === truetruetrue✓
Y17 retired === truetruetrue✓
Y18 retired === truetruetrue✓
Y19 retired === truetruetrue✓
Y20 retired === truetruetrue✓
Y21 retired === truetruetrue✓
Y22 retired === truetruetrue✓
Y23 retired === truetruetrue✓
Y24 retired === truetruetrue✓
Y25 retired === truetruetrue✓
Y26 retired === truetruetrue✓
Y27 retired === truetruetrue✓
Y28 retired === truetruetrue✓
Y29 retired === truetruetrue✓
Y30 retired === truetruetrue✓
Y31 retired === truetruetrue✓
Y32 retired === truetruetrue✓
Y0 pension === 0truetrue✓
Y1 pension === 0truetrue✓
Y2 pension === pensionTaxabletruetrue✓
Y3 pension === pensionTaxabletruetrue✓
Y4 pension === pensionTaxabletruetrue✓
Y5 pension === pensionTaxabletruetrue✓
Y6 pension === pensionTaxabletruetrue✓
Y7 pension === pensionTaxabletruetrue✓
Y8 pension === pensionTaxabletruetrue✓
Y9 pension === pensionTaxabletruetrue✓
Y10 pension === pensionTaxabletruetrue✓
Y11 pension === pensionTaxabletruetrue✓
Y12 pension === pensionTaxabletruetrue✓
Y13 pension === pensionTaxabletruetrue✓
Y14 pension === pensionTaxabletruetrue✓
Y15 pension === pensionTaxabletruetrue✓
Y16 pension === pensionTaxabletruetrue✓
Y17 pension === pensionTaxabletruetrue✓
Y18 pension === pensionTaxabletruetrue✓
Y19 pension === pensionTaxabletruetrue✓
Y20 pension === pensionTaxabletruetrue✓
Y21 pension === pensionTaxabletruetrue✓
Y22 pension === pensionTaxabletruetrue✓
Y23 pension === pensionTaxabletruetrue✓
Y24 pension === pensionTaxabletruetrue✓
Y25 pension === pensionTaxabletruetrue✓
Y26 pension === pensionTaxabletruetrue✓
Y27 pension === pensionTaxabletruetrue✓
Y28 pension === pensionTaxabletruetrue✓
Y29 pension === pensionTaxabletruetrue✓
Y30 pension === pensionTaxabletruetrue✓
Y31 pension === pensionTaxabletruetrue✓
Y32 pension === pensionTaxabletruetrue✓
Y3 pension >= Y2 pension (monotone COLA)truetrue✓
Y4 pension >= Y3 pension (monotone COLA)truetrue✓
Y5 pension >= Y4 pension (monotone COLA)truetrue✓
Y6 pension >= Y5 pension (monotone COLA)truetrue✓
Y7 pension >= Y6 pension (monotone COLA)truetrue✓
Y8 pension >= Y7 pension (monotone COLA)truetrue✓
Y9 pension >= Y8 pension (monotone COLA)truetrue✓
Y10 pension >= Y9 pension (monotone COLA)truetrue✓
Y11 pension >= Y10 pension (monotone COLA)truetrue✓
Y12 pension >= Y11 pension (monotone COLA)truetrue✓
Y13 pension >= Y12 pension (monotone COLA)truetrue✓
Y14 pension >= Y13 pension (monotone COLA)truetrue✓
Y15 pension >= Y14 pension (monotone COLA)truetrue✓
Y16 pension >= Y15 pension (monotone COLA)truetrue✓
Y17 pension >= Y16 pension (monotone COLA)truetrue✓
Y18 pension >= Y17 pension (monotone COLA)truetrue✓
Y19 pension >= Y18 pension (monotone COLA)truetrue✓
Y20 pension >= Y19 pension (monotone COLA)truetrue✓
Y21 pension >= Y20 pension (monotone COLA)truetrue✓
Y22 pension >= Y21 pension (monotone COLA)truetrue✓
Y23 pension >= Y22 pension (monotone COLA)truetrue✓
Y24 pension >= Y23 pension (monotone COLA)truetrue✓
Y25 pension >= Y24 pension (monotone COLA)truetrue✓
Y26 pension >= Y25 pension (monotone COLA)truetrue✓
Y27 pension >= Y26 pension (monotone COLA)truetrue✓
Y28 pension >= Y27 pension (monotone COLA)truetrue✓
Y29 pension >= Y28 pension (monotone COLA)truetrue✓
Y30 pension >= Y29 pension (monotone COLA)truetrue✓
Y31 pension >= Y30 pension (monotone COLA)truetrue✓
Y32 pension >= Y31 pension (monotone COLA)truetrue✓
Y0 ss === 0truetrue✓
Y1 ss === 0truetrue✓
Y2 ss === 0truetrue✓
Y3 ss === 0truetrue✓
Y4 ss === 0truetrue✓
Y5 ss === 0truetrue✓
Y6 ss > 0truetrue✓
Y7 ss > 0truetrue✓
Y8 ss > 0truetrue✓
Y9 ss > 0truetrue✓
Y10 ss > 0truetrue✓
Y11 ss > 0truetrue✓
Y12 ss > 0truetrue✓
Y13 ss > 0truetrue✓
Y14 ss > 0truetrue✓
Y15 ss > 0truetrue✓
Y16 ss > 0truetrue✓
Y17 ss > 0truetrue✓
Y18 ss > 0truetrue✓
Y19 ss > 0truetrue✓
Y20 ss > 0truetrue✓
Y21 ss > 0truetrue✓
Y22 ss > 0truetrue✓
Y23 ss > 0truetrue✓
Y24 ss > 0truetrue✓
Y25 ss > 0truetrue✓
Y26 ss > 0truetrue✓
Y27 ss > 0truetrue✓
Y28 ss > 0truetrue✓
Y29 ss > 0truetrue✓
Y30 ss > 0truetrue✓
Y31 ss > 0truetrue✓
Y32 ss > 0truetrue✓
Y7 ss > Y6 ss (primary onset jump)truetrue✓
Y8 ss >= Y7 ss (monotone after both claim)truetrue✓
Y9 ss >= Y8 ss (monotone after both claim)truetrue✓
Y10 ss >= Y9 ss (monotone after both claim)truetrue✓
Y11 ss >= Y10 ss (monotone after both claim)truetrue✓
Y12 ss >= Y11 ss (monotone after both claim)truetrue✓
Y13 ss >= Y12 ss (monotone after both claim)truetrue✓
Y14 ss >= Y13 ss (monotone after both claim)truetrue✓
Y15 ss >= Y14 ss (monotone after both claim)truetrue✓
Y16 ss >= Y15 ss (monotone after both claim)truetrue✓
Y17 ss >= Y16 ss (monotone after both claim)truetrue✓
Y18 ss >= Y17 ss (monotone after both claim)truetrue✓
Y19 ss >= Y18 ss (monotone after both claim)truetrue✓
Y20 ss >= Y19 ss (monotone after both claim)truetrue✓
Y21 ss >= Y20 ss (monotone after both claim)truetrue✓
Y22 ss >= Y21 ss (monotone after both claim)truetrue✓
Y23 ss >= Y22 ss (monotone after both claim)truetrue✓
Y24 ss >= Y23 ss (monotone after both claim)truetrue✓
Y25 ss >= Y24 ss (monotone after both claim)truetrue✓
Y26 ss >= Y25 ss (monotone after both claim)truetrue✓
Y27 ss >= Y26 ss (monotone after both claim)truetrue✓
Y28 ss >= Y27 ss (monotone after both claim)truetrue✓
Y29 ss >= Y28 ss (monotone after both claim)truetrue✓
Y30 ss >= Y29 ss (monotone after both claim)truetrue✓
Y31 ss >= Y30 ss (monotone after both claim)truetrue✓
Y32 ss >= Y31 ss (monotone after both claim)truetrue✓
Y0 rmd === 0truetrue✓
Y0 rmdRequired === falsetruetrue✓
Y0 rmdReinvested === 0truetrue✓
Y1 rmd === 0truetrue✓
Y1 rmdRequired === falsetruetrue✓
Y1 rmdReinvested === 0truetrue✓
Y2 rmd === 0truetrue✓
Y2 rmdRequired === falsetruetrue✓
Y2 rmdReinvested === 0truetrue✓
Y3 rmd === 0truetrue✓
Y3 rmdRequired === falsetruetrue✓
Y3 rmdReinvested === 0truetrue✓
Y4 rmd === 0truetrue✓
Y4 rmdRequired === falsetruetrue✓
Y4 rmdReinvested === 0truetrue✓
Y5 rmd === 0truetrue✓
Y5 rmdRequired === falsetruetrue✓
Y5 rmdReinvested === 0truetrue✓
Y6 rmd === 0truetrue✓
Y6 rmdRequired === falsetruetrue✓
Y6 rmdReinvested === 0truetrue✓
Y7 rmd === 0truetrue✓
Y7 rmdRequired === falsetruetrue✓
Y7 rmdReinvested === 0truetrue✓
Y8 rmd === 0truetrue✓
Y8 rmdRequired === falsetruetrue✓
Y8 rmdReinvested === 0truetrue✓
Y9 rmd === 0truetrue✓
Y9 rmdRequired === falsetruetrue✓
Y9 rmdReinvested === 0truetrue✓
Y10 rmd === 0truetrue✓
Y10 rmdRequired === falsetruetrue✓
Y10 rmdReinvested === 0truetrue✓
Y11 rmd === 0truetrue✓
Y11 rmdRequired === falsetruetrue✓
Y11 rmdReinvested === 0truetrue✓
Y12 rmd > 0truetrue✓
Y12 rmdRequired === truetruetrue✓
Y12 rmdReinvested > 0truetrue✓
Y13 rmd > 0truetrue✓
Y13 rmdRequired === truetruetrue✓
Y13 rmdReinvested > 0truetrue✓
Y14 rmd > 0truetrue✓
Y14 rmdRequired === truetruetrue✓
Y14 rmdReinvested > 0truetrue✓
Y15 rmd > 0truetrue✓
Y15 rmdRequired === truetruetrue✓
Y15 rmdReinvested > 0truetrue✓
Y16 rmd > 0truetrue✓
Y16 rmdRequired === truetruetrue✓
Y16 rmdReinvested > 0truetrue✓
Y17 rmd > 0truetrue✓
Y17 rmdRequired === truetruetrue✓
Y17 rmdReinvested > 0truetrue✓
Y18 rmd > 0truetrue✓
Y18 rmdRequired === truetruetrue✓
Y18 rmdReinvested > 0truetrue✓
Y19 rmd > 0truetrue✓
Y19 rmdRequired === truetruetrue✓
Y19 rmdReinvested > 0truetrue✓
Y20 rmd > 0truetrue✓
Y20 rmdRequired === truetruetrue✓
Y20 rmdReinvested > 0truetrue✓
Y21 rmd > 0truetrue✓
Y21 rmdRequired === truetruetrue✓
Y21 rmdReinvested > 0truetrue✓
Y22 rmd > 0truetrue✓
Y22 rmdRequired === truetruetrue✓
Y22 rmdReinvested > 0truetrue✓
Y23 rmd > 0truetrue✓
Y23 rmdRequired === truetruetrue✓
Y23 rmdReinvested > 0truetrue✓
Y24 rmd > 0truetrue✓
Y24 rmdRequired === truetruetrue✓
Y24 rmdReinvested > 0truetrue✓
Y25 rmd > 0truetrue✓
Y25 rmdRequired === truetruetrue✓
Y25 rmdReinvested > 0truetrue✓
Y26 rmd > 0truetrue✓
Y26 rmdRequired === truetruetrue✓
Y26 rmdReinvested > 0truetrue✓
Y27 rmd > 0truetrue✓
Y27 rmdRequired === truetruetrue✓
Y27 rmdReinvested > 0truetrue✓
Y28 rmd > 0truetrue✓
Y28 rmdRequired === truetruetrue✓
Y28 rmdReinvested > 0truetrue✓
Y29 rmd > 0truetrue✓
Y29 rmdRequired === truetruetrue✓
Y29 rmdReinvested > 0truetrue✓
Y30 rmd > 0truetrue✓
Y30 rmdRequired === truetruetrue✓
Y30 rmdReinvested > 0truetrue✓
Y31 rmd > 0truetrue✓
Y31 rmdRequired === truetruetrue✓
Y31 rmdReinvested > 0truetrue✓
Y32 rmd > 0truetrue✓
Y32 rmdRequired === truetruetrue✓
Y32 rmdReinvested > 0truetrue✓
Y0 rmdSpouse === 0truetrue✓
Y0 rmdPrimary === rmdtruetrue✓
Y1 rmdSpouse === 0truetrue✓
Y1 rmdPrimary === rmdtruetrue✓
Y2 rmdSpouse === 0truetrue✓
Y2 rmdPrimary === rmdtruetrue✓
Y3 rmdSpouse === 0truetrue✓
Y3 rmdPrimary === rmdtruetrue✓
Y4 rmdSpouse === 0truetrue✓
Y4 rmdPrimary === rmdtruetrue✓
Y5 rmdSpouse === 0truetrue✓
Y5 rmdPrimary === rmdtruetrue✓
Y6 rmdSpouse === 0truetrue✓
Y6 rmdPrimary === rmdtruetrue✓
Y7 rmdSpouse === 0truetrue✓
Y7 rmdPrimary === rmdtruetrue✓
Y8 rmdSpouse === 0truetrue✓
Y8 rmdPrimary === rmdtruetrue✓
Y9 rmdSpouse === 0truetrue✓
Y9 rmdPrimary === rmdtruetrue✓
Y10 rmdSpouse === 0truetrue✓
Y10 rmdPrimary === rmdtruetrue✓
Y11 rmdSpouse === 0truetrue✓
Y11 rmdPrimary === rmdtruetrue✓
Y12 rmdSpouse === 0truetrue✓
Y12 rmdPrimary === rmdtruetrue✓
Y13 rmdSpouse === 0truetrue✓
Y13 rmdPrimary === rmdtruetrue✓
Y14 rmdSpouse === 0truetrue✓
Y14 rmdPrimary === rmdtruetrue✓
Y15 rmdSpouse === 0truetrue✓
Y15 rmdPrimary === rmdtruetrue✓
Y16 rmdSpouse === 0truetrue✓
Y16 rmdPrimary === rmdtruetrue✓
Y17 rmdSpouse === 0truetrue✓
Y17 rmdPrimary === rmdtruetrue✓
Y18 rmdSpouse === 0truetrue✓
Y18 rmdPrimary === rmdtruetrue✓
Y19 rmdSpouse === 0truetrue✓
Y19 rmdPrimary === rmdtruetrue✓
Y20 rmdSpouse === 0truetrue✓
Y20 rmdPrimary === rmdtruetrue✓
Y21 rmdSpouse === 0truetrue✓
Y21 rmdPrimary === rmdtruetrue✓
Y22 rmdSpouse === 0truetrue✓
Y22 rmdPrimary === rmdtruetrue✓
Y23 rmdSpouse === 0truetrue✓
Y23 rmdPrimary === rmdtruetrue✓
Y24 rmdSpouse === 0truetrue✓
Y24 rmdPrimary === rmdtruetrue✓
Y25 rmdSpouse === 0truetrue✓
Y25 rmdPrimary === rmdtruetrue✓
Y26 rmdSpouse === 0truetrue✓
Y26 rmdPrimary === rmdtruetrue✓
Y27 rmdSpouse === 0truetrue✓
Y27 rmdPrimary === rmdtruetrue✓
Y28 rmdSpouse === 0truetrue✓
Y28 rmdPrimary === rmdtruetrue✓
Y29 rmdSpouse === 0truetrue✓
Y29 rmdPrimary === rmdtruetrue✓
Y30 rmdSpouse === 0truetrue✓
Y30 rmdPrimary === rmdtruetrue✓
Y31 rmdSpouse === 0truetrue✓
Y31 rmdPrimary === rmdtruetrue✓
Y32 rmdSpouse === 0truetrue✓
Y32 rmdPrimary === rmdtruetrue✓
Y0 irmaa === 0truetrue✓
Y0 irmaaBracket === 0truetrue✓
Y1 irmaa === 0truetrue✓
Y1 irmaaBracket === 0truetrue✓
Y2 irmaa === 0truetrue✓
Y2 irmaaBracket === 0truetrue✓
Y3 irmaa === 0truetrue✓
Y3 irmaaBracket === 0truetrue✓
Y4 irmaa === 0truetrue✓
Y4 irmaaBracket === 0truetrue✓
Y5 irmaa === 0truetrue✓
Y5 irmaaBracket === 0truetrue✓
Y6 irmaa === 0truetrue✓
Y6 irmaaBracket === 0truetrue✓
Y7 irmaa === 0truetrue✓
Y7 irmaaBracket === 0truetrue✓
Y8 irmaa === 0truetrue✓
Y8 irmaaBracket === 0truetrue✓
Y9 irmaa === 0truetrue✓
Y9 irmaaBracket === 0truetrue✓
Y10 irmaa === 0truetrue✓
Y10 irmaaBracket === 0truetrue✓
Y11 irmaa === 0truetrue✓
Y11 irmaaBracket === 0truetrue✓
Y12 irmaa === 0truetrue✓
Y12 irmaaBracket === 0truetrue✓
Y13 irmaa === 0truetrue✓
Y13 irmaaBracket === 0truetrue✓
Y14 irmaa === 0truetrue✓
Y14 irmaaBracket === 0truetrue✓
Y15 irmaa === 0truetrue✓
Y15 irmaaBracket === 0truetrue✓
Y16 irmaa === 0truetrue✓
Y16 irmaaBracket === 0truetrue✓
Y17 irmaa === 0truetrue✓
Y17 irmaaBracket === 0truetrue✓
Y18 irmaa === 0truetrue✓
Y18 irmaaBracket === 0truetrue✓
Y19 irmaa === 0truetrue✓
Y19 irmaaBracket === 0truetrue✓
Y20 irmaa === 0truetrue✓
Y20 irmaaBracket === 0truetrue✓
Y21 irmaa > 0truetrue✓
Y21 irmaaBracket === 1truetrue✓
Y22 irmaa > 0truetrue✓
Y22 irmaaBracket === 1truetrue✓
Y23 irmaa > 0truetrue✓
Y23 irmaaBracket === 1truetrue✓
Y24 irmaa > 0truetrue✓
Y24 irmaaBracket === 1truetrue✓
Y25 irmaa === 0truetrue✓
Y25 irmaaBracket === 0truetrue✓
Y26 irmaa === 0truetrue✓
Y26 irmaaBracket === 0truetrue✓
Y27 irmaa === 0truetrue✓
Y27 irmaaBracket === 0truetrue✓
Y28 irmaa === 0truetrue✓
Y28 irmaaBracket === 0truetrue✓
Y29 irmaa === 0truetrue✓
Y29 irmaaBracket === 0truetrue✓
Y30 irmaa === 0truetrue✓
Y30 irmaaBracket === 0truetrue✓
Y31 irmaa === 0truetrue✓
Y31 irmaaBracket === 0truetrue✓
Y32 irmaa === 0truetrue✓
Y32 irmaaBracket === 0truetrue✓
Y0 medicareCost === 0truetrue✓
Y1 medicareCost > 0truetrue✓
Y2 medicareCost > 0truetrue✓
Y3 medicareCost > 0truetrue✓
Y4 medicareCost > 0truetrue✓
Y5 medicareCost > 0truetrue✓
Y6 medicareCost > 0truetrue✓
Y7 medicareCost > 0truetrue✓
Y8 medicareCost > 0truetrue✓
Y9 medicareCost > 0truetrue✓
Y10 medicareCost > 0truetrue✓
Y11 medicareCost > 0truetrue✓
Y12 medicareCost > 0truetrue✓
Y13 medicareCost > 0truetrue✓
Y14 medicareCost > 0truetrue✓
Y15 medicareCost > 0truetrue✓
Y16 medicareCost > 0truetrue✓
Y17 medicareCost > 0truetrue✓
Y18 medicareCost > 0truetrue✓
Y19 medicareCost > 0truetrue✓
Y20 medicareCost > 0truetrue✓
Y21 medicareCost > 0truetrue✓
Y22 medicareCost > 0truetrue✓
Y23 medicareCost > 0truetrue✓
Y24 medicareCost > 0truetrue✓
Y25 medicareCost > 0truetrue✓
Y26 medicareCost > 0truetrue✓
Y27 medicareCost > 0truetrue✓
Y28 medicareCost > 0truetrue✓
Y29 medicareCost > 0truetrue✓
Y30 medicareCost > 0truetrue✓
Y31 medicareCost > 0truetrue✓
Y32 medicareCost > 0truetrue✓
Y4 medicareCost >= Y3 (tier 0 stable; both enrolled)truetrue✓
Y5 medicareCost >= Y4 (tier 0 stable; both enrolled)truetrue✓
Y6 medicareCost >= Y5 (tier 0 stable; both enrolled)truetrue✓
Y7 medicareCost >= Y6 (tier 0 stable; both enrolled)truetrue✓
Y8 medicareCost >= Y7 (tier 0 stable; both enrolled)truetrue✓
Y9 medicareCost >= Y8 (tier 0 stable; both enrolled)truetrue✓
Y10 medicareCost >= Y9 (tier 0 stable; both enrolled)truetrue✓
Y11 medicareCost >= Y10 (tier 0 stable; both enrolled)truetrue✓
Y12 medicareCost >= Y11 (tier 0 stable; both enrolled)truetrue✓
Y13 medicareCost >= Y12 (tier 0 stable; both enrolled)truetrue✓
Y14 medicareCost >= Y13 (tier 0 stable; both enrolled)truetrue✓
Y15 medicareCost >= Y14 (tier 0 stable; both enrolled)truetrue✓
Y16 medicareCost >= Y15 (tier 0 stable; both enrolled)truetrue✓
Y17 medicareCost >= Y16 (tier 0 stable; both enrolled)truetrue✓
Y18 medicareCost >= Y17 (tier 0 stable; both enrolled)truetrue✓
Y19 medicareCost >= Y18 (tier 0 stable; both enrolled)truetrue✓
Y20 medicareCost >= Y19 (tier 0 stable; both enrolled)truetrue✓
Y26 medicareCost >= Y25 (tier 0 post-IRMAA-window)truetrue✓
Y27 medicareCost >= Y26 (tier 0 post-IRMAA-window)truetrue✓
Y28 medicareCost >= Y27 (tier 0 post-IRMAA-window)truetrue✓
Y29 medicareCost >= Y28 (tier 0 post-IRMAA-window)truetrue✓
Y30 medicareCost >= Y29 (tier 0 post-IRMAA-window)truetrue✓
Y31 medicareCost >= Y30 (tier 0 post-IRMAA-window)truetrue✓
Y32 medicareCost >= Y31 (tier 0 post-IRMAA-window)truetrue✓
Y0 ltcCost === 0truetrue✓
Y1 ltcCost === 0truetrue✓
Y2 ltcCost === 0truetrue✓
Y3 ltcCost === 0truetrue✓
Y4 ltcCost === 0truetrue✓
Y5 ltcCost === 0truetrue✓
Y6 ltcCost === 0truetrue✓
Y7 ltcCost === 0truetrue✓
Y8 ltcCost === 0truetrue✓
Y9 ltcCost === 0truetrue✓
Y10 ltcCost === 0truetrue✓
Y11 ltcCost === 0truetrue✓
Y12 ltcCost === 0truetrue✓
Y13 ltcCost === 0truetrue✓
Y14 ltcCost === 0truetrue✓
Y15 ltcCost === 0truetrue✓
Y16 ltcCost === 0truetrue✓
Y17 ltcCost === 0truetrue✓
Y18 ltcCost === 0truetrue✓
Y19 ltcCost > 0truetrue✓
Y20 ltcCost > 0truetrue✓
Y21 ltcCost > 0truetrue✓
Y22 ltcCost > 0truetrue✓
Y23 ltcCost === 0truetrue✓
Y24 ltcCost === 0truetrue✓
Y25 ltcCost === 0truetrue✓
Y26 ltcCost === 0truetrue✓
Y27 ltcCost === 0truetrue✓
Y28 ltcCost === 0truetrue✓
Y29 ltcCost === 0truetrue✓
Y30 ltcCost === 0truetrue✓
Y31 ltcCost === 0truetrue✓
Y32 ltcCost === 0truetrue✓
Y0 debtPayments > 0truetrue✓
Y0 debtInterest > 0truetrue✓
Y0 debtPrincipalPaid > 0truetrue✓
Y1 debtPayments > 0truetrue✓
Y1 debtInterest > 0truetrue✓
Y1 debtPrincipalPaid > 0truetrue✓
Y2 debtPayments > 0truetrue✓
Y2 debtInterest > 0truetrue✓
Y2 debtPrincipalPaid > 0truetrue✓
Y3 debtPayments > 0truetrue✓
Y3 debtInterest > 0truetrue✓
Y3 debtPrincipalPaid > 0truetrue✓
Y4 debtPayments > 0truetrue✓
Y4 debtInterest > 0truetrue✓
Y4 debtPrincipalPaid > 0truetrue✓
Y5 debtPayments > 0truetrue✓
Y5 debtInterest > 0truetrue✓
Y5 debtPrincipalPaid > 0truetrue✓
Y6 debtPayments > 0truetrue✓
Y6 debtInterest > 0truetrue✓
Y6 debtPrincipalPaid > 0truetrue✓
Y7 debtPayments > 0truetrue✓
Y7 debtInterest > 0truetrue✓
Y7 debtPrincipalPaid > 0truetrue✓
Y8 debtPayments > 0truetrue✓
Y8 debtInterest > 0truetrue✓
Y8 debtPrincipalPaid > 0truetrue✓
Y9 debtPayments > 0truetrue✓
Y9 debtInterest > 0truetrue✓
Y9 debtPrincipalPaid > 0truetrue✓
Y10 debtPayments > 0truetrue✓
Y10 debtInterest > 0truetrue✓
Y10 debtPrincipalPaid > 0truetrue✓
Y11 debtPayments === 0truetrue✓
Y11 debtInterest === 0truetrue✓
Y11 debtPrincipalPaid === 0truetrue✓
Y12 debtPayments === 0truetrue✓
Y12 debtInterest === 0truetrue✓
Y12 debtPrincipalPaid === 0truetrue✓
Y13 debtPayments === 0truetrue✓
Y13 debtInterest === 0truetrue✓
Y13 debtPrincipalPaid === 0truetrue✓
Y14 debtPayments === 0truetrue✓
Y14 debtInterest === 0truetrue✓
Y14 debtPrincipalPaid === 0truetrue✓
Y15 debtPayments === 0truetrue✓
Y15 debtInterest === 0truetrue✓
Y15 debtPrincipalPaid === 0truetrue✓
Y16 debtPayments === 0truetrue✓
Y16 debtInterest === 0truetrue✓
Y16 debtPrincipalPaid === 0truetrue✓
Y17 debtPayments === 0truetrue✓
Y17 debtInterest === 0truetrue✓
Y17 debtPrincipalPaid === 0truetrue✓
Y18 debtPayments === 0truetrue✓
Y18 debtInterest === 0truetrue✓
Y18 debtPrincipalPaid === 0truetrue✓
Y19 debtPayments === 0truetrue✓
Y19 debtInterest === 0truetrue✓
Y19 debtPrincipalPaid === 0truetrue✓
Y20 debtPayments === 0truetrue✓
Y20 debtInterest === 0truetrue✓
Y20 debtPrincipalPaid === 0truetrue✓
Y21 debtPayments === 0truetrue✓
Y21 debtInterest === 0truetrue✓
Y21 debtPrincipalPaid === 0truetrue✓
Y22 debtPayments === 0truetrue✓
Y22 debtInterest === 0truetrue✓
Y22 debtPrincipalPaid === 0truetrue✓
Y23 debtPayments === 0truetrue✓
Y23 debtInterest === 0truetrue✓
Y23 debtPrincipalPaid === 0truetrue✓
Y24 debtPayments === 0truetrue✓
Y24 debtInterest === 0truetrue✓
Y24 debtPrincipalPaid === 0truetrue✓
Y25 debtPayments === 0truetrue✓
Y25 debtInterest === 0truetrue✓
Y25 debtPrincipalPaid === 0truetrue✓
Y26 debtPayments === 0truetrue✓
Y26 debtInterest === 0truetrue✓
Y26 debtPrincipalPaid === 0truetrue✓
Y27 debtPayments === 0truetrue✓
Y27 debtInterest === 0truetrue✓
Y27 debtPrincipalPaid === 0truetrue✓
Y28 debtPayments === 0truetrue✓
Y28 debtInterest === 0truetrue✓
Y28 debtPrincipalPaid === 0truetrue✓
Y29 debtPayments === 0truetrue✓
Y29 debtInterest === 0truetrue✓
Y29 debtPrincipalPaid === 0truetrue✓
Y30 debtPayments === 0truetrue✓
Y30 debtInterest === 0truetrue✓
Y30 debtPrincipalPaid === 0truetrue✓
Y31 debtPayments === 0truetrue✓
Y31 debtInterest === 0truetrue✓
Y31 debtPrincipalPaid === 0truetrue✓
Y32 debtPayments === 0truetrue✓
Y32 debtInterest === 0truetrue✓
Y32 debtPrincipalPaid === 0truetrue✓
Y0 roth > 0truetrue✓
Y0 preTax > 0truetrue✓
Y0 preTaxSpouse === 0truetrue✓
Y1 roth > 0truetrue✓
Y1 preTax > 0truetrue✓
Y1 preTaxSpouse === 0truetrue✓
Y2 roth > 0truetrue✓
Y2 preTax > 0truetrue✓
Y2 preTaxSpouse === 0truetrue✓
Y3 roth > 0truetrue✓
Y3 preTax > 0truetrue✓
Y3 preTaxSpouse === 0truetrue✓
Y4 roth > 0truetrue✓
Y4 preTax > 0truetrue✓
Y4 preTaxSpouse === 0truetrue✓
Y5 roth > 0truetrue✓
Y5 preTax > 0truetrue✓
Y5 preTaxSpouse === 0truetrue✓
Y6 roth > 0truetrue✓
Y6 preTax > 0truetrue✓
Y6 preTaxSpouse === 0truetrue✓
Y7 roth > 0truetrue✓
Y7 preTax > 0truetrue✓
Y7 preTaxSpouse === 0truetrue✓
Y8 roth > 0truetrue✓
Y8 preTax > 0truetrue✓
Y8 preTaxSpouse === 0truetrue✓
Y9 roth > 0truetrue✓
Y9 preTax > 0truetrue✓
Y9 preTaxSpouse === 0truetrue✓
Y10 roth > 0truetrue✓
Y10 preTax > 0truetrue✓
Y10 preTaxSpouse === 0truetrue✓
Y11 roth > 0truetrue✓
Y11 preTax > 0truetrue✓
Y11 preTaxSpouse === 0truetrue✓
Y12 roth > 0truetrue✓
Y12 preTax > 0truetrue✓
Y12 preTaxSpouse === 0truetrue✓
Y13 roth > 0truetrue✓
Y13 preTax > 0truetrue✓
Y13 preTaxSpouse === 0truetrue✓
Y14 roth > 0truetrue✓
Y14 preTax > 0truetrue✓
Y14 preTaxSpouse === 0truetrue✓
Y15 roth > 0truetrue✓
Y15 preTax > 0truetrue✓
Y15 preTaxSpouse === 0truetrue✓
Y16 roth > 0truetrue✓
Y16 preTax > 0truetrue✓
Y16 preTaxSpouse === 0truetrue✓
Y17 roth > 0truetrue✓
Y17 preTax > 0truetrue✓
Y17 preTaxSpouse === 0truetrue✓
Y18 roth > 0truetrue✓
Y18 preTax > 0truetrue✓
Y18 preTaxSpouse === 0truetrue✓
Y19 roth > 0truetrue✓
Y19 preTax > 0truetrue✓
Y19 preTaxSpouse === 0truetrue✓
Y20 roth > 0truetrue✓
Y20 preTax > 0truetrue✓
Y20 preTaxSpouse === 0truetrue✓
Y21 roth > 0truetrue✓
Y21 preTax > 0truetrue✓
Y21 preTaxSpouse === 0truetrue✓
Y22 roth > 0truetrue✓
Y22 preTax > 0truetrue✓
Y22 preTaxSpouse === 0truetrue✓
Y23 roth > 0truetrue✓
Y23 preTax > 0truetrue✓
Y23 preTaxSpouse === 0truetrue✓
Y24 roth > 0truetrue✓
Y24 preTax > 0truetrue✓
Y24 preTaxSpouse === 0truetrue✓
Y25 roth > 0truetrue✓
Y25 preTax > 0truetrue✓
Y25 preTaxSpouse === 0truetrue✓
Y26 roth > 0truetrue✓
Y26 preTax > 0truetrue✓
Y26 preTaxSpouse === 0truetrue✓
Y27 roth > 0truetrue✓
Y27 preTax > 0truetrue✓
Y27 preTaxSpouse === 0truetrue✓
Y28 roth > 0truetrue✓
Y28 preTax > 0truetrue✓
Y28 preTaxSpouse === 0truetrue✓
Y29 roth > 0truetrue✓
Y29 preTax > 0truetrue✓
Y29 preTaxSpouse === 0truetrue✓
Y30 roth > 0truetrue✓
Y30 preTax > 0truetrue✓
Y30 preTaxSpouse === 0truetrue✓
Y31 roth > 0truetrue✓
Y31 preTax > 0truetrue✓
Y31 preTaxSpouse === 0truetrue✓
Y32 roth > 0truetrue✓
Y32 preTax > 0truetrue✓
Y32 preTaxSpouse === 0truetrue✓
Y0 niitTax === 0truetrue✓
Y1 niitTax === 0truetrue✓
Y2 niitTax === 0truetrue✓
Y3 niitTax === 0truetrue✓
Y4 niitTax === 0truetrue✓
Y5 niitTax === 0truetrue✓
Y6 niitTax === 0truetrue✓
Y7 niitTax === 0truetrue✓
Y8 niitTax === 0truetrue✓
Y9 niitTax === 0truetrue✓
Y10 niitTax === 0truetrue✓
Y11 niitTax === 0truetrue✓
Y12 niitTax === 0truetrue✓
Y13 niitTax === 0truetrue✓
Y14 niitTax === 0truetrue✓
Y15 niitTax === 0truetrue✓
Y16 niitTax === 0truetrue✓
Y23 niitTax > 0truetrue✓
Y0 tax === fedTax + stateTax + ficatruetrue✓
Y1 tax === fedTax + stateTax + ficatruetrue✓
Y2 tax === fedTax + stateTax + ficatruetrue✓
Y3 tax === fedTax + stateTax + ficatruetrue✓
Y4 tax === fedTax + stateTax + ficatruetrue✓
Y5 tax === fedTax + stateTax + ficatruetrue✓
Y6 tax === fedTax + stateTax + ficatruetrue✓
Y7 tax === fedTax + stateTax + ficatruetrue✓
Y8 tax === fedTax + stateTax + ficatruetrue✓
Y9 tax === fedTax + stateTax + ficatruetrue✓
Y10 tax === fedTax + stateTax + ficatruetrue✓
Y11 tax === fedTax + stateTax + ficatruetrue✓
Y12 tax === fedTax + stateTax + ficatruetrue✓
Y13 tax === fedTax + stateTax + ficatruetrue✓
Y14 tax === fedTax + stateTax + ficatruetrue✓
Y15 tax === fedTax + stateTax + ficatruetrue✓
Y16 tax === fedTax + stateTax + ficatruetrue✓
Y17 tax === fedTax + stateTax + ficatruetrue✓
Y18 tax === fedTax + stateTax + ficatruetrue✓
Y19 tax === fedTax + stateTax + ficatruetrue✓
Y20 tax === fedTax + stateTax + ficatruetrue✓
Y21 tax === fedTax + stateTax + ficatruetrue✓
Y22 tax === fedTax + stateTax + ficatruetrue✓
Y23 tax === fedTax + stateTax + ficatruetrue✓
Y24 tax === fedTax + stateTax + ficatruetrue✓
Y25 tax === fedTax + stateTax + ficatruetrue✓
Y26 tax === fedTax + stateTax + ficatruetrue✓
Y27 tax === fedTax + stateTax + ficatruetrue✓
Y28 tax === fedTax + stateTax + ficatruetrue✓
Y29 tax === fedTax + stateTax + ficatruetrue✓
Y30 tax === fedTax + stateTax + ficatruetrue✓
Y31 tax === fedTax + stateTax + ficatruetrue✓
Y32 tax === fedTax + stateTax + ficatruetrue✓
Y0 ssdi === 0truetrue✓
Y0 withdrawal >= 0truetrue✓
Y0 withdrawalForSpending >= 0truetrue✓
Y1 ssdi === 0truetrue✓
Y1 withdrawal >= 0truetrue✓
Y1 withdrawalForSpending >= 0truetrue✓
Y2 ssdi === 0truetrue✓
Y2 withdrawal >= 0truetrue✓
Y2 withdrawalForSpending >= 0truetrue✓
Y3 ssdi === 0truetrue✓
Y3 withdrawal >= 0truetrue✓
Y3 withdrawalForSpending >= 0truetrue✓
Y4 ssdi === 0truetrue✓
Y4 withdrawal >= 0truetrue✓
Y4 withdrawalForSpending >= 0truetrue✓
Y5 ssdi === 0truetrue✓
Y5 withdrawal >= 0truetrue✓
Y5 withdrawalForSpending >= 0truetrue✓
Y6 ssdi === 0truetrue✓
Y6 withdrawal >= 0truetrue✓
Y6 withdrawalForSpending >= 0truetrue✓
Y7 ssdi === 0truetrue✓
Y7 withdrawal >= 0truetrue✓
Y7 withdrawalForSpending >= 0truetrue✓
Y8 ssdi === 0truetrue✓
Y8 withdrawal >= 0truetrue✓
Y8 withdrawalForSpending >= 0truetrue✓
Y9 ssdi === 0truetrue✓
Y9 withdrawal >= 0truetrue✓
Y9 withdrawalForSpending >= 0truetrue✓
Y10 ssdi === 0truetrue✓
Y10 withdrawal >= 0truetrue✓
Y10 withdrawalForSpending >= 0truetrue✓
Y11 ssdi === 0truetrue✓
Y11 withdrawal >= 0truetrue✓
Y11 withdrawalForSpending >= 0truetrue✓
Y12 ssdi === 0truetrue✓
Y12 withdrawal >= 0truetrue✓
Y12 withdrawalForSpending >= 0truetrue✓
Y13 ssdi === 0truetrue✓
Y13 withdrawal >= 0truetrue✓
Y13 withdrawalForSpending >= 0truetrue✓
Y14 ssdi === 0truetrue✓
Y14 withdrawal >= 0truetrue✓
Y14 withdrawalForSpending >= 0truetrue✓
Y15 ssdi === 0truetrue✓
Y15 withdrawal >= 0truetrue✓
Y15 withdrawalForSpending >= 0truetrue✓
Y16 ssdi === 0truetrue✓
Y16 withdrawal >= 0truetrue✓
Y16 withdrawalForSpending >= 0truetrue✓
Y17 ssdi === 0truetrue✓
Y17 withdrawal >= 0truetrue✓
Y17 withdrawalForSpending >= 0truetrue✓
Y18 ssdi === 0truetrue✓
Y18 withdrawal >= 0truetrue✓
Y18 withdrawalForSpending >= 0truetrue✓
Y19 ssdi === 0truetrue✓
Y19 withdrawal >= 0truetrue✓
Y19 withdrawalForSpending >= 0truetrue✓
Y20 ssdi === 0truetrue✓
Y20 withdrawal >= 0truetrue✓
Y20 withdrawalForSpending >= 0truetrue✓
Y21 ssdi === 0truetrue✓
Y21 withdrawal >= 0truetrue✓
Y21 withdrawalForSpending >= 0truetrue✓
Y22 ssdi === 0truetrue✓
Y22 withdrawal >= 0truetrue✓
Y22 withdrawalForSpending >= 0truetrue✓
Y23 ssdi === 0truetrue✓
Y23 withdrawal >= 0truetrue✓
Y23 withdrawalForSpending >= 0truetrue✓
Y24 ssdi === 0truetrue✓
Y24 withdrawal >= 0truetrue✓
Y24 withdrawalForSpending >= 0truetrue✓
Y25 ssdi === 0truetrue✓
Y25 withdrawal >= 0truetrue✓
Y25 withdrawalForSpending >= 0truetrue✓
Y26 ssdi === 0truetrue✓
Y26 withdrawal >= 0truetrue✓
Y26 withdrawalForSpending >= 0truetrue✓
Y27 ssdi === 0truetrue✓
Y27 withdrawal >= 0truetrue✓
Y27 withdrawalForSpending >= 0truetrue✓
Y28 ssdi === 0truetrue✓
Y28 withdrawal >= 0truetrue✓
Y28 withdrawalForSpending >= 0truetrue✓
Y29 ssdi === 0truetrue✓
Y29 withdrawal >= 0truetrue✓
Y29 withdrawalForSpending >= 0truetrue✓
Y30 ssdi === 0truetrue✓
Y30 withdrawal >= 0truetrue✓
Y30 withdrawalForSpending >= 0truetrue✓
Y31 ssdi === 0truetrue✓
Y31 withdrawal >= 0truetrue✓
Y31 withdrawalForSpending >= 0truetrue✓
Y32 ssdi === 0truetrue✓
Y32 withdrawal >= 0truetrue✓
Y32 withdrawalForSpending >= 0truetrue✓

Per-Person LTC 9/9

TestExpectedActualResult
T1 primary age 82 (idx 22) ltcCost292,526292,526✓
T1 primary age 85 (idx 25) ltcCost338,635338,635✓
T1 primary age 86 (idx 26) ltcCost — window 82-85 closed00✓
T2 spouse-insured, primary age 82 (idx 22) ltcCost — spouse only 7700✓
T2 spouse-insured, primary age 87 (idx 27) ltcCost — spouse 82373,346373,346✓
T2 spouse-insured, primary age 90 (idx 30) ltcCost — spouse 85432,194432,194✓
T2 spouse-insured, primary age 91 (idx 31) ltcCost — spouse 86, window closed00✓
T3 two streams sum, age 82 (idx 22) ltcCost526,547526,547✓
T4 insurance offset, age 82 (idx 20) ltcCost228,090228,090✓