refactor(games): rework room lifecycle events and remove chess plugin
Consolidate room leave/delete event handling into RoomManager emitter, remove redundant PLAYER_LEFT publishes from GameServer, and delete the chess game plugin (board, types, tests) in favor of the new plugin architecture. Add per-module CLAUDE.md files for leveling, guild-settings, feature-flags, db, api, and panel to improve agent navigability. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
9
shared/modules/feature-flags/CLAUDE.md
Normal file
9
shared/modules/feature-flags/CLAUDE.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Feature Flags Module
|
||||
|
||||
- **No caching.** Every `isFlagEnabled()` and `hasAccess()` call hits the database directly.
|
||||
- `isFlagEnabled(flagName)` checks global on/off state. `hasAccess(flagName, context)` checks both global state AND per-entity access records (guild, user, or role).
|
||||
- Access logic: flag must be globally enabled AND user must have an explicit access grant. Grants can target guildId, userId, or roleId independently.
|
||||
- Commands declare `beta: true` and optionally `featureFlag: string` in the Command interface. `CommandHandler` intercepts beta commands and calls `hasAccess()` before execution.
|
||||
- If a command has no explicit `featureFlag`, the command name (`interaction.commandName`) is used as the flag name fallback.
|
||||
- Flag names are case-sensitive. Convention is snake_case or camelCase — no enforcement.
|
||||
- Admin management via `/featureflags` command: CRUD on flags and access grants/revokes.
|
||||
10
shared/modules/guild-settings/CLAUDE.md
Normal file
10
shared/modules/guild-settings/CLAUDE.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Guild Settings Module
|
||||
|
||||
- `updateSetting()` uses a hardcoded `keyMap` to map friendly key names to DB columns. Use exact key names (e.g., `"studentRole"` not `"studentRoleId"`). Unknown keys throw `UserError`.
|
||||
- Type coercion per column: Discord IDs → BigInt automatically; `colorRoleIds` must be array; `featureOverrides` must be object; `moderationDmOnWarn` must be boolean; `moderationAutoTimeoutThreshold` must be number. Null values set columns to NULL.
|
||||
- **Caching:** `getGuildConfig()` (in `shared/lib/config.ts`) caches transformed settings for 60 seconds. Every mutation (`upsertSettings`, `updateSetting`, `addColorRole`, `removeColorRole`) calls `invalidateGuildConfigCache(guildId)` immediately.
|
||||
- If settings don't exist for a guild, the cache returns safe defaults — no errors thrown.
|
||||
- `featureOverrides` is a sparse `Record<string, boolean>` — no keys are predefined. Consumers must check key existence.
|
||||
- **No Discord validation:** The service does not verify that role/channel IDs actually exist in Discord. Invalid IDs are stored silently.
|
||||
- `addColorRole()` / `removeColorRole()` fetch the full settings, mutate the array in JS, then upsert — this is not atomic and can race under concurrent requests.
|
||||
- `terminalMessageId` and `terminalChannelId` are separate DB columns but grouped as `terminal: { channelId, messageId }` in the cached config. Setting one without the other can create orphaned data.
|
||||
9
shared/modules/leveling/CLAUDE.md
Normal file
9
shared/modules/leveling/CLAUDE.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Leveling Module
|
||||
|
||||
- **Level is derived, not stored.** Total XP is the source of truth. `getLevelFromXp()` recalculates level from cumulative XP on every `addXp()` call. Levels are monotonic — they never decrease.
|
||||
- XP curve is a power law: `xpForLevel(n) = floor(base * n^exponent)` where defaults are `base: 100`, `exponent: 1.5`. Config comes from `gameSettingsService` (30s cache TTL).
|
||||
- Chat XP (`processChatXp()`) awards random XP between `minXp` (5) and `maxXp` (15) per message, gated by a 60-second per-user cooldown (`TimerType.COOLDOWN`, key `TimerKey.CHAT_XP`). The cooldown is upserted atomically.
|
||||
- Quest/reward XP uses `addXp()` directly — it bypasses the chat cooldown.
|
||||
- XP boost multipliers come from active `TimerType.EFFECT` timers with key `'xp_boost'` (metadata field: `multiplier`).
|
||||
- All XP values are `bigint` in the DB but converted to `Number` for arithmetic. Watch for overflow at extremely high XP values.
|
||||
- `addXp()` and `processChatXp()` run inside transactions. They emit `XP_GAINED` (fire-and-forget) which the quest system listens to — the weight equals the XP amount.
|
||||
Reference in New Issue
Block a user