Skip to content

heezy-finance Roadmap

Phase 1 — Fix the Data ✅ Complete (2026-06-18)

Item Status
receipts.date_ts migration + backfill
merchant_normalized on receipts
category + merchant_normalized on bank_transactions
Bank transactions integrated into dashboard
Gas spend fixed: $140.52 (was $36)
Bank source filter + 🏦 badge in Purchases
Bank page with category cards + monthly chart
BoA parser: CC detection
Cap1 parser: fixed stmt_id bug
Statement NFS ingest pipeline (end-to-end)
Ingest pipeline: auto-category + merchant on insert
docs.heezy.info live on Cloudflare Pages

Phase 2 — Complete Bank Coverage ✅ Complete (2026-06-20)

Item Status Notes
Bank page deployment Category breakdown, monthly trends, top merchants
Bank integration into all views Overview, Purchases, Categories all include bank data
payment_type on receipts Dropdown UI added: cash / credit / debit / check / other
Purchases table sortable Date, Source, Vendor, Item, Total, Category columns sortable
Purchases date defaults Start Date = 1st of month, End Date = today
Rule-based bank categorization Ollama too slow on CPU; keyword rules working well
More BoA monthly statements Jan–Jul 2026 checking loaded
CC statements (BoA Visa, Cap1, Chase) BoA CC 14 statements, Chase CC 14 back to Oct 2025
Historical statement backfill 🔲 Nothing pre-Oct 2025 loaded on any account
Blocked: June 2026 statements Loaded

Gmail auto-fetch is not viable

BoA and Capital One send notification-only emails with no PDF attachments. Manual workflow: download from bank portal → drop to /nfs/heezy/ingest/raw/statements/new/ → picked up within 5 min.

CI/CD Lesson: Push to Gitea, Not GitHub

Issue: Pushing to GitHub origin instead of Gitea canonical repo breaks deployments.

Root Cause: GitHub is a mirrored backup only; act-runner watches Gitea. Pushing to origin (GitHub) doesn't trigger the pipeline.

Fix: Always push to Gitea remote first, then origin.

# ❌ Wrong — GitHub doesn't trigger act-runner
git push origin main

# ✅ Correct — Push to Gitea first, then GitHub
git push gitea main
git push origin main

Deployment Pipeline: 1. Push → Gitea main 2. Gitea webhook → act-runner triggers .gitea/workflows/deploy-heezy-finance.yml 3. Workflow builds Docker image, pushes to ECR 4. Workflow writes .deploy-trigger file to heezy-k8s repo 5. heezy-k8s auto-deploy workflow: kubectl apply -k, rollout restart, rollout status 6. Pod updates with new image

See CI/CD for full details.

Phase 3 — Global Date Range + Lookback ✅ Complete (2026-06-20)

Item Status Notes
Global date range selector Utilities/Mortgage historical lookback working
Utilities category drill-down Monthly breakdown with per-vendor trends
Mortgage payment tracking Historical payment records by date range
Category drill-down modal Click category cards to view monthly detail
Feature verification All 4 phases verified live on finance.heezy.info

Phase 4 — Hockey Holdem Receipt ✅ Complete (2026-06-20)

Item Status Notes
Receipt categorization Hockey Holdem vendor recognized + categorized
Line item detail modal Click receipt to view itemized breakdown
Payment type tracking Cash, credit, debit payment types recorded
Full integration Receipt appears in all views (Overview, Purchases, Hockey category)

Phase 5 — Parser Fixes & Account Coverage ✅ Complete (2026-07-07)

Item Status Notes
Fix Cap1 false-detected as EDJ Cap1 PDFs contain "EDWARD JONES" in txn descriptions; moved Cap1 check before EDJ in detect_bank()
Fix Chase Amazon closing date Opening/Closing Date X - Y regex now captures second (closing) date
Add missing accounts (BoA 7503, Chase 2609) Both inserted with display_group='debt'
Clean up bad statement rows from failed uploads Deleted edjbrok0000_2026-06-30, chasecrd2609_2026-05-20, stale account_balances row
Fix boacrdNone ghost rows Guard added in scan_statements.py: skip insert_bank_statement when account_last4=None and type is credit_card. 12 existing rows deleted. Now 0

