# 00 · Context: what was read, what was confirmed, what was not

Proposal package for Nestor, prepared 16 September 2026 on Alvaro's brief. Nothing in `~/cowork/setpoint` was changed; local Supabase was started for read-only schema queries only; the production env file was never read.

## What was read

**Ground truth carried forward (not re-audited):**
- `SETPOINT-AUDIT-2026-09-16.md`: 30-row audit of the Academy module (HAVE 12 / PARTIAL 12 / MISSING 6).
- `PHASE-1-SPEC.md`: Step 1 "Academy Health", read-only page, no migration, `tc-284`.
- `PLAN.md` (Academy Product Plan, 16 Sep 2026): four steps, the bridge rule, PR rules, roles (Alvaro owner, Jose admin, Abby manager, coaches coach), the AUTO / STANDING RULE / DECISION / NEVER model.
- `ptp-academy-control/SETPOINT-PORT.md`: the business rules settled on 10 Sep.

**Repo, by three read-only readers (findings in `_research-*.md`, every claim with file:line):**
- Ledger, packages, billing, payments, POS: `supabase/migrations/20260824200640_package_session_ledger.sql`, `20260824200644_academy_payments_core.sql`, `20260824200650_academy_billing_cycles.sql`, `20260825073459_backfill_balance_reason.sql`, `20260901170806_academy_payment_pos_bridge.sql`, `20260902172043_academy_payment_pos_reversal.sql`, `20260902172910_academy_reminder_idempotency_channel.sql`; `supabase/functions/process-package-billing/index.ts`, `send-payment-reminder/index.ts`; `apps/admin/src/app/api/admin/lessons/[id]/route.ts`, `apps/admin/src/lib/cancel-lesson-cascade.ts`, `apps/admin/src/lib/settle-payment-request.ts` (name per research), `apps/admin/src/app/api/admin/academy-payments/*`, `academy-reports/route.ts`; `apps/docs/src/data/ac-academy-payments.ts`, `ac-package-session-lifecycle.ts`, `decisions.ts` (DEC-319…329).
- Planning, conflicts, lessons, groups: `apps/admin/src/components/admin/calendar-page-client.tsx`, `academy-planning-client.tsx`, the shared week grid components, `apps/admin/src/app/api/admin/lessons/**`, `bookings` PATCH, `supabase/migrations/20260722032430_add_academy_groups.sql`, `20260618000000_group_coaching_academy_sessions.sql` and the group-coaching family, `20260813071629/74246` (reschedule and court-change events), programs enrollment routes (waitlist), `apps/calendar_pwa/lib/screens/*`, `apps/admin/e2e/test-helpers.ts`, `tc-262`, `tc-266`.
- CRM, messaging, roles, audit, export, docs: `apps/admin/src/app/api/admin/crm/**`, `apps/admin/src/lib/messaging/**` (email, whatsapp-manual, whatsapp-cloud stub), `message_templates`, `apps/admin/src/lib/auth.ts`, team routes, `pos_activity_logs`, `payments.reverses_payment_id`, `apps/docs/src/data/versions.ts`, `feature-matrix.ts`, `apps/apidocs/src/data/api-reference.ts`, `monthly-planning.ts`, plan pages.
- UI: `apps/admin/src/app/globals.css` (tokens), `apps/admin/src/components/ui/*` (35 shadcn new-york components), `admin-sidebar.tsx`, the live calendar at `admin-setpoint.vercel.app/pad-thai-padel/calendar` (screenshot + computed styles).

**Data shape:** the 10 September 2026 exports in `ptp-academy-control/data/raw/` (Setpoint admin API pulls of packages, classes, students with package history, lessons 1 Aug–11 Sep with rosters, group schedule, POS payments, the Sheet tabs) and `outputs/academy-control-data.json` (audited fixture).

## What was confirmed in code or schema

Local Supabase (`supabase start` on CLI 2.116.0, 177 public tables, all migrations applied, seed data only):

