docs: capture this session's learnings for a future pickup
ci / lint-test (push) Successful in 40s

CLAUDE.md gains the traps a new session would otherwise re-discover:

- Rules: a zero-condition rule matches everything (rule 43 would split all ~3,700
  transactions); preview-then-apply-by-id is the safe pattern and why it beats
  auto-applying on ingestion; how run provenance works.
- Shared expenses: transaction_splits.settled is dead data; getParticipantBalances
  is correct and must not be 'fixed'; settlement cannot be attributed per trip.
- The shared loan: separate ledger, fixed 50% with a tracked receivable, why the
  share must not be derived from actual payments, and why interest stays as spend.
- Extraction: balance assertions are the check that works, do not derive
  opening_balance or add a totals assertion (both would be tautological), Gemini
  invents summary fields it was not given, empty statements must not throw, FX is
  per-date, and CSV comparisons need millisecond ordering.

The design doc records Phase 0 as done - including that the original Phase 0 plan
was wrong, since reading the code first is what prevented breaking a working
balance page.

Known Gaps lists what is open: the unbuilt phases, 11 failing assertions, the
uncategorised Up rows, and the CSVs sitting in 030490e's history.
This commit is contained in:
2026-07-26 16:19:02 +10:00
parent 3f04cbd5e7
commit c465742635
2 changed files with 191 additions and 10 deletions
+53 -10
View File
@@ -144,16 +144,59 @@ payment did. A percentage-of-actual model would silently redefine her share as
## Migration path
1. **Fix the trip settlement bug first** — make `getTripAnalytics` and
`getParticipantBalances` agree. Low risk: all splits are currently unsettled,
so honouring the flag changes nothing today.
2. Add contexts; put every existing split in "Household"; every payment likewise.
3. Backfill `linked_transaction_id` for the four exact matches; flag the other
four for manual linking.
4. Create the "Pre-2026" closed context. Apply household split rules to
pre-cutoff transactions into it — fixes ~$97,627 of the trailing 12 months
currently shown as 100% yours.
5. Loan contributions and equity — last, and only after the questions below.
### Phase 0 — DONE (2026-07-26, commit `3f04cbd`)
Stop the trip view reporting a settlement breakdown it cannot compute.
The original plan was "make `getTripAnalytics` and `getParticipantBalances`
agree". **That plan was wrong** and reading the code before building is what
caught it:
- `getParticipantBalances` is *not* buggy. It computes `splits payments`,
which is coherent. Excluding settled splits there while still subtracting the
payments that settled them would have double-counted and broken a working page.
- The real defect was narrower: the trip view showed Settled/Unsettled from
`transaction_splits.settled`, which nothing sets. A correct per-trip figure is
not computable at all, because `split_payments` has no trip attribution.
So the fix was **subtractive**: the trip view now shows each participant's share
and points at Shared for what is actually owed.
Also removed `/api/splits/settle` — unreachable from the UI but live on its URL,
where one call with `participant_id` would mark every one of that person's splits
settled, writing a flag nothing reads.
`transaction_splits.settled` / `settled_at` still exist and are now pure dead
data. Phase 1 either repurposes them ("is my context closed?") or drops them.
### Phase 1 — settlement contexts
Add contexts; put every existing split in "Household"; every payment likewise.
Balance queries group by context. Touches `queries.ts` (both balance CTEs),
`shared/page.tsx`, `trips/[id]/page.tsx`, `split-payments/route.ts`. ~1 day.
### Phase 2 — link payments to transactions
Backfill `linked_transaction_id` for the four exact matches; flag the other four
for manual linking. On ingestion, propose a matching credit as a settlement
rather than silently categorising it `transfers`. ~half a day.
### Phase 3 — retroactive pre-2026 split
Create the "Pre-2026" closed context. Apply household split rules into it via the
rule preview (`/api/rules/[id]/matches`, built 2026-07-26) — fixes ~$97,627 of
the trailing 12 months currently shown as 100% yours. Then delete the `splitFrom`
cutoff entirely.
**Validate the ratio first.** This assumes today's 50/50 held through 2025. The
SplitMyExpenses CSVs should be used to *check* that assumption — not to
reconcile, since transactions were sometimes combined and exact matching is
impossible.
### Phase 4 — loan ledger
Contribution schedule, contributions recognised from `emi` credits, running
receivable. Independent of contexts — the loan is a separate ledger. ~12 days.
---