fix(paygate): preserve AI quota across account reset + exempt prod smoke account#233
Conversation
ResetAccount() zeroed AiMessagesUsedThisMonth/AiMessagesResetAt and the ad-reward allowances, so a user could exhaust their monthly AI messages, reset, and send again — an unbounded paygate bypass. (IncrementAiMessageCount also re-zeroes the counter when AiMessagesResetAt is null, so nulling the timestamp alone refilled on the next message.) Reset now preserves all metered-consumption state (monthly counter + rolling-window timestamp + ad-reward bonus/daily-cap) while still resetting genuine progress (onboarding, gamification, calendar). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e limit The post-deploy smoke suite signs in as the pinned SMOKE_TEST_EMAIL account and messages Astra every run; as a free account it would hit the 20/month cap after ~20 runs, and with the reset-refill bypass now closed, resetting no longer works around it. CanSendAiMessage now exempts that one account in Production, mirroring the existing SendCodeCommand smoke auth gate (ASPNETCORE_ENVIRONMENT=Production + matching SMOKE_TEST_EMAIL). Env-mutating tests (PayGate smoke + SendCode) are serialized via a shared xUnit ProcessEnvironment collection so they don't race on process-wide environment variables. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Clean fix for a two-vector paygate bypass (reset zeroed the counter, and a null AiMessagesResetAt would refill it on the next increment). Preserving all six metering fields in ResetAccount is the right call — nothing else needs resetting. The smoke-account exemption is correctly double-gated on environment and a specific email so no user-controllable bypass surface is added. Test coverage hits all three guard branches on IsProductionSmokeAccount and directly validates the post-reset counter behaviour. The ProcessEnvironmentCollection serialization approach is the correct way to protect tests that mutate process-wide env vars.
|



What
Two coupled paygate-quota fixes, one repo.
1. Account reset no longer refills the AI message quota (security)
User.ResetAccount()zeroedAiMessagesUsedThisMonth,AiMessagesResetAt, and the ad-reward allowances. That let any user exhaust their monthly AI messages → reset their account → send again, unbounded — a free bypass of the AI paygate. (BecauseIncrementAiMessageCount()re-zeroes the counter wheneverAiMessagesResetAtis null, nulling the timestamp on reset alone was enough to refill on the very next message.)Reset now preserves all metered-consumption state (monthly counter + rolling-window timestamp + ad-reward bonus/daily-cap) while still resetting genuine progress (onboarding, gamification, calendar tokens).
2. Exempt the production smoke account from the AI message limit (test-infra)
The post-deploy smoke suite signs in as the pinned
SMOKE_TEST_EMAILaccount and messages Astra on every run. As a free account it would hit the 20-message monthly cap after ~20 runs and start failing — and with the reset-refill bypass now closed, resetting no longer works around it.CanSendAiMessagenow exempts that single account only in Production, mirroring the existingSendCodeCommandsmoke auth gate (ASPNETCORE_ENVIRONMENT=Production+ matchingSMOKE_TEST_EMAIL).The two are coupled: fix #1 removes the refill that previously kept the smoke account alive, so fix #2 is what keeps it working — they ship together.
Notes
ResetAccountis internal domain logic; no mobile backward-compat impact.main, so merging here handles it; land chore(nuclear-review): behavior-preserving structural review — orbit-api #253 after.ProcessEnvironmentcollection to avoid racing on process-wide env vars.Tests
+2domain tests: reset preserves metered usage; reset-then-send does not refill (count goes 20 → 21, not → 1).+3application tests: prod smoke account bypasses over-limit; non-prod and non-smoke-email still blocked.🤖 Generated with Claude Code