fire-planner/fire_planner
Viktor Barzin 70101c836c
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fire-planner: What-If gains the chart-first scenario editor
The Plan-tab editors (interactive Gantt for life events, flex spending
rules) are now available in What-If too — with local state instead of
DB persistence so users can tweak before committing to a scenario.

Architecture refactor:

- EventGantt is now a controlled component. The `scenarioId` prop +
  internal useMutation/useQueryClient hooks went away; the component
  takes a `persister: { create, patch, delete }` prop and delegates
  every mutation through it. Plan tab wires it to lifeEventsApi +
  cache invalidation; What-If wires it to React local state with
  negative ids for new events.
- FlexRulesEditor is similarly controlled. Takes `rules + onChange`
  instead of a `scenario` object. Plan tab wraps it with the PATCH
  /scenarios/:id mutation; What-If wraps it with setFlexRules.

Backend:

- New stateless POST /scenarios/spending-profile-preview endpoint
  takes base_spending_gbp + horizon + life_events + flex_rules in the
  body and returns the same SpendingProfileResponse shape as the
  read-only /scenarios/{id}/spending-profile endpoint. Used by
  What-If to render the stacked-area chart against unsaved events.
- SpendingProfileResponse.scenario_id loosened to int | None to
  support the preview variant.

Frontend:

- What-If page gains `events` + `flexRules` local state, an
  EventGantt + FlexRulesEditor wired through them, and a Visx
  spending-profile chart fed by /spending-profile-preview.
- Sim auto-refresh: a 600ms debounced effect re-fires /simulate
  whenever the form / events / flex rules change. Manual "Run
  simulation" button stays as an immediate trigger.
- "Save as scenario" still works — preserves the scenario params but
  not yet the life events / flex rules (a Wave-3 follow-up could
  POST them after the scenario is created).

247 pytest pass; mypy + ruff + frontend typecheck/test/build all clean.
2026-05-12 19:35:28 +00:00
..
api fire-planner: What-If gains the chart-first scenario editor 2026-05-12 19:35:28 +00:00
ingest ingest: switch wealthfolio to pg-sync mirror reads 2026-05-09 21:33:48 +00:00
reporters Initial extraction from monorepo 2026-05-07 17:06:19 +00:00
returns returns: 3 models — Shiller bootstrap (default), manual %, Wealthfolio history 2026-05-10 01:04:25 +00:00
strategies strategies: spending input is honoured + new "Custom" preset with guardrails 2026-05-10 01:21:55 +00:00
tax Initial extraction from monorepo 2026-05-07 17:06:19 +00:00
__init__.py Initial extraction from monorepo 2026-05-07 17:06:19 +00:00
__main__.py ingest: switch wealthfolio to pg-sync mirror reads 2026-05-09 21:33:48 +00:00
actualbudget.py whatif: live data refresh, inflation-adjusted spending, legend fix 2026-05-10 11:27:22 +00:00
app.py fire-planner: SPA cache headers — index.html no-cache, hashed assets immutable 2026-05-10 17:01:11 +00:00
db.py fire-planner: Wave 2 chart-first — flex spending, categorised life 2026-05-10 16:49:04 +00:00
flex_spending.py fire-planner: Wave 2 chart-first — flex spending, categorised life 2026-05-10 16:49:04 +00:00
fx.py Initial extraction from monorepo 2026-05-07 17:06:19 +00:00
glide_path.py Initial extraction from monorepo 2026-05-07 17:06:19 +00:00
goals_eval.py fire-planner: ProjectionLab parity Wave 1 — tabbed shell, year stats, goals, 2026-05-10 12:49:44 +00:00
income_streams.py fire-planner: ProjectionLab parity Wave 1 — tabbed shell, year stats, goals, 2026-05-10 12:49:44 +00:00
life_events.py fire-planner: Wave 2 chart-first — flex spending, categorised life 2026-05-10 16:49:04 +00:00
scenarios.py strategies: spending input is honoured + new "Custom" preset with guardrails 2026-05-10 01:21:55 +00:00
simulator.py fire-planner: life-event spending bumps now reflected in fan + auto- 2026-05-10 19:17:57 +00:00