test(intake): the second review pass, and CLAUDE.md for the rules it set
ci / lint-test (push) Successful in 45s
ci / lint-test (push) Successful in 45s
Six of the nine fixes have a test that fails against the previous tree: the metadata row's currency, the Create-path hold, two concurrent batches booking once, mrkdwn escaping including plain_text, the escaped currency, and candidate-row currency. Three do not, and the reason is the same each time — the window needs a crash or a lost race that a test cannot force. Asserted instead: a stale metadataId falls back to the live row rather than reporting a phantom write; two writes racing to create a metadata row both resolve to one; the create path records a baseline at all. All three passed before as well.
This commit is contained in:
@@ -658,14 +658,33 @@ and the cross-repo contract are in smarthome `plans/intake-router-slice-plan.md`
|
||||
the email/order bridge books orders on its own schedule; `findDuplicateBatch`
|
||||
ignores *unresolved* batches by design, so two pending cards for one order
|
||||
would otherwise each book it.
|
||||
- **One `expense_metadata` row, agreed on by three queries.** A transaction can
|
||||
own a row *and* be the `matched_transaction_id` of another; `matchTransaction`
|
||||
returns the row it resolved through and `replaceLineItems` writes to exactly
|
||||
that one. All three (match, write, `GET /api/transactions/[id]/order`) order
|
||||
by `(transaction_id = $1) DESC **NULLS LAST**, id` — the `NULLS LAST` is
|
||||
load-bearing, because Postgres orders `DESC` as `NULLS FIRST` and the row that
|
||||
merely *points* at the transaction has a NULL `transaction_id`, so the clause
|
||||
meant to prefer the owned row preferred the other one.
|
||||
- **One `expense_metadata` row, agreed on by every query that resolves one.** A
|
||||
transaction can own a row *and* be the `matched_transaction_id` of another;
|
||||
`matchTransaction` returns the row it resolved through and `replaceLineItems`
|
||||
writes to exactly that one. Every reader orders by
|
||||
`(transaction_id = $1) DESC **NULLS LAST**, id` — the match, the write, the
|
||||
order-detail GET, `getTransactions`' provenance LATERAL, `merchantForTransaction`
|
||||
and the Slack details modal. The `NULLS LAST` is load-bearing: Postgres orders
|
||||
`DESC` as `NULLS FIRST` and the row that merely *points* at the transaction has
|
||||
a NULL `transaction_id`, so the clause meant to prefer the owned row preferred
|
||||
the other one. Measured 2026-08-21: 667 rows, 147 pointing-only, **zero**
|
||||
transactions resolving to more than one row — the ordering changes nothing
|
||||
today and exists so the readers still agree when multiplicity appears.
|
||||
- **The create is guarded by a transaction-scoped advisory lock on the
|
||||
PURCHASE**, not the batch. `ON CONFLICT (source, source_ref)` handles a double
|
||||
tap on one card; two *different* batches for one purchase have different
|
||||
`source_ref`s and both clear their match and duplicate checks before either
|
||||
inserts. The lock has to live inside `withTransaction` (`src/lib/db.ts`):
|
||||
`queryRaw` is `$queryRawUnsafe` on a **pooled** client, so every call is its
|
||||
own implicit transaction and `pg_advisory_xact_lock` taken in a statement of
|
||||
its own is released before the next statement runs. The loser reports
|
||||
`duplicate` pointing at the winner's transaction — the same answer the
|
||||
sequential path gives.
|
||||
- **`replaceLineItems` snapshots the undo baseline BEFORE it writes, on both
|
||||
paths**, and treats an UPDATE that touched no row as a failure rather than a
|
||||
success. The metadata INSERT is `ON CONFLICT (transaction_id) DO UPDATE` and
|
||||
carries the batch's **currency** — a hard-coded `'AUD'` made `OrderDetails`
|
||||
print `$` on a USD order, because it prefers `expense_metadata.currency`.
|
||||
- **`intake_batches.previous_line_items` is the undo baseline, written once.**
|
||||
Returning whatever the write just read makes a pantry retry get its own write
|
||||
back as the "previous" state, so undo restores the thing it undid. Recorded
|
||||
|
||||
Reference in New Issue
Block a user