Phase 6 — Infrastructure Resilience ✅ Mostly Complete

Focus: Remove node affinity pinning, fix PVC pattern for multi-node resilience.

Item Status Notes
Remove nodeSelector / nodeAffinity Gone from the deployment and from heezy-statement-scanner / heezy-budget-alerts. The pod currently runs on nebula-4, having moved off nebula-5 on its own
Implement & test failover Verified by real reschedules across nodes
Audit PVC necessity 🔲 /data is still mounted but app state lives in Postgres. Unclear the volume is needed at all
Design storage solution 🔲 PVC is still 2Gi RWO Longhorn. Strategy is Recreate to avoid a multi-attach deadlock, which costs a few seconds of downtime per deploy

Phase 7 — Reconciliation ✅ Complete

Shipped inside the dashboard, not as the separate heezy-finance-reconcile service originally sketched. A dedicated service was not worth the operational cost for matching that runs in milliseconds at request time.

Item Status Notes
Reconcile page /reconcile, see Reconcile Tab
Receipt to transaction matching Exact (date, amount) key, done in Python at request time
Manual link and override POST /api/reconcile/link, receipts.bank_transaction_id
Inline category reassignment POST /api/reconcile/update-category, feeds spending and budget analytics
Acknowledge / hide rows bank_transactions.reconcile_acknowledged
Orphaned receipt surfacing Receipts in range with no matching transaction
Fuzzy matching (date ±2 days, amount within 1%, merchant similarity) 🔲 Match is still exact-only. Confidence scoring never built
Order to transaction matching 🔲 orders.bank_transaction_id exists but nothing populates it

Phase 8 — Budgeting & Targets ✅ Complete

Item Status Notes
Monthly per-category budgets spend_budgets, Budget page
Annual per-category budgets annual_budgets, keyed on (category, year), for lumpy categories
Fixed vs variable cost split FIXED_COST_CATS in categories.py
Daily Discord budget alerts heezy-budget-alerts CronJob, 14:00 UTC
Financial targets page Emergency fund, 529s, investments, mortgage payoff, retirement/FIRE. See Targets Tab
Mortgage statement ingest mortgage_statements table, 9 statements

Phase 9 — Classification Quality ✅ Complete

Item Status Notes
Taxonomy moved to categories.py app.py and item_classifier.py share one list
Rules → cache → LLM pipeline item_category_cache, 135 entries. LLM is last resort, answers are stable across re-runs
real to numeric migration Every money column is numeric
Merchant rules split one per chain Was rendering every Meijer, Costco and Aldi debit as a vendor named "grocery"

Open Work

Item Priority Notes
Backfill 12 receipts with NULL date_ts Medium They are invisible to every date-filtered query
Surface the silent balance-skip Medium upsert_account_balance() logs WARN when no account matches; the upload response says nothing
Add liveness / readiness probes Medium A pod that cannot reach Postgres still reports Running
Fuzzy reconcile matching Low Exact (date, amount) misses pending-vs-posted date drift
FK from statements to accounts Low Still matched by institution + last4 + type string
Normalize bank_statements.statement_period_start/end Low Raw PDF text
Capital One and Edward Jones backfill Low Cap1 stops at 2026-06, EDJ starts at 2026-05

Current DB State

Counts as of 2026-08-11.

Table Count Notes
orders 284 25 distinct vendors, 7 without a grand_total
order_items 367 Rule, cache, or LLM categorized
receipts 155 12 without date_ts
receipt_items 821
bank_statements 80 BoA, Cap1, Chase, EDJ, one manual Venmo row
bank_transactions 1,582 Dashboard shows debits only
mortgage_statements 9 Fifth Third
accounts 27 17 active
account_balances 84
spend_budgets / annual_budgets 13 / 5
item_category_cache 135

Last updated: 2026-08-11