From ad5026b3eb47e85f3b09c9f032c3ce355f91785f Mon Sep 17 00:00:00 2001 From: archeious Date: Fri, 17 Apr 2026 17:57:46 -0600 Subject: [PATCH] =?UTF-8?q?retro:=20Session=201=20=E2=80=94=20greenfield?= =?UTF-8?q?=20to=20shipped=20posting=20ledger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Session1.md | 189 +++++++++++++++++++++++++++++++++++++++ SessionRetrospectives.md | 5 ++ 2 files changed, 194 insertions(+) create mode 100644 Session1.md create mode 100644 SessionRetrospectives.md diff --git a/Session1.md b/Session1.md new file mode 100644 index 0000000..d95b372 --- /dev/null +++ b/Session1.md @@ -0,0 +1,189 @@ +# Session 1 Notes — 2026-04-17 + +## What We Set Out to Do + +Greenfield: build a zero-based household budget tracker in Python + SQLite. +Start with a minimal scaffold, then iterate based on what the app needed +to actually be useful for zero-based budgeting. + +## What Actually Happened + +One long session, ten PRs merged, one name picked early (quartermaster, +fits the marchwarden / harbormind role-based naming convention in the +user's other repos). The arc: + +1. **#1 scaffold** — FastAPI + Jinja + HTMX + SQLAlchemy + Alembic + uv. + Budget config page with seven sections, Primary Debt Target as a + singleton pointer. +2. **#3 monthly view** — snapshot-from-config, deviation tags + (unchanged / edited / new_in_month), per-month debt target. Applied + column stored directly on `month_entry`. +3. **#5 backups** — `scripts/backup-db.sh` using `sqlite3.Connection.backup`, + alembic env.py hook, memorialised in a project-level `CLAUDE.md`. + This was *reactive*: I had wiped his live DB several times mid-session + as dev churn, and a single question from him ("What happened to the + previous data?") surfaced the incident. The backup script is the + fix that should have existed from the start. +4. **#7 zero-amount widget** — hero at the top of each page, + income minus all other sections, colour-tone by state. +5. **#9 wiki gitignore** — small chore after the initial wiki clone. +6. **#11 groups + Sinking Funds** — four display groups (Income, + Committed, Savings, Flexible) with collapsible `
` headers. + New `sinking_fund` section for emergency fund and other goal-funded + categories. No schema migration needed (Section enum has no + DB-level CHECK constraint). +7. **#13 notes field** — optional free-text per entry, copied through + the snapshot. Notes changes don't flip deviation state. +8. **#15 month lifecycle** — three explicit states (Planning / Active / + Closed) with a balance gate: close requires applied zero == $0.00. + Reopen allowed. Nothing sweeps automatically — the Primary Debt + Target is a hint, user manually moves applied to close. +9. **#17 UI redesign** — Barlow Condensed everywhere, warm cream paper, + burgundy accent sampled from the logo shield, logo anchors the zero + hero. Three mockups iterated first (`docs/mockups/`): loose + editorial (Fraunces), dense editorial, dense condensed-sans. The + last one shipped. +10. **#19 posting ledger** — `applied` dropped as a column; each + month_entry owns a ledger of `Posting` rows (date, amount, + optional description and payee). Applied is now a derived + `@property`. Migration seeds one opening-balance posting per + existing non-zero applied. Schema term `posting` chosen over + `transaction` or `month_entry_transaction`. + +Plus three small UX fixes on the ledger branch before merge: the txn +count was wrapping in a too-narrow cell; empty notes were hover-hidden +and couldn't be clicked through the hover gap; the full-width add form +was horizontally noisy, now collapsed behind a `+ add
` +disclosure. + +## Key Decisions & Reasoning + +* **Snapshot over mirror for months.** Month entries carry + `origin_name` / `origin_planned` at creation, and their own editable + `name` / `planned` afterwards. Deviation tags compare current vs + origin. This keeps month pages self-contained when the budget + changes later. +* **Primary Debt Target is a pointer, not a budgeted amount.** Jeff + runs avalanche (highest-interest first). Non-target debts get + minimums; target gets minimum plus whatever's leftover. The "leftover" + is *not* pre-allocated — it's whatever the user manually moves to + target's applied at close time. This shaped the close gate: balance + at $0 applied is the user's signal that they've done the allocation + by hand. +* **No auto-sweep on close.** I proposed automatic sweep of unassigned + applied to the target. He rejected: "The month cannot close until it + balances at 0. Target is just where it should go." Cleaner semantics; + forces the user to own the allocation. +* **UI direction chosen after three mockups.** Editorial ledger with + Fraunces felt too magazine-y at the density of a real working + ledger. Condensed-sans (Barlow Condensed) with the logo in the zero + hero had the right workaday-but-distinct feel. Fraunces is preserved + in mockup history for future explorations. +* **`posting` over `transaction`.** Transaction is a SQL reserved word + in some dialects and clashes with SQLAlchemy's own + `Session.transaction` concept. `posting` is accounting-correct (a + debit or credit posted to a ledger) and avoids every collision. + UI-facing language still says "transactions"; the split is + intentional. +* **Applied derived only.** No column, no setter, no click-to-edit on + applied. Users manage applied by adding / editing / deleting + postings. Makes the ledger the single source of truth. +* **Opening-balance migration.** Existing applied values become one + posting per non-zero entry with `description = "opening balance"`, + dated to `activated_at` or `created_at`. Preserves every pre-ledger + total cleanly; downgrade restores via `SUM`. + +## Surprises & Discoveries + +* **I wiped the live DB four times.** Twice during alembic autogen + (regenerating schema), twice during smoke tests. He caught it with a + single question. Saved a memory about never treating `quartermaster.db` + as dev scratch. The backup script now exists partly because of this. +* **Adding `sinking_fund` to the Section enum required no DDL.** The + `section VARCHAR(32)` column has no CHECK constraint in the actual + SQL schema, so new enum values are a pure Python change. Alembic's + autogenerate produced an empty migration; I deleted it. +* **Classic hover-gap bug on empty notes.** Hiding empty notes rows + and revealing on `:hover` of the adjacent entry row means moving the + cursor toward the notes row leaves the entry row and hides the + notes again. Visible only in live use; tests wouldn't have caught it. +* **The posting table name discussion.** I proposed + `month_entry_transaction`; he pushed back ("a bit verbose"). Short + negotiation landed on `posting` (my pick), which also fit the + editorial accounting aesthetic. +* **The em-dash rule.** I kept writing em-dashes in chat output + despite his global CLAUDE.md rule. I caught and fixed a few cases + in committed code (mockup templates) but I'm sure I still slipped + in chat. Worth keeping on my radar. + +## Concerns & Open Threads + +* **No browser verification.** Every smoke test was `curl`. The + inline-edit flows, HTMX partial swaps, and `
` expand/collapse + interactions were verified only at the HTML structure level. The + UX could have hover-gap bugs, focus-management issues, or layout + problems that show up only on a real browser. Jeff caught one of + these (empty notes) within minutes of first use; there are probably + more. +* **Over-budget isn't visually distinct.** The entry progress bar + caps at 100% but no longer shows the overflow nub past the 100% + line that the early mockups had. Mental note for a follow-up. +* **Budget-side inline edit for name / amount.** Currently requires + delete-and-recreate to change a budget entry's name or amount. Notes + are editable. Parity would match the month page's inline edit. + Deferred. +* **Posting dates are free-form.** Nothing stops a posting dated May 3 + from sitting on April's entry. Most users would expect the + constraint; deferred as "constrain posting dates to the month" on + the roadmap. +* **Closed-month archived tone.** Today closed months just carry + `disabled` on inputs and a muted state badge. A deeper archive + treatment (desaturated palette, watermark) is on the roadmap. +* **Deviation semantics on posting-only edits.** Notes don't flip + deviation. Postings don't either. But if the user edits `planned` + from $400 to $450, that *does* flip. Might be worth a gentle + indicator when the plan drifts vs when applied drifts, though the + current model is defensible. + +## Raw Thinking + +* **The logo was a turning point for the UI direction.** Before Jeff + mentioned it, I was committing to Fraunces serif. Once I saw the + shield-with-$0 mark I realised the app's identity is already + communicated by the logo, and the type should complement rather + than compete. The Barlow Condensed pairing (also condensed sans, + echoes the logo's wordmark) feels inevitable in retrospect. +* **Jeff's pattern of "explain your thinking" is a forcing function.** + When he asked for honest thoughts on the redesign, I tried to + flatter first. That triggered a course-correction in my own + response. The "ask for self-assessment" move gets more honest work + out of me than unsolicited reviews would. +* **Decision minimalism compounds.** Answers like "1) snapshot 2) one + applied number 3) explicit 4) yeah 5) yep" compress a 10-minute + back-and-forth into a 10-second exchange. The trade is that I have + to be extremely clear about which defaults I'd pick if he said + nothing — because often he does say nothing, and my defaults ship. +* **Posting is a better name than transactions.** Retrospectively, + the editorial tone of the rest of the app (colophons, italic + captions, the wordmark setting) benefits from "posting" as a + word of art. "Transactions" would have been neutral and forgettable. +* **Ten PRs in one sitting is a lot.** Jeff's profile noted sustained + multi-PR sessions as routine. This one pushed that envelope — and + the density was maintained without him ever asking for a slow-down. + Validates the profile's "capacity exceeds prior models" note. + +## What's Next + +Priority order for the next session: + +1. **Browser eyeball pass.** Run the full app in a real browser and + hunt for UX issues that curl + pytest miss. Expect: hover states, + focus management, layout overflow on mobile, empty-state edge + cases. +2. **Constrain posting dates to the month** (or at least warn on + out-of-range). Small scoped change. +3. **Budget-side inline edit for name / amount.** Matches month page + capability; removes the delete-and-recreate friction. +4. **Over-budget visual nub.** Restore the overflow indicator from + the early mockups on entry rows where applied > planned. +5. **Closed-month archived treatment** — follow-up on #15. diff --git a/SessionRetrospectives.md b/SessionRetrospectives.md new file mode 100644 index 0000000..bffc558 --- /dev/null +++ b/SessionRetrospectives.md @@ -0,0 +1,5 @@ +# Session Retrospectives + +| # | Date | Summary | +|---|---|---| +| [1](Session1) | 2026-04-17 | Greenfield to shipped ledger: 10 PRs merged (scaffold, monthly view, backups, zero amount, groups + sinking funds, notes, lifecycle, UI redesign, posting ledger + 3 fixups). 117 tests. |