Skip to content

LP-1d: studio cancels a class (credits back, drop-ins refunded, everyone told) - #99

Merged
TaylorONeal merged 25 commits into
mainfrom
lp-1d-cancel-class
Oct 13, 2026
Merged

TaylorONeal merged 25 commits into
mainfrom
lp-1d-cancel-class

Conversation

@TaylorONeal

Copy link
Copy Markdown
Owner

LP-1d: the studio cancels a class

Before: marking a class cancelled left every booking confirmed, every pack class and membership use spent, every drop-in charged, and nobody told.

Now, on /manage/schedule → Next two weeks, the owner taps Cancel on one class:

  1. Preview (preview_class_cancellation, changes nothing): each person booked or waiting and what they get back (pack class, membership class, refund amount, waitlist).
  2. Optional note to students.
  3. Cancel and tell N people → cancel-class edge function:
    • cancel_class() runs as the caller (staff check in the DB), in one transaction: class cancelled, waitlist cleared first (and promote_waitlist now refuses cancelled classes), bookings cancelled with reason class_cancelled (never a late cancel), so the ledger trigger returns pack and membership classes; each paid drop-in gets a refund_requests row for its unrefunded amount.
    • Each refund is sent to Stripe with idempotency key refund_request:<id>, reversing the Connect transfer and platform fee when present. charge.refunded then records it on the transaction as usual. Failures are kept on the row (failed, last_error) and can be retried with { retryRefunds: true }.
    • Everyone with an email gets one: class, time in the studio's time zone, the note (HTML-escaped), and what happens to their payment.
  4. Result: classes returned, money refunded, emails sent; a clear warning if a refund failed.

Tests: CC-01..10 (student, other studio and anon refused; preview changes nothing; credits back; waitlist cleared without promotion; second cancel refused; started class refused; membership usage back; drop-in refund queued for the unrefunded part; queue is staff read-only and one per payment). classCancel unit tests (copy, plurals, escaping). e2e of the whole owner flow. A mutation check (skip the waitlist step, skip the refund queue) fails CC-04.

Prod after merge: run scripts/db/prod/apply-00043.sql, deploy cancel-class (verify_jwt true; added to scripts/deploy-functions.sh).

Also: LP-1a marked done in BACKLOG; CLAUDE.md corrected (agents get 403 on workflow dispatch).

🤖 Generated with Claude Code

https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF


Generated by Claude Code

…one told)

- 00043: cancel_class / preview_class_cancellation, refund_requests queue (RLS, staff read only), no waitlist promotion into a cancelled class
- cancel-class edge function: cancels as the caller, sends queued Stripe refunds idempotently (reverses Connect transfers), emails everyone affected with the studio's note
- /manage/schedule: Next two weeks list with one-tap cancel, preview of who gets what back, result summary
- Tests: CC-01..10 (DB), classCancel unit tests, e2e cancel flow
- Docs: backlog, prod apply-00043.sql, deploy script; CLAUDE.md: workflow dispatch is Taylor's click

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF
@vercel

vercel Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
tandava Ready Ready Preview Oct 13, 2026 4:22pm UTC

Request Review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-13T16:29:37.121255Z a7a6bb3 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 708a570bfc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread supabase/functions/cancel-class/index.ts Outdated
Comment thread supabase/functions/cancel-class/index.ts Outdated
Comment thread supabase/functions/cancel-class/index.ts Outdated
Comment thread src/lib/classCancel.ts Outdated
Comment thread supabase/migrations/00043_studio_cancels_class.sql Outdated
…s failed, retry button, email readiness, currency, undo early check-in attendance

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 90c26aafbd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/components/manage/UpcomingClasses.tsx Outdated
Comment thread supabase/functions/cancel-class/index.ts Outdated
Comment thread supabase/functions/cancel-class/index.ts Outdated
Comment thread supabase/functions/cancel-class/index.ts Outdated
Codex on 90c26aa: a lost response or a function that dies after
cancel_class left refunds and emails with no recovery path.

- class_cancel_notices queued in the cancel transaction (with the note)
- class_cancel_followup(occ): durable refunds/emails sent and owed
- cancel-class resume mode works through what is owed; notices are
  claimed before sending; refund and notice writes are checked
- owner screen reads state back after every call, recovers from an
  unclear response, names failed and missing emails, and offers
  Finish follow-up on cancelled classes that still owe something

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5627f84a9c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread supabase/functions/cancel-class/index.ts Outdated
Comment thread supabase/migrations/00043_studio_cancels_class.sql
Comment thread src/lib/backend/supabase.ts Outdated
Comment thread supabase/functions/cancel-class/index.ts Outdated
…mail

Codex on 5627f84:
- refunds count as sent only when Stripe reports succeeded or pending;
  charge.refund.updated failed/canceled reopens the request with a new
  idempotency attempt (reopen_failed_refund)
- a drop-in paid after the class was cancelled queues its refund and
  notice (fulfill_stripe_checkout calls class_cancel_queue_late_payment)
