RetIQ Financial Validation Report
Independent verification of all financial calculations against authoritative sources
100.0%
pass rate
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
- 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
Source →Standard Deduction & Senior Additions (2025)
Internal Revenue Service
- 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
Source →Social Security Benefit Taxation (IRS Section 86)
Internal Revenue Service
- 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
Source →Social Security PIA & Benefit Calculation
Social Security Administration
- 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
Source →Required Minimum Distributions
Internal Revenue Service
- IRS Publication 590-B — Distributions from IRAs
- IRS Uniform Lifetime Table (Table III) — updated effective 2022
- IRS Joint Life and Last Survivor Expectancy Table (Table II) — for sole-beneficiary spouse >10 years younger
- Treasury Regulation §1.401(a)(9)-9(d) — post-2022 divisor tables
- SECURE 2.0 Act of 2022, Section 107 — RMD Start Age Changes
Source →Long-Term Capital Gains Tax Rates
Internal Revenue Service
- IRS Revenue Procedure 2024-40, Section 2.14 — Capital Gains Rate Thresholds
- IRC Section 1411 — Net Investment Income Tax (NIIT)
Source →Medicare IRMAA Surcharges (2025)
Centers for Medicare & Medicaid Services
- CMS 2025 Medicare Parts B and D Income-Related Monthly Adjustment Amounts
- CMS Standard Part B Premium: $185.00/month (2025)
Source →Retirement Contribution Limits (2025)
Internal Revenue Service
- IRS Notice 2024-80 — 2025 Retirement Plan Limitations
- SECURE 2.0 Act Section 109 — Enhanced Catch-Up for Ages 60-63
- IRC Section 219(b)(5)(B) — IRA Catch-Up Not Indexed
Source →ACA Premium Tax Credit
IRS / HHS / CMS
- IRS Rev. Proc. 2024-35 — Applicable Percentage Table for ACA Premium Tax Credit (Enhanced, 2021–2025)
- IRS Rev. Proc. 2025-25 — Original ACA Applicable Percentages (2026+)
- HHS 2025 Federal Poverty Level Guidelines — 48 contiguous states
- ARPA/IRA — Enhanced PTC (no 400% FPL cliff through 2025)
Source →Pension Enhancements (v2.0)
RetIQ Engine
- Per-pension COLA, survivor benefits (single-life, J&50/75/100%), taxable percentage (military disability, after-tax contributions)
- Mortality scenario testing for pension continuity
Test Results by Category
Federal Tax (MFJ) 10/10
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.
| Test | Expected | Actual | Result |
|---|
| Top of 10% bracket ($24,800) | 2,480 | 2,480 | ✓ |
| Top of 12% bracket ($100,800) | 11,600 | 11,600 | ✓ |
| Top of 22% bracket ($211,400) | 35,932 | 35,932 | ✓ |
| Top of 24% bracket ($403,550) | 82,048 | 82,048 | ✓ |
| Top of 32% bracket ($512,450) | 116,896 | 116,896 | ✓ |
| Top of 35% bracket ($768,700) | 206,584 | 206,584 | ✓ |
| In 37% bracket ($1,000,000) | 292,165 | 292,165 | ✓ |
| Zero income | 0 | 0 | ✓ |
| Mid-bracket $50,000 | 5,504 | 5,504 | ✓ |
| Mid-bracket $150,000 | 22,424 | 22,424 | ✓ |
Federal Tax (Single) 6/6
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.
| Test | Expected | Actual | Result |
|---|
| Top of 10% bracket ($12,400) | 1,240 | 1,240 | ✓ |
| Top of 12% bracket ($50,400) | 5,800 | 5,800 | ✓ |
| Top of 22% bracket ($105,700) | 17,966 | 17,966 | ✓ |
| Top of 24% bracket ($201,775) | 41,024 | 41,024 | ✓ |
| Mid-bracket $75,000 | 11,212 | 11,212 | ✓ |
| $200,000 income | 40,598 | 40,598 | ✓ |
Standard Deduction 8/8
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).
| Test | Expected | Actual | Result |
|---|
| MFJ base deduction | 32,200 | 32,200 | ✓ |
| Single base deduction | 16,100 | 16,100 | ✓ |
| Senior additional — MFJ (per person) | 1,650 | 1,650 | ✓ |
| Senior additional — Single | 2,050 | 2,050 | ✓ |
| OBBB Senior Deduction: $6,000 per person | 6,000 | 6,000 | ✓ |
| OBBB phaseout threshold — MFJ | 150,000 | 150,000 | ✓ |
| OBBB phaseout threshold — Single | 75,000 | 75,000 | ✓ |
| OBBB phaseout rate: 6% ($60 per $1,000 over threshold) | 0.06 | 0.06 | ✓ |
SS Taxation (MFJ) 6/6
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.
| Test | Expected | Actual | Result |
|---|
| Below base threshold: $20K SS, $10K other → 0% taxable | 0 | 0 | ✓ |
| At base threshold: $24K SS, $20K other → 0% taxable | 0 | 0 | ✓ |
| Between thresholds: $30K SS, $25K other → partial | 4,000 | 4,000 | ✓ |
| Above upper: $30K SS, $60K other → 85% rule | 25,500 | 25,500 | ✓ |
| Very high income: $40K SS, $200K other → 85% cap | 34,000 | 34,000 | ✓ |
| Zero SS benefit → zero taxable | 0 | 0 | ✓ |
SS Taxation (Single) 3/3
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.
| Test | Expected | Actual | Result |
|---|
| Below base: $20K SS, $10K other → 0% | 0 | 0 | ✓ |
| Between thresholds: $24K SS, $20K other → partial | 3,500 | 3,500 | ✓ |
| Above upper: $30K SS, $40K other → 85% rule | 22,350 | 22,350 | ✓ |
SS PIA 4/4
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).
| Test | Expected | Actual | Result |
|---|
| Low earner AIME=$1,000 → PIA=$900 | 900 | 900 | ✓ |
| Mid earner AIME=$5,000 → PIA≈$2,311 | 2,311 | 2,311 | ✓ |
| Max earner AIME=$15,375 → PIA≈$4,269 | 4,273.7 | 4,273 | ✓ |
| Income above wage cap ($200K) capped at $184,500 | 4,273 | 4,273 | ✓ |
FRA 16/16
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.
| Test | Expected | Actual | Result |
|---|
| Born 1937 → FRA 65y 0m | 780 | 780 | ✓ |
| Born 1938 → FRA 65y 2m | 782 | 782 | ✓ |
| Born 1939 → FRA 65y 4m | 784 | 784 | ✓ |
| Born 1940 → FRA 65y 6m | 786 | 786 | ✓ |
| Born 1941 → FRA 65y 8m | 788 | 788 | ✓ |
| Born 1942 → FRA 65y 10m | 790 | 790 | ✓ |
| Born 1943 → FRA 66y 0m | 792 | 792 | ✓ |
| Born 1954 → FRA 66y 0m | 792 | 792 | ✓ |
| Born 1955 → FRA 66y 2m | 794 | 794 | ✓ |
| Born 1956 → FRA 66y 4m | 796 | 796 | ✓ |
| Born 1957 → FRA 66y 6m | 798 | 798 | ✓ |
| Born 1958 → FRA 66y 8m | 800 | 800 | ✓ |
| Born 1959 → FRA 66y 10m | 802 | 802 | ✓ |
| Born 1960+ → FRA 67y 0m | 804 | 804 | ✓ |
| Born 1975 → FRA 67y 0m | 804 | 804 | ✓ |
| Born 1990 → FRA 67y 0m | 804 | 804 | ✓ |
SS Claiming 7/7
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.
| Test | Expected | Actual | Result |
|---|
| Claim at FRA (67) → 100% of PIA | 2,000 | 2,000 | ✓ |
| Claim at 62 (60 months early) → 70% of PIA | 1,400 | 1,400 | ✓ |
| Claim at 64 (36 months early) → 80% of PIA | 1,600 | 1,600 | ✓ |
| Claim at 65 (24 months early) → ~86.7% of PIA | 1,733 | 1,733 | ✓ |
| Claim at 70 (36 months late) → 124% of PIA | 2,480 | 2,480 | ✓ |
| Claim at 68 (12 months late) → 108% of PIA | 2,160 | 2,160 | ✓ |
| Claim at 72 → same as 70 (DRC capped) | 2,480 | 2,480 | ✓ |
SS Reverse 5/5
| Test | Expected | Actual | Result |
|---|
| PIA $3000 → claim 70 → reverse recovers ~$3000 | 3,000 | 3,000 | ✓ |
| PIA $2500 → claim 62 → reverse recovers ~$2500 | 2,500 | 2,500 | ✓ |
| PIA $2000 → claim 67 (FRA) → reverse recovers $2000 exactly | 2,000 | 2,000 | ✓ |
| PIA $4000 → claim 65 → reverse recovers ~$4000 | 4,000 | 3,999 | ✓ |
| Zero current monthly returns 0 | 0 | 0 | ✓ |
RMD Table 34/34
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+).
| Test | Expected | Actual | Result |
|---|
| Age 72 divisor = 27.4 | 27.4 | 27.4 | ✓ |
| Age 73 divisor = 26.5 | 26.5 | 26.5 | ✓ |
| Age 74 divisor = 25.5 | 25.5 | 25.5 | ✓ |
| Age 75 divisor = 24.6 | 24.6 | 24.6 | ✓ |
| Age 76 divisor = 23.7 | 23.7 | 23.7 | ✓ |
| Age 77 divisor = 22.9 | 22.9 | 22.9 | ✓ |
| Age 78 divisor = 22 | 22 | 22 | ✓ |
| Age 79 divisor = 21.1 | 21.1 | 21.1 | ✓ |
| Age 80 divisor = 20.2 | 20.2 | 20.2 | ✓ |
| Age 81 divisor = 19.4 | 19.4 | 19.4 | ✓ |
| Age 82 divisor = 18.5 | 18.5 | 18.5 | ✓ |
| Age 83 divisor = 17.7 | 17.7 | 17.7 | ✓ |
| Age 84 divisor = 16.8 | 16.8 | 16.8 | ✓ |
| Age 85 divisor = 16 | 16 | 16 | ✓ |
| Age 86 divisor = 15.2 | 15.2 | 15.2 | ✓ |
| Age 87 divisor = 14.4 | 14.4 | 14.4 | ✓ |
| Age 88 divisor = 13.7 | 13.7 | 13.7 | ✓ |
| Age 89 divisor = 12.9 | 12.9 | 12.9 | ✓ |
| Age 90 divisor = 12.2 | 12.2 | 12.2 | ✓ |
| Age 91 divisor = 11.5 | 11.5 | 11.5 | ✓ |
| Age 92 divisor = 10.8 | 10.8 | 10.8 | ✓ |
| Age 93 divisor = 10.1 | 10.1 | 10.1 | ✓ |
| Age 94 divisor = 9.5 | 9.5 | 9.5 | ✓ |
| Age 95 divisor = 8.9 | 8.9 | 8.9 | ✓ |
| Age 96 divisor = 8.4 | 8.4 | 8.4 | ✓ |
| Age 97 divisor = 7.8 | 7.8 | 7.8 | ✓ |
| Age 98 divisor = 7.3 | 7.3 | 7.3 | ✓ |
| Age 99 divisor = 6.8 | 6.8 | 6.8 | ✓ |
| Age 100 divisor = 6.4 | 6.4 | 6.4 | ✓ |
| Age 101 divisor = 6 | 6 | 6 | ✓ |
| Age 102 divisor = 5.6 | 5.6 | 5.6 | ✓ |
| Age 103 divisor = 5.2 | 5.2 | 5.2 | ✓ |
| Age 104 divisor = 4.9 | 4.9 | 4.9 | ✓ |
| Age 105 divisor = 4.6 | 4.6 | 4.6 | ✓ |
RMD Calc 4/4
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+).
| Test | Expected | Actual | Result |
|---|
| Age 73, $1M balance → $37,736 | 37,736 | 37,736 | ✓ |
| Age 80, $1M balance → $49,505 | 49,505 | 49,505 | ✓ |
| Age 90, $1M balance → $81,967 | 81,967 | 81,967 | ✓ |
| Age 100, $500K balance → $78,125 | 78,125 | 78,125 | ✓ |
RMD Joint Life 13/13
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).
| Test | Expected | Actual | Result |
|---|
| Table II: owner 75 / spouse 64 = 25.3 (IRS worked example) | 25.3 | 25.3 | ✓ |
| Table II: owner 73 / spouse 62 = 27.2 (11-yr gap boundary) | 27.2 | 27.2 | ✓ |
| Table II: owner 75 / spouse 51 = 35.8 (24-yr gap) | 35.8 | 35.8 | ✓ |
| Table II: owner 100 / spouse 89 = 6.8 (oldest at 11-yr gap) | 6.8 | 6.8 | ✓ |
| Table II: owner 120 / spouse 109 = 2.0 (table minimum) | 2 | 2 | ✓ |
| calcRMD uses Table II for 75/51 + sole beneficiary flag | 27,933 | 27,933 | ✓ |
| Exactly 10-year gap falls back to Uniform Lifetime | 40,650 | 40,650 | ✓ |
| Flag off → Uniform Lifetime even at 24-yr gap | 40,650 | 40,650 | ✓ |
| Missing Table II cell falls back to Uniform Lifetime | 40,650 | 40,650 | ✓ |
| usesJointLifeTable(75, 51, true) → true (24-yr gap + flag) | 1 | 1 | ✓ |
| usesJointLifeTable(75, 65, true) → false (exactly 10-yr gap) | 0 | 0 | ✓ |
| usesJointLifeTable(75, 51, false) → false (flag off) | 0 | 0 | ✓ |
| usesJointLifeTable(75, 80, true) → false (spouse older) | 0 | 0 | ✓ |
RMD Start 5/5
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+).
| Test | Expected | Actual | Result |
|---|
| Born 1950 → RMD starts at 72 | 72 | 72 | ✓ |
| Born 1951 → RMD starts at 73 | 73 | 73 | ✓ |
| Born 1959 → RMD starts at 73 | 73 | 73 | ✓ |
| Born 1960 → RMD starts at 75 | 75 | 75 | ✓ |
| Born 1980 → RMD starts at 75 | 75 | 75 | ✓ |
NIIT 1/1
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.
| Test | Expected | Actual | Result |
|---|
| Tax rate = 3.8% | 1 | 1 | ✓ |
IRMAA (MFJ) 12/12
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.
| Test | Expected | Actual | Result |
|---|
| Tier 0: below $218K — Part B annual | 2,435 | 2,435 | ✓ |
| Tier 0: below $218K — Part D annual | 0 | 0 | ✓ |
| Tier 1: $218K–$274K — Part B annual | 3,409 | 3,409 | ✓ |
| Tier 1: $218K–$274K — Part D annual | 174 | 174 | ✓ |
| Tier 2: $274K–$342K — Part B annual | 4,870 | 4,870 | ✓ |
| Tier 2: $274K–$342K — Part D annual | 462 | 462 | ✓ |
| Tier 3: $342K–$410K — Part B annual | 6,331 | 6,331 | ✓ |
| Tier 3: $342K–$410K — Part D annual | 746 | 746 | ✓ |
| Tier 4: $410K–$750K — Part B annual | 7,792 | 7,792 | ✓ |
| Tier 4: $410K–$750K — Part D annual | 1,031 | 1,031 | ✓ |
| Tier 5: above $750K — Part B annual | 8,279 | 8,279 | ✓ |
| Tier 5: above $750K — Part D annual | 1,092 | 1,092 | ✓ |
IRMAA (Single) 3/3
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.
| Test | Expected | Actual | Result |
|---|
| Tier 0: below $109K — Part B annual | 2,435 | 2,435 | ✓ |
| Tier 1: $109K–$137K — Part B annual | 3,409 | 3,409 | ✓ |
| Tier 5: above $500K — Part B annual | 8,279 | 8,279 | ✓ |
IRMAA 1/1
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.
| Test | Expected | Actual | Result |
|---|
| Base Part B premium = $202.90/month ($2,434.80/year) | 2,435 | 2,435 | ✓ |
Year Object 10/10
| Test | Expected | Actual | Result |
|---|
| Couple plan: spAge at year 0 = configured spouseAge | 62 | 62 | ✓ |
| Couple plan: spAge increments by year offset | 67 | 67 | ✓ |
| Couple plan: primaryDead/spouseDead false while both alive | 1 | 1 | ✓ |
| Single plan: spAge is null | 1 | 1 | ✓ |
| Spouse-dies plan: spAge numeric while spouse alive (primary 72) | 69 | 69 | ✓ |
| Spouse-dies plan: spAge null after spouse death (primary 75) | 1 | 1 | ✓ |
| Spouse-dies plan: primaryDead stays false post spouse death | 1 | 1 | ✓ |
| Primary-dies plan: primaryDead true post primary death | 1 | 1 | ✓ |
| Primary-dies plan: spAge still numeric (survivor age) post primary death | 72 | 72 | ✓ |
| Primary-dies plan: spouseDead stays false while spouse alive | 1 | 1 | ✓ |
IRMAA Per-Enrollee 3/3
| Test | Expected | Actual | Result |
|---|
| Couple both 65+ in MFJ Tier 1 → IRMAA = 2× per-person surcharge | 2,296 | 2,296 | ✓ |
| Couple, only primary 65+ → IRMAA = 1× per-person surcharge | 1,148 | 1,148 | ✓ |
| Single filer 65+ in single Tier 1 → IRMAA = 1× per-person surcharge | 1,148 | 1,148 | ✓ |
Medicare cash flow 3/3
| Test | Expected | Actual | Result |
|---|
| Couple both 65+: y.expenses includes Medicare cost (post-65 > pre-65 expenses) | 1 | 1 | ✓ |
| Single 65+: y.expenses includes 1× Medicare cost | 1 | 1 | ✓ |
| Pre-65: medicareCost is 0 (no Medicare in expenses) | 0 | 0 | ✓ |
State Tax 50/50
Sources: State DOR publications — progressive brackets, SS exemptions with age/income gates, retirement income exclusions. IRC §86 state conformity.
| Test | Expected | Actual | Result |
|---|
| Florida — no income tax | 0 | 0 | ✓ |
| Texas — no income tax | 0 | 0 | ✓ |
| California — progressive brackets on $100K | 3,246 | 3,246 | ✓ |
| New York — progressive brackets on $100K (2026 rates) | 5,068 | 5,068 | ✓ |
| Maryland — progressive brackets on $100K | 4,698 | 4,698 | ✓ |
| Illinois — flat 4.95% on $100K | 4,950 | 4,950 | ✓ |
| Pennsylvania — flat 3.07% on $100K | 3,070 | 3,070 | ✓ |
| Nevada — no income tax | 0 | 0 | ✓ |
| Washington — no income tax | 0 | 0 | ✓ |
| No state selected — zero tax | 0 | 0 | ✓ |
| FL: SS+IRA income, no tax | 0 | 0 | ✓ |
| TX: $200K income, no tax | 0 | 0 | ✓ |
| WA: any income, no tax | 0 | 0 | ✓ |
| NH: IRA distribution, no tax | 0 | 0 | ✓ |
| none: federal only, no state tax | 0 | 0 | ✓ |
| IL: SS+IRA both fully exempt | 0 | 0 | ✓ |
| IL: earned income taxable (4.95%) | 4,950 | 4,950 | ✓ |
| IL: mixed — wages taxable, IRA exempt | 2,475 | 2,475 | ✓ |
| PA: IRA exempt age 60+ | 0 | 0 | ✓ |
| PA: IRA taxable age 58 (3.07%) | 1,535 | 1,535 | ✓ |
| PA: SS always exempt (all ages) | 0 | 0 | ✓ |
| CA: MFJ $80K — progressive brackets | 2,046 | 2,046 | ✓ |
| CA: SS exempt, IRA taxable | 804 | 804 | ✓ |
| CA: single filer pays more than MFJ same income | 1 | 1 | ✓ |
| NY: MFJ $100K with $20K pension exclusion | 3,988 | 3,988 | ✓ |
| NY: SS exempt from state tax | 1 | 1 | ✓ |
| KS: SS exempt under $75K AGI | 1 | 1 | ✓ |
| KS: SS taxable above $75K AGI | 1 | 1 | ✓ |
| CT: SS exempt under $150K MFJ AGI | 1 | 1 | ✓ |
| RI: SS exempt under $114,900 AGI | 1 | 1 | ✓ |
| Invalid state code returns $0 | 0 | 0 | ✓ |
| Zero income returns $0 | 0 | 0 | ✓ |
| PA age gate: $40K IRA age 58 taxable | 1,228 | 1,228 | ✓ |
| PA age gate: $40K IRA age 60 exempt | 0 | 0 | ✓ |
| NC: flat 3.99% on $100K | 3,990 | 3,990 | ✓ |
| IN: flat 2.95% on $100K | 2,950 | 2,950 | ✓ |
| KY: flat 3.5% on $100K | 3,500 | 3,500 | ✓ |
| MS: flat 4.0% on $100K | 4,000 | 4,000 | ✓ |
| ID: flat 5.3% on $100K | 5,300 | 5,300 | ✓ |
| GA: flat 5.19% on $100K | 5,190 | 5,190 | ✓ |
| CO: flat 4.4% on $100K | 4,400 | 4,400 | ✓ |
| UT: flat 4.5% on $100K | 4,500 | 4,500 | ✓ |
| OH: $100K MFJ — 2.75% above $26,050 | 2,034 | 2,034 | ✓ |
| OH: $20K income — below $26,050 threshold, $0 tax | 0 | 0 | ✓ |
| MT: $60K MFJ — all at 4.7% (below $95K threshold) | 2,820 | 2,820 | ✓ |
| MT: $120K MFJ — split bracket | 5,878 | 5,878 | ✓ |
| MT: single $60K pays more than MFJ $60K (lower threshold) | 1 | 1 | ✓ |
| NE: $100K MFJ — three brackets (top rate 4.55%) | 4,284 | 4,284 | ✓ |
| OK: $20K MFJ — four-bracket structure (HB2764) | 471 | 471 | ✓ |
| SC: $100K MFJ — 2026 brackets (top rate 6.0%) | 5,344 | 5,344 | ✓ |
Contrib Limits 5/5
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.
| Test | Expected | Actual | Result |
|---|
| 401(k) elective deferral: $24,500 | 24,500 | 24,500 | ✓ |
| IRA contribution limit: $7,500 | 7,500 | 7,500 | ✓ |
| 401(k) catch-up (50–59, 64+): $8,000 | 8,000 | 8,000 | ✓ |
| SECURE 2.0 enhanced catch-up (60–63): $11,250 | 11,250 | 11,250 | ✓ |
| IRA catch-up (50+): $1,000 | 1,000 | 1,000 | ✓ |
QCD 1/1
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.
| Test | Expected | Actual | Result |
|---|
| Annual limit: $110,000 (2026) | 110,000 | 110,000 | ✓ |
Integration 26/26
| Test | Expected | Actual | Result |
|---|
| Projection covers 46 years (age 50–95) | 46 | 46 | ✓ |
| Net worth stays positive (no bust) | 1 | 1 | ✓ |
| Pre-retirement savings accumulate | 1 | 1 | ✓ |
| RMDs start at age 75 (born 1976) | 75 | 75 | ✓ |
| Tax computed for retirement year | 1 | 1 | ✓ |
| SS benefits start at claim age 67 | 67 | 67 | ✓ |
| Account balances sum to net worth | 395,753 | 395,753 | ✓ |
| Higher income → higher lifetime tax | 1 | 1 | ✓ |
| Roth conversion increases tax in conversion year | 1 | 1 | ✓ |
| Roth-first order depletes Roth faster | 1 | 1 | ✓ |
| California adds state tax vs no-state | 1 | 1 | ✓ |
| primaryEarned field present in year output | 1 | 1 | ✓ |
| spouseEarned field present in year output | 1 | 1 | ✓ |
| primaryEarned + spouseEarned === earned (invariant) | 180,000 | 180,000 | ✓ |
| FICA field present in pre-retirement year | 1 | 1 | ✓ |
| Pre-retirement FICA ≈ 7.65% of gross wages (below SS cap) | 9,180 | 9,180 | ✓ |
| Retirement-year FICA = 0 (no wages) | 0 | 0 | ✓ |
| Non-working spouse: total SS = primary own + spousal floor ($54K) | 54,000 | 54,000 | ✓ |
| Low-earning spouse ($700/mo own): total SS top-up to floor ($43,200/yr) | 43,200 | 43,200 | ✓ |
| High-earning spouse ($1,200/mo own > floor): no spousal top-up, total $38,400 | 38,400 | 38,400 | ✓ |
| Spousal benefit = $0 before primary files (primary age 67, files at 70) | 0 | 0 | ✓ |
| Spousal benefit activates when primary files at 70 | 60,240 | 60,240 | ✓ |
| spousalTopUp field exists in projection year output | 1 | 1 | ✓ |
| spousalTopUp value is correct ($18,000 for non-working spouse) | 18,000 | 18,000 | ✓ |
| No spousal benefit when spouse not enabled | 24,000 | 24,000 | ✓ |
| Spousal benefit stable year-over-year with zero COLA | 1 | 1 | ✓ |
Edge Case 6/6
| Test | Expected | Actual | Result |
|---|
| RMD on $0 balance = $0 | 0 | 0 | ✓ |
| RMD at age 110 (beyond table) uses fallback | 1 | 1 | ✓ |
| SS estimate with $0 income → $0 benefit | 0 | 0 | ✓ |
| calcTax on negative income → $0 | 1 | 1 | ✓ |
| IRMAA at exact $218K MFJ threshold → Tier 1 | 1 | 1 | ✓ |
| State tax with invalid code → $0 | 0 | 0 | ✓ |
SS Earnings Test 13/13
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.
| Test | Expected | Actual | Result |
|---|
| Under FRA: $20K earnings (below $23,400 limit) → $0 | 0 | 0 | ✓ |
| Under FRA: $50K earnings → $13,300 withheld | 13,300 | 13,300 | ✓ |
| Under FRA: $100K earnings, $16,800 benefit → capped at $16,800 | 16,800 | 16,800 | ✓ |
| At FRA (age 67, born 1960+): $200K earnings → $0 | 0 | 0 | ✓ |
| After FRA (age 70): $200K earnings → $0 | 0 | 0 | ✓ |
| Zero earnings → $0 | 0 | 0 | ✓ |
| FRA year (66.5): $70K earnings → $2,613 | 2,613 | 2,613 | ✓ |
| SSDI: exempt from earnings test | 0 | 0 | ✓ |
| Zero SS benefit → $0 | 0 | 0 | ✓ |
| Earnings at exact $23,400 limit → $0 | 0 | 0 | ✓ |
| Inflation indexing: 2030 under-FRA limit | 11,832 | 11,832 | ✓ |
| Integration: age 62 (working + SS) has withholding | 1 | 1 | ✓ |
| Integration: age 67 (FRA, retired) no withholding | 0 | 0 | ✓ |
ACA FPL 5/5
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.
| Test | Expected | Actual | Result |
|---|
| Household of 1 base FPL = $15,960 | 15,960 | 15,960 | ✓ |
| Household of 2 base FPL = $21,640 | 21,640 | 21,640 | ✓ |
| Household of 3 base FPL = $27,320 | 27,320 | 27,320 | ✓ |
| Household of 4 base FPL = $33,000 | 33,000 | 33,000 | ✓ |
| Household of 5 base FPL = $38,680 | 38,680 | 38,680 | ✓ |
ACA Subsidy 11/11
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.
| Test | Expected | Actual | Result |
|---|
| 150% FPL enhanced (2025) → 0% contribution, full subsidy | 0 | 0 | ✓ |
| 200% FPL enhanced (2025) → 2% applicable | 2 | 2 | ✓ |
| 300% FPL enhanced (2025) → 6% applicable | 6 | 6 | ✓ |
| 400% FPL enhanced (2025) → 8.5% applicable | 8.5 | 8.5 | ✓ |
| 500% FPL enhanced (2025) → still eligible (no cliff) | 1 | 1 | ✓ |
| Below 100% FPL → not eligible for subsidy | 0 | 0 | ✓ |
| 400%+ FPL original (2026) → no subsidy (cliff) | 0 | 0 | ✓ |
| 300% FPL original (2026) → 9.83% applicable | 9.83 | 9.83 | ✓ |
| 130% FPL original (2026) → 2% applicable | 2 | 2 | ✓ |
| Full subsidy at 150% FPL enhanced = benchmark premium | 20,000 | 20,000 | ✓ |
| 175% FPL enhanced → 1.0% applicable (interpolation) | 1 | 1 | ✓ |
ACA SLCSP 2/2
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.
| Test | Expected | Actual | Result |
|---|
| Age 55 single base → ~$9,000 | 9,000 | 9,000 | ✓ |
| Age 60 couple > age 55 single | 1 | 1 | ✓ |
Pension 15/15
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).
| Test | Expected | Actual | Result |
|---|
| Pension starts at age 65 with $24,000/yr | 24,000 | 24,000 | ✓ |
| Pension COLA: $24K at 2% after 5yr ≈ $26,497 | 26,497 | 26,498 | ✓ |
| Taxable 100%: pensionTaxable = pension | 24,000 | 24,000 | ✓ |
| Taxable 60%: pensionTaxable ≈ 60% of pension | 14,400 | 14,400 | ✓ |
| Taxable 0%: pensionTaxable = 0 (fully exempt) | 0 | 0 | ✓ |
| Single-life: pension = 0 after pensioner dies | 0 | 0 | ✓ |
| Joint & 50%: survivor gets 50% of pension | 12,000 | 12,000 | ✓ |
| Joint & 75%: survivor gets 75% of pension | 18,000 | 18,000 | ✓ |
| Joint & 100%: survivor gets 100% of pension | 24,000 | 24,000 | ✓ |
| J&50% + 60% taxable: survivor taxable = 50%*60%*24000 = 7200 | 7,200 | 7,200 | ✓ |
| Migration shim: legacy pension scalar produces correct income | 18,000 | 18,000 | ✓ |
| Two primary pensions sum: 12000 + 18000 = 30000 | 30,000 | 30,000 | ✓ |
| Staggered start ages: only first pension active at age 66 | 12,000 | 12,000 | ✓ |
| Staggered start ages: both active at age 67 | 30,000 | 30,000 | ✓ |
| Mixed-owner two pensions: primary 12000 + spouse 8000 = 20000 | 20,000 | 20,000 | ✓ |
Pension Partial Year 11/11
| Test | Expected | Actual | Result |
|---|
| Integer startAge=65, cola=0 → full $24K at age 65 (regression) | 24,000 | 24,000 | ✓ |
| Integer startAge=65, cola=0 → full $24K at age 70 (regression) | 24,000 | 24,000 | ✓ |
| startAge=65.5 → 50% prorated ($12K) at age 65 | 12,000 | 12,000 | ✓ |
| startAge=65.5 → full $24K at age 66 | 24,000 | 24,000 | ✓ |
| startAge=65.25 → 75% prorated ($18K) at age 65 | 18,000 | 18,000 | ✓ |
| startAge=65.75 → 25% prorated ($6K) at age 65 | 6,000 | 6,000 | ✓ |
| startAge=65.5 → $0 at age 64 (before activation) | 0 | 0 | ✓ |
| Lifetime total: fractional pension = integer total - half-year | 12,000 | 12,000 | ✓ |
| Spouse startAge=65.5 → 50% prorated ($6K) at spouse age 65 | 6,000 | 6,000 | ✓ |
| COLA compounds with fractional penYrs (age 70, startAge 65.5, 2% COLA) | 26,237 | 26,237 | ✓ |
| taxablePercent applies post-proration (60% of $12K = $7,200) | 7,200 | 7,200 | ✓ |
Additional Income 21/21
| Test | Expected | Actual | Result |
|---|
| Other income shows in projection at age 65 | 30,000 | 30,000 | ✓ |
| $30K additional income increases lifetime tax | 1 | 1 | ✓ |
| Tax increase is meaningful (>$10K lifetime) | 1 | 1 | ✓ |
| Default window: other income at retirement age | 30,000 | 30,000 | ✓ |
| Default window: other income at end age | 30,000 | 30,000 | ✓ |
| Late start (70): no other income at 65 | 0 | 0 | ✓ |
| Late start (70): no other income at 69 | 0 | 0 | ✓ |
| Late start (70): other income begins at 70 | 30,000 | 30,000 | ✓ |
| Early end (72): other income at 72 | 30,000 | 30,000 | ✓ |
| Early end (72): no other income at 73 | 0 | 0 | ✓ |
| Pre-retirement start (60): other income while working | 30,000 | 30,000 | ✓ |
| Window end (75): last year of other income | 30,000 | 30,000 | ✓ |
| After window (76): no other income | 0 | 0 | ✓ |
| $100K additional income raises IRMAA surcharges | 1 | 1 | ✓ |
| FillBracket: $50K other income reduces Roth conversion amount | 1 | 1 | ✓ |
| FillBracket: conversion reduced by ~$50K with other income | 1 | 0 | ✓ |
| SmartFill: $50K other income reduces Roth conversion | 1 | 1 | ✓ |
| TargetMAGI: $50K other income reduces Roth conversion | 1 | 1 | ✓ |
| Stuck state: empty incomeStreams + legacy additionalIncome → other = 0 | 0 | 0 | ✓ |
| Legacy plan: incomeStreams undefined + additionalIncome → other = 5000 | 5,000 | 5,000 | ✓ |
| Populated incomeStreams: additionalIncome ignored, other = stream amount | 12,000 | 12,000 | ✓ |
Life Insurance 12/12
Sources: IRC §101 (death benefit exclusion from gross income); engine logic: premium collection, term expiry, death benefit payout to taxable account
| Test | Expected | Actual | Result |
|---|
| Term premiums active before expiry (age 74) | 2,400 | 2,400 | ✓ |
| Term premiums stop at expiresAge (age 75) | 0 | 0 | ✓ |
| Term: no benefit if insured outlives term (death 80, expires 75) | 0 | 0 | ✓ |
| Term: benefit pays when death before expiry (death 72, expires 75) | 500,000 | 500,000 | ✓ |
| Permanent: premiums active before death (age 84) | 5,000 | 5,000 | ✓ |
| Permanent: premiums stop at death (age 85) | 0 | 0 | ✓ |
| Permanent: benefit pays at death age 85 | 500,000 | 500,000 | ✓ |
| Death benefit does NOT inflate MAGI by benefit amount (IRC §101) | 1 | 1 | ✓ |
| Death benefit increases brokerage balance | 1 | 1 | ✓ |
| No benefit when no death age configured | 0 | 0 | ✓ |
| Multiple policies: both pay out at death | 750,000 | 750,000 | ✓ |
| Spouse policy: benefit triggers at spouse death | 300,000 | 300,000 | ✓ |
REGS 13/13
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.
| Test | Expected | Actual | Result |
|---|
| REGS object exists | 1 | 1 | ✓ |
| REGS.version is 2025 | 1 | 1 | ✓ |
| REGS has 5 data sections | 5 | 5 | ✓ |
| federal.brackets_mfj has 7 brackets | 7 | 7 | ✓ |
| federal.brackets_single has 7 brackets | 7 | 7 | ✓ |
| irmaa.brackets_mfj has 6 tiers | 6 | 6 | ✓ |
| aca.pct_enhanced has 6 tiers | 6 | 6 | ✓ |
| TAX_BRACKETS_MFJ === REGS.federal.brackets_mfj | 1 | 1 | ✓ |
| IRMAA_BRACKETS_MFJ === REGS.irmaa.brackets_mfj | 1 | 1 | ✓ |
| ACA_PCT_ENHANCED === REGS.aca.pct_enhanced | 1 | 1 | ✓ |
| SS_WAGE_BASE matches REGS | 184,500 | 184,500 | ✓ |
| CONTRIB_401K matches REGS | 24,500 | 24,500 | ✓ |
| STD_DEDUCTION matches REGS | 32,200 | 32,200 | ✓ |
HSA 15/15
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.
| Test | Expected | Actual | Result |
|---|
| HSA contribution reduces federal taxable income | 1 | 1 | ✓ |
| HSA contribution does NOT reduce CA state income | 6,697 | 6,697 | ✓ |
| HSA contribution DOES reduce OR state income | 1 | 1 | ✓ |
| HSA balance grows at configured rate tax-free | 53,000 | 53,000 | ✓ |
| HSA medical withdrawal at age 60: tax-free | 0 | 0 | ✓ |
| HSA non-medical withdrawal at age 60: taxed + 20% penalty | 1 | 1 | ✓ |
| HSA withdrawal at age 67 (100% medical): tax-free | 0 | 0 | ✓ |
| HSA medical withdrawal at 67: zero MAGI impact | 0 | 0 | ✓ |
| HSA withdrawal at age 67 (50% medical): 50% taxed | 16,218 | 16,218 | ✓ |
| HSA: no RMD generated at any age | 0 | 0 | ✓ |
| Contribution limits enforced: self, age 56 = $5,400 max | 5,400 | 5,400 | ✓ |
| Contribution limits enforced: family, age 58 = $9,750 max | 9,750 | 9,750 | ✓ |
| Contributions stop at hsaContributionStopAge | 1 | 1 | ✓ |
| ACA MAGI reduced by HSA contribution | 1 | 1 | ✓ |
| Net worth includes HSA balance | 195,000 | 195,000 | ✓ |
Inherited IRA 16/16
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.
| Test | Expected | Actual | Result |
|---|
| $0 balance returns null (truthy check) | 1 | 1 | ✓ |
| $1M at 0% return, 22% bracket → net ~$780K | 780,000 | 780,000 | ✓ |
| Balance fully depleted after 10 years | 0 | 0 | ✓ |
| Year 1 dist at 0% return = balance/10 | 100,000 | 100,000 | ✓ |
| Higher bracket (24%) yields less net than 22% | 1 | 1 | ✓ |
| 7% growth: total distributed > initial balance | 1 | 1 | ✓ |
| Post-2019: $100K, 10 yrs left → $10,000 | 10,000 | 10,000 | ✓ |
| Post-2019: $100K, 1 yr left → full balance | 100,000 | 100,000 | ✓ |
| Post-2019: $0 balance → $0 | 0 | 0 | ✓ |
| Post-2019: $250K, 5 yrs left → $50,000 | 50,000 | 50,000 | ✓ |
| Pre-2020 stretch: age 50 yr 1, $1M → $27,624 | 27,624.309 | 27,624 | ✓ |
| Pre-2020 stretch: age 50 yr 5 (divisor 32.2), $1M → $31,056 | 31,055.901 | 31,056 | ✓ |
| Pre-2020 stretch: age 65 yr 1, $500K → $21,834 | 21,834.061 | 21,834 | ✓ |
| Pre-2020 stretch: divisor floor at 1.0 (divisor would go negative) | 500,000 | 500,000 | ✓ |
| Distribution never exceeds balance | 1,000 | 1,000 | ✓ |
| Unknown regime returns 0 | 0 | 0 | ✓ |
Inherited Acct 13/13
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.
| Test | Expected | Actual | Result |
|---|
| Year 0: regular pretax balance = 500K (untouched) | 500,000 | 500,000 | ✓ |
| Year 0: inherited pretax = 450K after first distribution | 450,000 | 450,000 | ✓ |
| Year 0: inherited distribution = 50K | 50,000 | 50,000 | ✓ |
| Year 9 (age 69): inherited balance = 0 (fully distributed) | 0 | 0 | ✓ |
| Year 10 (age 70): inherited dist = 0 (nothing left) | 0 | 0 | ✓ |
| Net worth = sum of all buckets including inherited | 1,266,180 | 1,266,180 | ✓ |
| Inherited pretax distributions increase lifetime taxes | 1 | 1 | ✓ |
| Inherited distributions preserve pretax (less drawdown needed) | 1 | 1 | ✓ |
| Roth conversion pulls from pretax only (not inherited) | 460,000 | 460,000 | ✓ |
| Inherited balance unchanged by Roth conversion (still 450K) | 450,000 | 450,000 | ✓ |
| No standard RMD when only inherited pretax exists | 0 | 0 | ✓ |
| Inherited Roth has lower lifetime tax than inherited traditional | 1 | 1 | ✓ |
| Inherited Roth depleted by year 9 | 0 | 0 | ✓ |
Inherited Stretch 8/8
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.
| Test | Expected | Actual | Result |
|---|
| Yr 1: age 55, $500K → ~$15,823 | 15,823 | 15,823 | ✓ |
| Stretch balance adds to netWorth yr 1 | 1 | 1 | ✓ |
| Inherited Roth stretch: tax-free distributions never raise tax | 1 | 1 | ✓ |
| Traditional stretch yr 1 inheritedDist recorded | 12,658 | 12,658 | ✓ |
| No regime field: v5.4 pooled behavior preserved | 9,091 | 9,091 | ✓ |
| Mixed stretch + post-2019: both distribute | 29,252 | 29,252 | ✓ |
| Stretch balance decreases year-over-year at 0% return | 1 | 1 | ✓ |
| EDB Disabled: yr 1 uses stretch math (100K / 31.6 age 55) | 3,165 | 3,165 | ✓ |
CG Harvesting 4/4
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.
| Test | Expected | Actual | Result |
|---|
| Disabled: zero harvested | 1 | 1 | ✓ |
| Enabled with basis: harvests in low-income year | 1 | 1 | ✓ |
| No unrealized gains: zero harvested | 1 | 0 | ✓ |
| Harvested amount ≤ unrealized gains | 1 | 1 | ✓ |
TLH 4/4
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.
| Test | Expected | Actual | Result |
|---|
| Disabled: zero offset | 1 | 1 | ✓ |
| $5K losses: offset applied | 1 | 1 | ✓ |
| $3K ordinary offset when minimal CG | 1 | 0 | ✓ |
| Carry-forward accumulates across years | 1 | 1 | ✓ |
Itemized Deductions 4/4
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.
| Test | Expected | Actual | Result |
|---|
| Disabled: uses standard deduction | 1 | 1 | ✓ |
| SALT $10K + charity $25K beats standard for single filer | 1 | 0 | ✓ |
| Low SALT, no charity: standard wins for MFJ senior | 1 | 1 | ✓ |
| Medical 7.5% AGI floor applied correctly | 1 | 1 | ✓ |
SALT Cap (OBBB) 6/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).
| Test | Expected | Actual | Result |
|---|
| 2025 base cap $40,000 MFJ/Single | 40,000 | 40,000 | ✓ |
| 2026 cap with 1% growth: $40,400 | 40,400 | 40,400 | ✓ |
| 2030 reversion to $10,000 TCJA baseline | 10,000 | 10,000 | ✓ |
| 2025 phase-down at MAGI $550K: cap $25,000 | 25,000 | 25,000 | ✓ |
| 2025 phase-down floor at MAGI $700K: cap $10,000 | 10,000 | 10,000 | ✓ |
| 2026 phase-down threshold also grows: MAGI $505K → full cap | 40,400 | 40,400 | ✓ |
DAF Bunching 4/4
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.
| Test | Expected | Actual | Result |
|---|
| Disabled: charity same every year | 1 | 1 | ✓ |
| Every-2yr: bunch year has 2x charity | 1 | 1 | ✓ |
| Every-2yr: off year has zero charity | 1 | 1 | ✓ |
| Lifetime charity total unchanged vs no bunching | 1 | 1 | ✓ |
QLAC 6/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.
| Test | Expected | Actual | Result |
|---|
| Disabled: no QLAC payout or balance | 1 | 1 | ✓ |
| Purchase reduces pre-tax balance at purchase age | 1 | 1 | ✓ |
| RMD lower with QLAC than without | 1 | 1 | ✓ |
| Payout starts at payoutStartAge as ordinary income | 1 | 1 | ✓ |
| 2026 QLAC limit: $210,000 | 210,000 | 210,000 | ✓ |
| Engine caps purchase at QLAC_LIMIT when user requests more | 210,000 | 210,000 | ✓ |
Roth Conversion Gate 12/12
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'.
| Test | Expected | Actual | Result |
|---|
| Before primary retires: conversion caps at IRA (convertible) balance only | 50,000 | 50,000 | ✓ |
| After convertible pool drained, conversions stop even with gated balance remaining | 0 | 0 | ✓ |
| At primaryRetires age, gated 401k unlocks and permits conversion again | 1 | 1 | ✓ |
| Without gate, conversions continue at age 62 from full pretax pool | 100,000 | 100,000 | ✓ |
| Absent conversionGate field = always convertible (backward compat) | 100,000 | 100,000 | ✓ |
| Spouse-gated account: still locked when spouse < spouseRetirementAge | 0 | 0 | ✓ |
| Spouse-gated account: unlocks when spouse reaches spouseRetirementAge | 1 | 1 | ✓ |
| spouseRetires gate with spouseEnabled=false coerces to always-convertible | 50,000 | 50,000 | ✓ |
| specificAge gate: still locked before the specified age | 0 | 0 | ✓ |
| specificAge gate: unlocks at the specified primary age | 1 | 1 | ✓ |
| Account identity holds with gated tranches in pool | 0 | 0 | ✓ |
| RMDs draw proportionally from full pool including gated tranches | 1 | 1 | ✓ |
Dual-Owner RMD 9/9
| Test | Expected | Actual | Result |
|---|
| Spouse RMD fires at spAge 73 (projection yr 4) | 1 | 1 | ✓ |
| Primary RMD = 0 at primary age 69 (below RMD start) | 0 | 0 | ✓ |
| Total rmd = rmdSpouse when only spouse at RMD age | 22,487 | 22,487 | ✓ |
| Spouse RMD amount ≈ balance ÷ 26.5 at age 73 | 22,487 | 22,487 | ✓ |
| Primary RMD fires at primary age 75 (projection yr 10) | 1 | 1 | ✓ |
| Spouse RMD still firing at spAge 79 (yr 10) | 1 | 1 | ✓ |
| Total rmd = rmdPrimary + rmdSpouse when both at RMD age | 61,814 | 61,814 | ✓ |
| Invariant: preTax = preTaxPrimary + preTaxSpouse every year | 1 | 1 | ✓ |
| Opt-out: no owner:spouse account → preTaxSpouse stays 0 | 1 | 1 | ✓ |
Dual-Owner Growth 2/2
| Test | Expected | Actual | Result |
|---|
| Primary bucket grows at 5% (per-owner rate) | 638,141 | 638,141 | ✓ |
| Spouse bucket grows at 7% (per-owner rate) | 701,276 | 701,276 | ✓ |
Dual-Owner Rollover 2/2
| Test | Expected | Actual | Result |
|---|
| Primary death → spouse absorbs pool (preTaxPrimary = 0) | 0 | 0 | ✓ |
| Spouse death → primary absorbs pool (preTaxSpouse = 0) | 0 | 0 | ✓ |
Debt Amortization 11/11
| Test | Expected | Actual | Result |
|---|
| Year 1 interest: $200K @ 6% / $1,199.10/mo | 11,933 | 11,933 | ✓ |
| Year 1 principal: $200K @ 6% / $1,199.10/mo | 2,456 | 2,456 | ✓ |
| Year 1 ending balance: $200K @ 6% / $1,199.10/mo | 197,544 | 197,544 | ✓ |
| Year 1 total paid: 12 × $1,199.10/mo | 14,389 | 14,389 | ✓ |
| $200K @ 6% / $1,199.10/mo pays off in 30 years | 30 | 31 | ✓ |
| Zero-rate loan: $12K @ 0% / $1,000/mo — no interest | 0 | 0 | ✓ |
| Zero-rate loan: $12K @ 0% / $1,000/mo — paid off in year 1 | 1 | 1 | ✓ |
| Zero balance: no payment, no interest | 0 | 0 | ✓ |
| Payment < monthly interest: balance unchanged | 200,000 | 200,000 | ✓ |
| Engine integration: year-1 interest matches helper | 11,933 | 11,933 | ✓ |
| Engine integration: year-1 payments matches helper | 14,389 | 14,389 | ✓ |
Relocation Scenario 7/7
| Test | Expected | Actual | Result |
|---|
| No relocation params: CA tax non-zero pre-retirement (age 60) | 1 | 1 | ✓ |
| Move at 70: pre-move year (age 65) uses original state | 1 | 1 | ✓ |
| Move at 70: post-move year (age 75) uses target state | 0 | 0 | ✓ |
| Move at 70: cutover year (age 70 itself) uses target state | 0 | 0 | ✓ |
| Move at currentAge: target state for entire projection | 0 | 0 | ✓ |
| Move beyond endAge: original state for entire projection | 1 | 1 | ✓ |
| Pre-retirement move at 55: working year (age 60) uses target state | 0 | 0 | ✓ |
Roth Multi-Phase 6/6
| Test | Expected | Actual | Result |
|---|
| Empty phases array: identical to absent phases (regression) | 9,419 | 9,419 | ✓ |
| Single phase = single-target (full-window 12% phase) | 0 | 0 | ✓ |
| Two-phase: 24% phase produces larger conv than 12% phase | 1 | 1 | ✓ |
| Gap year (age 68): zero conversion | 0 | 0 | ✓ |
| Phase boundary: endAge is inclusive (age 67 is in phase 1) | 1 | 1 | ✓ |
| Outer rc.startAge gates phases: age 60 outside outer window → 0 | 0 | 0 | ✓ |
Account Canon 13/13
| Test | Expected | Actual | Result |
|---|
| Legacy type:cash → type:taxable | 1 | 1 | ✓ |
| Legacy type:cash → subtype:cash | 1 | 1 | ✓ |
| Legacy type:cash preserves balance | 65,000 | 65,000 | ✓ |
| Legacy type:cash preserves returnRate | 4.5 | 4.5 | ✓ |
| Canonical taxable (no subtype) unchanged | 1 | 1 | ✓ |
| Type pretax passes through unchanged | 1 | 1 | ✓ |
| Type roth passes through unchanged | 1 | 1 | ✓ |
| Type hsa passes through unchanged | 1 | 1 | ✓ |
| Type inherited-pretax passes through unchanged | 1 | 1 | ✓ |
| Type inherited-roth passes through unchanged | 1 | 1 | ✓ |
| canonicalizeAccount is idempotent | 1 | 1 | ✓ |
| Legacy type:cash without returnRate → default 4 | 4 | 4 | ✓ |
| Legacy cash account → engine cash bucket > 0 in year 0 | 1 | 1 | ✓ |
Optimizer Blend 8/8
| Test | Expected | Actual | Result |
|---|
| Pre-tax overridden to nominalReturn | 7 | 7 | ✓ |
| Roth overridden to nominalReturn | 7 | 7 | ✓ |
| Taxable-investment overridden | 7 | 7 | ✓ |
| Cash subtype keeps its rate | 4.5 | 4.5 | ✓ |
| HSA overridden to nominalReturn | 7 | 7 | ✓ |
| Input array not mutated | 5 | 5 | ✓ |
| blendedAccountsForOptimizer is idempotent | 7 | 7 | ✓ |
| Integration: real Roth 10% > blended Roth 7% by year 30 | 1 | 1 | ✓ |
Divergence 8/8
| Test | Expected | Actual | Result |
|---|
| pre-tax 5% vs Roth 9% → shouldWarn:true | 1 | 1 | ✓ |
| pre-tax 5% vs Roth 9% → divergence == 4 | 4 | 4 | ✓ |
| pre-tax 6% vs Roth 7.5% (1.5pp) → shouldWarn:false | 0 | 0 | ✓ |
| Exactly 2.0pp → shouldWarn:true | 1 | 1 | ✓ |
| Roth < $10K → shouldWarn:false | 0 | 0 | ✓ |
| No Roth → shouldWarn:false | 0 | 0 | ✓ |
| Multi-account pre-tax: weighted avg 5.4% | 5.4 | 5.4 | ✓ |
| Multi-account divergence 2.6pp → shouldWarn:true | 1 | 1 | ✓ |
Tax Payment v6.8 11/11
| Test | Expected | Actual | Result |
|---|
| Single retiree FL: tax > 0 in year 1 | 1 | 1 | ✓ |
| Single retiree FL: withdrawal exceeds raw expense gap | 1 | 1 | ✓ |
| Couple MFJ GA: state tax > 0 with $50K taxable pension | 1 | 1 | ✓ |
| Couple MFJ GA: withdrawal covers fed+state tax bill | 1 | 1 | ✓ |
| Stable-income retiree: iteration converges from year 1+ (warm-start) | 1 | 1 | ✓ |
| Year record exposes taxConvergenceFlag as boolean | 1 | 1 | ✓ |
| NIIT triggers for high-MAGI single via iteration | 1 | 1 | ✓ |
| SS taxation propagates through iteration (couple MFJ) | 1 | 1 | ✓ |
| wdOrder honored: roth untouched when earlier sources suffice | 1 | 1 | ✓ |
| Pre-retirement: taxConvergenceFlag stays false (no iteration) | 1 | 1 | ✓ |
| Pre-retirement: withdrawal stays 0 (no fillGap path) | 1 | 1 | ✓ |
Roth 5-year 7/7
| Test | Expected | Actual | Result |
|---|
| Age 58 (yr 3 post-conv): $5K Roth wd → ~$500 penalty (iter cascade adds slight overshoot) | 530 | 556 | ✓ |
| Age 60: Roth wd → no penalty (age >59.5 exemption) | 0 | 0 | ✓ |
| Age 61: Roth wd → no penalty (age + 5-yr both clear) | 0 | 0 | ✓ |
| Two conversions, $15K wd at age 54 → penalty oldest-first ≈ $1500-$1700 | 1,600 | 1,611 | ✓ |
| Age 56 ($5K wd, 1yr post-conv) → ~$500 penalty | 530 | 500 | ✓ |
| Age 60 ($10K wd, 5yr post-conv but >59.5) → no penalty | 0 | 0 | ✓ |
| No conversions, age 55 Roth wd → no penalty (empty tranches) | 0 | 0 | ✓ |
Inherited Roth Backload 7/7
| Test | Expected | Actual | Result |
|---|
| Default even-spread: year 2026 has non-zero inherited Roth dist | 1 | 1 | ✓ |
| Back-load: zero distribution in 2026 (year 4 of 10) | 1 | 1 | ✓ |
| Back-load: zero distribution in 2032 (year 9 of 10) | 1 | 1 | ✓ |
| Back-load: full balance distributed in deadline year 2033 | 1 | 1 | ✓ |
| Back-load total distributions > even-spread total (extra compounding) | 1 | 1 | ✓ |
| Back-load: inherited Roth balance = 0 after year 10 | 1 | 1 | ✓ |
| Back-load: lifetime federal tax not increased by inherited Roth timing | 1 | 1 | ✓ |
Brokerage Dividend Yield 9/9
| Test | Expected | Actual | Result |
|---|
| 4% × $1M brokerage = $40K dividend income (year 0) | 40,000 | 40,000 | ✓ |
| $40K brokerage QD reflected in MAGI | 1 | 1 | ✓ |
| Ordinary dividend yield produces higher fed tax than qualified | 1 | 1 | ✓ |
| Account without dividendYield: zero dividend income | 0 | 0 | ✓ |
| Multi-account: dividends sum correctly | 40,000 | 40,000 | ✓ |
| Mixed qualified + ordinary: brokerageDividendIncome sums both | 40,000 | 40,000 | ✓ |
| Untouched brokerage: dividend income grows with balance over 10 years | 1 | 1 | ✓ |
| Drained brokerage: dividend income shrinks as balance is withdrawn | 1 | 1 | ✓ |
| No brokerage accounts: zero div income, no NaN | 1 | 1 | ✓ |
QD Pipeline Regression 4/4
| Test | Expected | Actual | Result |
|---|
| $50K QD stream raises lifetime fed tax | 1 | 1 | ✓ |
| $50K QD stream reflected in MAGI | 1 | 1 | ✓ |
| High-MAGI single filer: NIIT > 0 on QD | 1 | 1 | ✓ |
| $30K QD alone < $48,350 single 0% LTCG threshold → $0 fed tax | 0 | 0 | ✓ |
Monte Carlo Envelopes 6/6
| Test | Expected | Actual | Result |
|---|
| p10/median/p90 returned and equal-length | 1 | 1 | ✓ |
| p10 <= median <= p90 at EVERY age (the fan-chart property) | 1 | 1 | ✓ |
| Envelope entry has age/year/retired/netWorth fields | 1 | 1 | ✓ |
| Final-year p10 < final-year p90 (non-degenerate) | 1 | 1 | ✓ |
| Final-year envelope spread > year-0 spread (variance grows) | 1 | 1 | ✓ |
| successRate is integer 0-100 | 1 | 1 | ✓ |
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.
| Test | Expected | Actual | Result |
|---|
| SS portion = wages × 6.2% | 6,200 | 6,200 | ✓ |
| Medicare portion = wages × 1.45% | 1,450 | 1,450 | ✓ |
| Total = SS + Medicare, no Addl Med here | 7,650 | 7,650 | ✓ |
| SS portion at cap = cap × 6.2% | 11,439 | 11,439 | ✓ |
| Medicare at cap = cap × 1.45% (no cap on Medicare) | 2,675 | 2,675 | ✓ |
| SS portion caps at wage base × 6.2% | 11,439 | 11,439 | ✓ |
| Medicare continues on full wage amount | 3,625 | 3,625 | ✓ |
| Zero wages → all zero | 0 | 0 | ✓ |
| SS portion limited to remaining base | 5,239 | 5,239 | ✓ |
| Medicare still on full wages | 2,175 | 2,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.
| Test | Expected | Actual | Result |
|---|
| Having a pre-tax account reduces pre-retirement federal tax | 1 | 1 | ✓ |
| FICA unchanged by pre-tax 401(k) choice | 9,180 | 9,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.
| Test | Expected | Actual | Result |
|---|
| No phases → base income at year 0 | 120,000 | 120,000 | ✓ |
| Phase income overrides base during covered years | 150,000 | 150,000 | ✓ |
| Age outside phases uses base income | 120,000 | 120,000 | ✓ |
| Phase-level incomeBreakdown reduces FICA base by §125 + payrollHSA | 10,098 | 10,098 | ✓ |
| Phase SE does not pollute FICA (separate channels) | 0 | 0 | ✓ |
| W-2 + SE in same phase: FICA on W-2 only (unchanged by SE) | 7,650 | 7,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.
| Test | Expected | Actual | Result |
|---|
| $50K net SE: secaBase = $46,175 | 46,175 | 46,175 | ✓ |
| $50K net SE: SS portion = $5,726 | 5,726 | 5,726 | ✓ |
| $50K net SE: Medicare = $1,339 | 1,339 | 1,339 | ✓ |
| $50K net SE: total = $7,065 | 7,065 | 7,065 | ✓ |
| $50K net SE: halfDeduction ≈ $3,533 | 3,533 | 3,533 | ✓ |
| Negative SE: zero SE tax | 0 | 0 | ✓ |
| Negative SE: zero half-deduction | 0 | 0 | ✓ |
| $400 net SE: under SECA floor → zero SE tax | 0 | 0 | ✓ |
| Wage base sharing: SS portion limited | 4,278 | 4,278 | ✓ |
| Wage base sharing: Medicare uncapped | 2,143 | 2,143 | ✓ |
SE Tax Integration 10/10
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.
| Test | Expected | Actual | Result |
|---|
| SE tax in year output ≈ $11,304 | 11,304 | 11,304 | ✓ |
| seNetEarnings = $80K | 80,000 | 80,000 | ✓ |
| seHalfDeduction ≈ $5,652 | 5,652 | 5,652 | ✓ |
| SE loss: zero SE tax | 0 | 0 | ✓ |
| SE loss: seNetEarnings = -$20K | -20,000 | -20,000 | ✓ |
| SE loss: seHalfDeduction = 0 | 0 | 0 | ✓ |
| SE loss reduces MAGI vs baseline | 1 | 1 | ✓ |
| W-2 + SE: SE tax respects wage base sharing | 6,421 | 6,421 | ✓ |
| Working-age SE: seTax > 0 | 1 | 1 | ✓ |
| Spouse SE: seTax > 0 | 1 | 1 | ✓ |
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).
| Test | Expected | Actual | Result |
|---|
| Below threshold MFJ: full 20% deduction | 20,000 | 20,000 | ✓ |
| SSTB above upper MFJ: $0 deduction | 0 | 0 | ✓ |
| Non-SSTB above upper MFJ no W-2: $400 floor | 400 | 400 | ✓ |
| SSTB phase-in midpoint MFJ: half deduction | 10,000 | 10,000 | ✓ |
| Taxable income limit MFJ: capped at 20% × (taxable − net capgain) | 10,000 | 10,000 | ✓ |
| QBI below $1,000: no deduction | 0 | 0 | ✓ |
| Single below threshold: full 20% deduction | 10,000 | 10,000 | ✓ |
| Single SSTB above upper: $0 deduction | 0 | 0 | ✓ |
| Two-business household MFJ: combined deduction | 16,000 | 16,000 | ✓ |
| Mixed SSTB/non-SSTB above upper: $400 floor via non-SSTB | 400 | 400 | ✓ |
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.
| Test | Expected | Actual | Result |
|---|
| W-2 + SE: qbiDeduction > 0 | 1 | 1 | ✓ |
| W-2 + SE $100K: qbiDeduction ≈ 20% × (netSE − halfSE) | 18,932 | 18,932 | ✓ |
| Pure SE $100K: deduction capped by household taxable income limit | 1 | 1 | ✓ |
| SSTB in phase-in range: deduction reduced vs full | 1 | 1 | ✓ |
| SSTB conversion: conversionTax > 0 | 1 | 1 | ✓ |
| TLH + QBI: deduction computed (no asymmetry crash) | 1 | 1 | ✓ |
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.
| Test | Expected | Actual | Result |
|---|
| $100K netSE age 45 auto-max: employee = $24,500 | 24,500 | 24,500 | ✓ |
| $100K netSE age 45 auto-max: employer ≈ $18,587 | 18,587 | 18,587 | ✓ |
| Traditional employee classification | 24,500 | 24,500 | ✓ |
| Roth employee classification | 0 | 0 | ✓ |
| Age 62 SECURE 2.0 catch-up: employee = $35,750 | 35,750 | 35,750 | ✓ |
| §415(c) binds: employer + employee = annual addition limit | 72,000 | 72,000 | ✓ |
| §415(c) clamp produces employer = $47,500 | 47,500 | 47,500 | ✓ |
| §402(g) aggregation: Solo limited by W-2 deferral target | 4,500 | 4,500 | ✓ |
| Auto employer respects §415(c) | 18,587 | 18,587 | ✓ |
| Roth employee: tradEmployee = 0 | 0 | 0 | ✓ |
| Roth employee: rothEmployee tracks amount | 24,500 | 24,500 | ✓ |
| Roth employer: rothEmployerAmt > 0 | 18,587 | 18,587 | ✓ |
| Roth employer: tradEmployer = 0 | 0 | 0 | ✓ |
| Manual deferral $10K respected | 10,000 | 10,000 | ✓ |
| Manual employer 10% = $9,294 | 9,294 | 9,294 | ✓ |
| Negative SE: zero employee deferral | 0 | 0 | ✓ |
| Negative SE: zero employer profit-share | 0 | 0 | ✓ |
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.
| Test | Expected | Actual | Result |
|---|
| solo401kEmployee surfaces in year output | 24,500 | 24,500 | ✓ |
| solo401kEmployer ≈ $18,587 | 18,587 | 18,587 | ✓ |
| Traditional Solo reduces MAGI | 1 | 1 | ✓ |
| Roth employee preserves AGI | 1 | 1 | ✓ |
| Traditional Solo: qbiDeduction not larger than without Solo | 1 | 1 | ✓ |
| Roth employee preserves QBI deduction | 1 | 1 | ✓ |
| Roth employer: same QBI input as traditional employer (W-2+SE) | 1 | 1 | ✓ |
| Roth employer: higher MAGI than traditional employer | 1 | 1 | ✓ |
| Spouse Solo 401(k): contributions > 0 | 1 | 1 | ✓ |
Pre-flight 3/3
| Test | Expected | Actual | Result |
|---|
| calcSpousalBenefit exists | function | function | ✓ |
| calcSpousalBenefit has 4 parameters (not old 2-param version) | 4 | 4 | ✓ |
| ssReverseAdjustPIA exists | true | true | ✓ |
Spousal SS 13/13
| Test | Expected | Actual | Result |
|---|
| Spouse at FRA (67), no own record → 50% of worker PIA | 12,000 | 12,000 | ✓ |
| Worker PIA = $3,000/mo, spouse at FRA → $18,000/yr floor | 18,000 | 18,000 | ✓ |
| Spouse own = $9,600/yr (< $12K floor) → top-up = $2,400 | 2,400 | 2,400 | ✓ |
| Spouse own = $600/mo ($7,200/yr) → top-up = $4,800 | 4,800 | 4,800 | ✓ |
| Spouse own = $12,000/yr (= floor) → top-up = $0 | 0 | 0 | ✓ |
| Spouse own = $13,200/yr (> floor) → top-up = $0 | 0 | 0 | ✓ |
| Spouse claims at 62 (60mo early), no own record → 35% of worker PIA | 8,400 | 8,400 | ✓ |
| Spouse claims at 65 (24mo early) → ~43.3% of worker PIA annual | 10,400 | 10,400 | ✓ |
| FRA=66, claims at 62 (48mo early) → 37.5% of worker PIA | 9,000 | 9,000 | ✓ |
| Spouse claims at 70 (past FRA) → same as FRA, no DRC increase | 12,000 | 12,000 | ✓ |
| Spouse claims at 68 (past FRA) → same as FRA | 12,000 | 12,000 | ✓ |
| Worker PIA = $0 → top-up = $0 | 0 | 0 | ✓ |
| Spouse own = $0, worker PIA = $0 → $0 | 0 | 0 | ✓ |
COLA 2/2
| Test | Expected | Actual | Result |
|---|
| Spousal benefit grows with COLA at age 68 vs 67 | 1 | 1 | ✓ |
| Spousal top-up at 68 ≈ $18,360 (18,000 × 1.02) | 18,360 | 18,360 | ✓ |
UI Wording 4/4
| Test | Expected | Actual | Result |
|---|
| "Spousal SS Benefit" Plan Health lever title exists in HTML | 1 | 1 | ✓ |
| Lever says benefit is "already included in your projection" | 1 | 1 | ✓ |
| Lever links to SS tab (inputTab=ss) | 1 | 1 | ✓ |
| Old 2-param calcSpousalBenefit signature is NOT present | 1 | 1 | ✓ |
Reverse round-trip 16/16
| Test | Expected | Actual | Result |
|---|
| PIA $1500 claim 62 → adj $1050 → reverse $1500 | 1,500 | 1,500 | ✓ |
| PIA $1500 claim 65 → adj $1300 → reverse $1500 | 1,500 | 1,500 | ✓ |
| PIA $1500 claim 67 → adj $1500 → reverse $1500 | 1,500 | 1,500 | ✓ |
| PIA $1500 claim 68 → adj $1620 → reverse $1500 | 1,500 | 1,500 | ✓ |
| PIA $1500 claim 70 → adj $1860 → reverse $1500 | 1,500 | 1,500 | ✓ |
| PIA $2500 claim 62 → adj $1750 → reverse $2500 | 2,500 | 2,500 | ✓ |
| PIA $2500 claim 65 → adj $2166 → reverse $2499 | 2,500 | 2,499 | ✓ |
| PIA $2500 claim 67 → adj $2500 → reverse $2500 | 2,500 | 2,500 | ✓ |
| PIA $2500 claim 68 → adj $2700 → reverse $2500 | 2,500 | 2,500 | ✓ |
| PIA $2500 claim 70 → adj $3100 → reverse $2500 | 2,500 | 2,500 | ✓ |
| PIA $3500 claim 62 → adj $2450 → reverse $3500 | 3,500 | 3,500 | ✓ |
| PIA $3500 claim 65 → adj $3033 → reverse $3500 | 3,500 | 3,500 | ✓ |
| PIA $3500 claim 67 → adj $3500 → reverse $3500 | 3,500 | 3,500 | ✓ |
| PIA $3500 claim 68 → adj $3780 → reverse $3500 | 3,500 | 3,500 | ✓ |
| PIA $3500 claim 70 → adj $4340 → reverse $3500 | 3,500 | 3,500 | ✓ |
| Zero current monthly returns 0 | 0 | 0 | ✓ |
Primary already-collecting 4/4
| Test | Expected | Actual | Result |
|---|
| Age 75: ss = current monthly × 12 (COLA 0%) | 50,400 | 50,400 | ✓ |
| Age 80: ss still 50400 (COLA 0%) | 50,400 | 50,400 | ✓ |
| Age 75 with COLA 2%: ss = 50400 (zero COLA years) | 50,400 | 50,400 | ✓ |
| Age 80 with COLA 2%: ss ≈ 50400 × 1.02^5 ≈ 55648 | 55,648 | 55,646 | ✓ |
Spousal floor 1/1
| Test | Expected | Actual | Result |
|---|
| Already-collecting primary supplies recovered PIA for spousal floor | 54,000 | 54,000 | ✓ |
Spouse already-collecting 2/2
| Test | Expected | Actual | Result |
|---|
| Primary not yet filed, spouse already collecting → ss > 0 | 1 | 1 | ✓ |
| Spouse benefit constant when primary still not filed (COLA 0%) | 1 | 1 | ✓ |
Both already-collecting 2/2
| Test | Expected | Actual | Result |
|---|
| Year 0 (age 73): combined ss ≈ (3500+1800) × 12 = 63600 | 63,600 | 63,600 | ✓ |
| Five years later (age 78): combined ss ≈ 63600 × 1.02^5 ≈ 70221 | 70,221 | 70,219 | ✓ |
Backwards compat 1/1
| Test | Expected | Actual | Result |
|---|
| Non-working spouse with ssAlreadyCollecting:false → $54K at FRA | 54,000 | 54,000 | ✓ |