feat(billing): unify upgrade routing with reason context + storage/tables limit emails#5171
feat(billing): unify upgrade routing with reason context + storage/tables limit emails#5171waleedlatif1 wants to merge 6 commits into
Conversation
…bles limit emails
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview In-app limit UX: storage upload failures surface an actionable toast via New threshold emails for storage and tables (80% / 100%) use one Reviewed by Cursor Bugbot for commit 01ee798. Configure here. |
Greptile SummaryThis PR introduces a unified upgrade-routing system with a central
Confidence Score: 5/5Safe to merge — the dedup claim is race-free, the re-arm hysteresis is correct, all email paths are fire-and-forget, and the migration is a backward-compatible additive column with a sensible default. The atomic UPDATE … WHERE current < desired RETURNING claim correctly prevents duplicate emails even under concurrent calls. The re-arm path now receives priorUsage for the tables case and uses rearmOnly=true on the storage decrement path. Per-recipient email failures are isolated in individual try/catch blocks. The new limit_notifications JSONB column ships with NOT NULL DEFAULT '{}', so existing rows are handled without a backfill. Dead-link fix for credits emails is verified correct. No regressions found in any call site. No files require special attention — the core notification logic, migration, email template, and routing changes all look correct. Important Files Changed
Reviews (5): Last reviewed commit: "fix(billing): resolve recipients before ..." | Re-trigger Greptile |
…sage + decrement)
|
@greptile |
|
@cursor review |
…full clear / wipe-rebuild)
|
@greptile |
|
@cursor review |
…send on a shrink)
|
@greptile |
|
@cursor review |
…rn the dedup threshold
|
@greptile |
|
@cursor review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 01ee798. Configure here.
| const projected = params.currentRowCount + params.addedRows | ||
| if ((projected / limit) * 100 >= TABLE_ROW_NOTIFY_PERCENT) { | ||
| void maybeNotifyTableRowLimit(params.workspaceId, params.currentRowCount, projected, limit) | ||
| } |
There was a problem hiding this comment.
Upsert inserts skip table emails
Medium Severity
Table row threshold emails run only from assertRowCapacity, but upsertRow enforces capacity on the insert branch with wouldExceedRowLimit and never calls that helper. Upserts that add rows and cross the 80%/100% bands therefore skip warning and limit-reached emails that inserts and batch inserts receive.
Reviewed by Cursor Bugbot for commit 01ee798. Configure here.


Summary
lib/billing/upgrade-reasons.ts) — the source of truth for the language shown when a usage limit routes a user to the upgrade page. The same copy drives both the upgrade-page header and the threshold emails so they never drift.?reason=(nuqs) and swaps its header ("Upgrade to scale your tables", "…with your teammates", etc.); generic header when absent.buildUpgradeHref(workspaceId, reason): credits chip (credits), teammates (seats), tables row-limit toast (tables), file-upload storage error (storage, via a shareduseLimitUpgradeToast). Generic "Explore plans" links (billing settings, deploy gate) route through the same helper without a reason.LimitThresholdEmail. Dedup is a race-free atomic claim (single conditionalUPDATE … WHERE current < desired RETURNINGagainst a newlimit_notificationsjsonb column onuser_stats/organization, migration0248), with hysteresis re-arm below 70%. One sharedmaybeNotifyLimitresolves user vs. org scope for both call sites./workspace?billing=upgrade, which redirects to home and drops the param). Re-pointed to the live upgrade/billing-settings routes.Type of Change
Notes
LimitCategoryincludesseats) is ready if the seat model changes.getEmailPreferences+billingUsageNotificationsEnabled) and are best-effort/fire-and-forget so they never block a mutation.Testing
upgrade-reasons.test.ts(4) andlimit-notifications.test.ts(9 — claim win/lose, dead band, re-arm, opt-outs, billing-disabled). Existing logger/tables suites green (60 tests total).bun run check:api-validation,bun run check:react-query, typecheck, and biome all pass.Checklist