The user kept asking what each strategy means. Adding two layers:
1. Inline hint under each dropdown (Jurisdiction, Strategy, Glide
path) — short one-liner that updates as the selected option
changes. So no clicking required to see what trinity vs
guyton_klinger does.
2. <details> panel at the bottom: "About the model" with all
strategies, glide paths, jurisdictions described in plain
English, plus a note on how the engine treats tax (post-fix:
drain = w + tax(w)) and the returns model (Shiller block
bootstrap, 60/40 ≈ 4.6% real long run).
Plain-English originals — Trinity / Guyton-Klinger / VPW are
public-domain finance concepts.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
step={1000}/step={10000} on £ inputs blocked any non-multiple value
from passing browser validation. Surfaced when the Wealthfolio
autofill landed `1050195.09` into the NW field — Chrome rejected
it: "the two nearest valid values are 1050000 and 1060000". Same
class as the n_paths bug from earlier today.
- Remove step on spending_gbp / nw_seed_gbp / savings_per_year_gbp /
floor_gbp across What-If, ScenarioNew, ScenarioEdit. Default step=1
(any positive integer).
- Round NW autofill to whole pounds (Math.round) since the user
doesn't think in pence at this scale and the rounded value
satisfies any integer step.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two fixes:
(1) Simulator: portfolio drain is now `w + tax(w)`, not just `w`.
The pre-2026-05-10 engine recorded tax in tax_hist but never
subtracted it from the portfolio, so changing jurisdiction only
moved the median_lifetime_tax cell — the fan chart, success
rate, and ending percentiles were identical for UK vs Cyprus
vs Malaysia. (The PLAYBOOK_VIKTOR.md memo from 2026-04-26
explicitly noted this: "Success rate is regime-independent…
tax doesn't drain the portfolio in this simulator.")
Mental model now: spending_target is what the user takes home;
the tax bill is an additional drag on the same pool. Higher-tax
jurisdictions therefore drain faster and lower the success
rate, which is the user's intuition. Trinity 4% effectively
becomes "4% take-home + tax overhead". 188 tests still pass —
most use Malaysia (0%) or hit the regime-independent code paths.
(2) /what-if and /scenarios/new now pre-fill nw_seed_gbp from
GET /networth on first mount (when the wealthfolio_sync mirror
has data), so opening the form starts from the user's real
portfolio total instead of the £1.5M placeholder. Once the user
edits the field, subsequent NW refetches don't clobber it
(nwAutoFilled latch).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
step=500 with min=100 made valid values 100, 600, 1100, … so typing
a round number like 500 or 1000 tripped browser validation
("Please enter a valid value. The two nearest valid values are 100
and 600."). Found via headless click-through against the deployed
instance — the Run-simulation submit was silently rejected.
step=100 keeps a sensible up/down increment while accepting any
multiple of 100 (100, 200, …, 5000, …, 50000).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three small UX wins:
- /scenarios/:id Run now — POSTs /simulate with the scenario's params
and renders the result in a "Live preview run" card below the
persisted projection. Removes the recompute-or-wait friction.
- /what-if Save as scenario — appears once a simulation has run.
Prompts for a name (with a sensible default), POSTs the form values
to /scenarios as a user scenario, redirects to its detail page.
- /scenarios/:id/edit — PATCH form for user scenarios. Pre-fills from
current scenario; on save invalidates the scenarios query and
navigates back to detail. Backend already rejects PATCH on
cartesian; the UI also hides the Edit button for them.
api.scenarios gained patch(). 7 tests pass, typecheck + build clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New /what-if route. Sticky form on the left (jurisdiction, strategy,
glide, NW seed, spending, savings, horizon, optional floor for
vpw_floor, MC paths) submits to POST /simulate; results panel renders
summary stats + the new FanChart.
FanChart component layers seven series:
- p10 invisible baseline (line, transparent)
- p10→p25 stacked area (low opacity)
- p25→p75 stacked area (IQR, mid opacity)
- p75→p90 stacked area (low opacity)
- p50 solid median line (drawn last, prominent)
- p10 + p90 dashed lines on top of the bands
Stacking deltas keeps the band fills clean — plotting raw quantiles
each as their own area would overlap badly. Reusable by scenario
detail in the next chunk (same ProjectionPoint[] shape).
5 tests pass (was 2). 470 KB gzipped (ECharts).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>