Skip to content

Plan: finances.justinsforge.com, household + business finance hub

Date: 2026-06-08 Approved spec: A phased finance hub at finances.justinsforge.com (behind Cloudflare Access, home-button pill). Forge sqlite DB is source of truth; a Google Sheet is auto-maintained as the enduring human-readable mirror (new sheet seeded from the existing "Wieb Spending Plan", original left intact). SimpleFIN Bridge pulls bank/card balances + transactions read-only; a manual-entry layer holds illiquid assets/liabilities (house, cars, household, inventory, A/R, private loans, non-linkable investments) so total net worth matches the spreadsheet (~$600k). Accounts labeled owner (Justin/Krystal/Joint) + scope (personal or one of SIP/GUS/WIEB/NOVA/JWVR) + source (auto/manual). Pages: Accounts, Transactions, Recurring, Monthly breakdown, Net Worth (personal + per-business + total), Weekly snapshot/history chart. Coordinator bot both queries and edits the record by natural language. ERPNext stays the books/tax system of record; this hub is net-worth/cash/subscription visibility over the same 5 entities. Out of scope (this round): double-entry accounting (ERPNext owns that), bill pay / account interaction (read-only only), tax filing, investment trading, Krystal's own login (view-access decision deferred), mobile-native app.

Phasing

  • Phase 1 (foundation): SimpleFIN sync + labeled accounts + transactions + manual-asset layer + net worth + website skeleton + weekly snapshot + Google-Sheet mirror + history import. Replaces the Wieb Spending Plan.
  • Phase 2: recurring/subscription detection + monthly breakdown + alerts + coordinator bot query/edit tools. Replaces Rocket Money.
  • Phase 3: UI polish, charts, Krystal sharing.

Phase 2 and 3 task lists are outlined here and will be expanded to full granular tasks when Phase 1 lands (Justin will adjust as he sees it).


Phase 0: Prerequisites (Justin actions + auth)

Task 1: SimpleFIN Bridge signup + token claim

  • Files: ~/.forge-secrets/simplefin.env (chmod 600), scripts/forge_simplefin_client.py
  • What: Justin signs up at bridge.simplefin.org, links all personal + business bank/card accounts, generates a one-time setup token. forge_simplefin_client.py claims the token (POST to the claim URL) -> receives the long-lived access URL -> writes SIMPLEFIN_ACCESS_URL=... to the env file. Token is single-use; claim immediately.
  • Verification: python3 -c "import sys; sys.path.insert(0,'scripts'); from forge_simplefin_client import SimpleFIN; print('accounts:', len(SimpleFIN().accounts(with_transactions=False)))" prints a non-zero account count.
  • Commit: finance: SimpleFIN client + token claim

Task 2: Service account for Google Sheets/Calendar write-back

  • Files: ~/.forge-secrets/google-sa.json (chmod 600), scripts/forge_google_sa.py
  • What: Create service account [email protected] via gcloud, download JSON key, enable Sheets + Drive APIs on the project. forge_google_sa.py mints access tokens from the key (google-auth). This SA also unblocks the dead calendar OAuth (separate fix, same key). Justin shares the target sheet(s) with the SA email.
  • Verification: python3 -c "import sys; sys.path.insert(0,'scripts'); from forge_google_sa import sa_token; print('token len', len(sa_token(['https://www.googleapis.com/auth/spreadsheets'])))" prints a positive length.
  • Commit: finance: forge-personal-assistant service account for Google APIs

Phase 1: Foundation

Task 3: Finance DB schema

  • Files: scripts/forge_finance_db.py, creates data/finance/finance.db
  • What: sqlite schema + connection helper + idempotent upserts. Tables: accounts (id, name, last4, institution, owner, scope, source, kind[asset/liability], include_in_nw, simplefin_id, active), transactions (id, account_id, posted_at, amount, description, merchant_norm, simplefin_txn_id UNIQUE, category), balances (account_id, as_of, balance, source), manual_values (account_id, as_of, value), weekly_snapshots (week_of, account_id, balance, UNIQUE(week_of, account_id)).
  • Verification: python3 -c "import sys; sys.path.insert(0,'scripts'); import forge_finance_db as d; d.init(); print(sorted(d.tables()))" lists all five tables.
  • Commit: finance: sqlite schema + db helpers

