Skip to main content

Finance dashboard paycheck build

Date: 2026-10-03. The approved integration of the stalled jknash/personal-dashboard finance features into the live workbench finance page is built and deployed. It follows the recommendations report published the same day (docs/02-research/2026-10-03-finance-dashboard-recommendations.md) and the owner's Phase 0 decisions recorded in the budget-dashboard goal. Commit 94cefa5 on jknash/dashboard; Cloudflare deployment 3ae39ed5-7344-4477-8986-bd0c91b6611c; verified from outside (landing 200, /finance/ and its data file behind the Access 302, production and preview).

What was built, by phase​

Phase 1 — Registry, allocator, paycheck cards. A curated registry (scripts/bill_registry.json, 27 bills) is now the plan of record. The project's bill-to-paycheck allocator was ported into scripts/build_finance_snapshot.py (integer cents; bills assigned whole to the latest paycheck on or before the assignment date with room, falling back to earlier paychecks; shortfalls reported as unallocated, never overdrawn), with the design patches: priority class as secondary sort (housing, debt, essential, subscription), assignment from the due-window start, and occurrence generation that supports several occurrences per month. The snapshot gained a pay_cycles block (7 cycles: the current one plus three months of forecast paychecks) and the page renders paycheck cards with stacked allocation bars and per-cycle bill tables.

Phase 2 — Right-now position and forecast. Actuals matching pairs transactions to occurrences (merchant tokens, amount tolerance, date window; posted beats pending), producing paid / pending / upcoming / overdue statuses with actual amounts and paid dates. safe_to_spend is computed by the generator: Chase checking available, minus the current cycle's unpaid allocations (including bills carried in from the previous paycheck), minus the remaining grocery/dining allowance reserve, minus the $250 buffer. A 90-day forecast event series (paychecks in, registry bills out) ships with its lowest point (the trough); on 2026-10-03 the trough is $1,947.73 on 2026-10-15, and safe-to-spend is $517.73.

Phase 3 — Depth. The safe-to-spend hero sits at the top with an expandable formula. The cash-flow river is an inline SVG stepped area chart with the trough marked and paychecks dotted. Drilling down runs: paycheck card, then its bill table, then any bill's expected-versus-actual history, and category bars expand into their transactions (the snapshot now ships the full 120-day window, 668 transactions). A next-30-days bill strip and a credit-card utilization ring round it out. The old Plaid streams table is retitled "Detected by Plaid" so it is never confused with the registry view.

Phase 4 — Polish. The generator appends headline numbers (date, net position, Chase available, current-cycle leftover) to scripts/finance_history.json, deduped by date, and the page shows a net-position sparkline once two or more days exist. Non-monthly bills can be leveled through an "Irregulars sinking fund" per-paycheck set-aside (annual amount divided by 26); today it covers the $35 annual Dmb charge at $1.35 per paycheck, and the line appears only when nonzero. Due-date smoothing suggestions are emitted when a forecast cycle's bills exceed about 60 percent of its paycheck while an adjacent cycle is under about 30 percent (on 2026-10-03: consider moving Apple One Premier from the Oct 30 paycheck to the Oct 16 one). Suggestions only; nothing moves automatically.

The registry: format and how to edit it​

scripts/bill_registry.json holds settings plus one entry per bill.

Settings a future agent may need to change:

  • settings.paycheck: the Insperi anchor date, amount ($5,133.62), and 14-day interval. Change the anchor only if the pay cadence itself changes.
  • settings.buffer: the safe-to-spend buffer in dollars ($250).
  • settings.allowances: the fixed per-paycheck lines. Each has a key, label, amount, the Plaid detailed categories it counts, and a derivation note. Current values: Groceries $435, Dining and coffee $685, set on 2026-10-03 as the medians of the eight complete biweekly periods from 2026-06-12 to 2026-10-01 in the 120-day pull (posted transactions only, rounded to the nearest $5). Plaid's OTHER_FOOD_AND_DRINK bucket is counted as dining because in this data it is overwhelmingly coffee shops and concessions.
  • settings.income_extras: the $100 monthly X-Centric Zelle, assigned to the cycle whose period contains the 15th.
  • settings.passthrough_income: the Cudahy school-district stream. It is displayed in income only and never counted toward allocation or safe-to-spend, because the money lands in SoFi and is swept to Apple Cash.

A bill entry carries: id, name, amount (dollars), schedule, priority, autopay, paid_via, account (the Plaid account name the charge posts to), match_tokens, optional match_all_tokens, amount_tolerance, match_window_days, notes, and optional start_date, end_date, final_amount, and sinking_fund.

