Skip to content

fix(comment-automation): block private replies once the bot quota is reached - #1720

Merged
sung17 merged 1 commit into
mainfrom
fix/comment-private-reply-bot-quota
Nov 14, 2026
Merged

sung17 merged 1 commit into
mainfrom
fix/comment-private-reply-bot-quota

Conversation

@sung17

@sung17 sung17 commented Nov 14, 2026

Copy link
Copy Markdown
Collaborator

Problem

The bot message quota is enforced by the chat worker's gate (apps/worker/src/chat/worker.ts). A comment automation's text and AIAgent private replies never pass through it: they go straight to the comment-anchored Send API (executePrivateReply / processCommentAIReply). So once the owner's bot message quota was reached, every other bot send was blocked, but these DMs kept going out. They were still counted, so usage climbed past the limit.

Follow-up to #1719, which makes sends dropped by the chat gate show as failed instead of sent.

Fix

  • New private-reply-quota.ts: isPrivateReplyQuotaReached(workspaceId) wraps isBotMessageQuotaReached and fails open (logs { err }, returns false) if the lookup throws.
  • resolvePrivateReplyBlockedReason takes botMessageQuotaReached as a third blocked reason, after the channel's own rules (claimed DM, reply window). A blocked reply records a failed event with Private reply not sent: Bot message limit reached through the existing recordBlockedPrivateReply path. Nothing is sent, and no provider error is logged.
  • processCommentAutomation reads the quota at most once per comment, and only when an automation has a private reply to send.
  • The deferred TikTok private reply re-checks the quota at send time, because it can run out during the deferral wait.
  • As with any blocked private reply, the reply-once slot is released when nothing else was dispatched. A contact who comments again after a top-up or a new billing period still gets an answer.

Not changed: public replies and flow DMs are already blocked by the chat gate. flow DMs are also checked here, so a blocked one does not spend the comment's single DM.

Tests

  • apps/worker/__tests__/comment-private-reply-quota.test.ts (new): the helper, including the fail-open case, and the blocked-reason ordering
  • apps/worker/__tests__/comment-automation.test.ts: an over-quota text DM is recorded failed and not sent; the quota is read once per comment and not at all without a private reply
  • apps/worker/__tests__/deferred-private-reply.test.ts: the deferred DM is blocked and recorded

All 12 worker test files touching comment automation pass (622 tests). pnpm lint, pnpm check:invariants and pnpm --filter worker check-types pass.

🤖 Generated with Claude Code

…reached

A `text` or `AIAgent` private reply goes straight out through the
comment-anchored Send API, never through the chat worker, so the bot
message quota gate never saw it: an over-quota workspace kept sending
these DMs while every other bot send was blocked.

The private reply gate now checks the owner's bot message quota (read at
most once per comment, fail-open on a lookup error) and, when it is
reached, records a `failed` event with "Private reply not sent: Bot
message limit reached" instead of sending. The deferred TikTok private
reply re-checks it at send time. The reply-once slot is released as for
any other blocked private reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the bug Something isn't working as expected label Nov 14, 2026
@sung17
sung17 merged commit d347267 into main Nov 14, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working as expected

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant