Files
retire-planner/REPORT.md
T

48 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RETIRE PLANNER — Execution Report
**Run:** DSH local agent, PLAN.md v1 (2026-10-08). Status: **IN PROGRESS**.
**Target:** https://retire.krisforbes.ca — Canadian retirement calculator, 100% client-side.
---
## Stage 0 — Preflight ✅ (all pass)
| Check | Command | Result |
|---|---|---|
| Workspace | `pwd` | `/home/ubuntu/projects/retire-planner` ✅ |
| Node | `node --version` | v26.7.0 ✅ |
| Git | `git --version` | 2.53.0 ✅ |
| Gitea | `curl http://192.168.0.123:3000/api/v1/version` | `{"version":"28.0.0"}` ✅ |
| Unraid SSH | `ssh root@192.168.0.123 'echo ok'` | `ok` ✅ — **note:** plain `ssh` fails on this box with `Bad owner or permissions on /etc/ssh/ssh_config.d/20-systemd-ssh-proxy.conf` (symlink owned by nobody). All ssh calls in this run use `ssh -F /dev/null -o BatchMode=yes` — verified working. |
| Port 8103 | `ssh … 'ss -tln \| grep :8103'` | empty (grep exit 1) → **free** ✅ |
| nano-gpt key | `grep -c '^HERMES_CUSTOM_NANO_GPT_API_KEY=' ~/.hermes/.env` | `1` ✅ (value never printed) |
| Repo exists? | `GET /api/v1/repos/krisf/retire-planner` | **404** → no existing repo ✅ |
| Clean slate | retireapp container / Caddyfile retire block / `/mnt/better/appdata/retireapp` | all absent ✅ |
| rsync | local + remote `which rsync` | both present ✅ |
## Stage 1 — Engine + live constants ✅
- `site/engine/engine.js` (UMD, pure fns) + `site/engine/canada.js` generated by `scripts/build_canada.js` from live fetches.
- Sources fetched (evidence in `scripts/constants-evidence.json`): ESDC CPP/quarterly report (CPP max **$1,507.65**/mo @65, YMPE **$74,600**), canada.ca OAS page (max **$762.50** Oct–Dec 2026, +0.6%/mo deferral to +36%, ×1.10 @75, clawback 0.15 over **$95,323**), canada.ca GIS page (single max **$1,138.90**/mo, thr 23,112; couple 685.56×2, thr 30,528), CRA 2026 federal brackets (14/20.5/26/29/33 @ 58,523/117,045/181,440/258,482), taxtips.ca 2026 provincial tables all 13 (ON top 13.16%, ON surtax 20%/36% modelled from FAQ thresholds, QC abatement 16.5% in engine).
- RRIF minimums: CRA statutory factor table (Reg. 8301(1)): 71→5.28 … 94→12.18, 95+→20. (Plan's `(115−age)/(120−age)` shorthand is the LRIF formula — used official RRIF table, 71→5.28 matches plan test.)
- Canada.ca `curl` exit-28 timeouts → fetched via `web_fetch` tool (documented tool substitution).
- Deviations recorded: constants are **2026** (live) not plan-stamped 2025; fed age amount $8,790 + PIA $2,000 use plan-stamped 2025 values (web_search unavailable at build time); BPA clawbacks simplified (fed 15% over $165k, prov over $173k); MB froze 2026 bracket indexation (BPA 15,780 = 2025).
## Stage 2 — Tests ✅
- `node tests/engine.test.js` → **36 PASS, 0 FAIL** (full output in git log of this run).
- Plan's 5 gate assertions: cpp 60→0.64× / 70→1.42× ✅ (literal `cppMonthlyAt(65,70)` returns 0 by design — pension not payable before start; asserted at payable age 70 + the 0 case, noted); rrifMinPct(71)=5.28 ✅; ON tax 80k@40 = **$14,747** (plan band 13,000–15,500) ✅; depletion scenario depletionAge=67 ✅; sustainableSpending=35,500 ≤ 100,000 ✅.
- Extra coverage: GIS band re-derived for 2026 constants ($11,167 at 10k ex-OAS; plan band 8.2–9.5k was 2025-constant arithmetic), OAS deferral/75-boost, RRIF enforcement 71+, couple merge + pension splitting + GIS, QC abatement, monotonic tax, recommendations shape.
## Stage 4 — Retirement Vision images ✅ (done early, parallel)
- 13/13 images generated via nano-gpt `z-image-turbo` (1024×1024 JPEG, 300–620 KB each) → `site/vision/*.png` + `manifest.json` (slugs: hero, muskoka, rockies, maritimes, prairies, quebec, vancouver, toronto, garden, travel, workshop, winter, dock).
- Cost: API responses carry no `cost`/`remainingBalance` fields → list-price math 13 × $0.0119 = **$0.155** (cap $2; abort conditions unenforceable, documented). Log: `.fetch/vision_log.jsonl`.
## Stage 3 — UI ✅
- `site/index.html` (marker `<!-- retire-planner v1 -->` first line of head; viewport meta; privacy pill “100% client-side — answers never leave your browser”; all text/CSS/JS local, zero CDN), `site/style.css` (mobile-first light theme, sticky panel → mobile bottom-sheet), `site/app.js` (vanilla, no framework).
- 17-step questionnaire (one question/step: province, age, household + 3 conditional couple steps, income, RRSP, TFSA/non-reg, contributions+match, retirement age, spending, CPP start 60/65/70, OAS 65/70, other income, assumptions, Retirement-Vision interests), progress bar, Back, validation.
- Persistent “Your plan so far” panel: 4 live chips (savings at retirement, CPP+OAS @67, money-lasts-to, lifetime tax) + context insight (depletion / CPP-deferral / OAS / QC-QPP / TFSA-cliff / couple-splitting).
- 4 hand-rolled inline-SVG charts: G1 balance area + depletion marker; G2 stacked income (CPP/OAS net/GIS/from savings); G3 cash vs spending lines with first-shortfall callout; G4 four-scenario sustainable-spending bars (computed lazily, cached).
- Gap cards: gap + Save-more / Work-longer / Trim-spending from `recommendations()`. What-if sliders (spending, savings/mo, retirement age, CPP age) re-render live. Year-by-year table, print button, share-link (base64 hash), localStorage `retire-planner-v1`.
- Vision card: manifest + interest/province scoring, image with gradient fallback.
- `tests/ui.smoke.js` — headless DOM shim driving the full flow: **17/17 PASS** (caught & fixed 3 real bugs: missing get/set on assumptions step, SVG path helper signature, stamp rounding).
- Served smoke: python http.server 8099 → /, css, js, manifest, png all 200.