Three root causes patched:
1. aiAnalyzer.ts — processBatch apiFailedMessages path:
After reverting messages to 'pending', suppress shouldScheduleNext
(was true by default) to prevent scheduleConversationAnalysis from
firing immediately and racing with the recovery worker that will pick
up those same pending messages on its next poll cycle.
Also release the conversationProcessing lock immediately after the
revert so the cooldown timer (not the full processing-timeout) gates
the next attempt.
2. messages.routes.ts — POST /messages/:id/reanalyze:
Add a per-message reanalyzeInFlight Set. Concurrent requests for
the same ID now return HTTP 409 instead of issuing duplicate UPDATEs
and triggering multiple recovery worker activations.
Also narrow the SQL predicate to 'WHERE id = AND ai_status != pending'
so a click that arrives while the recovery worker already picked the
message up is a no-op at the DB level.
Fix#1 (CRITICAL): Wire message_analyzed through Redis EventBroadcaster
- DG aiAnalyzer.ts: Add broadcastAnalysisCompleted() helper that publishes
to both in-memory WS broadcaster AND Redis EventBroadcaster
- DG bootstrap.ts: Pass eventBroadcaster to startPendingAIAnalysisWorker()
- Fixes broken real-time chain so analysis results appear instantly
Fix#2: Collapsible AI Analysis with Rich Formatting
- MessageCard: AI analysis now collapsible with color-coded severity border
(red/yellow/blue), summary line showing categories + confidence + severity
- Default collapsed for clean, expanded for warn/flagged
Fix#3: Toast Notifications for Flagged Content
- Wrap app in ToastProvider; ModerationAlertListener component listens for
moderation_alert custom events and shows toast with emoji + details
Fix#4: Discord-style Message Grouping
- MessageFeed: Group consecutive messages from same user within 5 min
- MessageCard: Compact variant hides avatar, reduces padding for non-first
Fix#5: Moderation Action Buttons in UI
- BE: POST /api/messages/:id/moderate endpoint + publishCommand() for Redis
- FE: Delete/Warn buttons on flagged/warned cards with confirmation dialog
Fix#6: Search Results Include Full Data
- BE analysis.service.ts: SELECT all 26 message columns instead of just 11
- Search results now render with full MessageCard including images/analysis
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Replace stub analytics.repository.ts with real PostgreSQL queries using pg.Pool
- Add /api/guilds, /api/config, /api/auth/login, /api/ui-state (GET/POST)
- Add /api/review, /api/recordings, /api/analysis/search
- Add /api/messages/:id/reanalyze endpoint
- Add /api/analytics/heatmap and /api/analytics/topics
- Implement media routes (stub responses, backend has no Discord voice client)
- Add WebSocket server at /ws with heartbeat and broadcast functions
- Fix analytics route paths to match frontend contract (dual paths for backward compat)
- Export getPool() from database module for raw SQL queries
- Register all new routers in app.ts
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>