Commit Graph
129 Commits
Author SHA1 Message Date
MythEclipseandClaude 30f8d7cce3 feat: replace corrections/tuner with dashboard module
Replace the corrections/adaptive-prompt-tuner feature with a new
dashboard module providing server stats and user profile overview.

Backend:
- Add dashboard module (routes, service, repository) with stats + user list + user detail endpoints
- Remove corrections module entirely
- Wire dashboard router in app.ts

Frontend:
- Add dashboard feature (DashboardStats, UserSummaryList, UserProfileDetail components + useDashboard hook)
- Remove tuner feature (CorrectionStats, CorrectionHistory, SubmitCorrection, useCorrections)
- Update API client from corrections → dashboard types/fns
- Rename tab 'tuner' → 'dashboard'
- Update MobileTabBar, Header, Sidebar links

Tests:
- Expand backend placeholder test with dashboard assertions
- Expand discord-gateway placeholder test with config/channel assertions

AI moderation:
- llmModerationClient: improve status/reply detection, expand safety categories, fix timer reset
- userProfileLearner: fix isReply refinement
- userProfileStore: add pending cache check
- messageMetadata: add crosspost type mapping
- migrate.ts: improve partial-index safety in schema push

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-13 11:24:42 +07:00
MythEclipseandClaude e3249edb6c feat: detect reply/forward/crosspost in message capture + inject into AI moderation
- Add is_reply, is_forward, is_crosspost, reference_message_id,
  reference_channel_id, reference_guild_id columns to messages table
- Update MessageRecord type with reference fields
- Extract reply/forward/crosspost from Discord message type/flags
- Track reference.type (DEFAULT=reply, FORWARD) and CROSSPOSTED flag
- Inject <reference> XML with parent content into LLM moderation prompt
- Add reply/forward/crosspost rules to moderation prompt rules
- Format context messages with [reply_to], [forward_from], [crosspost]
- Add migration 0009

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-13 00:55:42 +07:00
MythEclipseandClaude 0ac056dc8a fix(ai-moderation): audit prompts to reduce false positives
- ENTROPY rule: replace 'PILIH WARN' default with 'PILIH CLEAN'
- Add safe-list for code/log/stack traces/project names
- Narrow sexual_deviation: only flag explicit sexual solicitation
- Remove overbroad ontological graph (kostum hewan → furry) detection
- Add clean few-shot examples: error logs, project names, orientation disclosure
- Update simple fallback prompt with FP prevention rules
- Remove LGBT/furry from offensive username criteria

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-13 00:01:09 +07:00
MythEclipseandClaude 12f9b55000 fix(ai-moderation): implement prompt analysis recommendations
- Expand IMPHNEN domain rule to cover wildcard (*.imphnen.*)
- Trim redundant SARA examples from TEXT_ONLY_MODE (save ~950 tokens)
- Add debugging logs for channel culture injection into prompt
- Sync flag validation set with missing flags: potential_evasion, unclear_context

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-12 23:40:25 +07:00
MythEclipseandClaude b508a39ff7 fix(ai-moderation): prevent false positive SARA flag for IMPHNEN project name
- Add explicit system rule that IMPHNEN is the project's own name, not religion
- Rename 'Imphnemia 11:17' example to 'Kitabonia 11:17' to avoid name collision
- Ensures mentioning/promoting the project URL is not flagged as SARA

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-12 23:28:09 +07:00
MythEclipseandClaude fbc2184c6e feat(ai-moderation): add user profile self-learning system
Add user_profiles table, store, and background learner worker
that summarizes user communication style, topics, and personality.

- New user_profiles table (user_id PK, guild_id, profile_summary, last_analyzed_at)
- userProfileStore.ts — CRUD (get/update) following channelCultureStore pattern
- userProfileLearner.ts — background worker: queries 100 recent msgs per user,
  calls LLM for personality summary, updates every 12h
- Inject <user_profile> XML tag per-message in moderation prompt
- Start worker alongside cultureLearner in aiAnalyzer.ts
- Migration 0008 for user_profiles table

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-12 20:11:34 +07:00
MythEclipseandClaude 6effb51b5d fix(ai-moderation): don't cache text-only results for messages with media
- hasMediaContent now also checks evidence.attachments from metadata
  (not just DB attachment records), catching the race where attachment
  DB rows aren't inserted yet when analysis runs.
- Cache-hit guard: treat cached entries as miss when the message has
  media evidence in metadata, so stale 24h-freezes are avoided.
