events: capacity-safe transactional booking on persist + 24 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 36s

booking.sx: per-occurrence append-only stream, roster = replay. Booking
decided against an observed (roster, last-seq) snapshot, committed via
persist/append-expect — atomic check+append, no overbooking, no lock.
Explicit last-seat race test: two bookers, one booked, one conflict, roster
capped. Idempotent per actor. 97/97 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-07 01:44:43 +00:00
parent 15e9503b05
commit 80a2dee22f
6 changed files with 313 additions and 8 deletions

View File

@@ -18,7 +18,7 @@ capacity rules, transactional booking, and a flow-driven notification dispatcher
## Status (rolling)
`bash lib/events/conformance.sh`**73/73** (Phase 1 complete: calendar + availability + api)
`bash lib/events/conformance.sh`**97/97** (Phase 1 complete + Phase 2 capacity-safe booking)
## Ground rules
@@ -62,9 +62,11 @@ lib/events/api.sx ── (events/schedule) (events/book) (events/agenda) ──
- [x] tests + scoreboard + conformance.sh (73/73)
## Phase 2 — Ticketing + booking
- [ ] capacity rules; transactional booking → `persist` (no overbooking)
- [x] capacity rules; transactional booking → `persist` (no overbooking)
- [ ] wire `booking.sx` into `api.sx` (persist-backed `ev/book`)
- [ ] cancellation (tombstone events) + seat release
- [ ] paid tickets compose with `commerce` order flow
- [ ] tests: capacity edge, double-book guard, conflict detection
- [x] tests: capacity edge, double-book guard, conflict detection
## Phase 3 — Notification delivery (flow)
- [ ] `notify.sx` — reminder/digest flows over injected transport
@@ -79,6 +81,16 @@ lib/events/api.sx ── (events/schedule) (events/book) (events/agenda) ──
## Progress log
- 2026-06-07 — **Phase 2 start: capacity-safe booking.** `booking.sx`: one
append-only persist stream per occurrence; roster = stream replayed. Booking
decisions made against an OBSERVED (roster, last-seq) snapshot, committed via
`persist/append-expect` — append only if last-seq unchanged, else a conflict
value the booker retries. This makes capacity-check + append atomic at the
persist boundary: no overbooking, no lock. `ev/book!` (retrying),
`ev/book-with-observed`, `ev/roster`, `ev/seats-left`. Idempotent per actor
(:already). Explicit last-seat race test: two bookers on the same snapshot →
one :booked, one :conflict, roster never exceeds capacity; loser retry →
:full (or next seat when room remains). 24 tests, 97/97 green.
- 2026-06-07 — **Phase 1 complete.** `api.sx`: immutable `store`
({:events :bookings}) facade over calendar + availability. `ev/schedule`,
`ev/book`, `ev/agenda`, `ev/agenda-for`, `ev/free?`, `ev/next-free`,