# Lessons — what we got wrong, and would do differently

A running, honest log (rian's idea, 2026-07-12). The ADRs record what we
DECIDED; this records what we MISSED, so the next pass is better. Add to it
freely — being wrong on the record beats being wrong silently.

## L1 — Added a model but didn't update its importer (2026-07-12)
Slice 2a added the clean recurrence model (`project_billing.is_recurring`/
`cycle_months`/`billing_timing`) but nobody went back and taught
`sheet_books.py` to populate it — so every imported project defaulted to
non-recurring and a monthly engagement (OA Region 6) showed as
milestone-with-no-amount. **Lesson:** when a migration adds a column that an
importer/transform should feed, updating the feeder is part of that change,
not a separate task. Add a check: every non-nullable-meaningful column has a
source in every writer that creates the row.

## L2 — The flat project model couldn't represent reality (2026-07-12)
`projects.tenant_id` + a single `client_party_id` cannot express one project
worked by BW and billed by PlusROI — so the import duplicated Second Wind
across tenants. We should have caught that projects (like parties) are a
cross-tenant object BEFORE importing PlusROI. **Lesson:** before importing a
second source into a shared model, pressure-test the anchor against a KNOWN
cross-cutting real example (Second Wind) — if it can't hold it, fix the
anchor first. Projects are becoming the 2nd canonical object; they need the
same tenant-agnostic + roles treatment parties got.

## L3 — "Backend passes" ≠ "the flow works" (2026-07-12)
The PlusROI soft-launch had 970 green tests and API-verified persona flows,
but the actual UI to DO those flows (filter/sort, a create-invoice button)
wasn't there — so day-to-day it wasn't usable. Claude was over-confident it
would "go smoothly." **Lesson:** a persona scenario isn't done until it's
walked in the UI as that persona, click by click — API proof is necessary,
not sufficient. Budget the UI-flow work explicitly, per use case.

## L4 — (reserve) importer field-coverage
The importer silently skipped address/phone/contacts-as-records. **Lesson:**
an importer should ACCOUNT for every source column (map it, or log a
deliberate skip with a reason) — the same reconciliation discipline we
already apply to rows, applied to columns. (Add the column-disposition check
to the guided importer.)