Task 4: SimpleFIN sync into DB

  • Files: scripts/forge_finance_sync.py
  • What: Pull accounts + transactions from SimpleFIN, upsert accounts (new ones default scope=personal, source=simplefin, kind inferred from balance/type), upsert transactions idempotent on simplefin_txn_id, record current balance into balances. Re-runnable; same data twice = same end state.
  • Verification: python3 scripts/forge_finance_sync.py --once && python3 -c "import sys; sys.path.insert(0,'scripts'); import forge_finance_db as d; print('txns', d.count('transactions'), 'accts', d.count('accounts'))" prints non-zero counts; running twice does not duplicate rows.
  • Commit: finance: SimpleFIN -> DB sync (idempotent)

Task 5: Manual asset/liability layer + account labeling CLI

  • Files: scripts/forge_finance_accounts.py
  • What: CLI to add/edit manual accounts (house, cars, inventory, A/R, private loans, non-linkable investments) and to label any account's owner/scope/kind/include_in_nw. Manual values stored in manual_values with as_of. Seed the known manual items from the Wieb Spending Plan.
  • Verification: python3 scripts/forge_finance_accounts.py add-manual --name "4606 Franklin Park Home" --kind asset --scope personal --owner Joint --value 378000 && python3 scripts/forge_finance_accounts.py list | grep -i franklin
  • Commit: finance: manual asset/liability layer + labeling CLI

Task 6: Net worth engine

  • Files: scripts/forge_finance_networth.py
  • What: Compute net worth from latest balances + latest manual values: total assets, total liabilities, net worth, broken out personal + per-business entity (SIP/GUS/WIEB/NOVA/JWVR) + grand total. Matches the sheet's rollups.
  • Verification: python3 scripts/forge_finance_networth.py --json | python3 -c "import sys,json; d=json.load(sys.stdin); print('total_nw' in d and 'by_scope' in d)" prints True.
  • Commit: finance: net worth rollup engine

Task 7: Website skeleton (service + tunnel + access)

  • Files: sites/finances/app.py, sites/finances/templates/, ~/.config/systemd/user/forge-finances.service
  • What: Flask/FastAPI app on Console:8096 matching the forge-tasks-checklist.service pattern. CF tunnel hostname finances.justinsforge.com + Cloudflare Access. Home-button pill ("<- JustinsForge.com"). Landing route renders net-worth summary from forge_finance_networth.
  • Verification: curl -fsS http://127.0.0.1:8096/ | grep -i "net worth" returns the summary block; systemctl --user status forge-finances.service shows active.
  • Commit: finance: dashboard service + tunnel + CF Access

Task 8: Accounts + Transactions + Net Worth pages

  • Files: sites/finances/app.py, sites/finances/templates/accounts.html, transactions.html, networth.html
  • What: Accounts page (grouped by personal/business entity, auto vs manual, include-in-NW toggle reads from DB). Transactions page (searchable feed, filter by account/scope). Net Worth page (personal + per-business + total, assets/liabilities).
  • Verification: for p in accounts transactions networth; do curl -fsS http://127.0.0.1:8096/$p >/dev/null && echo "$p ok"; done prints all three ok.
  • Commit: finance: accounts, transactions, net worth pages

Task 9: Weekly snapshot job

  • Files: scripts/forge_finance_snapshot.py, ~/.config/systemd/user/forge-finance-snapshot.service, forge-finance-snapshot.timer
  • What: Write each account's current balance (auto + manual) into weekly_snapshots keyed by week_of (idempotent). Timer fires weekly (Sun 18:00 CT). Continues the enduring weekly series.
  • Verification: python3 scripts/forge_finance_snapshot.py --now && python3 -c "import sys; sys.path.insert(0,'scripts'); import forge_finance_db as d; print('snap rows', d.count('weekly_snapshots'))" prints non-zero; re-run same week does not duplicate.
  • Commit: finance: weekly snapshot job + timer

