Migrated from monorepo during Forgejo registry consolidation 2026-05-07
Find a file
Viktor Barzin b82770b5c4
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
deploy: combined Dockerfile — FastAPI serves the SPA in prod
Three-stage build:
  1. node:22-alpine — `npm ci` + `npm run build` produces frontend/dist
  2. python:3.12-slim — poetry installs backend deps into a venv
  3. python:3.12-slim — runtime, copies the venv + frontend/dist,
     sets FRONTEND_DIST=/app/frontend_dist

Backend gates the API surface on FRONTEND_DIST:

- Unset (dev / tests): routers mount at root (/networth, /scenarios,
  …). 172 tests still pass unchanged. The Vite dev server proxies
  `/api/*` → backend stripping the prefix.
- Set (prod): routers mount under `/api/*`. The SPA bundle mounts at
  `/` with html=True so React Router owns client routing for paths
  like `/scenarios`, `/what-if`. Same-origin, no CORS, one deploy.

Operational endpoints (`/healthz`, `/metrics`, `/recompute`) stay at
root in both shapes.

Existing Woodpecker pipeline picks this up unchanged — same context,
same Dockerfile path, just produces a richer image.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 22:25:52 +00:00
alembic
fire_planner deploy: combined Dockerfile — FastAPI serves the SPA in prod 2026-05-09 22:25:52 +00:00
frontend frontend: run-now + save-as-scenario + edit form (CRUD complete) 2026-05-09 22:20:21 +00:00
tests
.dockerignore deploy: combined Dockerfile — FastAPI serves the SPA in prod 2026-05-09 22:25:52 +00:00
.gitignore
.woodpecker.yml
alembic.ini
Dockerfile deploy: combined Dockerfile — FastAPI serves the SPA in prod 2026-05-09 22:25:52 +00:00
PLAYBOOK_VIKTOR.md
poetry.lock
pyproject.toml
README.md

fire-planner

Risk-adjusted, tax-minimised FIRE retirement planner. Consumes today's portfolio, savings rate, and RSU vest schedule from sibling services (wealthfolio, payslip-ingest, hmrc-sync) and returns the after-tax probability of success for each combination of jurisdiction, withdrawal strategy, and "year you break UK tax residency".

Layout

  • fire_planner/ — package
    • tax/ — per-jurisdiction tax engines (UK, nomad, Malaysia, Thailand, Cyprus, Bulgaria)
    • returns/ — Shiller 1871+ data + block bootstrap sampler
    • strategies/ — Trinity 4% SWR, Guyton-Klinger guardrails, VPW
    • ingest/ — pulls from wealthfolio / payslip-ingest / hmrc-sync
    • simulator.py — vectorised NumPy MC engine
    • scenarios.py — Cartesian product over (jurisdiction × strategy × leave-UK-year × glide)
    • app.py — FastAPI on-demand /recompute
    • __main__.pyclick CLI: ingest, simulate, recompute-all, migrate

Common commands

poetry install
pytest -v
mypy .
ruff check .
yapf --recursive .

# Run migrations against the local DB:
DB_CONNECTION_STRING=postgresql+asyncpg://... alembic upgrade head

# CLI
DB_CONNECTION_STRING=... python -m fire_planner ingest
DB_CONNECTION_STRING=... python -m fire_planner simulate --scenario=cyprus-vpw-leave-y3
DB_CONNECTION_STRING=... python -m fire_planner recompute-all

Schema

Six tables in fire_planner schema on pg-cluster-rw:

  • account_snapshot — daily NW per account (Wealthfolio)
  • scenario — Cartesian-product scenario definition
  • mc_run — execution metadata + summary stats per (scenario, run_at)
  • mc_path — sparse storage (top decile, bottom decile, median)
  • projection_yearly — deterministic point projection per scenario
  • scenario_summary — denormalised fast-read for Grafana