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.pyclaims the token (POST to the claim URL) -> receives the long-lived access URL -> writesSIMPLEFIN_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.pymints 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, createsdata/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 intobalances. 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_valueswithas_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)"printsTrue. - 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 fromforge_finance_networth. - Verification:
curl -fsS http://127.0.0.1:8096/ | grep -i "net worth"returns the summary block;systemctl --user status forge-finances.serviceshows 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"; doneprints 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_snapshotskeyed 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_snapshotsand seedaccounts/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 -40shows the parsed mapping for Justin's review; after approval,--committhen 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 --oncedaily (07:00 CT). Fail-loud: notify on sync failure (SimpleFIN down / auth dead). - Verification:
systemctl --user list-timers | grep forge-finance-syncshows 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;recurringtable 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 toolstool_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, runbash 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.shpasses;grep -i finances MEMORY.mdshows 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]