Task 10: Google Sheet enduring mirror

  • Files: scripts/forge_finance_sheet_mirror.py
  • What: Using the service account (Task 2), write the latest weekly snapshot into a NEW Google Sheet "Wieb Finances (auto)" (created once, shared with Justin + Krystal). One column per week, rows per account, mirroring the Wieb Spending Plan layout. Called at end of the weekly snapshot job. Original sheet untouched.
  • Verification: python3 scripts/forge_finance_sheet_mirror.py --push-latest && echo "check the 'Wieb Finances (auto)' sheet has this week's column" (manual visual confirm of the new column).
  • Commit: finance: Google Sheet enduring mirror via service account

Task 11: History import from Wieb Spending Plan (collaborative)

  • Files: scripts/forge_finance_import_history.py
  • What: Parse the existing "Wieb Spending Plan" sheet (id 1ZN3uE67btKGh2gR53vR43MZNeo7hsMIinT_whOX529g), map each weekly column + account row into weekly_snapshots and seed accounts/manual_values. Dry-run first; Justin reviews the account-name -> scope mapping before committing the import. Idempotent on (week_of, account_id).
  • Verification: python3 scripts/forge_finance_import_history.py --dry-run | head -40 shows the parsed mapping for Justin's review; after approval, --commit then net-worth history chart populates.
  • Commit: finance: import Wieb Spending Plan weekly history

Task 12: Net-worth history chart

  • Files: sites/finances/app.py, sites/finances/templates/networth.html
  • What: Render a net-worth-over-time line chart from weekly_snapshots (total + per-scope toggles) on the Net Worth page.
  • Verification: curl -fsS http://127.0.0.1:8096/networth | grep -i "chart" returns the chart container; visual confirm the line spans imported history.
  • Commit: finance: net worth history chart

Task 13: Daily sync timer

  • Files: ~/.config/systemd/user/forge-finance-sync.service, forge-finance-sync.timer
  • What: Run forge_finance_sync.py --once daily (07:00 CT). Fail-loud: notify on sync failure (SimpleFIN down / auth dead).
  • Verification: systemctl --user list-timers | grep forge-finance-sync shows the scheduled timer.
  • Commit: finance: daily SimpleFIN sync timer

Phase 2: Recurring + monthly + bot (outline, expand after Phase 1)

  • Recurring-detection engine (forge_finance_recurring.py): normalize merchants, detect weekly/monthly/annual cadence, flag subscriptions, detect new + amount-changed; recurring table with status detected/confirmed/ignored.
  • Recurring page + confirm/dismiss/manual-add UI.
  • Monthly breakdown page (spend by month/category/entity).
  • Notify on newly-detected recurring charge (forge notify -> coordinator).
  • Coordinator bot tools: tool_finance_networth, tool_finance_recurring, tool_finance_balance, plus edit tools tool_finance_update_manual_value, tool_finance_add_account (natural-language record editing).
  • Tile on justinsforge.com home landing.

Phase 3: Polish (outline)

  • Category rules + merchant cleanup, charts per page, mobile layout, Krystal view-access via CF Access policy, export.

Final task (run at end of Phase 1)

Task 14: Register and document

  • Files: MEMORY.md (index entry), memory/general/reference_finances_app.md, run bash scripts/forge_eval_run.sh
  • What: Topic file documenting the DB schema, scripts, service, secrets, SimpleFIN + SA auth, and the Sheet mirror. Add MEMORY.md index line. Run eval harness. Add a tile to the justinsforge.com home landing.
  • Verification: bash scripts/forge_eval_run.sh passes; grep -i finances MEMORY.md shows the index entry.
  • Commit: finance: register + document finances.justinsforge.com

Open threads to resolve during build

  • Justin's cut-off "maybe even allow..." (mobile entry? alerts? Krystal login?) -> capture when he clarifies.
  • Krystal dashboard access: default you-only via CF Access for now; add her policy in Phase 3.
  • SimpleFIN coverage gaps: any institution SimpleFIN can't link becomes a manual account.

[Claude Code, 2026-06-08]