Skip to content

fix(paygate): preserve AI quota across account reset + exempt prod smoke account#233

Merged
thomasluizon merged 2 commits into
mainfrom
fix/ai-quota-reset-and-smoke-exemption
Jun 24, 2026
Merged

fix(paygate): preserve AI quota across account reset + exempt prod smoke account#233
thomasluizon merged 2 commits into
mainfrom
fix/ai-quota-reset-and-smoke-exemption

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

What

Two coupled paygate-quota fixes, one repo.

1. Account reset no longer refills the AI message quota (security)

User.ResetAccount() zeroed AiMessagesUsedThisMonth, 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. (Because IncrementAiMessageCount() re-zeroes the counter whenever AiMessagesResetAt is 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_EMAIL account 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.

CanSendAiMessage now exempts that single account only in Production, mirroring the existing SendCodeCommand smoke auth gate (ASPNETCORE_ENVIRONMENT=Production + matching SMOKE_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

Tests

  • +2 domain tests: reset preserves metered usage; reset-then-send does not refill (count goes 20 → 21, not → 1).
  • +3 application tests: prod smoke account bypasses over-limit; non-prod and non-smoke-email still blocked.
  • Full suite green: 3546 passed, 0 failed.

🤖 Generated with Claude Code

thomasluizon and others added 2 commits June 23, 2026 21:39
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>

@claude claude 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.

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.

@sonarqubecloud

Copy link
Copy Markdown

@thomasluizon
thomasluizon merged commit 71450b2 into main Jun 24, 2026
8 checks passed
@thomasluizon
thomasluizon deleted the fix/ai-quota-reset-and-smoke-exemption branch June 24, 2026 00:47
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.

1 participant