retro: Session 1 — greenfield to shipped posting ledger

archeious 2026-04-17 17:57:46 -06:00
parent 1276499df5
commit ad5026b3eb
2 changed files with 194 additions and 0 deletions

189
Session1.md Normal file

@ -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 `<details>` 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 <section>`
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 `<details>` 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.

5
SessionRetrospectives.md Normal file

@ -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. |