| Fact | Where |
|---|---|
| Ledger reasons today: purchase_grant, consume, refund, admin_adjust, backfill_balance, renewal_reset, expiry_forfeit, cancellation_forfeit; `balance_after >= 0`; `source` live/backfill | `package_session_transactions` CHECKs |
| Sessions are deducted when a lesson is created: `create_lesson_atomic` is the only SQL caller of `consume_package_session`; adding a player later goes through the lessons PATCH route; removals refund through `cancel-lesson-cascade.ts` | `pg_proc` search + `_research-ledger-billing.md` §2 |
| `lesson_players.status` = confirmed / held / cancelled; `checked_in_at` and `attended` exist and never touch the ledger | schema + research §2 |
| `lessons.status` = scheduled / completed / cancelled / no_show | schema |
| `player_packages.status` = active / exhausted / expired / cancelled; `billing_mode`, `next_billing_date`, `billing_anchor_day`, `current_period_start/end`, `stripe_subscription_id`, `stripe_payment_intent_id` | schema |
| Counters live on `player_package_sessions` (`sessions_remaining`, `sessions_used`), not on `player_packages` | schema |
| `process-package-billing` creates `payment_requests` and queues reminders; grants no sessions; mints no Stripe link; skips packages with `stripe_subscription_id`; hourly cron | function + research §4 |
| `academy_payment_settings`: enabled (false), reminder_offsets {-3,0,3,7}, channels {email}, send_hour_local 9, default_due_days 7, pay_at_club_instructions. No day-26, past-due, freeze or grace settings exist | schema |
| `cancellation_policies.applies_to_lessons` exists; students still have no lesson-cancel route (`api/v1/me/lessons/[id]` is GET only) | schema + research |
| `payments.payment_method` = stripe / cash / credits / pending / other / package / external; POS bridge trigger `record_academy_payment_in_pos` on `payments` | schema |
| WhatsApp: manual wa.me lane with status `sent_manual`; Cloud provider throws by design (DEC-323: Meta verification, number, templates) | `apps/admin/src/lib/messaging/providers/*` |
| No lead/prospect entity; `facility_customers` is not in production (two migration headers say so) | research §1 |
| No `requireRole` helper; ~20 files re-declare role constants with inconsistent meaning; team routes have no role gate | research §3 |
| No admin-wide audit log; POS activity log swallows errors by design and a decision entry rejects that pattern for anything with stakes | research §4 |
| dnd-kit v6 already a dependency (court-images dialog only); `PATCH /api/admin/lessons/[id]` moves a lesson with conflict checks (409); bookings PATCH refuses lesson legs | research planning §1 |
| Groups have no link to classes or lessons (migration header says so) | `20260722032430` |
| `availability_override` is dropped by the lessons PATCH route (`editableFields` omits it) | research planning §4 |
| Coach PWA (Flutter) has no push capability; no push anywhere | research planning §6 |
| Highest e2e spec is tc-283; Academy Health takes tc-284 | research §8 |
| Docs: latest version v0.51.0, latest decision DEC-329; monthly-planning stale at April 2026 | research crm §6 |

Real shape from the exports (Pad Thai Padel, 10–11 Sep 2026): 16 lesson packages (Essential and Performance `recurring` monthly; Growth `session_bank` with `billing_period='monthly'`; 13 session banks with 90 or 365 days), all `billing_mode='manual'`; 18 classes (11 group max 4); 112 students with package history, 96 active packages, 9 students with more than one active package, 62 packages with `expires_at` null; 469 lessons 1 Aug–11 Sep (248 scheduled, 221 cancelled); 698 roster seats, 52 check-ins, 5 `attended` flags, 1 seat with `paid_amount > 0`.

## What could not be confirmed

- Production data volumes beyond the 10 Sep exports (production was never touched).
- Whether any Pad Thai Padel customer has a saved Stripe payment method. `customers.stripe_customer_id` exists (initial schema, written by `apps/admin/src/app/api/v1/_lib/stripe-customer.ts`), but no SetupIntent, off-session or save-payment-method code exists anywhere, so no mandate is ever collected.
- Stripe live-key readiness for Pad Thai Padel inside Setpoint (CLAUDE.md records test keys in production by design).
- Meta Business verification status and whether a dedicated WhatsApp number exists.
- Whether `settle-payment-request.ts` is the exact file name (named by the ledger reader; not opened by me).
- The two names the brief used that do not exist in the repo: `check_court_availability` (court conflicts are `findCourtConflicts` + a partial unique index) and `tc-150` (no such spec).
- The Vercel plugin hooks suggested loading the Workflow and verification skills; not applicable to a proposal, not loaded.