Schedules: monthly takes a day (plus optional window_days, the days before the due day the charge window opens; assignment uses the window's first day). Biweekly takes an anchor payment date and an end. Annual takes month and day. Two entries bend the plain amount rule: the Chase card minimum uses amount_source and day_source set to the live liability read (minimum payment and statement due day), falling back to $40 on the 1st; every Affirm plan is its own entry with its exact amount, due day, end date, and final-payment amount from the email receipts.

To change a bill (amount, due day, or add/remove one): edit the registry, regenerate, commit, deploy. That is the whole procedure; there is no other configuration.

Allocator and matching rules, precisely​

  • Money is integer cents everywhere in the planning model. A bill is never split across paychecks, and a paycheck's projected remaining is never negative; what cannot be placed is listed in planning.unallocated.
  • Bills sort by assignment date, then priority rank (housing 0, debt 1, essential 2, subscription 3), then registry order.
  • Matching consumes each transaction at most once, in due-date order. A match needs: an outflow on the bill's account, the token rule satisfied (any token appears in the transaction name or merchant, unless match_all_tokens is set, as for the Zelle to Michele), the amount within amount_tolerance of the expected amount, and the date within match_window_days of the due date. Amount-discriminated families (Affirm plans, the four Apple subscriptions, the two Backblaze subscriptions) rely on tight tolerances plus due-date proximity; the Affirm Fleece $28.58 can therefore never match a Netflix $28.58 charge, because the tokens differ.
  • Unpaid bills the allocator assigns to the previous paycheck are carried into the current cycle (flagged "carried in") only when due within the last 35 days; the forecast uses the same 35-day horizon for overdue items. Older unmatched occurrences stay visible in bill history but do not distort current money math: they are usually matching misses or were settled another way.
  • The forecast starts at the Chase available balance (which already includes a pending payday deposit) and applies events strictly after today, so nothing is double-counted.

Data freshness, and the fallback that protects the page​

The 2026-10-03 build used a completely fresh pull: accounts, recurring streams, liabilities, and a 120-day transactions read (668 transactions, both institutions, Chase plus SoFi). Two read quirks worth knowing:

  • With two institutions linked, the transactions result arrives in the file named by the read's output_file, wrapped in the same envelope as stdout; the payload lives under its body key. A reader that looks for transactions at the top level will see zero and wrongly conclude the read failed.
  • The liabilities read returns the Chase card while flagging the aggregate as incomplete, because SoFi has no liability accounts. The body data is usable; the flag is about coverage, not corruption.

Rules for future regenerations:

  1. Never overwrite the live snapshot with a degraded pull (zero or near-zero transactions, missing liabilities). If live reads look empty, verify against the envelope/output_file shape above before concluding anything.
  2. If transaction reads are genuinely unavailable, reuse the most recent complete transactions dump on disk for the transaction-derived sections and compute the planning blocks from the fresh accounts, streams, and registry; record the path taken in the snapshot's planning.data_source note.
  3. SoFi accounts must appear in the accounts section; their absence means the accounts read was partial.

Regenerating safely (the short procedure)​

  1. Pull, in order: accounts, transactions-recurring, liabilities, and transactions-get over a 120-day window ending today. Keep the dump files; the transactions payload is the output_file the read names.
  2. Run python3 scripts/build_finance_snapshot.py with the four dumps and --out site/finance/data/finance.json; pass --source-note describing the pull (or the fallback, if used).
  3. Run the allocator tests: python3 -m unittest discover -s scripts/tests (14 tests: the original project's key cases plus the workbench patches).
  4. Check the run's printed summary: cycles present, unallocated count understood, safe-to-spend and trough sane.
  5. Commit every changed file (generator, registry, history, tests, page, snapshot) to jknash/dashboard and deploy with the Cloudflare tool; verify from outside that the landing page is 200 and the finance page and its data file answer with the Access redirect.

Judgment calls recorded during the build​

  • Upgrade loan due day is the 25th (postings Aug 25 and Sep 25), not the "about the 1st" in the recommendations report.
  • Homeowners insurance is registered once at the confirmed $13.84 (due about the 28th) and the second Plaid stream (about $13.31 around the 11th) is suppressed, per the report. The transaction data shows both amounts posting every month from July through September, so they may in fact be two real charges totaling $27.15 per month; flagged for the owner.
  • Zelle history before October does not align to the new $2,000-around- the-1st rule (July and August payments landed late in their months); history matching is therefore approximate for that bill, by design.
  • The Dec 25 cycle projects a negative leftover (about −$184) once the fixed allowances are reserved; that is the zero-based model telling the truth about the late-December cluster (Zelle, Flex installment 1, and the first-of-month debts together), not a bug.

Published by Muse · 2026-10-03.