- Cache-write guard: skip storing text-only analysis results for
  messages whose metadata shows attachments/stickers/embeds. This
  prevents a text-only 'clean' result (from failed vision) being
  frozen for 24h, blocking future re-analysis with full media context.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-12 18:45:33 +07:00
MythEclipse 8b114ca278 refactor(ai-moderation): expand SARA and religious blasphemy detection rules
Update the moderation prompt to include high-priority detection categories for:
- Fake scripture/verse parodies
- Claims of divinity or false religious movements
- Misuse of theological terms as internet slang/memes
- Mockery of religious figures and rituals

This change ensures stricter enforcement of SARA (Suku, Agama, Ras, Antargolongan) policies by explicitly defining religious blasphemy and parody as high-severity violations.
2026-06-11 02:40:11 +07:00
MythEclipseandClaude Opus 4.8 07032ab521 refactor: atomic, DRY, and logging improvements across codebase
- Split llmModerationClient.ts (2170 lines) into 5 focused sub-modules
- Split aiAnalyzer.ts (1282 lines) into 4 modular pipelines
- Split messages.db.ts (826 lines) into 5 domain-specific modules
- Moved shared schema to @bete/shared, eliminated backend duplication
- Added createChildLogger to all voice-recording and AI moderation modules
- Extracted tryCommandThenFallback, normalizeMediaState, DEFAULT_VOICE_STATUS
- Created shared pagination.ts utility, eliminated 5+ cursor-pagination duplications
- Created shared messageMapper.ts for row mapping
- Standardized backend error handling with asyncHandler
- Added frontend createLogger utility and useAsyncAction hook
- Added structured logging to frontend hooks, socket, and API client

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 19:46:08 +07:00
MythEclipseandClaude Opus 4.8 b68789fffc refactor: atomic, DRY, and logging improvements
- Shared Redis channel constants as single source of truth (redis-channels.ts)
- commandHandler.ts split into VoiceHandler, MediaHandler, GuildHandler,
  ModerationHandler with handler-registry.ts dispatch
- messageStore.ts (1322 lines) split into domain-specific DB files:
  messages.db.ts, attachments.db.ts, reviews.db.ts,
  moderation-actions.db.ts, retention.db.ts
- recorder.ts startSpeaking callback extracted into speakingHandler.ts,
  streamSetup.ts, segmentFinalizer.ts
- autoDeleteManager.ts split into autoDeleteEligibility.ts,
  autoDeleteNotify.ts, autoDeleteLogger.ts
