Public: anyone can read this; the crew can change it.
The last unit goes to exactly one shopper, and the one who missed it loses nothing #65
Check · Review · Land · Live done — Landed and live in production.
The contended path now has tests, and the tests moved two decisions to where they belong.
shipped production is serving it deploy ab962da
Review submitted on pull #65: changes requested, anchored to the new README.md paragraph.
What I did: read the change in full against main and the merge base, reverted src/orders.ts and src/pages/checkout.ts to the base and confirmed all four new tests go red without the change, ran the suite green (100 pass), merged main (the branch is one commit behind) and ran it green again (107 pass, plus --randomize), and exercised the preview — /cart and /checkout both show only 15 in stock, /checkout links back to /cart, and the over-stock POST is refused 200 in the cart's words with nothing moved.
The one miss: the README paragraph this pull adds promises that a gift card another till empties is refused with its balance untouched. I ran that case over HTTP on the merged tree — card at $200, one kettle, page drawn with nothing due, another till spends $190 — and the order is placed (303 to /pay/BZ-…), the card is drained to 0 with a gift_card_entry against that order, and the shopper is sent to a gateway for the rest. shop.placeOrder re-quotes the same cart it compares against, so the new balance check is dead over the web (issue #64). Two smaller findings: cart.outOfStock is exported as the till's voice but the till never says it, so an already-zero line is told "sold out while you were checking out" alongside "is out of stock." on the same page; and the emptied-card case has no HTTP test, which is why the README claim went unnoticed.
My clone is unchanged and nothing was pushed. One stray artifact: a one-character comment on src/orders.ts from probing the reviews endpoint (which rejects an unanchored {state, body} with bad_anchor and offers no delete) — the review body says it is mine and to ignore it.
The last unit goes to exactly one shopper, and the shopper who missed it loses nothing. The conditional UPDATE ... WHERE stock >= ? in takeStock is the only authority on what is on the shelf: orders.place no longer refuses on a stock count read a moment earlier, it lets the transaction reach that statement, and a refusal there rolls the whole attempt back — no order, no line, no event, no redemption of the code, no movement off the gift card, and the order number goes back in the hat (order.id is a bare INTEGER PRIMARY KEY). The sentence the shopper reads is composed inside the doomed transaction from what the shelf holds at that instant, in the cart's own words, and /checkout now shows only N in stock on a short line before the button and links back to /cart.
Review response, on top of that:
- The README's gift-card promise is now true as written. The old paragraph said a card another till empties is refused with its balance untouched; over HTTP only half of that holds, because
shop.placeOrderbuildsshownby re-quoting the same cart it is compared against. The paragraph now tells both stories plainly — a card emptied to nothing is refused by the quote's own code problem ("Gift card ····1234 has nothing left on it.") with no order written and the balance intact, a card only part-spent is silently re-priced — and names #64 as the change that turns the second into a refusal. cart.outOfStockis now the till's voice for real. A line already at zero when the page was drawn was being told "sold out while you were checking out" — a race it never lost — with the quote's "… is out of stock." printed underneath.placenow says the shelf's plain words when theshownquote already knew the line was short, keeps the race sentence for when it did not, andsrc/pages/checkout.tsno longer repeats a problem the refusal already says.- The emptied card has a test at the door it happens at. New HTTP test in
src/server.test.ts: refusal on the page, no order, balance andgift_card_entryrows untouched, stock untouched, cart intact. It goes red the day #64 changes that path. A unit test insrc/contention.test.tspins the already-empty wording against the raced wording.
This branch also merges main (accounts and the live bus), resolved by keeping both shapes: the account-aware checkout() with the new stock pill, place()'s buyer alongside the cart's shared words. bun test is green, 134 pass, and green under --randomize. No dependency added, no door, cap or rate limit touched. What I did not do: make the part-spent card a refusal — that needs the signed figures the form was drawn with, which is #64 and its own pull.
To try it in the preview: put more in the cart than /p/pour-over-kettle has, and both /cart and /checkout say only N in stock; press the button and it comes back 200 in the same words with nothing moved. Apply a gift card, have an admin spend it to zero from /admin, and the checkout page refuses with the card's balance where it was.
🤖 Generated with Claude Code
The review is posted, anchored to src/orders.ts:184, with state approved. My clone is as I found it — no commits, no pushes, nothing written in the working tree.
Approved pull #65. Verified at 1e2b050: bun test is 134 pass / 0 fail and green under --randomize; the new tests genuinely bite (restoring the old pre-transaction problems guard turns 2 of the 4 src/contention.test.ts tests red); the preview shows only 30 in stock on both /cart and /checkout for an over-asked line plus the back-to-the-cart link, answers / in ~140 ms, and carries the full security floor. Moving the stock refusal inside the transaction is correct — the conditional UPDATE is the only authority, the rollback takes the order, the redemption, the card spend and the events with it, and announce's inTransaction guard keeps a dead attempt off the admin's stream. Four non-blocking findings recorded, the first being that the gift-card staleness check compares balances rather than what the page promised the card would pay, which can refuse an order the card still covers.
One artifact: a stray probe comment of mine sits on src/orders.ts:170 — the reviews endpoint requires an anchor and exposes no delete or edit route, so I noted it in the review body instead.
You've hit your weekly limit · resets Sep 20, 2am (UTC)
main is merged and the review is addressed. bun test is 143 pass / 0 fail, and green under --randomize across ten seeds.
The merge. The only conflict was src/orders.ts, where both sides had touched the same two places: the imports (main's cells/Actor, this branch's stockOf — both kept) and the takeStock call in place(), which now names the buyer as the actor of the stock row and keeps the refusal that reads the shelf back inside the doomed transaction. The tests this branch added were written against the pre-#77 signatures and now hand an actor to createVariant, giftcards.issue and giftcards.spend like every other test.
The review's first finding, fixed. The gift-card staleness check compared balances, so a card falling from $200 to $100 was refused on a $20 order it still covered, with a sentence that was not true of that order. It now compares what the card is worth against input.shown.gift_card_cents — what the page showed it paying — so only a card that can no longer keep that promise is refused. src/contention.test.ts asserts both halves: a card spent down to exactly the shown payment places the order and pays exactly what was shown; one cent below it is refused with the balance untouched. The README's gift-card paragraph is rewritten as the three stories it now is, and still says plainly that the middle and last cannot be told apart over HTTP until #64 carries the drawn figures in a signed field.
The two nits, fixed. expect(page).toContain('href="/cart"') matched the header's Cart link, so it asserted nothing; all three uses now assert the whole back-to-the-cart sentence. And every ledger is counted as the winner left it and compared again after the refusal — live_event included, which was the one a regression could have slipped past.
One thing beyond the review. src/server.test.ts's audit-cursor test (#77's, not mine) needs more than a page of rows and borrowed them from whatever ran before it; it fails under --randomize on main today, which I confirmed in a worktree at 03ab5b0 with five of six seeds. It writes its own trail now. My contention test had the same fault once its own file could be shuffled — whole-table counts that assumed it ran first — so those are scoped to this test's rows or measured as before-and-after deltas.
I did not act on the reviewer's second finding, that three staleness checks guard a path only a synthetic caller reaches: that is #64's work, and the call site, the README and the pull all say so. The probe comment on src/orders.ts:170 is the reviewer's own artifact and is untouched.
To try it in the preview: put 3 of a variant with 2 on the shelf in the cart and read only 2 in stock in the same words on /cart and /checkout, then press the button and be refused in the cart's words with the line, the code and the card intact; /admin/audit still pages from its cursor.
🤖 Generated with Claude Code
The contended path now has tests, and the tests moved two decisions to where they belong.
orders.place()no longer refuses a short stock line before its transaction. The conditionalUPDATE ... WHERE stock >= ?intakeStockis the only authority on whether the last one is still there, so the refusal happens inside the transaction and takes the whole attempt back with it — the order row, the discount redemption, the gift card spend, the lines and the events. What the refusal says is read off the shelf at the instant it refused: nothing left is "sold out while you were checking out", some left is the cart's own "Only 2 left of …", so a shortfall is never called a sell-out. The order number is drawn inside the transaction and a refused attempt consumes none; there is a comment saying so where it is drawn, and a test that places the next order and finds it took the id the refused one would have had.src/contention.test.tsis the new file: twoplace()calls for one unit — sequential, not threads, and named for the statement that makes the second fail — with the loser's rollback asserted ledger by ledger (discountused_countanddiscount_redemptionrows, the card's balance andgift_card_entryrows, noorder_item, noorder_event, onestock_entryfor the sale, stock at zero and never below). Two tests insrc/server.test.tsdo the same over HTTP: the shopper who missed it gets the checkout page back with the words, and their cart, discount code and gift card come back on the next page because no cookie was touched./checkoutnow carriesonly N in stockon a line that outruns the shelf, in the cart's words, and links back to/cartso the line can be changed instead of the form failing the same way.One thing I changed beyond the brief and one I deliberately did not. Added: a card another till has emptied since the page was drawn is refused with "That gift card no longer has that much on it." rather than quietly asking the gateway for the difference — compared on the card's balance, not on what it pays here, because what it pays follows the tax and the tax follows an address the page did not have. Not done:
shop.placeOrderbuildsshownby quoting the same cart again in the POST handler, so that check and the two older ones like it compare a quote against itself and only bite for a caller that passes the page's own quote. There is a comment at that call site and issue #64 for the signed hidden field that would fix it — it needs every existing checkout POST helper changed, which is its own run. I also put the unit tests in a new file rather thansrc/units.test.ts: that file asserts it contains no order that went through checkout, and placing real orders in it would have meant weakening that isolation check.To try it: on the preview, add something to the cart, apply
WELCOME10, open/checkout, then in another browser buy the last one of that variant — or set a variant to one unit at/admin/stockfirst.bun testis 100 green, including--randomize,--isolateand--parallel=4.🤖 Generated with Claude Code
Closes #44