| #1492 |
No bookable-slot waitlist — light EVENT_SOLD_OUT hook now, denormalized slot-queue post-MVP |
P3 |
| #1493 |
No no-show automation — no NO_SHOW state, no auto-close on absent attendance, no money/credit reconciliation (revives #471) |
P2 |
| #1494 |
Sold plans can't be retired — no consultant PATCH archivedAt on webinar/class; booked 1:1 plans can't retire-and-stop-new-bookings |
P2 |
| #1495 |
No reallocation on availability shrink — edited-away availability orphans SCHEDULED bookings (#1206 is the creation direction only) |
P2 |
| #1496 |
COUNTERED reschedule has no dedicated deadline — counter-round runs on the original 72h creation timer |
P2 |
| #1497 |
Reschedule rate-limit is global-per-user (no per-appointment cap); openForAppointmentId ghost hold until hourly sweep |
P2 |
| #1498 |
Reschedule/cancellation docs drifted — assert unenforced counter deadline, omit rate-limit shape, imply structured policy from free text |
P3 |
| #1499 |
Org cancellation policy free-text + Appointment.cancellationPolicySnapshot is Json — denormalize to typed/versioned policy (schema directive) |
P2 |
| #1500 |
Partial-tier refund undefined for credit-funded bookings — free credit has no cost basis (#1161 → MANUAL_REVIEW) |
P2 |
| #1501 |
Dispute gate admin bypass TODO (#1319) ownerless since umbrella closed — needs named, audited carve |
P2 |
| #1502 |
Refund subsystem lacks timeout budget, atomic refund+reallocation, and priority |
P2 |
Summary
Big Pickle verdict on the booking lifecycle & event planning slice — the umbrella over 11 newly-filed findings across rescheduling, cancellation, reallocation, event/plan planning, and refund design, plus the waitlist decision. Complements #1433 (booking system HLD/LLD verdict, 10 findings) and #1421 (finance verdict). Everything here is new code-grounded verification from the lifecycle pass.
The waitlist verdict
Two distinct concepts, settled (see #1492):
Waitlistmodel (prisma/schema.prisma:4130-4158) is newsletter/lead-capture (email + DPDP consent +EVENT_SOLD_OUTsource) — a pre-launch scarcity/email-build mechanic. Keep it.EVENT_SOLD_OUTas a notify-me hook (zero new schema). Defer a real slot-queue post-MVP, gated on recurring sell-outs + cancel batching. Any future queue must be a denormalized typed model, not JSON (schema directive).The 11 new findings
EVENT_SOLD_OUThook now, denormalized slot-queue post-MVPNO_SHOWstate, no auto-close on absent attendance, no money/credit reconciliation (revives #471)archivedAton webinar/class; booked 1:1 plans can't retire-and-stop-new-bookingsSCHEDULEDbookings (#1206 is the creation direction only)COUNTEREDreschedule has no dedicated deadline — counter-round runs on the original 72h creation timeropenForAppointmentIdghost hold until hourly sweepAppointment.cancellationPolicySnapshotisJson— denormalize to typed/versioned policy (schema directive)Cross-cutting themes
Payment.expiresAt, EXPIRED vs REJECTED vs CANCELLED). Any fix here is money-first; no backfill of past refunds (no-backfill posture).expire-reschedule-proposals,expire-stale-requests) — idempotent, cron-locked, fail-open-with-error-set.Appointment.cancellationPolicySnapshot Json?([finance][P2 MEDIUM] Org cancellation policy is free-text + Appointment.cancellationPolicySnapshot is Json — denormalize to a typed, versioned policy model (schema directive: no JSON) #1499), the credit cost-basis gap ([finance][P2 MEDIUM] Partial-tier refund undefined for credit-funded bookings: free credit has no per-booking cost basis, so a partial session refund cannot be quoted structurally (#1161 left this at MANUAL_REVIEW) #1500), and any future waitlist ([booking][P3 LOW] No bookable-slot waitlist — only a newsletter Waitlist exists; use EVENT_SOLD_OUT notify-me pre-MVP, defer a denormalized slot-queue post-MVP #1492) are all governed by this.launch: pre-mvp; polish/automation/doc gaps ([booking][P2 MEDIUM] No no-show automation: SCHEDULED appointments never auto-close on absent attendance, no NO_SHOW state, no money/credit reconciliation (revives #471) #1493 no-show, [booking][P3 LOW] Reschedule/cancellation docs drifted: assert a per-round counter deadline that isn't enforced (single 72h timer), omit the global rate-limit shape, and imply a structured refund policy from free text #1498 doc drift, waitlist queue) arelaunch: post-mvp.Verified code anchors (all read directly this pass)
prisma/schema.prisma:4130-4158—Waitlist(newsletter) +EventSoldOutnote +cancellationPolicySnapshot Json?at :4186prisma/schema.prisma:3677-3688—AppointmentStatus(noNO_SHOW)lib/booking/transitions.ts:474-494—RESCHEDULE_ALLOWED_FROM,COUNTEREDsingle-arrow,RESCHEDULE_OPEN_STATUSESlib/booking/reschedule-proposals.ts:24—PROPOSAL_MAX_LIFETIME_HOURS = 72single timerscripts/appointments/expire-reschedule-proposals.ts— sweeps open statuses byexpiresAt, clearsopenForAppointmentIdlib/rate-limit.ts:174—eventMutationLimiter10/min/user shared across event mutationsapp/api/appointments/[appointmentId]/reschedule/route.ts:93,598,946— limiter keying +openForAppointmentIdreservation +#1319commentapp/api/plans/webinars/[webinarPlanId]/route.ts:62,classes/[classPlanId]/route.ts:62— DELETE refused, no consultant archive writerapp/api/organizations/[orgId]/catalog/route.ts:303-329— only bulkarchivedAtwriter (org scope)utils/slotAllocation/*,jobs/appointments/reconcile-slot-availability.ts— no shrink reallocationlib/stream/call-presence.ts+session-handlers.ts+webhook-events.ts— raw no-show signals, unconsumedlib/payments/operations/refund.ts— refund flow (holds, expiry, MANUAL_REVIEW partial-tier)Related
openForAppointmentIddangling class — echoed by [booking][P2 MEDIUM] Reschedule rate-limit is global-per-user (no per-appointment cap) and openForAppointmentId can act as a ghost hold until the hourly expiry sweep releases it #1497 ghost-hold)Suggested sequencing