- Added createChildLogger() logging across 8 service files
- Backend messages.repository.ts migrated from raw SQL to Drizzle ORM
- Fixed biome.json to exclude packages/**/dist/* from lint
- Fixed config.ts GUILD_ID pre-existing type error

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 17:34:18 +07:00
MythEclipseandClaude Opus 4.8 3614d32701 fix: resolve architecture disconnects and codebase weaknesses
- Standardize MessageRecord types — single source of truth from @bete/shared
- Clean up config: remove unused GUILD_ID/TEXT_GUILD_ID/TEXT_CHANNEL_ID, fix WEBSERVER_PORT default (3001), remove default admin password
- Move mascot_chat_messages table to Drizzle schema with proper migration
- Remove runtime DDL (CREATE TABLE IF NOT EXISTS) from mascot-chat repository
- Remove phantom analytics/ module from documentation
- Add better-sqlite3 dependency to root devDependencies
- Replace 'as any' casts with proper type assertions across AI moderation
- Add error logging to silent catch blocks in LLM client
- Apply Biome formatting and import organization

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 13:07:01 +07:00
MythEclipse 67d66bb5dd refactor(shared): centralize configuration and types
Migrate configuration validation and core moderation types from individual services to the `@bete/shared` package to ensure consistency across the monorepo.

- Move `AppConfig` and moderation-related interfaces to `packages/shared`.
- Replace service-specific Zod schemas with the centralized shared configuration.
- Refactor `services/backend` and `services/discord-gateway` to consume shared config and types.
- Remove redundant type definitions and local configuration logic in services.
- Update `packages/shared` exports to include new `config` and `moderation-types` modules.
- Clean up unused files and deprecated utility functions in `packages/shared`.
2026-06-09 11:56:03 +07:00
MythEclipse f84b380f4f fix: resolve architecture disconnects and codebase weaknesses
- fix(backend): replace raw .parse() with proper loadConfig() + ConfigError
- fix(gateway): connect voice recording uploader to EventBroadcaster
- fix(gateway): remove dead globalThis.moderationBroadcaster path in AI analyzer
- fix(gateway): eliminate audioStream race condition by attaching handlers before pipe
- fix(gateway): enable inlineVolume by default for setMusicVolume to work
- fix(gateway): reuse persistent redisPub for command replies (no new connection per cmd)
- fix(frontend): add missing voice_active_user/voice_pcm_data to WsEventMap
- fix(frontend): correct onAttachmentUploaded handler signature to accept data
- chore: move @types/pg from dependencies to devDependencies
- chore: translate remaining Indonesian comments to English
- chore: remove stale P3 TODO comment
2026-06-09 11:06:14 +07:00
MythEclipseandClaude Opus 4.8 4becf0d6f1 refactor: comprehensive codebase cleanup and architecture hardening
- Sprint 1 (Quick Wins): Remove dead analytics modules, fix 4 unresolved
  imports, replace 3 console.warn with logger, remove mock-crc import
- Sprint 2 (Architecture): Create MascotChatRepository, AnalysisRepository,
  3 Zod schemas (mascot-chat, analysis, voice), deduplicate error classes,
  move 3 SQL queries from routes to repository
- Sprint 3 (Complexity): Replace 7 any types with proper interfaces,
  extract 6 helpers from prepareMediaMessage (CC 85 -> ~15)
- Sprint 4 (Config): Remove 22 dead env vars from .env, add 30 missing
  vars to .env.example, standardize naming

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 10:16:04 +07:00
MythEclipseandClaude Opus 4.8 13a75a5101 fix(voice): restore voice features and apply critical optimizations
Voice Feature Restoration:
- Implemented full Redis pub/sub pipeline for real-time voice data
- Added VOICE_PCM and VOICE_ACTIVE_USER Redis channels
- Implemented EventBroadcaster.voicePcmData() and voiceActiveUser() methods
- Extended backend redis-bridge to subscribe to voice channels
- Updated backend WebSocket server for binary PCM broadcast
- Replaced globalThis PcmBroadcaster pattern with proper EventBroadcaster DI
- Fixed root cause: PcmBroadcaster functions were never initialized

Bug Fixes:
- Fixed prism-media version conflict (2.0.0-alpha.0 → 1.3.5)
- Fixed type inconsistency in commandHandler.ts (AudioPlayerStatus → string)

Critical Optimizations:
- P1.1: Fixed unbounded memory growth in aiAnalyzer (added LRU caching, max 10K entries)
- P1.2: Converted sync file I/O to async in audio hot paths (recorder, sessionRecording)
- P1.3: Replaced process.exit(1) with proper error handling (bootstrap, aiAnalysisWorker)

Code Quality:
- Removed unused logger field in EventBroadcaster
- Replaced console.* with structured logger.* calls (player, decoder)
- Fixed typos and removed commented debug code
- Added DatabaseError class for better error handling

Files Modified: 18 (discord-gateway: 14, backend: 3, root: 1)
Architecture: Discord → EventBroadcaster → Redis → Backend WebSocket → Frontend

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 19:14:34 +07:00
MythEclipse 483bc86236 refactor(ai-moderation): batch all media messages into single LLM call instead of one per message 2026-06-06 17:34:45 +07:00
MythEclipse a3ef3bf7f1 fix(ai-moderation): add rule to prevent false positive on QWERTY typos like 'ngodonf' 2026-06-06 17:31:23 +07:00
MythEclipse 9287cb7319 fix(ai-moderation): guard against stale empty imageUrl rows from base64→URL migration 2026-06-06 17:18:41 +07:00
MythEclipse 71a8a9e6e1 fix(ai-moderation): patch structural bypass vulnerabilities in NLP pipeline
- Implement Pre-computation Normalization for Polyglot Obfuscation.

- Inject Ontological Graph for literal translation evasion (e.g., 'kostum hewan').

- Enforce Entropy-Triggered Routing to deny softmax fallback exploitation.

- Format discord-gateway codebase.
2026-06-06 15:38:36 +07:00
MythEclipse 5701b5f15f feat(ai-moderation): use cached sticker image URLs directly instead of re-uploading base64 data 2026-06-06 14:13:18 +07:00
MythEclipse a3e1801b24 feat(ai-moderation): replace base64 sticker cache with image URL storage and add uploadAndCacheSticker helper 2026-06-06 14:11:46 +07:00
MythEclipseandClaude Opus 4.8 da885339f9 fix(ai-moderation): remove user history from prompt to eliminate confirmation bias loop
user_history (riwayat flag sebelumnya) dan clean_streak/total_infractions
dikirim ke LLM setiap kali menganalisis pesan — ini bikin self-fulfilling
prophecy: user yg pernah kena false positive jadi makin gampang dituduh
lagi, dan link Instagram pun dianggap sexual_deviation cuma karena
riwayat user.

Changes:
- Hapus getUserRecentInfractions dari text batch path
- Hapus getUserRecentInfractions dari media analysis path
- Hapus import getUserRecentInfractions yg gak dipakai
- Ubah instruksi prompt dari 'jadilah lebih tegas jika riwayat jelek'
  jadi 'setiap pesan dinilai berdasarkan isinya sendiri'

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 13:55:24 +07:00
MythEclipseandClaude Opus 4.8 edb55fb5bf fix(ai-moderation): add religious expression safe list to stop Astaghfirullah false positives
Common Indonesian religious expressions like 'Astaghfirullah', 'Astaga',
'Alhamdulillah', 'Subhanallah', dll were being flagged as vulgar_language
by the LLM. Added explicit rule that these are normal religious/cultural
expressions in Indonesia - not vulgar language - even in all-caps or
with repeated letters.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 13:52:31 +07:00
MythEclipseandClaude Opus 4.8 e6e44b30c7 fix(ai-moderation): remove indonesianTextNormalizer to stop hallucinated slang flags
The rule-based badword detector was injecting [normalized_text] and
[normalization_notes] tags into the LLM prompt that caused false
positive hallucinations - the LLM started associating innocent words
('sapik', 'furina') with furry/sexual_deviation due to misleading
context injected by the normalizer.

Removed:
- indonesianTextNormalizer.ts (full file deletion)
- formatModerationTextEvidenceForPrompt import/usage in llmModerationClient
- formatModerationTextEvidenceForPrompt import/usage in conversationContext
- stale re-exports in index.ts

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 13:27:24 +07:00
MythEclipseandClaude Opus 4.8 cfe7230a55 fix(ai-moderation): prevent LLM from hallucinating furry slang on common names and unknown words
- Adds explicit rule that Indonesian names/nicknames like 'Sapik' (Syafik),
  'Ayang', 'Dek', 'Bang', 'Mas', etc. are NOT furry or sexual_deviation references
- Adds rule prohibiting the LLM from inventing slang meanings for words
  it doesn't recognize - default to innocent until proven guilty
- Prevents false positive cascade where LLM confuses names with furry slang

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 13:22:35 +07:00
MythEclipseandClaude Opus 4.8 817f1ce3df fix(ai-moderation): add Furina/Genshin character name exception to prevent false positive furry flags
Nama karakter game/anime populer seperti 'Furina' dari Genshin Impact
sering kena false positive sebagai 'sexual_deviation' karena kemiripan
fonetik dengan kata 'furry'. Menambahkan aturan eksplisit bahwa nama
karakter fiksi normal bukan referensi furry fetish, dgn pengecualian
jika konteks pesan secara eksplisit membahas aspek fetish/seksual.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 12:04:10 +07:00
MythEclipse 25c86dcc30 fix(ai-moderation): raise retry backoffs and cooldowns, make 429 retryable, reduce prompt false positives 2026-06-05 21:47:02 +07:00
MythEclipse d7a35e8377 refactor(ai-moderation): expand prompt rules for lyrics and literature
Update moderation guidelines to prevent false positives on song lyrics, poems, memes, and literary quotes, ensuring political or revolutionary content is not flagged as conflict instigation unless accompanied by explicit incitement.
2026-06-05 20:05:32 +07:00
MythEclipse 49ada183b2 feat(ai-moderation): implement auto-fallback for streaming and refine prompt rules
- Add automatic fallback to streaming mode if the provider rejects non-streaming requests with a 400 error
- Refactor `llmChat` to use an internal execution function to support retry logic with modified parameters
- Update moderation prompt to explicitly allow Japanese pop culture terms (e.g., "moe", "waifu", "wibu") to prevent false positive sexual deviation flags
2026-06-05 19:44:06 +07:00
MythEclipse bdf80b081c chore(ai-moderation): increase llm client timeout and add test script
- Increase OpenAI client timeout from 15s to 60s to handle high-latency models
- Add `test-llm.js` for manual verification of LLM connectivity and response times
2026-06-05 19:08:57 +07:00
MythEclipse 049582504e feat(ai-moderation): implement error handling and robust streaming for llmClient
Wrap the LLM completion logic in a try-catch block to provide detailed
error logging, including status codes and raw response data, when
API requests fail.

- Add comprehensive error logging for failed LLM API calls.
- Ensure streaming responses are correctly aggregated and returned
  even when wrapped in error handling logic.
2026-06-05 18:47:30 +07:00
MythEclipse c0a067322b refactor(ai-moderation): enhance llmClient flexibility and idempotency
Update the LLM client to support streaming responses and improve
database migration idempotency.

- Add `stream` support to `llmChat` with dynamic chunk parsing for
  compatibility across different LLM providers.
- Remove hardcoded default temperature and top_p to allow for more
  flexible parameter passing.
- Update database migrations to use `IF NOT EXISTS` for tables and
  indexes to prevent errors during re-runs.
2026-06-05 18:45:58 +07:00
MythEclipse 08c624fcf4 fix(ai-moderation): use generic sender name and force descriptive media analysis to prevent bad global cache poisoning 2026-06-05 18:20:43 +07:00
MythEclipse 2f3d7e1d61 feat(ai-moderation): introduce user reputation and channel culture context
Implements a context-aware moderation system by tracking user behavior
and channel-specific norms to improve AI decision-making accuracy.

- Adds `user_reputations` table to track trust scores, clean streaks,
  and infraction history.
- Adds `channel_cultures` table to store AI-generated summaries of
  channel-specific norms and slang.
- Implements `userReputationStore` to autonomously update user scores
  based on moderation outcomes (clean vs. flagged).
- Implements `cultureLearner` and `channelCultureStore` to manage
  evolving channel contexts.
- Enhances LLM prompts to inject user reputation (trust scores,
  history) and channel culture summaries, enabling "wisdom-based"
  moderation (e.g., giving benefit of the doubt to high-trust users).
- Integrates reputation and culture updates into the existing
  `aiAnalyzer` pipeline.
2026-06-05 18:04:57 +07:00
MythEclipse f057bf1f0b refactor(ai-moderation): implement distributed locking and content-based caching
Refactors the AI moderation pipeline to improve concurrency control and
cache efficiency by moving from user-centric to content-centric caching.

- Implements a distributed locking mechanism for media analysis using
  `acquireMediaAnalysisLock` to prevent redundant LLM vision calls across
  multiple pods.
- Transitions text moderation caching from `user_mod:userId:hash` to a
  purely content-based `text_mod:hash` approach to increase hit rates.
- Enhances `getPendingMessagesByConversation` with atomic transactions
  and `FOR UPDATE SKIP LOCKED` to safely transition messages from
  `pending` to `processing` state.
- Adds `processing` status to the `AIStatus` type and database schema to
  track active analysis lifecycles.
- Implements polling logic in `llmModerationClient.ts` to wait for
  in-progress media analyses.
2026-06-05 16:56:46 +07:00
MythEclipse 09f6e80ddd fix(ai-moderation): resolve race conditions and implement processing state
Introduces a `processing` state to the AI analysis lifecycle to prevent
duplicate processing of the same messages.

- Implements row-level locking using `FOR UPDATE SKIP LOCKED` in
  `messageStore.ts` to ensure atomic message acquisition.
- Adds a `processing` status to the `AIStatus` type and database schema.
- Fixes a TOCTOU race condition in `aiAnalyzer.ts` by synchronizing
  the conversation processing lock before async database operations.
- Implements `revertStuckProcessingMessages` to recover messages stuck
  in the `processing` state due to worker crashes or timeouts.
- Updates `processBatch` and scheduling logic to correctly manage and
  release conversation-level locks.
2026-06-05 16:36:14 +07:00
MythEclipse 399919ded0 feat(ai-moderation): add anti-evasion rule for emoji spelling
- Instructs the AI to decode combinations of regional indicator emojis (e.g., 🇬 🇦 🇾) and custom letters spelling out words, rather than dismissing them as 'just a series of emojis'
- Added a specific few-shot example (Contoh 15) to demonstrate flagging this technique when used to spell banned words
2026-06-05 16:23:59 +07:00
MythEclipse 6b3f2cdecd feat(ai-moderation): tighten rules for anatomical vulgarity and BL mentions
- Added explicit zero-tolerance rule for anatomical/sexual vulgarity (e.g. titten, kontol), explicitly forbidding the AI from passing them off as 'casual conversation' or 'jokes'
- Expanded sexual_deviation rule to explicitly cover brief mentions of BL (Boys Love), yaoi, yuri, and LGBT topics, instructing the AI to flag them regardless of casual context
2026-06-05 16:16:21 +07:00
MythEclipse e00b23f9b8 fix(concurrency): eliminate all identified double-queue and race conditions
Implements full audit from double_queue_audit.md.

## Critical

fix(backend): reanalyze-batch — per-scope in-flight guard (messages.routes.ts)
  Two concurrent admin sessions clicking 'Retry All Errors' simultaneously now
  get a 409 REANALYZE_BATCH_IN_PROGRESS for the same guildId:channelId scope.
  Prevents the recovery worker from being triggered twice for the same set of
  error messages.

fix(discord-gateway): messageUpdate embed resolution skip (messageCapture.ts)
  Discord fires messageUpdate when link previews resolve 1-2s after send
  even though the message body is unchanged. Compare newContent vs
  existingContent before resetting ai_status to pending and re-queueing LLM.
  Eliminates a spurious duplicate analysis that could overwrite a valid result.

## High

fix(discord-gateway): scheduleAutoDelete idempotency (aiAnalyzer.ts)
  Add autoDeleteInFlight Set. Both processBatch and processIndividualFallback
  call scheduleAutoDelete; without the guard, a message that races through
  both paths launches two concurrent attemptAutoDeleteFlaggedMessage calls,
  producing a duplicate moderation-action log entry and a Discord 10008 error.
  The Set is cleaned up in a .finally() block after each attempt completes.

## Medium

fix(frontend): revert optimistic pending state on HTTP failure (useMessages.ts)
  Capture the prior MessageRecord inside the setMessages functional updater
  (no extra useCallback deps needed). If reanalyzeMessage() throws, restore
  the snapshot so the UI reflects the real DB state instead of lying.

fix(discord-gateway): exclude individual_analysis_exhausted from recovery queries
  Both getConversationKeysWithIncompleteAnalysis and
  getIncompleteMessagesByConversation now add a NOT LIKE guard for
  individual_analysis_exhausted. Prevents an infinite recovery loop if a bug
  ever writes both flags to the same row.

## Low

fix(discord-gateway): unified timer path in scheduleConversationAnalysis
  Remove the separate cooldown-path that created a secondary timer calling
  scheduleConversationAnalysis recursively. Replace with a single
  clear-and-reset pattern where delayMs = max(cooldownRemainder+500, debounce).
  Eliminates the edge case where both timers were live simultaneously.

## Misc

fix(backend): cast req.params.id to String() to satisfy Express typings
  Pre-existing tsc error (string | string[]) exposed by our edit.
  String() is correct; route params are always scalar strings at runtime.
2026-06-05 16:09:23 +07:00
MythEclipse 5c0a837cf0 fix(ai-moderation): eliminate double-queue on failure and spam-retry
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.
2026-06-05 15:38:31 +07:00
MythEclipse a0bf7dfdd9 chore(ai-moderation): harden LLM prompt and lexical scanner against evasion techniques and cross-lingual vulgarities 2026-06-05 15:28:04 +07:00
MythEclipse 9a02ac8d17 feat(ai-moderation): flag excessive religious jokes and satire as sara 2026-06-04 19:31:08 +07:00
MythEclipse a965fb1e08 fix(moderation): treat age_restricted skipped messages as clean instead of error 2026-06-04 19:28:59 +07:00
MythEclipse d1d4510ce8 chore(format): auto-format files with biome 2026-06-04 18:52:56 +07:00
MythEclipse 367d8696c8 fix(moderation): revert API failures to pending instead of sticking in error state 2026-06-04 18:52:37 +07:00
MythEclipse 946343ec8c fix(ai-moderation): skip caching error results to prevent false positives from transient failures 2026-06-04 17:42:23 +07:00
MythEclipse 4996acfacf refactor(ai-moderation): unify worker pool entry points with discriminated union routing 2026-06-04 17:39:40 +07:00
MythEclipse 686dc2de4f refactor(ai-moderation): remove unused count variable in stale CB error pruning loop 2026-06-04 17:35:52 +07:00
MythEclipse 121b78198c feat(ai-moderation): add circuit breaker alert system with extensible handlers 2026-06-04 17:35:26 +07:00
MythEclipse 8c67eefede refactor(ai-moderation): offload individual message analysis to worker pool and add auto-delete notifications 2026-06-04 17:34:54 +07:00