Changelog

A complete record of every update shipped to RetIQ. Newest first. No entries are ever removed.

v6.18.10
QLAC purchase cap updated to 2026 limit ($210,000)

The QLAC purchase cap moves from the SECURE 2.0 base of $200,000 to the 2026 inflation-adjusted limit of $210,000. The cap is now sourced from REGS.retirement_accounts.qlac_limit — the same regulatory data layer that holds federal brackets, contribution limits, and IRMAA thresholds — so future inflation adjustments land via the regulatory data feed without code changes. Updated in the engine, Inputs → Pension & QLAC card, Strategy Report alternative, manual, features page, and AI guide. Sourced from IRS Notice 2025-67; SECURE 2.0 Act §202; Treas. Reg. §1.401(a)(9)-6.

Two new validation tests cover the constant value and the engine's enforcement of the cap when a user requests more than the limit.

This release does not change the per-individual nature of the cap. RetIQ currently models a single household-level QLAC; a future release will add per-spouse QLAC modeling so married couples can reflect the full $420,000 of combined eligibility ($210,000 each).

Separately, repaired the QLAC test fixture in test-validation.js. The previous minimal fixture was missing several engine-required fields (birthYear, nominalReturn, SS config, etc.), which caused runProjection() to return NaN throughout the projection. Three of the four existing QLAC tests had been passing for the wrong reason — their boolean-ternary outputs fell through to 0, and the integer tolerance of 1 absorbed the failure. Fixture repaired to a complete params object; all four tests now pass against real numeric output. No engine changes; the assertions were correct, only the inputs feeding them were broken.

v6.18.9
Today's $ toggle: inline rate is now read-only and links to Assumptions

The small % infl. input on the Today's $ toggle looked editable but discarded any value typed into it — both render() and renderDollarToggle() reassigned the displayed rate from state.params.inflation on every redraw, overwriting any inline edit before it could take effect.

The inline input is now a read-only, clickable label that jumps to Inputs → Assumptions and scrolls the Inflation Rate field into view. Single source of truth: change the rate once, in Assumptions, and the toggle reflects it everywhere — matching what the iOS and macOS apps already do. Allowing a divergent display rate would produce mathematically meaningless numbers (deflating nominal projections by a rate that doesn't equal the engine's inflation yields neither today's dollars nor future dollars), so removing the editable surface is also the mathematically correct choice.

New regression tests in test-ui-paths.js lock in the read-only markup and the navigation helper export.

v6.18.8
Dashboard — per-spouse income rows

The "This Year" card on the dashboard now shows Your Income and Spouse Income as separate rows when a spouse is enabled, mirroring the labels on the Inputs tab. Solo plans continue to show a single Employment Income row.

Engine: new primaryEarned and spouseEarned fields on the year-output object. Additive only — no math changes. The existing earned field is unchanged and still equals primaryEarned + spouseEarned. Three new validation tests assert field presence and the sum invariant.

v6.18.7
Healthcare tab card order fix

v6.18.6 missed a card when merging Medicare into Healthcare. The Pre-Medicare Coverage Cost Summary — which shows gap length, total net cost, and a year-by-year cost table for the pre-Medicare years — was sitting at the bottom of the Healthcare tab, separated from its config card by Medicare and LTC. Moved it to immediately follow Pre-Medicare Healthcare. New order: Pre-Medicare Healthcare config → its Cost Summary → Medicare & IRMAA → Long-Term Care. Pre-Medicare config and its derived display now sit together; Medicare and LTC come after as the later phases.

v6.18.6
Healthcare and Medicare combined into one tab

The Inputs → Medicare tab had no inputs — just an IRMAA bracket table, lifetime stats, and a chart, all computed from the projection. A user pointed out the disconnect: the tab name promised inputs, the page delivered display. The fix is structural rather than cosmetic: combine Healthcare and Medicare into a single Healthcare tab with three cards stacked vertically — Pre-Medicare Healthcare, Medicare & IRMAA, and Long-Term Care. Reading order matches age progression (before 65, 65+, typically 80+).

The web sub-nav drops from 14 tabs to 13. The Medicare card content is unchanged — same bracket table, same chart, same lifetime stats — just relocated. If a saved plan was sitting on the Medicare tab when it last loaded, the next render quietly redirects to Healthcare so nothing appears broken. Plan Health levers and AI guide references that pointed at the old Medicare tab now point at Healthcare.

This brings the web app's IA in line with the iOS native app, which already groups Pre-Medicare and Medicare under a single Healthcare disclosure.

v6.18.5
Peak Net Worth and Peak RMD now correct in Today's $

A user diagnosed this one cleanly: "the peak is identified in future $s, and then displayed (accurately) in both toggle settings. That can lead to the scenario where the peak in future $s (converted to current $s) is less than the current value (current $s), so the display is off." Exactly right.

The Snapshot was identifying the peak year by largest nominal Net Worth, then deflating that single value when the Today's $ toggle was on. For retirement plans where Net Worth grows nominally for a decade or two before declining, the nominal-peak year is far enough in the future that its deflated value is smaller than year 0 (Current Net Worth). One user saw $4.0M Current and $2.9M Peak in Today's $ — mathematically impossible when computed correctly, since year 0 is always one of the candidates for "max."

Fix: deflate each year individually first, then take the max. The maximum of the per-year deflated values can never be less than year 0's value. Matches the v6.18 Projection Table totals fix — same pattern, single-statistic version.

Same fix applies to Peak RMD in the Active Strategies card and to the Plan Health levers helper. The dual-owner Peak RMD sub-line now reads per-owner amounts from the year where the deflated total RMD peaks, so the sub-line reconciles with the main value. 11 new regression tests in test-ui-paths.js reproduce the reported scenario and lock the corrected behavior.

Future $ display is unchanged. For retirement plans declining in real terms (the common case), Peak Today's $ will typically equal Current Today's $ — today is your peak purchasing-power year, before withdrawals erode it.

v6.18.4
Honest stress test labels and std dev guidance

The Monte Carlo Stress Test was advertised as "worse than historical" but at default settings (6% return std dev, fat-tailed distribution) it was actually gentler than Historical replay — because Historical inherits the actual ~19% S&P 500 variance from 1928–2024 data, while Stress Test's variance is governed by the std dev parameter you set. Six percent fat-tailed is dominated by Historical's nineteen percent intrinsic variance, regardless of fat tails.

Three honesty fixes:

The default values (6% / 1.5%) are unchanged so saved plans don't shift. Users who want a real stress test now have correct guidance to set honest values.

Engine math is unchanged. Validation tests unaffected.

v6.18.3
Pensions and QLACs honor fractional start ages

If you set a pension's start age to a fractional value like 65.5, the engine was using integer-floor activation: at age 65 the comparison 65 >= 65.5 is false, so the pension was skipped entirely. At age 66 it activated with the full annual amount. The user who reported this had a $36K pension starting at 65.5 and lost six months of income (~$18K) from the projection.

Now the engine activates the pension when the user's age year overlaps the start age and pays a prorated amount for the first partial year. Setting startAge: 65.5 with a $36K annual amount pays $18K in the age-65 fiscal year, then $36K every year after. Same fix applies to the spouse pension and to QLAC payout start age. Integer start ages (the most common case) produce identical results to before — this is a backward-compatible bug fix.

Tax treatment: pension payments are gross income in the year received per IRC §61(a)(11), and QLAC payouts are taxable as ordinary income per IRC §401(a)(9)(F) and Treas. Reg. §1.401(a)(9)-6 Q&A-17 — both fully consistent with reporting the prorated cash actually received in a partial year. taxablePercent and survivorBenefit ratios apply post-proration; the math is associative.

11 new regression tests in test-validation.js covering integer baseline, fractional 0.25/0.5/0.75 cases, pre-activation zero, lifetime invariant, spouse pension proration, COLA compounding over fractional years, and taxablePercent post-proration application. Engine validation total goes from 519 to 530, all green.

v6.18.2
Honest Medicare display + correct Expenses guidance

The engine adds Medicare premiums and IRMAA surcharges to expenses automatically once you reach Medicare age (CMS bills the beneficiary, so it's a real cash outflow). Three places didn't reflect that consistently:

Engine math is unchanged. The retirement expenses field continues to mean "your non-medical retirement spending today, in today's dollars" — which is what most users were intending to enter anyway. If you previously included an estimate of Medicare premiums in your annual expense figure (per the old guidance), your projection was overstating costs by roughly that amount each year past 65; you should now subtract it out for an accurate model.

v6.18.1
Today's $ toggle now applies to Scenarios, Monte Carlo, and Medicare charts

Three charts were ignoring the dollar toggle and showing only Future $ values regardless of the user's selection: the Scenarios comparison chart, the Monte Carlo fan chart and statistics (Median Final, 10th/90th percentile), and the Medicare Costs by Age chart on the Medicare input tab. The status bars above the Scenarios chart already deflated correctly — only the chart itself was wrong — so users saw a numeric mismatch between the stat row and the chart. Monte Carlo was the most confusing: nothing updated when toggling unless the user re-ran the simulation.

Root cause: each chart's data construction fed buildChart raw nominal values from the projection without applying the per-year deflate() helper. Now they follow the dashboard's net-worth-chart convention: deflate each year individually before passing to the chart. Same pattern as the v6.18 fix to the projection table totals row.

Audit-discovered: the same bug existed in 11 additional charts — Bear Market stress test, Healthcare Inflation stress test, Relocation comparison, three Survivor Center charts (NW/SS/Tax), four Roth Conversion Impact Analysis charts (Advantage, Pre-Tax/Roth balance comparisons, Annual Conversions), and three Roth Conversion Optimizer charts (Cumulative Tax, Conversion Advantage, Account Composition). All 14 chart sites are now fixed.

4 new regression tests in test-ui-paths.js covering the chart-data deflation convention. The deploy gate's per-year-deflation pattern grep continues to enforce consistency across the codebase.

v6.18
Projection Table totals row now uses per-year deflation

The year-by-year Projection Table's totals row was computing each currency column total by summing nominal values across all years, then deflating that single total once at the midpoint year. That works for uniformly-distributed streams but is wrong for anything concentrated early or late in retirement. Roth conversions cluster in early retirement — per-year deflation barely shrinks them, midpoint deflation over-deflates by ~10 years' worth, understating the total. RMDs and late-life healthcare cluster late — opposite direction, overstating. Now matches the dashboard's Active Strategies badge convention: deflate each year individually, then sum.

If you were comparing the dashboard's "Roth Conv" badge in Today's $ against the projection table's "Roth Conv" totals row, you'd see different numbers (one user reported $1.1M vs $764K for the same plan). Future $ totals were unaffected because no deflation is applied in nominal mode. After this release, the badge and the table agree.

Same fix applies to every other currency column in the totals row — Tax, RMD, Withdrawals, IRMAA, Medicare, ACA, Charity, Capital Gains, Cash Interest. All now match the dashboard's per-year deflation convention. 4 new regression tests in test-ui-paths.js.

Behavioral note: anyone running side-by-side Today's $ comparisons between the dashboard and the table will now see consistent numbers. Anyone who only used the table totals will see slightly different numbers depending on the stream's distribution — the new numbers are correct, the old ones were biased.

v6.17.6
Final coercion sweep: 0% honored for employer match, SS trust fund, cash events, Roth %

Closing chapter on the ||-vs-?? bug pattern. v6.17.4 fixed the dollar-toggle. v6.17.5 swept the next 23 sites. This release fixes the last 4 field families I knew about: employer match override (custom percent and cap), SS Trust Fund reduction (the headline scenario where users model "what if Congress doesn't act"), cash event net percent (sale, inheritance, business-sale modeling), and Roth conversion percentage-of-balance.

The most user-impactful fix here is SS Trust Fund: setting reduction percent to 0% (meaning "Congress acts, full benefits paid") was being silently coerced to 20%. Anyone running side-by-side comparisons of the no-action vs. action scenarios was getting the wrong baseline.

Pattern category is now retired across the codebase. The deploy-gate regression tests and post-deploy curl checks lock the behavior. Any future || regression at any of the 30+ sites we've fixed since v6.17.4 will fail the gate.

v6.17.5
Sweep: 0% honored for every numeric assumption

Continuation of v6.17.4. The audit there flagged ~15 additional sites with the same X || N coercion pattern across the codebase. After review, every one of them was a real bug — in this codebase, every numeric user input has a meaningful 0 case. This release converts all of them to ?? so explicit 0 is honored.

Field families fixed: nominal return (4 sites — Strategy Report, PDF export, Survivor Center, Plan Health), healthcare inflation chains (Pre-Medicare and Medicare), LTC inflation, survivor expense reduction, expense phase multipliers (later/late retirement), and Guyton-Klinger guardrails (lower, upper, adjustment).

Behavioral note: if you had any plan with one of these fields explicitly set to 0% (e.g., a "stable healthcare costs" stress scenario, or a "guardrails do nothing" experiment), your projection and Today's $ display will now actually reflect the 0% you typed. Pre-fix, those plans were silently running at the field's default rate.

11 new regression tests in test-ui-paths.js covering every fixed field family. The bug pattern is now closed across the codebase: a future X || N regression at any of these sites would fail the deploy gate.

v6.17.4
Fix: Inflation 0% now honored by Today's $ toggle

Setting Assumptions → Inflation to 0% had no effect on the dollar-toggle: the top-line rate display kept reading 3% (or whatever the previous value was), and the inline rate input on the toggle accepted edits but didn't stick after the next render. The pension display in Today's $ also continued to degrade over time as if inflation were still 3%. Two lines in the dollar-toggle and main render functions used JavaScript's || operator to fall back when the inflation field was missing — but 0 || 3 === 3 in JavaScript, so an explicit user-entered 0 was silently coerced to the default. Both lines now use ?? (nullish coalescing), which only triggers the fallback for null or undefined — not for an intentional 0.

This is the same coercion pattern that affected SS COLA before v6.16 (where setting SS COLA = 0% silently ran 2%). When that fix shipped, I patched six engine sites but didn't audit the rest of the codebase. This release runs a comprehensive audit and closes the pattern across the dollar-toggle. New regression tests in test-ui-paths.js lock the behavior so a future || regression at either site would fail the deploy gate.

Behavioral note: any saved plan with inflation: 0 will now actually project at 0% inflation. Pre-fix, those plans were silently running at 3% deflation in the Today's $ display (Future $ was unaffected). If you had a plan where you'd set 0% to test a "real-dollar" scenario, your Today's $ numbers will now match Future $ values exactly, which is the correct behavior.

v6.17.3
Cash Interest income now visible on dashboard and projection

Interest from cash, HYS, money market, CD, and short-term Treasury holdings — the cashInterest field the engine has always produced — was missing from the dashboard’s Inflows section and the year-by-year Projection Table. The engine routed it correctly through every tax pipeline (NIIT, ordinary income, MAGI, Social Security provisional), but a user with $200K in a money-market account at 2% yield wouldn’t see the $4,000/year of interest anywhere unless they manually checked the chart-overlay “Cash Interest Income” box. Now it appears as a row on the Retirement-tab and Working-tab Income sections, and as a Cash Int column in the Projection Table (auto-hides at zero). Net Annual Cash Flow bar on the Retirement tab includes it in the inflows side.

No engine math change. Existing validation tests cover the underlying calculation and tax routing — this release surfaces the number to UI.

v6.17.2
Fix: phantom $6K “Other Income” for empty-portfolio plans

Long-running issue where the projection’s OTHER INC column showed a flat $6,000/year on plans that had no income inputs and no portfolio — particularly visible to users who were testing the app with a zeroed-out scenario. Cause: the legacy additionalIncome field defaulted to $6,000 in fresh plans, the web UI was replaced by Income Streams in v4.x and never exposed an input for it, and the engine’s backward-compatibility shim was virtualizing the legacy field even on plans that had already been migrated to streams (i.e., had incomeStreams: []). The combination produced a stale $6K floor that no UI control could clear.

Three-layer fix. The seed default is $0, so new plans never reproduce the state. State migration zeros stale additionalIncome on any plan that already has an incomeStreams array (legitimate pre-v4.x plans, where incomeStreams is genuinely undefined, are untouched). The engine’s legacy virtualization shim now requires incomeStreams === undefined to fire, so the phantom can’t return through any load path even if a saved plan slips past migration.

Three new validation tests cover the stuck state, legacy passthrough, and stream precedence. Reported by users running zero-input scenarios in v6.16.x; an earlier attempt today shipped a partial fix that didn’t address the engine-side back-fill.

v6.17.1
Fix: Annual Benefit summary card now reflects current benefit when already collecting

If you had “I’m already collecting Social Security” toggled on, the green “Annual Benefit” summary card under each SS card was sourcing its number from the legacy SSA-statement / income-based path and ignoring your entered current monthly benefit. On the spouse side this showed as $0 when no legacy fields were filled in; on the primary side it showed slightly off (off by whatever drift was in the leftover manual entry). Now both cards read directly from the entered current benefit when already-collecting is on, and the card label drops “at Claim Age X” since it doesn’t apply.

The reduction-percentage and Earnings Test warnings that sit beneath the summary card are also suppressed in already-collecting mode — both are forward-looking warnings about claiming decisions, irrelevant once you’ve already claimed.

No engine change. The projection itself was already correct (v6.16 wired the engine path); this fix only addresses the input-tab summary display.

v6.17
Multiple pensions per person

You can now model more than one pension per person on the Pension tab. Use the “+ Add Pension” button to add a second (or third, or more) pension for yourself or your spouse — useful for households where one person has benefits from multiple former employers, or military retirement plus a civilian pension. Each entry has its own start age, COLA, survivor option, and taxable percentage. The engine sums all enabled entries each year.

The single-pension input cards from prior versions are still there as the first entry; they auto-migrated when you opened the app under v6.16.x. Saved plans load unchanged.

Manual updated. Engine math is unchanged from v6.16.x — the underlying array structure was already in place; this release exposes it in the UI.

v6.16.2
Pension data structure refactor (internal)

The engine and state now store pensions as an array (pensions[]) instead of two scalar params (pension and spousePension). Saved plans auto-migrate on load. No user-visible change — the input UI still shows the same Your Pension and Spouse Pension cards. This is preparation work for a future update that will let you model multiple pensions per spouse (military + civilian, multiple former employers, etc.).

5 new validation tests cover the migration shim, two-pension-per-spouse summation, staggered start ages, and mixed-owner pairs. All existing pension tests pass unchanged on the new array-backed engine.

v6.16.1
Manual update for “Already collecting” SS mode

Documentation pass following v6.16. The Social Security sections of manual.html and the in-app iOS manual now describe the third entry mode (“Already collecting”) alongside the existing income-based estimate and SSA-statement-at-FRA modes, and call out that the entered amount is gross of Medicare Part B premium deduction in all three modes. No engine or UI logic changes.

v6.16
“Already collecting” mode for Social Security

You can now tell RetIQ that you (or your spouse) are already collecting Social Security and enter your current monthly benefit directly, rather than back-solving for the FRA amount. The new toggle sits on the SS card — check it, enter what you actually receive each month, and the engine uses that figure as your starting SS income with COLA accruing forward from your current age. Independent toggle for primary and spouse, so households where only one person has claimed are handled cleanly.

For households with a non-working or low-earning spouse, RetIQ still needs an approximate PIA to compute the spousal floor and survivor benefits. The new ssReverseAdjustPIA helper recovers that PIA by reversing the SSA early/late adjustment formula (POMS RS 00615.301 and RS 00615.304) using the age you started collecting. The recovered figure is in current dollars rather than original-FRA dollars — past COLAs are baked in — which means survivor and spousal calculations are slightly approximate for households that have been collecting for many years. The error is small in practice; precision back-out of historical COLAs is a future enhancement.

Help text for the existing FRA-amount field also clarifies that the entered value is the gross benefit, before any Medicare Part B premium deduction. RetIQ models Medicare costs separately on the expense side, so the gross figure (Box 5 of your SSA-1099) is what the engine needs.

Behavior change for plans with COLA = 0%. Six SS COLA sites in the engine were silently coercing an explicit 0% COLA to 2% (because 0 || 2 === 2 in JavaScript). All six now use ?? 2, so 0% means 0%. If you had a saved plan with SS COLA set to 0, your projection's Social Security income line will now actually stay flat instead of growing 2%/year. The fix is correct; the prior behavior was a latent bug.

27 new tests in test-already-collecting-ss.js covering reverse-PIA round-trips, primary already-collecting integration, spousal floor with recovered PIA, spouse-only-collecting cases, both-spouses-collecting cases, and a backwards-compatibility check against the existing spousal-SS suite. 5 additional unit tests in test-validation.js for the new helper. All four suites green: test-validation.js, test-spousal-ss.js, test-self-employed.js, test-already-collecting-ss.js.

Authority: 42 U.S.C. §402(q) (early-claiming reduction); 42 U.S.C. §402(w) (delayed retirement credits); SSA POMS RS 00615.301, RS 00615.304. Medicare gross/net basis: 42 U.S.C. §1395s; SSA POMS GN 02410.180; IRS Pub. 915 (gross benefit on SSA-1099 Box 5).

v6.15.4
Two bug fixes: phantom Other Income and stale brokerage dividend tracking

Two engine fixes reported by a customer.

1. Phantom $6k Other Income. A legacy additionalIncome field in the seed plan default was carrying forward into newly-created plans even after the income-streams replacement shipped in v4.x. The migration that was supposed to convert the legacy field into a real, editable income stream was gated on incomeStreams === undefined — so plans that already had an empty incomeStreams: [] array (the v4.x default) skipped the migration entirely, and the engine's backward-compat block kept injecting a phantom $6k/yr ordinary-income stream every projection year. Affected anyone whose plan had been initialized between v4.x and v6.15.3 with the seed default. Fix: decouple the income-streams init from the legacy migration so both run independently, zero the legacy field idempotently after migration, and zero the seed default. Existing plans self-heal on app load.

2. Brokerage dividend yield computed off the static input balance. The per-account dividend yield loop was reading acct.balance — the year-0 input — on every projection year. Result: dividend income stayed flat forever even as the brokerage compounded or was drawn down. Customer described it precisely: “even though my brokerage account grows in the projection, the income included in Other Income is flat.” Fix: each brokerage account holds a constant share of the pooled balance (share = acct.balance / totalStartingBrokerage, computed once); per-year dividend = share × taxableInv × yield ÷ 100. Pool growth and pool withdrawals now propagate to dividend income correctly. Single-account plans collapse to share = 1 and behave intuitively. Empty-pool guard prevents division-by-zero when no brokerage accounts are present.

3 new validation tests in test-validation.js lock the year-over-year behavior: untouched brokerage grows dividend income, drawn-down brokerage shrinks it, no brokerage accounts produces zero with no NaN. 7 new tests in test-ui-paths.js cover the migration stuck-state, both-populated-non-conflict, and idempotency cases.

v6.15.3
Validation completeness + QBI threshold consolidation

Three small post-SE-module cleanups:

Total tests in merged validation-results.json: 616 (up from 586 at v6.15.2). Engine math unchanged.

v6.15.2
Projection Table — Conversion Tax column

New Conv Tax column in the year-by-year Projection Table: federal income tax owed specifically on Roth conversions for that year (a subset of Federal Tax). Surfaced separately so you can see exactly what each conversion year costs in tax. Engine math is unchanged — conversion tax is computed as the delta between actual ordinary income tax with the conversion and the no-conversion baseline, and is paid from your designated tax source (cash, brokerage, or IRA withholding) outside the regular retirement-year tax cascade.

This closes a "where did my money go" gap reported by users running Roth conversions: conversion tax was paid from the designated source separately from the cascade, producing brokerage drain that didn't match the W/D column. The new column makes the conversion-year tax explicit. Auto-hides for users who don't run conversions (all-zero filter, same pattern as the existing TAD / TAD Tax columns).

The same field also added to the chart-overlay categories (so users can include Conversion Tax in the multi-line chart above the table) and to the CSV export. Engine field conversionTax has been in year output since v6.0; this surfaces it in the most-used view.

v6.15.1
Dashboard "This Year's Plan" cleanup

Two clarity fixes on the dashboard "This Year's Plan" card, both in response to user feedback:

Engine math is unchanged. The Working-tab cash flow definition is also unchanged.

v6.15
Solo 401(k) Contributions for Self-Employed Users

Self-employed users can now model Solo 401(k) contributions: employee elective deferral (Traditional or Roth) and employer profit-share. The engine handles all the cross-cutting tax interactions: §402(g) aggregation across W-2 and Solo deferrals; §415(c) annual addition cap on combined employee + employer; §404(a)(8) 20% sole-prop employer limit on (net SE − half-SE-tax); age-based catch-up including the SECURE 2.0 §109 enhanced catch-up for ages 60–63; AGI reduction for traditional contributions; QBI reduction for traditional employee, traditional employer, and Roth employer per Treasury Regulation §1.199A-3(b)(1)(vi) and IRS Notice 2024-2 Q&A L9.

The Self-Employed card on the Income tab already had Solo 401(k) input controls (added in v6.0 as scaffolding); v6.15 wires the engine math behind them. Help text now flags the QBI interaction with Roth employer contributions.

Engine simplification: the §402(g) capacity calculation uses your W-2 401(k) deferral target from Contribution Routing. If your annual savings pool is insufficient to fund the full W-2 target, the engine slightly understates available Solo deferral capacity by the savings-pool shortfall. Rare for typical SE users; on the roadmap for a future precision update.

Roadmap: SEP-IRA, SIMPLE-IRA, defined benefit plans, after-tax / Mega Backdoor Roth contributions, S-corp employer contribution math (25% of W-2 wages), and per-state §199A conformity are Phase 2 enhancements. SECURE 2.0 §603 mandatory Roth catch-up will ship with S-corp support since it requires FICA-wage tracking that sole prop / SMLLC / partnership entities lack.

26 new validation tests in test-self-employed.js (83 total, was 57). 503 + 30 main suites unchanged.

v6.14.1
SE module cleanup — cascade target, QBI baseline symmetry, validation page

Three small fixes to the self-employment module landing as a patch ahead of v6.15 Solo 401(k):

  1. The v6.8 tax-payment cascade target now includes SE tax for architectural correctness. Currently a no-op since SE tax only fires in working years and the cascade only runs in retirement, but the fix prevents a latent bug if a future feature allows post-retirement SE income.
  2. The per-baseline §199A QBI computation now applies the tax-loss-harvesting ordinary offset to all three projection scenarios uniformly (no-conversion, conversion-only, actual), eliminating a small asymmetry in conversion delta math for self-employed users actively running TLH near the SSTB threshold.
  3. The public validation page (/validation.html) now includes the 57 self-employment test results that previously ran in a separate file and weren’t surfaced.

New run-all-tests.js wrapper script runs all three test files (test-validation.js, test-spousal-ss.js, test-self-employed.js) and merges results into validation-results.json. Standalone test runners continue to work for development. build-validation.js now produces validation.html with 560 tests across 65 categories (was 503 / 58).

v6.14
Section 199A QBI Deduction

Self-employed users now receive the §199A Qualified Business Income deduction automatically. The engine computes 20% × (net SE earnings − half-SE-tax deduction) per business, applies the SSTB phaseout for users above the income threshold, caps the combined deduction at 20% × (taxable income − net capital gain) at the household level, and applies the new $400 minimum deduction added by the One Big Beautiful Bill Act for taxpayers with at least $1,000 of QBI.

2026 thresholds per IRS Rev. Proc. 2025-32 §4.26: $403,500 MFJ ($553,500 phase-in completion) and $201,750 single ($276,750 completion). The OBBBA extended the phase-in range from $100,000 to $150,000 MFJ and from $50,000 to $75,000 single.

The deduction is computed per scenario across all three projection baselines (no-conversion, conversion-only, conversion-plus-TAD) so Roth Conversion Optimizer recommendations correctly capture conversion-induced QBI loss when conversion pushes a user across the SSTB threshold. The Tax-Aware Drawdown smartFill bracket-room calculation also accounts for QBI, ensuring SE users get accurate target-bracket recommendations.

State-level §199A conformity is on the v6.x backlog (most states conform but California, New Jersey, and Pennsylvania do not). W-2 wage / UBIA modeling for SE businesses with employees, QBI loss carryforward, and aggregation elections are also Phase 2 enhancements. Solo 401(k) contributions further reducing QBI per Treas. Reg. §1.199A-3 will land alongside the Solo 401(k) contribution math in v6.15. 13 new validation tests in test-self-employed.js (56 total, was 41); 503 + 30 main suite tests unchanged.

v6.13
Self-Employment Tax

Net Self-Employment Earnings now flow through the engine. Positive earnings produce SE tax (IRC §1401: 12.4% OASDI on net × 0.9235 sharing the SS wage base with W-2 wages per §1402(b)(2), plus 2.9% Medicare with no cap). The half-of-SE-tax deduction (IRC §164(f)) reduces AGI. Negative net earnings — business losses — reduce AGI for the year with no SE tax owed.

SE tax is included in the total annual tax bill, and the v6.8 tax-payment cascade automatically draws it from the portfolio in retirement years. Three new fields surface in the projection year output: seTax, seNetEarnings, and seHalfDeduction. SE income propagates correctly through MAGI, IRMAA, ACA subsidies, SS taxation, and state tax (which uses the same federal AGI starting point most states piggyback on).

Solo 401(k) contributions and the QBI deduction (IRC §199A) are still on the roadmap (v6.14 and v6.15 respectively); the Self-Employed card on the Income tab indicates which features remain pending. 20 new validation tests in test-self-employed.js (41 total, was 21).

v6.12
Monte Carlo per-year percentile envelopes & Roth/TAD recommendation baseline

Monte Carlo per-year percentile envelopes. The p10/median/p90 lines on the Monte Carlo chart are now true per-year percentile envelopes across all simulations, not three sample paths chosen by terminal net worth. Previously, the median path could appear above the p90 path at intermediate ages because the path that ended highest may have started below the median path. With per-year envelopes, p10 ≤ median ≤ p90 is guaranteed at every age. Final-year values are unchanged.

Roth vs TAD legacy recommendation now considers all four strategies. The Longevity Crossover chart already plotted No Strategy alongside Roth Only, TAD Only, and Roth + TAD, but the recommendation text only compared Roth vs TAD — so it could claim “Roth wins at all ages” even when No Strategy was higher than both. Now the recommendation identifies the winning strategy across all four at the youngest and oldest age in the range, and reports any crossover. This catches cases where No Strategy beats Roth or TAD because the conversion/drawdown tax cost outweighs the heir-bracket discount on pre-tax inheritance.

6 new validation tests for the Monte Carlo envelopes (locking p10 ≤ median ≤ p90 at every age plus 5 supporting invariants). Total validation tests: 503. Spousal-SS suite unchanged at 30/30.

v6.11
Social Security display precision

FRA is now displayed in human-readable form (e.g., “66 yrs 4 mos” instead of “66.3”), eliminating the false-equivalence problem where a fractional FRA (66.333… for birth year 1956) and a user-entered claim age of 66.3 displayed identically. Sub-1% claim-age reductions now show with one decimal place (“reduced 0.3%”) instead of being rounded to the contradictory “reduced 0%.”

The loud red “Claiming before FRA permanently reduces benefit by X%” warning is suppressed when the reduction is below 1% — the inline label is sufficient for tiny reductions. SS Claim Age help text now shows the user’s actual FRA in human-readable form and clarifies that the input is interpreted as decimal years (66.5 = 66 yrs 6 mos, not 66 yrs 5 mos).

No engine or math changes — projections are identical to v6.10. All 497/497 validation and 30/30 spousal-SS tests remain green.

v6.10
Inherited Roth IRA Back-Load Option

Inherited Roth IRA accounts now support an optional back-load distribution timing: zero distributions during years 1–9 of the 10-year window, full balance in year 10. This pattern is fully legal under IRS T.D. 10001 (July 19, 2024) — inherited Roth IRAs have no annual RMD requirement during the 10-year window, only the year-10 deadline. At positive return rates, back-loading captures more tax-free growth than the default even-spread. Lifetime tax is unchanged (Roth distributions are tax-free regardless of timing).

Restricted to inherited Roth accounts; inherited traditional IRAs continue to use even-spread because the “at least as rapidly” rule may apply when the decedent died on or after their RBD — a future enhancement could add a decedent-RBD toggle to unlock back-load on traditional inherited accounts.

The new toggle appears below the regime dropdown when an inherited Roth account is on the post-2019 regime (it’s hidden for pre-2020 stretch and EDB regimes, which use Single Life Expectancy math). 7 new validation tests lock the behavior. Total validation tests: 497.

v6.9
Brokerage Dividend Yield

New optional field on brokerage accounts: Dividend Yield (% per year) and tax type (qualified or ordinary). Splits the existing return rate into a dividend portion (recognized as income annually) and an appreciation portion (unrealized until withdrawal). Qualified dividends taxed at LTCG preferential rates per IRC §1(h)(11); ordinary dividends taxed at marginal rates. Reinvested dividends step up cost basis dollar-for-dollar per IRS Pub 550. Total return rate is unchanged — this is purely a tax/accounting split. Useful when a brokerage account holds high-yield holdings whose dividend income materially affects MAGI, IRMAA, NIIT, or ACA eligibility.

Dividend income flows through the same tax pipeline that already handles qualified-dividend Income Streams: SS taxation (IRC §86), LTCG bracket stacking (IRC §1(h)(11)), NIIT (IRC §1411), MAGI, IRMAA, ACA, and state tax. Existing accounts with no dividend yield set are fully backward-compatible (the field defaults to 0 and no dividend income is generated).

10 new validation tests (6 direct feature tests, 4 regression tests locking the existing-correct qualified-dividend pipeline) lock the new behavior. Total validation tests: 490.

v6.8
Tax Payment from Portfolio

The engine now correctly draws from your portfolio to pay retirement-year federal income tax, state income tax, capital gains tax, and Net Investment Income Tax. Previously these taxes were computed and shown in the projection but did not actually drain account balances, leaving projections optimistic. Conversion tax (if you have Roth conversions enabled) continues to be paid from the source you've designated on the Roth Conversion tab — that part hasn't changed.

Mechanically, the engine wraps the gap-fill withdrawal cascade in a fixed-point iteration. Each pass computes the gap, draws from your accounts in your configured withdrawal order, computes the resulting tax bill, and feeds that tax bill back into the next pass's gap. The iteration converges in 2–4 passes per retirement year for typical scenarios. Years where iteration converges slowly (usually right at bracket transitions, IRMAA tier boundaries, or RMD-onset years) get a small ⓘ indicator next to the Fed Tax column in the Projection table — see the manual for details.

What you'll notice: The same plan inputs will produce projections noticeably more conservative than v6.7.5. Terminal net worth comes in lower, withdrawal totals reflect the actual tax outflow, Monte Carlo success rates are a few points lower for tax-heavy plans. Your plan didn't get worse; the model got more accurate.

The Roth Conversion Optimizer's recommendations may shift more than other surfaces. Pre-v6.8, the no-conversion baseline benefited disproportionately from the bug because its larger future RMD-era tax bill went unmodeled. Under v6.8, with that tax bill correctly drained from the portfolio, conversions look more attractive than they did before — sometimes meaningfully so. If the Optimizer previously recommended against conversions for your plan, re-run it under v6.8. The new math may give a different answer; for some plans, the direction of the recommendation reverses.

v6.8 also implements the IRC §408A(d)(3)(F) 5-year conversion clock. Each Roth conversion now carries its own 5-year window for purposes of the 10% early-withdrawal penalty on conversion principal. If you withdraw converted principal from your Roth before age 59½ AND within 5 years of the conversion year, the engine applies the 10% penalty. This matters most for users planning a Roth conversion ladder — converting Traditional IRA to Roth in your 50s and accessing the converted principal during the 5-year window. The 5-Year Rule Tracker on the Roth Conversion tab shows which conversions have matured. Users with substantial direct Roth contributions and aggressive early withdrawal plans may see slightly overstated penalties — full §408A(d)(4) ordering is on the v6.9 backlog (see manual for details).

Recommendation if you've saved a plan in v6.7.5 or earlier: open it under v6.8 and re-run your Strategy Report and Roth Optimizer to get fresh recommendations against the corrected baseline. The projections will recompute automatically on open; the strategy comparisons regenerate when you open those tabs.

11 validation tests in a "Tax Payment from Portfolio" category and 7 tests in a "Roth Conversion 5-Year Rule" category lock the new behavior. Total test count: 696 across all suites (validation 480, spousal-ss 30, self-employed 21, ui-paths 48, joint-life 117).

Verification. v6.8's engine math was audited across 8 categories against external authorities (IRS, SSA, CMS, HHS, statutory law, and mathematical theory). All 8 audit items pass. The verification trail is in the repo at audit/v6.8/phase1/ — readable per-item findings with cited authorities, test cases, and mathematical reasoning for the iteration math.

v6.7.5
Roth Optimizer ranks under blended returns + divergence warning

When users set per-account return rates that diverge across pre-tax and Roth (e.g., bond-heavy pre-tax at 5%, stock-heavy Roth at 9%), the Roth Optimizer’s candidate projections compounded the spread into a fictitious advantage for conversion that had nothing to do with taxes — converted dollars appeared to jump from a slow-growth bucket to a fast-growth bucket, and the “Maximize Net Worth” mode amplified the bias the most. Reported on Bogleheads.

The optimizer now ranks every candidate strategy using a blended return rate (the Nominal Investment Return from Assumptions) for all non-cash accounts. Cash accounts keep their actual rate because cash interest is taxed as ordinary income each year and changing its rate would distort MAGI/IRMAA scoring rather than just bucket growth. Important: only the optimizer’s candidate ranking is blended. Your main projection, Strategy Report, Monte Carlo, and applied optimizer results all continue to use your actual per-account returns — only the comparison inputs are normalized.

Independently, when pre-tax and Roth balance-weighted return rates differ by 2 percentage points or more (and both buckets hold $10,000 or more), the optimizer card now surfaces a warning explaining the bias risk for the main projection and how to remove it (set the same return rate on all accounts in the Accounts tab, or leave them blank to inherit the Nominal Investment Return). A second disclosure note above the strategies table explains that ranking uses blended returns and applied results use real returns.

Validation suite gained an Optimizer Blended Returns category covering the helper’s normalization, idempotency, immutability, the divergence threshold logic (boundary at exactly 2.0pp inclusive, balance gate at $10,000, missing-bucket handling, balance-weighted averaging across multiple accounts), and an end-to-end integration test confirming that blending materially changes projected end-state Roth balances.

v6.7.4
Cash/HYS account stranded-balance bug fix

The default plan and two of three demo families seeded their Cash / HYS account with a legacy schema (type:'cash') that the projection engine’s bucket filters did not recognize. The balance appeared in the Inputs > Accounts “Total Portfolio” and the Dashboard “Current Net Worth” (which both summed all accounts blindly), but was silently excluded from the projection. Reported on Bogleheads.

The fix has four layers: a pure canonicalizeAccount helper in the engine module that rewrites the legacy schema to the canonical type:'taxable', subtype:'cash' form; a migration step in migrateParams that runs the helper on every load (idempotent — safe for plans already in canonical form); seed-data fixes in the default plan and demo families; and bucket-aware totals in the Inputs and Dashboard tabs that filter to recognized account types and surface a visible warning if any unrecognized account is detected. Existing saved plans are migrated automatically on next load.

Defensive: the new total-hardening guards against future schema drift — if a new account type is introduced and an old build evaluates a plan saved under the new schema, the warning fires instead of silently undercounting.

Validation suite gained a new Account Schema Canonicalization category covering the helper’s normalization, idempotency, pass-through for canonical types, and an end-to-end integration test confirming a legacy cash account placed via the helper lands in the engine’s cash bucket.

v6.7.3
Break-Even Tax Rate display clarified

The Conversion Impact Analysis card’s Break-Even Tax Rate (BETR) display previously read “Convert if future rate > X%” with a tooltip describing it as the “future marginal tax rate”. That framing was misleading: BETR is not compared to your current marginal rate — it’s the average effective rate the no-conversion pretax pool would need at withdrawal for conversion to break even, measured against the projection’s implicit effective rate.

The distinction matters because for retirees with modest pretax balances, the projection’s effective rate on the pretax pool can be much lower than their marginal rate — standard deductions, senior additions, and the OBBB senior deduction shelter most of the pool from tax. A user reading “BETR 1.2%, my future marginal will be at least 12%, so convert” was getting the wrong answer in cases where the projection’s Final NW Δ was already showing the conversion losing money.

The card’s sub-label now reads Future effective rate threshold, and the tooltip explicitly disclaims the marginal-rate reading. The iOS manual’s BETR explanation got the same correction. The math is unchanged — only the labels and explanations are clearer. Final NW Δ remains the bottom-line answer when these metrics seem to disagree.

v6.7.2
Documentation pass: v6.4–v6.7 alignment

Documentation update bringing the user manual, feature list, and LLM crawler files current with changes shipped over the past several batches.

Manual. Added a Phased Brackets paragraph under Roth Conversion Strategies covering the v6.7 multi-phase feature. Added an escrow-exclusion clarification to the Liabilities tab Monthly Payment description (matching the v6.4 in-app help text). Replaced specific pre-v6.6 dollar amounts in the Relocation Scenario example with descriptive language — the actual savings now depend on your move age, remaining retirement years, and home equity, and the engine computes them precisely from your inputs.

Features. Added Phased Brackets to the Roth Conversion Strategies card. Updated the projection chart cap from “up to 4” to “up to 6” matching the v6.4 bump.

LLM crawler files. Updated test counts in llms.txt and llms-full.txt to 417 (matching the validation page). Added Phased Brackets to the strategy list in llms-full.txt so AI assistants give accurate answers about the feature.

No engine math changes. All 633 tests passing.

v6.7.1
Multi-phase hygiene + stale banner removal

Three small bug fixes from the v6.7 multi-phase Roth Conversion feature crossing module boundaries, plus removal of the long-stale “What's New in v5.0” dashboard banner.

Strategy Report alternatives now correctly test single-target. The dashboard's Strategy Report tests alternative strategies (e.g., “Smart Fill Roth 22%”) against your current plan. Previously, when you had Phased Brackets configured, those alternatives were inadvertently using your phased setup with overridden start/end ages — a mislabeled comparison. Fixed: alternatives now explicitly clear phases for an honest single-target comparison.

Roth Optimizer apply now clears phases. When you apply an optimizer result, your prior Phased Brackets setup is explicitly cleared (it was previously preserved invisibly, which could silently reactivate if you switched strategy back to Fill Bracket later).

Plan summary reflects phases. The Strategy Check-In card and AI Guide context now show “Roth Smart Fill (Phased)” or similar when phases are active, instead of falling back to the legacy single-target percentage.

“What's New in v5.0” banner removed. The dashboard banner introduced with v5.0 was hardcoded and hadn't been updated through nine subsequent version bumps. The full changelog page is the canonical source for what's new — the banner pattern was creating more confusion than it cleared up.

AI Guide reference updated. The BYOK AI feature reference now describes Phased Brackets so Claude/GPT/Gemini answers about the feature are accurate.

No engine math changes. All 633 tests passing.

v6.7
Multi-phase Roth Conversion brackets

The Roth Conversion tab's Fill Tax Bracket and Smart Fill (IRMAA-Aware) strategies now support phased bracket targets — define different target brackets for different age ranges within your conversion window. Useful when you plan to move to a different state mid-conversion (the canonical example: California 12% bracket while in CA, then 24% in Washington with no state tax, optionally back to 12% if you return to CA later).

Toggle “Use Phased Brackets” on the Roth Conversion tab when Fill Bracket or Smart Fill is your strategy. Add as many phases as you need, each with its own age range and target bracket. First-match-wins if phases overlap; years not covered by any phase produce no conversion. The outer Start Age / End Age remain the master gate for the conversion window — phases subdivide within it.

Existing plans are unchanged. The engine falls back to your single targetBracket setting when no phases are defined, so saved plans continue to behave identically. The Roth Conversion Optimizer continues to test single-target candidate strategies.

6 new tests in a new Roth Multi-Phase category lock down the regression case (empty phases identical to absent phases), single-phase equivalence, two-phase ordering, gap-year zero-conversion, phase boundary inclusivity, and outer-window gating. Total now 417 tests in test-validation.js, 633 across all suites.

v6.6
Relocation Scenario moveAge honored by engine

The Relocation Scenario now correctly applies the origin state tax rate before the move and the target state tax rate from the move year onward. Previously the scenario overwrote stateCode at the top of the projection, so the target state's rate was applied to every year — including pre-move years — producing inflated “tax savings” numbers when comparing against the baseline. The engine now resolves an effectiveStateCode per simulation year using the moveAge cutover, and both the pre-retirement and post-retirement state-tax call sites consume that resolved code. Behavioral change: pre-existing Relocation scenarios will show smaller state-tax-savings deltas than before — the new numbers are accurate, not a regression. Seven new tests added in a dedicated Relocation Scenario validation category covering no-relocation regression, pre-move year, post-move year, exact cutover boundary, move at currentAge, move beyond endAge, and pre-retirement move.

v6.5.1
Dangling label fix

The Projection tab's category selector instruction now reads “Select up to 6 categories” — the cap was raised to 6 in v6.5 but the user-facing label still said 4. Same fix applied to the corresponding sentence in the manual. Cosmetic only; no behavior change.

v6.5
UI polish — Effective Rate, more chart categories, larger legend text

Three small additions from user feedback. The Conversion Impact Analysis card on the Roth Conversion tab now shows an Effective Rate on Conversions stat alongside the existing Tax on Conversions — the blended average rate (total conversion tax ÷ total amount converted across all years). Compare it to the Marginal Rate Curve to see how much of the rate ladder a given strategy climbed.

The Projection tab chart can now overlay up to 6 categories simultaneously (previously 4). Useful for seeing how multiple income, expense, and balance series interact in the same view.

Chart legend text bumped from 10px to 12px across the app — Monte Carlo, Projection, Roth Conversion, Scenarios, and every other chart. More readable on small screens and at typical viewing distances.

v6.4
Mortgage amortization rebuilt — monthly accrual

Mortgage and loan amortization is now computed monthly: each month’s interest accrues on the declining balance from the prior month, matching how real fixed-rate loans amortize. Previously the engine used a single annual interest accrual (balance × annualRate once per year), which slightly overstated early-year interest and pushed payoff out by a few months on most loans. The same calculation now drives both the on-card payoff display in the Liabilities tab and the Debt Payments series in the Projection chart, via a shared amortizeYear() helper in src/engine/09-engine.js.

Existing plans with mortgages will recompute slightly different numbers — typically lower lifetime interest and an earlier payoff age. The shift is small (months, not years) but visible on next open. Plans without mortgages or loans are unaffected.

The Monthly Payment field on the Liabilities tab now has a help line clarifying that it expects principal & interest only — exclude property tax, insurance, and other escrow items. Many lender statements lump these together, and entering the full PITI value would inflate apparent payments and distort the payoff timeline.

11 new tests in a new Debt Amortization category lock the helper against textbook amortization values: year-1 interest/principal/balance for a 30-year fixed, 30-year payoff timing, zero-rate edge case, zero-balance edge case, underwater-payment edge case, and engine integration verifying the projection wires through the same helper. Total now 404 tests in test-validation.js, 620 across all suites.

v6.3
UI logic fixes — Earnings Test trigger and routing breakdown

Two refinements from user feedback. (1) The Social Security Earnings Test advisory in the SS tab now only appears when the user will actually have earned income overlapping the claim-to-FRA window — claim age strictly before retirement age. Previously the warning fired for anyone claiming pre-FRA, even when they would already be retired and have no wages, which was misleading. The engine itself was always correct; only the advisory text was over-eager. Same fix applied to the spouse advisory. (2) The Household Savings Summary in the Accounts tab now breaks the routed amount down by Traditional vs Roth, so the summary reflects the user’s actual Type selection on each contribution row instead of just showing a combined “401(k) + IRA” total.

v6.2
Label & string corrections

Five small fixes from user feedback. (1) The Accounts tab now reads contribution limits from the regulatory module instead of a stale hardcoded label, so the IRS Notice 2025-67 figures ($24,500 401(k), $7,500 IRA) match what the engine actually applies. (2) The “Break-even vs 62” column in the SS Claiming table now has a tooltip explaining what the age means. (3) “TAD” is now spelled out as “Tax-Aware Drawdown” on the Roth Conversion tab where the acronym first surfaces. (4) The QCD eligibility age now renders as 70½ rather than “701/2”. (5) The Projection tab chart category labeled “Liabilities” is renamed to “Debt Payments” — the value is annual debt service, not outstanding balance, and the old label was misleading.

v6.1.1
Relocation Scenario Fix

Relocation Scenario fix. Clicking “Compare Relocation” on the Scenarios tab threw a TypeError and the scenario did not render. The scenario dispatch guard in src/27-ui-scenarios.js excluded the bear and hcInflation string scenarios but not relocation. Replaced the deny-list with a positive typeof === 'number' check. Reported by an app user.

v6.1
OBBB SALT Cap Alignment & v6 Polish

The SALT deduction cap is no longer hardcoded at $10K. A new saltCapFor(year, magi) helper in src/engine/07-regulatory.js implements OBBB Act P.L. 119-21 amending IRC §164(b)(6): $40,000 base cap for 2025-2029 with 1% annual growth, MAGI phase-down reducing the cap by 30% of excess above $500,000 (threshold also grows 1%/yr), floor of $10,000, and reversion to $10,000 TCJA baseline in 2030. Applied at three engine call sites: main deduction flow, Tax-Aware Drawdown scenario, and Smart Fill Roth conversion bracket-room calculation. The Smart Fill site had been silently applying the old $10K cap — a latent bug that conservatively under-sized Roth conversions for users in high-tax states. Now fixed.

Two new Tax Strategy workflow cards building on v6.0’s per-owner RMD tracking. Coordinate Dual-Owner RMDs covers account ownership assignment, identifying the earlier RMD start year, and aligning Roth conversion windows to the earlier-start spouse. Apply the Joint Life RMD Election covers the four eligibility conditions (spouse enabled, age entered, gap strictly greater than 10 years, sole beneficiary), enabling the election, and stress-testing survivor scenarios. Existing Find and Fill Your Tax Valley and Optimize Your Roth Conversion Strategy workflows gained dual-owner awareness sentences pointing users to the Staged RMD timeline insight in the Strategy Report Card.

Feature-knowledge entries updated throughout v6.0 work: FICA bullet in Tax Engine, new RMDs section (per-owner tracking, Staged RMD timeline, Joint Life election, spousal rollover), W-2 Detail opt-in, Self-Employed Income Model foundation. Per-owner and Joint Life RMD router templates added for the Guide. Counts refreshed: 14 workflows (was 8), 18 templates (was 13), 423 validation tests. Version banner and Anthropic model string updated.

Federal Tax bracket citations updated from Rev. Proc. 2024-40 §2.01 (2025 tax year) to Rev. Proc. 2025-32 §4.01 (2026 tax year) in validation-metadata.json and the VALIDATION_DATA test entries. IRS source URL updated from rp-24-40.pdf to rp-25-32.pdf. Top-of-page source grid card year label now reads “(2026).” Projection footer model-notes corrected from “2025 tax brackets” to “2026 tax brackets (OBBB Act; IRS Rev. Proc. 2025-32).” Standard Deduction citations intentionally retained at Rev. Proc. 2024-40 pending verification of the corresponding section in the new procedure.

The “Itemizing?” and “Bunch Year?” columns now render as Yes/No instead of 1/0 in the HTML data table. Totals row shows an em-dash for boolean columns instead of “Total.” Both columns also added to the CSV export as numeric 1/0 for pandas/Excel analysis. The Yes/No rendering became especially useful after the SALT cap update, which makes the engine legitimately flip between itemized (2025-2029) and standard (2030+) for high-SALT users.

manual.html and features.html SALT-related prose updated to reflect OBBB cap mechanics. src/24-ui-inputs.js SALT field help text and the Itemized Deduction Comparison blurb updated. src/29c-ai-guide.js Itemized vs Standard Deduction feature knowledge updated.

6 new tests in a new SALT Cap (OBBB) category covering the 2025 base cap ($40,000), 2026 growth ($40,400), 2030 reversion, phase-down at MAGI $550,000 (cap $25,000), phase-down floor at MAGI $700,000 ($10,000), and threshold growth verification. Total now 423 automated tests, 44 categories.

v6.0
Per-Owner RMD Tracking & Self-Employed Income Foundation
v5.8.0
Joint Life RMDs for spouses more than 10 years younger

When a spouse is more than 10 years younger than the IRA owner and is the sole beneficiary of the owner’s pre-tax accounts, the IRS allows RMDs to be calculated using the Joint Life and Last Survivor Expectancy table (IRS Pub 590-B Table II) instead of the Uniform Lifetime table (Table III). The Joint Life divisors are larger, so the RMD is smaller — meaningfully so at wider age gaps. At a 24-year gap, the RMD drops roughly 31%. RetIQ previously used Uniform Lifetime in all cases, which overstated RMDs for couples with this age structure.

A checkbox labeled “Spouse is sole beneficiary of my pre-tax accounts” now appears in the RMD summary card on the Accounts tab, but only when the plan has a spouse enabled and the spouse is more than 10 years younger. The checkbox is unchecked by default — it has to be affirmatively set because the IRS rule requires that the spouse be the sole beneficiary of the IRA, which is a separate legal election from having a spouse in the plan. Most beneficiary designations name multiple beneficiaries (e.g., spouse as primary and children as contingent) and do not qualify.

The engine re-evaluates eligibility every projection year. If the spouse dies during the plan, RMDs automatically revert to Uniform Lifetime the following year. If the owner dies first and the spouse rolls the IRA into their own, Joint Life no longer applies (the now-sole owner has no qualifying beneficiary under the rule). Eligibility requires all four conditions simultaneously: both spouses alive, flag set, spouse’s age tracked, and age gap strictly greater than 10. Exactly a 10-year gap does not qualify — the IRS rule is “more than 10 years younger.”

The table covers owner ages 72 through 120 and spouse ages 20 through (owner − 11), a total of 3,234 divisor cells transcribed from IRS Pub 590-B (2025 revision, Jan 21 2026 print). Cells where the age gap is 10 or less are intentionally omitted — those cases fall through to Uniform Lifetime.

Plans saved before v5.8.0 load without the new flag set, so RMDs match the pre-change behavior until the user explicitly opts in via the checkbox. No migration needed.

Validation: 374 tests passing, +13 from v5.7.16. The 13 new tests live in a new category (“RMD Joint Life”) and cover the IRS published worked example (owner 75 / spouse 64 = 25.3 divisor), the 11-year gap boundary, the 24-year user-reported scenario, the 10-year gap non-qualification, the flag-off disable, missing-cell Uniform Lifetime fallback, and the eligibility helper’s edge cases (null spouse age, spouse older than owner, flag unset). A separate developer-test file test-joint-life.js runs 117 thorough backward-compatibility sweeps across the full Uniform Lifetime age range — run locally; not part of the public validation report.

Source: IRS Publication 590-B, Appendix B, Table II — Joint and Last Survivor Life Expectancy. Treasury Regulation §1.401(a)(9)-9(d). IRC §401(a)(9).

v5.7.13
Mobile header width cap
v5.7.12
Mobile/tablet header polish
v5.7.11
Mobile header layout
v5.7.10
Activate path for installed PWA
v5.7.9
Global dollar toggle
v5.7.8
Plan Health & Big Levers toggle-aware
v5.7.7
Dollar Toggle: Dashboard Stragglers
v5.7.6
Dollar Toggle: Inputs Panel (Comparison Blocks)
v5.7.5
Dollar Toggle: Inputs Panel (Simple Sections)
v5.7.4
Dollar Toggle: Survivor Center
v5.7.3
Dollar Toggle: PDF Export & Scenarios
v5.7.2
Dollar Toggle Persistence & Broader Deflation
v5.7.1
Gemini Free-Tier Callout on Guide Tab
v5.7
Employer Plan Roth Conversion Gate

Many employer plans (401(k), 403(b), 457(b)) don’t permit in-plan Roth rollovers while you’re still employed — conversions from those accounts have to wait until you separate from service. RetIQ now models this with a per-account conversion gate. Each pre-tax account has a new dropdown under its row on the Accounts tab: Always (default, correct for IRAs and plans that permit in-service IRR), After primary retires, After spouse retires (when a spouse is enabled), or After specific age.

Under the hood, the engine partitions your pre-tax balance into a convertible pool plus a list of gated tranches. Gated tranches grow at the same weighted pre-tax return as the rest of the pool and roll into the convertible pool automatically when their gate opens. Roth conversions and Tax-Aware Drawdown cap at the convertible portion; RMDs, QCDs, QLAC, and regular withdrawals reduce the full pool proportionally so gated balances shrink along with the rest. No migration needed — accounts with no gate set default to Always, preserving all existing plan behavior.

Gated balances surface in three places: the Roth Conversion Optimizer card shows a gated-balance summary, the Strategy Report Card shows a cyan callout explaining why conversion-based strategies may show reduced amounts, and the AI Guide answers “in-plan conversion” / “employer plan” questions with your specific gated total. The Tax Strategy workflow (“Optimize Your Roth Conversion Strategy”) includes a new tip pointing users to the Accounts tab before running the Optimizer.

Demo: the Lees example plan now marks the spouse’s 401(k) as gated until the spouse retires, showing how the Roth Optimizer respects the constraint.

Dev: added a localhost/127.0.0.1 hostname bypass for the license gate so local QA works without the dev.html bootstrap dance. Zero impact on production behavior — the bypass only activates when the hostname matches a local dev server.

Validation: 361 tests passing, +11 from v5.6.4. New tests in Category 17 (Roth Conversion Gate) cover: convertible-pool cap, pool exhaustion mid-window, gate unlock at retirement age, backward compatibility (accounts without the field), spouse-gated age conversion, spouse-disabled coercion to “always”, specific-age gate, pool-identity reconciliation, and RMD proportional reduction.

v5.6.4
AI Settings Polish — Easier First-Time Setup
v5.6.3
AI Guide Quality Pass
v5.6.2
Guide Chat Reset & Layout Polish
v5.6.1
AI Settings Modal Fixes
v5.6
Eligible Designated Beneficiary (EDB) Stretch Exceptions

The inherited account regime dropdown now includes three additional options covering SECURE Act Eligible Designated Beneficiary exceptions to the 10-year rule: Disabled beneficiary (IRC §72(m)(7)), Chronically ill beneficiary (IRC §7702B(c)(2)), and Not more than 10 years younger than decedent. All three use the same IRS Pub 590-B Table I Single Life Expectancy stretch math as v5.5’s pre-2020 grandfathered regime.

The engine’s stretch-regime filter is now a set membership check rather than an equality test, so adding future stretch variants doesn’t require engine changes. Existing pre-2020 stretch and post-2019 10-year accounts are unaffected.

Surviving spouse (uses spousal rollover into own IRA — add as Pre-Tax / Roth instead), minor children (stretch-to-10-year transition at age of majority), and successor beneficiary pass-through on a non-spouse beneficiary’s death. These edge cases warrant financial advisor guidance.

349 validation tests pass (was 348): 1 new test confirms EDB Disabled routes to the same Single Life Expectancy math as pre-2020 stretch.

v5.5
Pre-2020 Stretch IRA Regime

Inherited IRA and Inherited Roth accounts now support a per-account regime selector. Default is Post-2019 (SECURE Act 10-year), which is v5.4’s existing behavior. The new Pre-2020 stretch (grandfathered) option uses IRS Pub 590-B Table I (Single Life Expectancy) with the subtract-1 method for non-spouse beneficiaries who inherited before December 31, 2019 and are taking distributions over their own life expectancy.

When pre-2020 stretch is selected, two new input fields appear: Age at first RMD (your age in the calendar year of your first required distribution) and Years elapsed (full years between your first RMD and today). The engine computes the forced annual distribution as balance ÷ (initial divisor − years elapsed) and decrements the divisor by 1.0 each subsequent year.

Stretch distributions feed the same income counters as post-2019 distributions: traditional flows through ordinary income, SS taxation, MAGI, IRMAA, and NIIT correctly; Roth distributions are tax-free. Balances grow at the nominal return rate between distributions and contribute to net worth.

Backward compatible by default — accounts without a regime field continue to use v5.4’s pooled post-2019 math exactly as before. 348 validation tests pass (up from 331): 10 new unit tests on the Single Life Expectancy helper + 7 integration tests covering migration safety, mixed-regime portfolios, and per-account tracking.

Not yet modeled (planned for later): Eligible Designated Beneficiary exceptions (surviving spouse, minor children, disabled, chronically ill, and beneficiaries within 10 years of the decedent’s age), extra voluntary distributions above the forced minimum, and successor beneficiary pass-through on owner death.

v5.4
Inherited IRA Account Types

Two new account types: Inherited IRA and Inherited Roth IRA. Enter the year inherited to start the SECURE Act 10-year clock. The engine tracks inherited accounts as separate buckets with forced annual distributions spread evenly over the remaining window. Inherited traditional IRA distributions are taxed as ordinary income and flow through SS taxation, MAGI, IRMAA, and NIIT calculations. Inherited Roth distributions are tax-free. Both types are automatically excluded from Roth conversions, Tax-Aware Drawdown, and standard RMDs.

Inherited distribution cash offsets spending needs before voluntary withdrawals from other accounts. Excess distributions are reinvested into brokerage with full cost basis tracking. Inherited accounts are not part of the configurable withdrawal order — distributions are forced on the 10-year schedule.

Inherited balances and distributions appear in the projection chart (3 new selectable categories), data table (3 new columns), and dashboard net worth stacked area. The Plan Summary shows an INHERITED badge with total distributions. Legacy composition includes any remaining inherited balance at end-of-plan. One-time expenses can now be withdrawn from inherited accounts.

The Strategy Report Card shows a new insight when inherited accounts exist, explaining they are excluded from all Roth and Tax-Aware Drawdown alternatives with the distribution deadline and total. The Roth Conversion tab displays a pink callout noting inherited pre-tax is not eligible for conversion and showing the excluded balance. A new Dashboard lever card shows the inherited distribution timeline, estimated tax cost, and deadline.

Manual updated with full Inherited IRA Accounts subsection covering 10-year distribution, Roth exclusion, RMD exclusion, tax treatment, spending priority, and spousal rollover note. Features and landing pages updated. New “Plan Around an Inherited IRA” guided workflow. 13 new automated tests (331 total) cover separate bucket tracking, distribution math, depletion timing, net worth identity, tax impact, Roth conversion exclusion, RMD exclusion, inherited Roth behavior, and non-interference with existing account types.

v5.3
50-State Tax Sync, Relocation Analysis & Automated State Tax Updates

Complete sync of all 50 states + DC to Tax Foundation 2026 data. 35 states updated: flat-rate corrections (NC, CO, GA, IN, KY, MS, UT, ID), bracket restructures (OH, MT, NE, OK, SC, AR, KS, ND), new high-income tiers (MD, NM, DC), Massachusetts 4% millionaire surtax added, and 13 states with inflation-adjusted thresholds and filing-status-specific brackets. New York’s bottom 5 rates lowered for 2026. All rates verified against the Tax Foundation’s authoritative spreadsheet.

New Relocation Scenario card in the Scenarios tab. Model moving to a different state with an optional home sale (§121 exclusion applied automatically), mortgage payoff, expense adjustment, and full before/after comparison. The Strategy Report Card now surfaces “Relocate to Florida” automatically for users in income-tax states, showing estimated lifetime state tax savings.

State tax rates now update from Cloudflare KV alongside federal regulatory data. Future rate changes are pushed to all users without an app update.

16 new state tax validation tests covering all updated states. 318 total tests passing.

v5.2
Dashboard Redesign

The separate Plan Summary paragraph, key metrics row, highest-impact lever cards, and Maximum Spending calculator are consolidated into a single tabbed card with three views: Snapshot (key metrics grid with active strategy badges), Focus Areas (highest-impact levers with max spending calculator), and Details (income, savings, and lifetime totals). Every number now appears exactly once — no redundancy between prose and metrics.

Replaces two stacked collapsible accordion cards with a single card using a Working / Retirement tab toggle. New features: Account Transfers section for Roth conversions and Tax-Aware Drawdown (marked “does not affect totals”), standalone Healthcare line item, ⊕ info icons for contextual notes, dimmed zero-value rows (e.g., $0 state tax in Florida), and a Net Annual Cash Flow bar at the bottom showing surplus or drawdown rate.

The four dashboard charts — Net Worth, Income & Spending, Taxes & RMDs, and Cash Flows — now display one at a time in a tabbed container instead of a 2×2 grid. Each chart is full-width and taller for better readability on all screen sizes.

Now uses a two-dimensional tab layout. Goal tabs run horizontally (Max Net Worth, Min Taxes, Min Tax+IRMAA, Max SS Income, Tax-Free Legacy). Strategy category tabs run vertically (SS Timing, Roth, Drawdown, Combined, Retirement Age, Tax Optimization, Asset Allocation). A green dot indicates which category contains the best alternative. Cross-tab insights link to the best overall strategy when it lives in a different category. Maximum 3–4 cards visible at any time.

v5.1
Dashboard Inflation View & Debt Amortization

The Today’s $ / Future $ toggle now appears on the Dashboard, not just the Projection tab. When enabled, all stat values (Peak Net Worth, End Net Worth, IRMAA, Peak RMD), the Plan Summary prose, and all four dashboard charts display in inflation-adjusted present-value dollars. Lifetime sums like total tax, total IRMAA, and total Roth conversions are deflated per-year before summing — giving you a properly weighted present-value total rather than a rough approximation.

Debts can now track a remaining balance and interest rate. When a balance is entered, the engine computes the interest/principal split each year, tracks the declining balance, and automatically stops payments when the debt is paid off — no need to guess an end age. The Liabilities tab shows each debt as a card with payoff age, total interest paid, and a principal vs. interest breakdown. Debts without a balance continue working as fixed monthly payments for obligations like alimony. Two new Projection chart categories — Debt Interest and Debt Principal Paid — let you visualize the paydown schedule.

The Roth Conversion Optimizer now tests conversion windows up to 40 years (previously 25). Early retirees with long runways benefit from a wider search that can find strategies spanning decades. Sampling increased from 20 to 30 windows for better coverage.

New opt-in controls in the Accounts tab let you specify exactly where your pre-retirement savings go: 401(k) deferral (Traditional or Roth) and IRA contribution (Traditional or Roth), with the remainder flowing to brokerage. Previously, savings were auto-split 60/25/15 across Pre-Tax, Roth, and Brokerage. Routing amounts stay constant across income phases and savings growth. Employer match is always calculated against your 401(k) deferral and deposited to Traditional Pre-Tax, regardless of your 401(k) type. Available in Full mode only — Simple mode retains the automatic split.

v5.0
AI Guide — Bring Your Own Key

New Guide ✦ tab with an AI-powered assistant that knows your plan, your numbers, and every RetIQ feature. Ask questions like “Am I on track?”, “Should I explore Roth conversions?”, or “How do I prepare my spouse?” — and get answers personalized to your actual plan data.

13 pre-written answers cover the most common retirement planning questions with instant, accurate responses that cite your real numbers and walk you through the app step by step. No AI model needed for these — they work offline. Topics include Roth conversions, IRMAA, Social Security timing, stress testing, survivor planning, legacy, healthcare, spending, and Tax-Aware Drawdown.

For questions beyond the templates, connect your own API key from Anthropic (Claude), OpenAI (GPT), Google (Gemini), Perplexity, Meta (Llama), Groq, DeepSeek, or Mistral. Your key is stored locally in your browser. Plan data is sent to your chosen provider only when you ask a question — never stored, never logged by RetIQ. You pay the provider directly (typically a few cents per conversation).

The Guide’s welcome screen analyzes your plan and shows personalized suggestions based on detected patterns: high pre-tax balance with no Roth strategy, significant IRMAA surcharges, single-life pension with a spouse, plan running short, or early retirement without healthcare. Tap any suggestion for an instant detailed answer.

The 8 Guided Workflows (Tax Valley, Roth Optimization, ACA Coordination, Stress Testing, Survivor Risk, Spouse Roadmap, Pension Survivor, Legacy) continue to work exactly as before. The AI Guide is an additional way to get help, not a replacement.

API keys are stored in your browser’s localStorage on your device only. When you ask a question, your plan summary and the question are sent through RetIQ’s stateless Cloudflare Worker proxy to the selected provider. The proxy forwards the request and returns the answer — it never stores, logs, or inspects your key or data. Without cloud AI enabled, all computation stays 100% local.

Marginal Rate Curve — Tax Torpedo Visualization

New "Analyze Marginal Rates" chart on the Roth Conversions tab. Sweeps conversion amounts from $0 to your pre-tax balance and plots the true effective marginal tax rate at each level. Reveals the "tax torpedo" — where SS taxation stacking, capital gains bracket shifts, OBBB senior deduction phaseout, and IRMAA surcharges all compound on the same dollar. Your current conversion amount is shown as a reference line.

v4.8
Strategy Check-In

New Dashboard card that tracks whether your active strategy is still optimal as your portfolio changes. Click “Save Check-In” to snapshot your current metrics and strategy. When you later update account balances, the card compares your new projection against the snapshot and shows one of three states:

The card answers: “Given where my portfolio actually is today, should I change anything?” Update Snapshot to accept the new baseline, or click Review to jump to the Strategy Report Card.

v4.7.2
Roth Chart Fix, Guided Workflows & Strategy Report Clarity

Fixed the Conversion Advantage chart and breakeven age calculation, which were double-counting conversion taxes paid from cash or brokerage. Since v4.6 added explicit conversion tax deduction from accounts, the chart’s cumulative tax adjustment was subtracting those taxes twice — making conversions look permanently unprofitable. The chart now correctly excludes conversion taxes (already reflected in net worth) from its tax adjustment. Breakeven ages and the Conversion Advantage line are now accurate. Affects both the Impact Analysis and the Roth Optimizer charts.

Fixed Roth conversion alternatives in both the Dashboard and Survivor Strategy Report Cards. Alternatives were creating bare rothConversion objects that dropped the user’s IRA basis (Form 8606) and conversion tax source settings from v4.6. This overstated conversion tax cost for anyone with after-tax IRA contributions and mismodeled tax payment source. Alternatives now preserve all existing Roth settings while overriding only the strategy-specific parameters.

Four new workflows added, bringing the total from 8 to 12:

The Survivor Strategy Report Card now tests a “Deploy Cash to Brokerage” alternative, matching the Dashboard Strategy Report Card. Previously only available in the Dashboard.

Each strategy alternative now shows a category tag (SS Timing, Roth Strategy, Tax-Aware Drawdown, Combined, etc.) so users can see at a glance what Apply will change. New info box explains how Apply works: different-category strategies layer, same-category strategies replace, and Combined strategies change multiple settings at once. Each alternative uses its own preset parameters, not the user’s manual input tab settings.

New note at the top of the Roth Conversion Impact Analysis clarifies that it compares the user’s exact manual settings against no conversions, while the Dashboard Strategy Report Card tests different preset strategies. If results differ, the strategies are different — not contradictory.

New manual sections: “How Alternatives Are Tested” explains why Report Card results can differ from the Roth Conversions tab. “How Apply Works” documents the layering behavior with concrete examples and a recommended iterative workflow. Roth-related workflows updated with guidance on the Report Card vs. Impact Analysis relationship.

v4.7.1
QLAC — Qualified Longevity Annuity Contract

New engine feature for modeling a Qualified Longevity Annuity Contract purchased inside an IRA or 401(k). Enter purchase amount (SECURE 2.0 max $200,000), purchase age, payout start age (must begin by 85), and annual payout from your insurance company quote. The purchase moves money from pre-tax to an illiquid QLAC balance — not a taxable event. RMDs naturally decrease since the pre-tax balance is smaller. At payout age, guaranteed lifetime income starts, fully taxable as ordinary income. QLAC balance included in net worth. Strategy Report tests a $200K QLAC for plans with $300K+ in pre-tax accounts.

4 new QLAC tests. Total: 302 automated tests, 35 categories.

v4.7
Tax Optimization Suite

New engine feature that automatically detects unused room in the 0% long-term capital gains bracket each year and steps up brokerage cost basis at zero tax. IRMAA-aware — caps the harvest at the next Medicare surcharge threshold. Toggle in the Accounts tab (requires cost basis to be set). Visible in the Projection Table and tested as a Strategy Report alternative.

Enter your estimated annual harvestable losses and any carry-forward balance. The engine applies IRS rules each year: losses offset capital gains dollar-for-dollar, then up to $3,000/yr of ordinary income, with unlimited carry-forward. Projection Table shows the offset applied and running carry-forward. Strategy Report tests a $5K/yr TLH scenario.

When enabled, the engine compares standard and itemized deductions each year and picks the winner. Itemizable: SALT (capped $10K), medical expenses above 7.5% AGI, and non-QCD charitable giving (capped 60% AGI). Senior and OBBB additions apply only to standard. Inputs for SALT and medical in the Accounts tab; charitable giving flows in automatically from the Charity tab.

Concentrate 2 or 3 years of charitable giving into a single Donor-Advised Fund contribution. Bunch years itemize; off years take the standard deduction. Total giving unchanged. Toggle and cycle selector in the Charity tab. Strategy Report tests bunching with itemized deductions enabled.

Roth Smart Fill and Tax-Aware Drawdown Smart Fill now account for itemized deductions when computing bracket room. If your itemized total exceeds the standard deduction, the engine sees the larger deduction and converts or draws down more to fully utilize the bracket space.

Three new Dashboard lever cards proactively surface tax optimization opportunities: 0% CG Harvesting (when unrealized gains exist with 0% bracket room available), Tax-Loss Harvesting (for brokerage accounts over $100K), and Itemized Deduction comparison (when SALT + charity may beat the standard deduction). Each shows estimated lifetime savings and links to the relevant settings.

New in-app callout and manual section on the December conversion timing strategy: convert late in the year with IRS withholding to avoid estimated-tax penalties, then replenish via the 60-day indirect rollover. Covers the one-per-12-month rollover limit vs. unlimited direct trustee-to-trustee conversions.

16 new tests across 4 categories (CG Harvesting, TLH, Itemized Deductions, DAF Bunching). Total: 298 automated tests, 34 categories.

v4.6.1
Cash Drag Detection & UX Polish

New Dashboard lever card flags excess cash earning below market rate. Triggers when cash exceeds $100K and 15% of portfolio with a meaningful return gap. Shows the dollar amount above a 1-year reserve and the compounded opportunity cost over your projection horizon.

The Strategy Report Card now tests a “Deploy Cash to Brokerage” alternative that models moving excess HYSA funds into your brokerage while keeping a 1-year expense reserve. Ranked alongside other strategies under a new Asset Allocation category.

The 5-Year Rule Tracker card on the Roth Conversions tab is now hidden when all conversions happen after age 59½, since every row would show immediate access. The card still appears whenever any conversion is before 60.

The Tax Valley and Roth Strategy workflows now reference BETR, IRA basis, and conversion tax source in their step descriptions and checkpoints.

v4.6
BETR Framework & Roth Conversion Enhancements

The Conversion Impact Analysis now displays a Break-Even Tax Rate — the future marginal tax rate at which converting and not converting produce equal after-tax outcomes. If your expected future rate is above the BETR, conversion is favorable. Derived from Vanguard’s BETR research framework.

New input: Nondeductible IRA Basis. If your traditional IRA contains after-tax contributions, the engine now applies the IRS pro-rata rule — only the pre-tax portion of each conversion is taxed. Basis depletes proportionally as you convert over multiple years. Previously, all conversions were treated as fully taxable.

New dropdown: Pay Conversion Tax From. Choose between Cash/Brokerage (recommended — full amount stays in Roth), Cash Only, Withhold from IRA (less lands in Roth), or Not Modeled (legacy behavior). The engine now explicitly deducts conversion taxes from the selected account, with brokerage cost basis tracked proportionally on liquidation.

A new Insights card alerts high-income users whose traditional IRA balance blocks efficient backdoor Roth contributions due to the pro-rata rule. Explains how converting the IRA clears the obstacle and shows the annual backdoor contribution potential.

v4.5
Pre-Retirement Income Phases

Added income phase support to the projection engine. Each phase defines an age range with its own salary, savings amount, and growth rates. When no phases are defined, income works exactly as before — a single salary with uniform growth. When phases are defined, ages not covered by any phase fall back to base income rather than zeroing out.

New “Pre-Retirement Income Phases” card in the Income tab. Click “Add Income Phase” to create your first phase, pre-populated from your current income, savings, and growth rate. Add a second phase for a career wind-down, part-time period, or sabbatical. Each phase shows income at start and end ages. Spouse phases are supported independently.

Designed for people a few years from retirement who plan to take a lower salary, go part-time, or change careers before fully retiring. Previously, the only option was a single income with uniform growth — no way to model a pay cut at age 62 followed by retirement at 65.

v4.4
Projection Visibility, Spouse Fix & QA Improvements

Added six new columns to both the projection chart and data table: Other Income, Qualified Dividends, Tax-Aware Drawdown, TAD Tax Cost, HSA Balance, and HSA Contributions. These were already computed by the engine but not visible to users. All auto-hide when values are zero.

Previously selected chart categories that no longer had data (e.g., after disabling a feature) could become invisible but still count toward the 4-category limit, blocking new selections. Stale keys are now filtered automatically on every render.

Disabling and re-enabling the spouse checkbox now correctly preserves the spouse’s age. Previously, re-enabling left the age at zero, silently zeroing out spouse income, savings, and Social Security in the projection.

Fixed the “Reset to recommended order” button in the Assumptions tab, which was broken due to an HTML escaping issue. The button now works correctly.

Retirement age scenarios (Retire at 40, 50, etc.) are now hidden when below or equal to your current age, since they would produce identical projections to the baseline.

v4.4.1
Roth Optimizer: Wider Windows for Early Retirees

The optimizer now tests conversion windows up to 25 years (previously capped at 15). Early retirees with long runways to RMDs can now see Fill 12% strategies tested over realistic time horizons, giving the optimizer a fairer comparison between aggressive short-window conversions and conservative long-window strategies.

v4.3
Conversion & TAD Tax Payment

Roth conversion and Tax-Aware Drawdown taxes are now explicitly paid from non-retirement accounts (cash/HYS first, then brokerage) before year-end growth. Previously, the tax was computed and reported but not deducted from any account balance, slightly overstating net worth in conversion years.

The This Year card no longer double-counts conversion and drawdown taxes. Federal tax now excludes amounts already shown in the Roth Conversions and Tax-Aware Drawdown sections.

v4.2
2026 Tax Year Update & Automated Regulatory Monitor

All embedded tax constants updated to 2026 figures. Sources: IRS Rev. Proc. 2025-32, IRS Notice 2025-67, CMS Nov 2025, HHS 2026 FPL, IRS Rev. Proc. 2025-19.

Deployed a Cloudflare Worker that runs weekly. It fetches IRS, SSA, CMS, and HHS source pages, uses AI to extract updated figures, validates them against a strict schema, and pushes changes to Cloudflare KV. All three platforms receive updated tax data on next load with no app update required.

New Workflows tab (Full mode, Pro only) with 8 step-by-step walkthroughs for complex multi-tab analyses: tax valley optimization, Roth conversion strategy, ACA/Roth/withdrawal coordination, end-to-end stress testing, survivor risk analysis, spouse roadmap preparation, pension survivor benefit selection, and after-tax legacy optimization. Each workflow includes numbered steps, navigation cues, and checkpoint prompts.

After clicking Apply on any alternative strategy, a “Reset to Original” button appears. One click reverts all Apply experiments and restores your plan to where you started. The snapshot clears on page reload.

Scenario buttons replaced with a grouped dropdown menu (Retirement Age, Social Security, Expenses & Returns). Added Retire at 40 and Retire at 50 scenarios. Cleaner on mobile — renders as the native iOS/Android picker.

Return rates are now clamped to −5% – 25%, growth rates to 0% – 20%, inflation to 0% – 15%, and SS COLA to 0% – 10%. Prevents accidental entries (e.g., typing 76 instead of 7.6) from producing runaway projections.

Raised the simultaneous chart category limit from 3 to 4 in the Projection and Scenarios tabs.

v4.1
Tax-Aware Drawdown, Dawn Theme & More

Voluntary pre-tax withdrawal strategy that fills a target bracket before RMDs begin, reducing future RMD burden. IRMAA-aware, OBBB-aware, integrates with the Roth optimizer. Proceeds land in brokerage with full cost basis for stepped-up basis at death.

Third theme alongside dark and light — warm cream palette with a purple accent. Now the default on all platforms.

Fifth Strategy Report goal. Ranks strategies by after-tax heir value: Roth + taxable (stepped-up basis) + HSA, minus pre-tax discounted by the heir’s bracket under the SECURE Act 10-year rule. Shown when legacy planning is enabled.

Dedicated tab (visible when a spouse is configured) with three sub-tabs: Overview, Strategies, and Adopt Plan. Supports adopting the survivor’s roadmap in both directions.

v4.0
Modular Architecture, Regulatory Data System & Freemium Model

Rewrote the build system. The app is now assembled from 36 modular source files plus a pure-math engine with zero DOM dependencies, enabling per-platform builds for web, iOS, and macOS from a single codebase.

Centralized all tax, Medicare, Social Security, ACA, and contribution limit constants into a single REGS data structure that fetches updates from Cloudflare KV on load. No app update required to receive new tax year figures. Falls back to embedded defaults if offline.

Launched a free Simple tier and $49.99 one-time in-app purchase for full unlock on iOS and macOS. Web remains $49 one-time via Stripe. The free tier includes data entry, basic projections, the dashboard, save/load, and the Johnson family example.

RetIQ is now available on the iOS App Store and Mac App Store, alongside the existing web app. All three share the same calculation engine and save file format.

Added the One Big Beautiful Bill Act senior standard deduction ($6,000/person, phasing out at 6% above $75K single / $150K MFJ, effective 2025–2028). Updated all affected brackets and validation tests.

v3.2
Life Insurance Modeling

Term and permanent life insurance policies modeled with annual premiums and tax-free death benefits (IRC §101). Supports multiple policies per person and spousal policies. Death benefits deposit into the survivor’s brokerage account at the time of death and are reflected in legacy composition.

v3.1
One-Time Cash Events & LTC Insurance Inflation

Model planned property sales, inheritances, or other lump-sum events at a specific age. Capital gains calculated automatically with IRC §121 primary home exclusion ($250K single / $500K MFJ). Proceeds can flow into the brokerage or cash account.

Long-term care insurance benefits now compound over time. Choose from None, 2%, 3%, or 5% compound inflation protection. The engine compounds the insurance benefit alongside projected care costs so projections reflect realistic coverage value at the age it’s actually needed.

v3.0
Essentials & Full Control Modes

Two modes, one engine. Essentials presents a streamlined view for straightforward retirement planning — fewer tabs, fewer fields, the same math underneath. Full Control reveals all tabs and every advanced feature. Toggle anytime without affecting your data or calculations.

v2.1
Monte Carlo Model Selection & Roth Optimizer Improvements

Added model selection: Historical (U.S. market distributions 1926–present), Normal (parametric), and Conservative (lower expected return). Each affects the return and volatility assumptions across 2,000 simulated paths.

Added IRMAA-aware Smart Fill — fills the target bracket while capping conversions before crossing an IRMAA threshold. Added custom schedule mode with a per-year editable table and a “Generate Optimal” button that finds the best conversion amount for each year in the window.

v2.0
Pension, Legacy Goals, Liabilities, LTC & Charitable Giving

Expanded pension modeling: survivor benefit options (single-life, joint & 50/75/100%), configurable taxable percentage, COLA, and start age. Survivor benefit flows into the surviving spouse’s income correctly.

Legacy composition showing after-tax heir value breakdown across account types. Target legacy amount with a gap indicator. Charitable bequest modeling.

Debt schedules for mortgages, alimony, student loans, and other long-term obligations. Each has an annual amount and end age, subtracted from cash flow in the years it applies.

Dedicated LTC tab with annual care cost, start age, duration, and LTC insurance benefit offset. 2025 national cost benchmarks embedded as defaults.

Annual giving with start/end age, one-time gifts, and Qualified Charitable Distribution from pre-tax accounts. QCD reduces MAGI dollar-for-dollar up to the annual limit, offsetting RMD income.

v1.9
ACA Subsidy Engine

Full Affordable Care Act premium tax credit calculation based on projected MAGI and household size. Supports both the enhanced subsidy table (ARPA/IRA, no income cliff through 2025) and the original table (400% FPL cliff) for post-2025 planning. Subsidy reduces out-of-pocket healthcare cost in the projection year by year.

v1.8
Healthcare Source Selection & Pre-Medicare Gap

Six pre-Medicare healthcare source options: ACA Marketplace, COBRA, Spouse’s employer plan, VA/Medicaid, Retiree plan, and self-pay. Each has a different default cost and medical inflation rate. The engine models the full gap between early retirement and Medicare eligibility at 65.

v1.7
Social Security Earnings Test

The SSA retirement earnings test for early claimers. Benefits withheld at $1 for every $2 earned above the annual exempt amount (under FRA all year), or $1 for every $3 (in the year of FRA). Withheld benefits are recredited as a permanent benefit increase after FRA. Exempt amounts indexed annually by the Average Wage Index.

v1.6
This Year Summary Card

Dashboard card showing the current year’s projected cash flows: income sources, withdrawals, conversions, taxes owed, and net. When retirement is within 5 years, a second card previews the retirement year’s prorated income and new tax picture.

v1.5
4-Account System, SSDI & Validation Suite

Separated Cash/HYS and Brokerage into independent accounts with configurable return rates, tax treatment, and withdrawal order. Cash interest treated as ordinary income; brokerage withdrawals trigger capital gains using a 50% cost basis estimate with 0/15/20% tiered rates based on income.

Social Security Disability Income modeled for both spouses, with conversion to retirement benefits at FRA and correct survivor rules.

Automated validation suite covering federal tax, SS PIA, IRMAA, ACA subsidies, RMDs, and more — each test citing its authoritative source (IRS, SSA, CMS). Viewable at retirementiq.app/validation.html.

Contextual help icons throughout input forms with plain-English explanations of retirement finance terminology.