Public: anyone can read this; the crew can change it.

Every ledger row says who did it, and /admin/audit is the whole money trail on one page #77

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`stock_entry`, `payment`, `order_event`, `gift_card_entry` and `discount_redemption` now carry `actor_id`, `actor_name` and `actor_role`, added through `addColumn` so the rows written before the column say `unknown` rather than guess. `src/actor.ts` is the one place an actor comes from — the buyer for a purchase, the admin for an admin action, `stripe` for a webhook, `system` for the seed, the for…

shipped production is serving it deploy ab962da

opened this

stock_entry, payment, order_event, gift_card_entry and discount_redemption now carry actor_id, actor_name and actor_role, added through addColumn so the rows written before the column say unknown rather than guess. src/actor.ts is the one place an actor comes from — the buyer for a purchase, the admin for an admin action, stripe for a webhook, system for the seed, the forge's account at the terminal — and every writer (note, addStock, takeStock, setStock, the payment and refund inserts, the gift-card and discount rows, createVariant's opening stock) takes it as a required argument, so the call sites are all accounted for rather than defaulted.

src/audit.ts merges the five ledgers into one timeline: one indexed read per table with a limit and a merge in memory, never five scans — each table gained an (at DESC, id DESC) index — and paging is a cursor of (when, which table, which row) so rows arriving while somebody reads cannot shift the page. GET /admin/audit is that timeline, admin-only, filtered by order number and paged with ?before=; for one order it prints the arithmetic instead of a claim (charges plus the gift card, less refunds and what went back on the card, against what the order says was paid) and says whether the two agree. /admin/orders/:number shows the actor on every payment and every event, and the terminal has the same trail on an eighth tab, with / narrowing it to one order and the same reconciliation at its foot.

Worth a look in the preview: /admin/audit and /admin/audit?q=<order> after a purchase and a partial refund, and the audit tab over ssh. What I did not do: admin_change keeps its own two actor columns and gained no role, and it is not merged into the timeline — it has its own page and is not money. No arithmetic changed; src/units.test.ts's totals tests pass with only their call signatures updated. Tests cover a buyer-named sale, an admin-named refund, a stripe-named webhook charge, a terminal-named stock move, the page's ordering and reconciliation, the admin-only gate, and that the cursor neither repeats nor skips a row.

🤖 Generated with Claude Code

Closes #12

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs