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 akey,label,amount, the Plaid detailed categories it counts, and aderivationnote. 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_tokensis set, as for the Zelle to Michele), the amount withinamount_toleranceof the expected amount, and the date withinmatch_window_daysof 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 itsbodykey. 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:
- 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_fileshape above before concluding anything. - 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_sourcenote. - SoFi accounts must appear in the accounts section; their absence means the accounts read was partial.
Regenerating safely (the short procedure)
- 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_filethe read names. - Run
python3 scripts/build_finance_snapshot.pywith the four dumps and--out site/finance/data/finance.json; pass--source-notedescribing the pull (or the fallback, if used). - Run the allocator tests:
python3 -m unittest discover -s scripts/tests(14 tests: the original project's key cases plus the workbench patches). - Check the run's printed summary: cycles present, unallocated count understood, safe-to-spend and trough sane.
- Commit every changed file (generator, registry, history, tests, page,
snapshot) to
jknash/dashboardand 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.