Skip to content

fix(moderation): refuse a re-scan while one is queued or running, from every entry point - #473

Open
deanrie wants to merge 1 commit into
cursor:mainfrom
deanrie:fix/rescan-guard-shared
Open

deanrie wants to merge 1 commit into
cursor:mainfrom
deanrie:fix/rescan-guard-shared

Conversation

@deanrie

@deanrie deanrie commented Oct 25, 2026

Copy link
Copy Markdown

What

moderation.rescanPlugin now refuses while a scan for the plugin is already queued or running, and the MCP rescan_plugin tool relies on that instead of its own copy of the check.

Why

Two entry points queue an admin re-scan. The MCP tool checks the row first and refuses while scan_status is pending/scanning with updated_at younger than 15 minutes (its description promises exactly that). The web action rescanPluginAction, behind the Re-scan buttons on the Stuck tab, the flagged review list and the plugin admin panel, calls rescanPlugin directly, which sets pending and sends a queue message unconditionally.

So a click while the first scan is still queued (waiting for the one-minute drain, or invisible under the 900 s visibility timeout) or running puts a second message for the same plugin into plugin_scans. Each message becomes a full runPluginScan, that is a second billable agent run, and the two applyVerdict writes race. The admin panel shows a spinner for pending/scanning but keeps the button enabled; disabled={busy} only covers the button's own request.

Change

  • moderation.ts: SCAN_IN_PROGRESS_MS (15 min, the same threshold getStuckScans and the recover cron use) and isScanInProgress(state); rescanPlugin loads the row and throws ModerationError before the status write. Rows older than 15 minutes are still re-scannable, since that is the definition of stuck everywhere else.
  • lib/mcp/tools.ts: rescan_plugin drops the private constant and check and calls rescanPlugin with the resolved id. adminTool already returns ModerationError as the tool error text, so the tool's reply is unchanged.

The web action needs no edit: safe-action.ts already surfaces ModerationError as the toast text.

Checked

The check is read-then-write, so two requests inside the same few milliseconds can still both pass; the button busy state covers that at human speed. A drain-side skip of messages older than the plugin's last_scanned_at would close the remaining window and the equal VT_SECONDS / STALE_AFTER_MS case in the recover cron; left for a separate change.

@vercel

vercel Bot commented Oct 25, 2026

Copy link
Copy Markdown

@deanrie is attempting to deploy a commit to the Anysphere Team on Vercel.

A member of the Team first needs to authorize it.

This branch has not been deployed

No deployments
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