Setpoint Academy proposal · Working fileAll deliverables · Scope · Mockups

Red team — subscription + session-ledger target model

Reviewed 16 Sep 2026 against the real Setpoint tree (/Users/Alvaro/cowork/setpoint), read-only. Attack surface: _target-model.md §1-§5 vs the shipped ledger, billing cycle, group-coaching and POS-bridge code.


1. Reservation without deduction

2. Ledger reasons, the CHECK, and the append-only invariant

3. The backfill / flag switch

4. Billing

5. Roster follows package

6. Preconditions for Pad Thai Padel on_done on 1 Nov 2026

  1. get_player_package_remaining_sessions returns purchased − consumed − reserved, and api/admin/lessons/recurring uses that; otherwise the series cap over-books from hour one.
  2. Every roster seat carries a player_package_id chosen by a deterministic rule, written at insert, with the reservation counted under a lock in that same statement.
  3. A dedicated ledger_mode_switch reason exists, reverses balance without NULLing lessons.player_package_id, and its inverse migration is written and tested.
  4. The past-lesson inference defaults to attended, with no_show only inside a verified check-in window, and Jose signs a dry-run whose commit is one transaction against that snapshot.
  5. Growth exists as a NEW recurring template; the 9 multi-package students and the 62 NULL-expires_at packages are resolved by hand before the flag moves.
  6. Check-in actually happens every session — the whole model rests on it. If coaches do not check in, auto_mark_no_show_hours decides revenue by default, and that must be a conscious club setting, not a fallback.
  7. Paid, non-package seats are excluded from roster sync, and the removal job sends notice.
  8. ledger_mode is scoped to the academy or template, not the facility.
  9. Billing changes ship separately from ledger changes. Do not move money and move the invariant in the same release.

Verdict

KEEP: the subscription states, the day-1/3/7 dunning cadence, the bridge tiers, roster-follows-package as a concept, and the ledger itself as the balance's single source of truth. CHANGE: reservations must be a locked, package-attributed fact on the seat row, not a computed count and not a delta-0 ledger row; one consume reason with the flavour in metadata; the switch needs its own reason and an inverse. DROP: off-session auto-charging on the 1st for this release — no mandate is ever collected anywhere in Setpoint, and most Thai academy money is cash, transfer or PromptPay, which cannot be charged at all. THREE CHANGES THAT MOST REDUCE RISK: (1) make get_player_package_remaining_sessions reservation-aware and lock the reservation at insert, before any flag flips; (2) default the backfill's past lessons to attended, never no_show, and give the switch a ledger_mode_switch reason that does not NULL the package link; (3) ship billing and the ledger as two separate releases.