- cancelled classes whose time passed stay listed while they owe anything
- students told the studio is arranging their refund get a refund-sent
  email once it goes through

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e7ec0caffa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/classCancel.ts
Comment thread supabase/functions/cancel-class/index.ts Outdated
Comment thread src/lib/classCancel.ts Outdated
Codex on e7ec0ca (P2s):
- class_cancel_mark_handled: owner closes notices after contacting
  people directly; Mark as handled on the result
- an interrupted send (row left sending) is never reclaimed; it shows as
  may-not-have-gone-out until the owner marks it handled
- charge.refunded for a fully refunded charge settles queued class-cancel
  refunds (settle_class_cancel_refunds)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8cc9bddf37

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread supabase/migrations/00043_studio_cancels_class.sql Outdated
Comment thread src/lib/classCancel.ts Outdated
…mations

Codex on 8cc9bdd (P2s):
- Mark as handled closes a refund confirmation only once it is owed
  (refund has gone through); one still waiting on its refund stays open
- no-email students count as follow-up owed until the owner marks them
  handled, so the class keeps offering Finish follow-up

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8997e78227

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread supabase/migrations/00043_studio_cancels_class.sql Outdated
Comment thread supabase/functions/cancel-class/index.ts Outdated
… uncertain

Codex on 8997e78 (P2s):
- marking an unsent drop-in notice handled while its refund is still
  owed sets refund_told failed, so the refund-sent email (or a reminder)
  follows once the refund goes through
- a send that throws in transit stays sending (may have been delivered)
  instead of becoming failed and retryable; refused sends still retry

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e85f30d754

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread supabase/migrations/00043_studio_cancels_class.sql Outdated
Comment thread supabase/functions/cancel-class/index.ts Outdated
Comment thread supabase/functions/cancel-class/index.ts Outdated
…; flag uncertain sends

Codex on e85f30d:
- P1: charge.refund.updated reopens a failed refund matched by the
  attempt in its metadata, so a failure that lands before the sent-state
  write still counts and a later retry uses a fresh key
- notices are claimed just before each send, so a run that dies partway
  leaves untouched rows pending for Try again
- sendEmail marks thrown provider errors uncertain (may have been
  delivered); cancel-class leaves those in sending instead of retrying

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF
…refund made by hand

Codex on f678dad (P1): event order is not guaranteed; the webhook
re-reads the charge and its refunds, and settledByHand requires a live
refund made by hand as well as no live refund of ours.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 02127e6161

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread supabase/functions/stripe-webhook/index.ts Outdated
Comment thread supabase/functions/cancel-class/index.ts Outdated
…ts reuse the key

Codex on 02127e6:
- P1: charge.refunded records the charge as Stripe has it now, so an
  older snapshot cannot undo a later refund failure. An unknown charge
  (the signed smoke-test event) falls back to the snapshot and never
  settles the queue, which also keeps the Stripe smoke test working
- StripeIdempotencyError and rate limits no longer advance the refund
  attempt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3db0ea643c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread supabase/functions/cancel-class/index.ts Outdated
Codex on 3db0ea6 (P2): the outcome write requires the row to still be
sending.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e12c63c90f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/components/manage/UpcomingClasses.tsx Outdated
Codex on e12c63c (P2): cancel-class statusOnly returns the follow-up
state with email readiness; the dialog uses it when reopening a
cancellation or recovering from a lost response, falling back to the
database state if the function is unreachable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 79c1f21f03

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread supabase/functions/cancel-class/index.ts Outdated
Codex on 79c1f21 (P2): after a partial refund by hand, the retry
refunds what is left (new key, since the amount changed) and records it;
nothing left settles the request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa35083097

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread supabase/functions/cancel-class/index.ts Outdated
Comment thread supabase/functions/cancel-class/index.ts Outdated
Codex on fa35083 (P2s): when the amount changes the request moves to a
new attempt (key and metadata), so a late failure of an earlier
full-amount try cannot reopen it; amount_cents keeps the total owed so
the follow-up reports the full refund.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c12eaf9218

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread supabase/functions/cancel-class/index.ts
Comment thread supabase/migrations/00043_studio_cancels_class.sql Outdated
…ations reopen

Codex on c12eaf9:
- P1: a refund Stripe returns as requires_action stays on its attempt;
  the next run resolves the existing refund from Stripe before deciding
  (sent, still waiting, or failed and a new attempt)
- a later refund failure also reopens a confirmation the owner gave by hand

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE3mWAihH4WmMmN3LpP7KF

Copy link
Copy Markdown
Owner Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: a7a6bb316f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@TaylorONeal
TaylorONeal merged commit 7353a2a into main Oct 13, 2026
7 checks passed
@TaylorONeal
TaylorONeal deleted the lp-1d-cancel-class branch October 13, 2026 16:30

This branch was successfully deployed

1 active deployment
Preview — a7a6bb31 Deployed Oct 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants