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>
Backend (real implementations, no more stubs):
- Redis pub/sub bridge: subscribes to discord-gateway events (message/attachment/voice) and broadcasts to WS clients
- Redis command channel: backend publishes voice/media commands, discord-gateway executes and replies
- Voice service: connectVoice/disconnectVoice/getVoiceStatus via Redis commands with graceful fallback
- Media service: queue/skip/stop/volume via Redis commands, reads status from Redis cache
- Messages repository: ALL 7 methods now use real PostgreSQL queries (findMany, findById, findByChannel, create, update, delete, getAttachmentsByChannel)
- Analytics: period returns {start,end} epoch millis, overview includes hourly/topics/top_users, worst_flags as string[]
- Health check: actually queries SELECT 1 against database
- VoiceStatus type fixed: {connected, activeGuildId, activeChannelId, activeChannelName}
- Guild type: includes icon: string | null
- asyncHandler: accepts Promise<unknown> instead of Promise<void>
Discord Gateway:
- CommandHandler: subscribes to 'backend:command' Redis channel, executes voice/media commands, publishes replies
- Publishes voice:status and media:status to Redis for backend caching
- Shutdown handler updated to close command handler
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>