Repository navigation
Let an admin lift or change one account's weekly AI cap - #261
Conversation
Staging test accounts keep hitting the $2 weekly AI budget. Two new user_settings columns, writable only by the service role, override it per account: weekly_budget_override_usd replaces the cap and weekly_budget_unlimited skips it. NULL and false keep the global cap. The daily limit is unchanged. checkRateLimit passes the account's cap to consume_ai_quota and claim_ai_session_quota, and peekRateLimit compares against it, so the pre-round quota check agrees with the real reservation. The columns are read apart from getSubscriptionInfo, so functions deployed before the migration keep working, and a failed read keeps the global cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
| Layer / File(s) | Summary |
|---|---|
Budget settings and access restrictions supabase/migrations/20261028120000_weekly_budget_override.sql, supabase/tests/user_settings_privileges_test.sql |
The migration adds the nullable numeric override and the unlimited flag, with a constraint that rejects negative overrides. SQL tests check the defaults, constraint, and authenticated-user access restrictions. |
Resolve and apply the weekly cap supabase/functions/_shared/entitlements.ts, supabase/functions/_shared/entitlements.test.ts, supabase/functions/_shared/usage-limits.ts, docs/architecture.md, .claude/skills/verify-debatelab/features/usage-limits.md |
Entitlement checks resolve the account’s cap and pass it to quota reservations. Weekly-limit peeks use the resolved cap and skip weekly denial when it is null. Tests cover cap selection and peek behavior; documentation describes the settings and a test setup caveat. |
Estimated code review effort: 3 (Moderate) | ~20 minutes
Sequence Diagram(s)
sequenceDiagram
participant Entitlements
participant user_settings
participant QuotaRPC as Session-ledger and per-call quota RPCs
Entitlements->>user_settings: Read weekly budget settings
user_settings-->>Entitlements: Return override and unlimited flag
Entitlements->>QuotaRPC: Reserve quota with resolved weekly cap
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Note
🎁 Summarized by CodeRabbit Free
-
Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing.
Comment @coderabbitai help to get the list of available commands.
Staging test accounts keep hitting the $2 weekly AI budget. This adds a per-account override, set the same way as
daily_limit_override: inuser_settings, by an admin or the service role only.What changes
20261028120000_weekly_budget_override.sql(additive) adds two columns topublic.user_settings:weekly_budget_override_usd numeric(nullable,CHECK >= 0). NULL keeps the globalWEEKLY_AI_BUDGET_USD($2). A value replaces it.0refuses every request that week.weekly_budget_unlimited boolean NOT NULL DEFAULT false. When true, the weekly check is skipped, even if an override value is set.getWeeklyBudgetUsdin_shared/entitlements.tsreads both.checkRateLimitpasses the result asp_weekly_cap_usdtoconsume_ai_quotaandclaim_ai_session_quota. Neither RPC changes; a NULL cap already skips the weekly check in both.peekRateLimit(thequotaCheckthat Crossfire and Sparring send before a round) compares against the same per-account value, so an unlimited account can start rounds. The daily limit logic is unchanged.getSubscriptionInfo's select. If functions are deployed before the migration, that query fails, and the code logs a warning and keeps the global cap. It never lifts the cap on a failed read.Users cannot set it
20260819000000_security_hardening.sqlrevoked table-level INSERT/UPDATE/DELETE onuser_settingsfromanonandauthenticated. Users can update only named preference columns (language,theme,feature_configs,custom_system_prompt,output_language,updated_at), so new columns start out server-only and no grant changes are needed. The client'suser_settingswrites insettings-store.tsname only preference columns. The SECURITY DEFINER functions that writeuser_settings(promo redemption, payment handling) list their columns explicitly and never touch these two. The pgTAP test checks the column privileges and also runs a realUPDATEas an authenticated user, which fails with42501.Usage screens
The plan card and Settings show only daily numbers, so they are unchanged for an overridden account. The weekly allowance shows up only as the refusal dialog and the Sparring/Crossfire pre-round message. Both now follow the account's own cap: an unlimited account never sees them, and a capped account still does.
Tests
entitlements.test.ts): no override sends the global cap, a value (number, numeric-as-string, 0) replaces it, unlimited sendsnull, a failed override read keeps the global cap, and the pre-round peek respects the override. Three of these fail onmain's code.user_settings_privileges_test.sql): authenticated has no UPDATE privilege on either column, a real self-update of each column is denied, new accounts default to no override, and a negative value is rejected.What ran
Local only, at
682d83b2. GitHub Actions is billing-blocked, so CI failures on this PR are expected and say nothing about the code.NODE_OPTIONS=--max-old-space-size=3072 npm run verifygot through docs check, jargon gate, lint, typecheck, unit tests (714 passed), collaboration tests (30 passed), edge tests (498 passed) and the build. It was stopped duringtest:sw, so the service-worker check and the e2e smoke run did not complete.user_settings_privileges_test.sqlassertions have not been run yet) and theverify-debatelabusage-limits recipe. Adam skipped them because the change is small.Enabling it on staging (after the migration is applied there)
This lifts the cap for
seed-adminandseed-pro.seed-trialandseed-expiredstay on the normal $2 cap, so the weekly-limit screens can still be tested onseed-trial:The staging seed reruns after every sync. It upserts
user_settingswithresolution=merge-duplicatesand sends only the plan columns, so the flag survives reseeds. To undo:SET weekly_budget_unlimited = false.Deploying
supabase/migrations/20261028120000_weekly_budget_override.sql.aiandai-stream.voicealso bundlesentitlements.tsbut only uses functions this PR doesn't touch, so redeploying it is optional.stripe-webhookandcreate-checkoutbundleusage-limits.ts, where only a comment changed.maintriggers the staging sync timer (ops/staging-sync/run.sh), which applies pending migrations and deploys changed functions on staging within about 5 minutes. Production is not touched by the merge.Not covered
export_account_dataexportsuser_settingsgenerically, so a user's own export will include the two new columns. Redacting them would mean replacing that whole function, which is out of scope here.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation