[P0] Fix moderation infinite re-review loop #27

Closed
opened 2026-03-20 22:49:20 +00:00 by replit · 1 comment
Owner

Problem

processPending() in artifacts/api-server/src/lib/moderation.ts runs every 30 seconds and queries all events with status = 'pending'. When autoReview() flags an event it sets reviewedBy and reviewReason but leaves status as 'pending'. This means every flagged event gets picked up and re-sent to Claude Haiku on every poll cycle — an infinite loop that burns real AI tokens.

A stuck event (e.g. an XSS test payload that Claude reliably flags) will generate a new Haiku call every 30 seconds indefinitely.

Root cause (line 179–188 of moderation.ts)

// After AI flags — status stays 'pending', only reviewedBy is set
await db.update(relayEventQueue)
  .set({ reviewReason: result.reason, reviewedBy: 'timmy_ai' })
  .where(eq(relayEventQueue.eventId, eventId));

Fix

Add a 'flagged' status value to the schema enum. After AI flags an event, transition status → 'flagged' so processPending()'s WHERE status = 'pending' filter naturally excludes it. Admins then review status = 'flagged' events via the relay admin panel.

Acceptance criteria

  • Flagged events do not appear in subsequent processPending() polls
  • Admin relay panel can list status = 'flagged' events
  • Existing approved / auto_approved / rejected flows are unchanged
  • DB migration adds 'flagged' to the status enum
## Problem `processPending()` in `artifacts/api-server/src/lib/moderation.ts` runs every 30 seconds and queries all events with `status = 'pending'`. When `autoReview()` flags an event it sets `reviewedBy` and `reviewReason` but **leaves `status` as `'pending'`**. This means every flagged event gets picked up and re-sent to Claude Haiku on every poll cycle — an infinite loop that burns real AI tokens. A stuck event (e.g. an XSS test payload that Claude reliably flags) will generate a new Haiku call every 30 seconds indefinitely. ## Root cause (line 179–188 of moderation.ts) ```ts // After AI flags — status stays 'pending', only reviewedBy is set await db.update(relayEventQueue) .set({ reviewReason: result.reason, reviewedBy: 'timmy_ai' }) .where(eq(relayEventQueue.eventId, eventId)); ``` ## Fix Add a `'flagged'` status value to the schema enum. After AI flags an event, transition `status → 'flagged'` so `processPending()`'s `WHERE status = 'pending'` filter naturally excludes it. Admins then review `status = 'flagged'` events via the relay admin panel. ## Acceptance criteria - Flagged events do not appear in subsequent `processPending()` polls - Admin relay panel can list `status = 'flagged'` events - Existing `approved` / `auto_approved` / `rejected` flows are unchanged - DB migration adds `'flagged'` to the status enum
replit added the aibackend labels 2026-03-20 22:49:20 +00:00
claude self-assigned this 2026-03-22 23:37:26 +00:00
Collaborator

PR #71 created.

Added flagged status to the schema enum. When autoReview() flags an event, status now transitions from pendingflagged, so processPending() naturally skips it on the next poll cycle. Also added flagged count to admin stats and a DB migration with an index on status.

4 files changed, 14 additions, 4 deletions.

PR #71 created. Added `flagged` status to the schema enum. When `autoReview()` flags an event, status now transitions from `pending` → `flagged`, so `processPending()` naturally skips it on the next poll cycle. Also added `flagged` count to admin stats and a DB migration with an index on `status`. 4 files changed, 14 additions, 4 deletions.
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: replit/timmy-tower#27