Fix AI credit overspend and uncharged generations in ai-bot#5235
Fix AI credit overspend and uncharged generations in ai-bot#5235jurgenwerk wants to merge 2 commits into
Conversation
The ai-bot's room lock serializes only per room, so a user firing concurrent requests across N rooms passed validateAICredits N times against the same stale balance and overspent (CS-11504). The in-memory barrier didn't help: waitForPendingCreditTracking only waits on an already-in-flight debit, and trackAiUsageCost early-returned when a same-user debit was pending, silently dropping all but one concurrent cost. Wrap the credit gate -> generate -> debit section in PgAdapter.withUserCostLock, the per-user cross-replica barrier CS-11128 already applies to the realm-server proxy paths. The inline-cost debit is awaited inside the lock so the next same-user request validates against the post-deduction balance. The rare no-inline-cost fallback is registered in the tracking map inside the lock (so the next request observes it) but its up-to-10-minute fetch+debit runs detached, since we can't pin a DB connection that long. Split the spend logic into spendInlineCost and scheduleFallbackCostTracking in lib/credit-tracking.ts and add tests covering no-coalescing of debits and the fallback map bookkeeping. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5246f03 to
b30d8ac
Compare
- Guard the in-lock cost debit with try/catch so a billing-write failure can't skip the activeGenerations cleanup and leave a stale entry that would abort a later, already-finished run. - Chain each fallback debit onto any prior pending one for the same user so the tracking map represents all in-flight debits, and only delete the entry while it still points at the current chain — an earlier debit settling can no longer unlink a newer fallback that overwrote it. - Add a regression test for that older-settles-first race. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: db3bb89b56
ℹ️ 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".
| await assistant.pgAdapter.withUserCostLock( | ||
| senderMatrixUserId, | ||
| async () => { |
There was a problem hiding this comment.
Avoid holding the room lock while waiting on user cost lock
When the same user already has a generation running in another room, this awaits withUserCostLock after the handler has acquired this room's lock and built eventList/promptParts, but before activeGenerations.set is reached below. A later message or stop event in this same room during that wait will not find an active generation to abort, then acquireRoomLock returns false because the room lock is still held, so that event is dropped; once the other room finishes, this request generates from stale history. Consider moving the per-user wait before taking the room lock/history or registering an abortable queued generation before waiting.
Useful? React with 👍 / 👎.
(Written by Claude on Matic's behalf.)
Fixes CS-11504 and CS-11505 — both HIGH severity.
What was broken
The ai-bot locked credit checking per room, not per user, and recorded costs after generating.
if (trackAiUsageCostPromises.has(user)) return) → a steady stream of messages = free generations.The fix
Wrap check credits → generate → charge in the per-user
withUserCostLockthat the realm-server proxy paths already use. The charge is awaited inside the lock, so a concurrent same-user request blocks until the prior charge lands and then validates against the real balance. Every generation's cost is recorded (no more drop-if-busy).Where to look
ai-bot/main.tswithUserCostLock(most of the diff is just re-indentation from nesting)ai-bot/lib/credit-tracking.tsai-bot/tests/credit-tracking-test.ts🤖 Generated with Claude Code