diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..61f291d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,2216 @@ +# AGENTS.md — ShaiBot Repository Guide for AI Agents + +> **Repository:** ShaiBot +> **Runtime:** Node.js / CommonJS / Discord.js / MongoDB +> **Purpose of this file:** Give AI coding agents enough architectural, state, collection, concurrency, economy, progression, and interaction context to modify ShaiBot safely without rediscovering the same invariants on every task. +> +> **Critical rule:** The current repository code is always the source of truth. This document is a map and contract, not a substitute for reading the files involved in a change. If this file and code disagree, inspect the relevant implementation and update this guide after the code is settled. + +--- + +# 1. What ShaiBot Is + +ShaiBot is a modular Discord bot built around a fictional Cookie economy and a set of community/game systems. + +Major feature families include: + +- Cookie economy +- `/cookies` collection, giving, stealing, balance checks, and leaderboard +- Crumb Saucer game hub +- Slots +- Blackjack +- Roulette +- Vault Heist +- Bankruptcy/starter recovery +- Multiplayer Poker +- Player profiles +- Achievements +- Guild-unique/global badges +- Activity/day/time tracking +- Crumble Strike cooperative boss battles +- Raid planning +- Records/stat presentation +- Miscellaneous social/community commands + +The project strongly prefers **shared utilities in `utils/`** with thin command/button/select-menu wrappers. + +Do not redesign unrelated systems while implementing a focused feature. + +--- + +# 2. Repository Layout + +Typical high-level structure: + +```text +ShaiBot/ +├── assets/ +├── buttons/ +│ ├── _global/ +│ ├── crumbSaucer/ +│ ├── crumbleStrike/ +│ ├── poker/ +│ └── profile/ +├── commands/ +│ ├── applications/ +│ └── miscellaneous/ +├── events/ +├── handlers/ +├── modals/ +├── selectMenus/ +│ └── poker/ +├── utils/ +├── index.js +├── COOKIE_LOGIC.md +├── README.md +├── package.json +└── .env # never commit +``` + +Important rule: + +```text +Discord wrapper + ↓ +shared utility + ↓ +Mongo/economy/profile systems +``` + +If behavior can be reached from multiple interfaces, the implementation should exist once in a utility rather than being copied into commands/buttons. + +Example: + +```text +/cookies collect + │ + ▼ + crumbCollect.js + ▲ + │ +Crumb Saucer Bakery +``` + +--- + +# 3. Runtime / Boot Architecture + +`index.js` is the runtime entry point. + +It: + +1. Loads environment variables. +2. Creates and exports the Mongo client. +3. Creates and exports the Discord client. +4. Initializes Discord.js collections for: + - commands + - legacy commands + - aliases + - buttons + - select menus + - modals +5. Connects MongoDB before exposing handlers. +6. Initializes important indexes/systems, including Cookie and profile indexes. +7. Loads handler modules from `handlers/`. +8. Logs the Discord client in. + +Relevant environment variables include: + +```text +D_TOKEN +M_URI +M_DB +BOT_AUTHOR_ID +PROFILE_TIMEZONE +``` + +Current profile timezone fallback is: + +```text +Europe/Berlin +``` + +Do not import/use an uninitialized Mongo connection during module bootstrap if it creates a circular-initialization problem. Several utilities intentionally require `mClient` from `index.js` only after `index.js` has exported it. + +--- + +# 4. Handler / Discovery Model + +ShaiBot uses filesystem discovery. + +Handlers populate Discord.js collections such as: + +```js +client.commands +client.legacyCommands +client.aliases +client.buttons +client.selectMenus +client.modals +``` + +Do not manually wire every new button into `InteractionCreate.js` if the existing loader can discover it. + +Before adding a new interaction type: + +1. Inspect the relevant handler loader. +2. Match the expected export shape. +3. Match the existing `customId` naming convention. +4. Keep the module thin. + +`events/InteractionCreate.js` is the central router for interactions. Poker also required String Select Menu routing, so do not assume only buttons/slash commands are handled. + +--- + +# 5. Discord Interaction Conventions + +General project behavior: + +- User-facing game/menu output is usually public. +- Unauthorized/error/cooldown/debug-style feedback may be ephemeral. +- Existing messages are edited where practical. +- Animation-style systems should be rate-limit conscious. +- Button visibility is never authorization. +- Critical state must not live only in an embed/footer. + +## Potentially slow interactions + +For new code that may perform: + +- MongoDB work +- economy changes +- persistent game transitions +- profile evaluation +- public-message refreshes + +prefer acknowledging early. + +For commands: + +```js +await interaction.deferReply({ + flags: MessageFlags.Ephemeral +}); +``` + +For component flows that intentionally edit an existing message, use the interaction response method appropriate for that handler. + +Poker specifically uses early ephemeral deferral for stateful actions. + +Do not globally defer every interaction from the dispatcher. Different handlers legitimately use `reply`, `update`, `deferReply`, or `deferUpdate`. + +--- + +# 6. Interaction Ownership / Sessions + +`utils/interactionSession.js` centralizes single-owner interaction sessions. + +Its job is to make a public interactive message visible to everyone while restricting controls to the session owner. + +The session layer validates: + +```text +session.ownerId === interaction.user.id +``` + +and can validate session kind. + +It also supports legacy owner inference for older footer-based sessions. + +Use this utility for **single-owner** interaction sessions where it fits. + +Do **not** force true multiplayer systems such as Poker into an owner-only session abstraction. Multiplayer authorization must come from the multiplayer game state. + +When closing a managed interaction session, mark it inactive using the shared session utility instead of inventing a second ownership record. + +--- + +# 7. Permissions + +`utils/permissions.js` centralizes admin/author checks. + +Important concept: + +```text +Allowed administrative user += +Discord Administrator +OR +BOT_AUTHOR_ID from .env +``` + +Use `requireAdminAccess()` for admin maintenance flows rather than reimplementing permission checks. + +`BOT_AUTHOR_ID` is optional and comes from: + +```js +process.env.BOT_AUTHOR_ID +``` + +Never hard-code an author Discord ID in application logic. + +--- + +# 8. MongoDB Philosophy + +ShaiBot intentionally supports MongoDB deployments that do not rely on multi-document transactions. + +Do not casually introduce transaction requirements. + +The project instead uses combinations of: + +- atomic filtered updates +- unique indexes +- compare-and-swap/version checks +- compensating writes +- idempotency markers +- persistent recovery state + +When modifying money or persistent game state, reason about process crashes between every important write. + +--- + +# 9. Important MongoDB Collections + +Current important collections include: + +```text +items_cookies +cooldown_cookies +items_games +items_special_games +stats_profiles +items_achievements +guild_badges +config_achievements +``` + +Other feature-specific collections may exist. Read the utility owning that system rather than assuming collection names. + +## `items_cookies` + +Cookie balances. + +Core shape: + +```js +{ + userId: "...", + cookies: 2500 +} +``` + +Some systems may temporarily add fields such as: + +```js +appliedCookieOperations +stealable +``` + +These fields have lifecycle semantics; do not remove them blindly. + +## `cooldown_cookies` + +Per-user/per-guild cooldown document. + +Conceptual shape: + +```js +{ + userId, + guildId, + + collect: , + steal: , + heist: +} +``` + +Cooldown values are Unix timestamps indicating availability. + +## `items_games` + +Existing persistent game/session state used by legacy/simple systems. + +Do not migrate a stable game out of this collection merely for aesthetic consistency unless there is a real architectural need. + +## `items_special_games` + +Persistent state for lifecycle-heavy/special games. + +Currently used by Poker with a common envelope and game-specific payload. + +Keep shared envelope concerns generic but do not build a giant generic game engine. + +## Profile collections + +`utils/profileSystem.js` owns: + +```text +stats_profiles +items_achievements +guild_badges +config_achievements +``` + +Treat these collection names as part of the profile system contract. Do not duplicate profile collection logic throughout command/button modules. + +--- + +# 10. Cookie Economy — Source of Truth + +`utils/cookieEconomy.js` is the shared economy utility. + +Use it rather than writing ad-hoc balance updates in game/application files. + +Important primitives include concepts such as: + +```text +getCookieBalance +changeCookies +transferCookies +getCooldown +setCooldown +claimCooldown +Cookie vault helpers +idempotent Cookie operations for Poker +temporary ephemeral reply helpers +index initialization +``` + +## Balance safety + +Negative balance changes use an atomic filter: + +```text +cookies >= amount +``` + +If the debit cannot be applied, the utility throws: + +```text +INSUFFICIENT_COOKIES +``` + +Code may represent expected failures via `error.message` rather than `error.code`. + +## Transfers + +Transfers intentionally do not depend on Mongo transactions. + +Safe conceptual sequence: + +```text +atomic debit source +→ credit destination +→ if destination credit fails, best-effort compensate source +``` + +Critical compensation failure must remain visible in logs. + +Never implement a second transfer function inside a command. + +## Cooldowns + +Use `claimCooldown()` for actions where simultaneous/double-click interactions must not both claim the same cooldown. + +It handles insert/update races with unique indexes. + +--- + +# 11. Cookie Operation Idempotency + +Some systems, especially Poker, use idempotent Cookie operation IDs. + +Temporary field: + +```js +appliedCookieOperations: [ + "poker_buyin:...", + "poker_cashout:..." +] +``` + +Purpose: + +```text +operation retries after crash +must not debit/pay twice +``` + +Lifecycle rule: + +1. Marker exists during uncertain/recoverable period. +2. Persistent game state confirms operation. +3. Marker is cleared after durable success. +4. If the array becomes empty, unset the field rather than retaining `[]`. + +Never clear an idempotency marker before recovery is safe without it. + +--- + +# 12. `/cookies` Application + +`commands/applications/cookies.js` provides the main Cookie user interface. + +Current subcommands include: + +```text +collect +steal +check +give +leaderboard +``` + +## Collect + +`/cookies collect` delegates to `utils/crumbCollect.js`. + +The Crumb Saucer Bakery must use the same shared collection implementation so both interfaces share: + +- cooldown +- reward generation +- balance update +- profile tracking + +Do not reintroduce a separate Bakery collection algorithm. + +## Give + +Use the shared economy transfer path. + +Profile metadata currently distinguishes giving with values such as: + +```js +type: "cookies_given" +source: "give" +targetUserId +``` + +and recipient gain may carry: + +```js +type: "cookie_gain" +source: "give" +senderUserId +``` + +Preserve this metadata because achievements/social relationship tracking depend on it. + +--- + +# 13. PvP Cookie Stealing + +Normal player-to-player stealing is separate from Vault Heist. + +The current steal model is documented in `COOKIE_LOGIC.md`. + +Key current balance values: + +```text +Cooldown: 15 minutes +Base/equal-wealth chance: 55% +Success range: 30%–80% +Wealth scaling: 12.5 points per 10x wealth ratio +Heat per recent attempt: 7.5 points +Heat lifetime: 1 hour, linear decay +Victim hourly loss budget: 15% of balance at window start +Per-hit hard cap: 25,000 Cookies +``` + +Loot bands: + +```text +50% → 1–3% +30% → 3–5% +15% → 5–8% +5% → 8–10% +``` + +Final loot is modified by difficulty and capped by: + +- victim balance +- remaining hourly exposed budget +- hard cap + +## Victim heat + +Profile-side `stealProtection.attempts` tracks recent valid robbery attempts. + +All attackers contribute to the same victim heat. + +The current attempt must not penalize itself. + +## Victim exposure budget + +`items_cookies.stealable` may contain: + +```js +{ + amount, + windowStartedAt +} +``` + +The first valid attempt after the window expires opens/breaches the vault and initializes exposure. + +Successful robbery reserves exposure atomically **before** transferring Cookies. + +If the transfer fails, refund the reservation. + +Do not consume the attacker's cooldown for a robbery rejected only because the victim's exposure budget is exhausted. + +--- + +# 14. Crumb Saucer Hub + +`utils/crumbSaucer.js` is the shared hub renderer/logic. + +`commands/applications/crumbSaucer.js` opens the hub. + +Buttons live primarily under: + +```text +buttons/crumbSaucer/ +``` + +Current hub-connected features include: + +- Cookie Bakery +- Slots +- Blackjack +- Roulette +- Vault Heist +- Bankruptcy +- Profile +- Poker + +Do not add a new top-level command for a feature whose product requirement is to live inside the Saucer. + +--- + +# 15. Cookie Bakery / `crumbCollect.js` + +`utils/crumbCollect.js` is authoritative for Cookie collection. + +The Bakery and `/cookies collect` share it. + +Current conceptual behavior: + +```text +claim shared "collect" cooldown +→ generate reward +→ change Cookies +→ track profile Cookie gain +→ return amount / next timestamp / balance +``` + +Keep the same cooldown key across all collection interfaces. + +--- + +# 16. Slots + +Core logic: + +```text +utils/crumbSlots.js +``` + +UI buttons: + +```text +buttons/crumbSaucer/slots*.js +``` + +Features include: + +- wager buttons +- symbol reels +- triplet payouts +- jackpot +- Run It Back +- profile events +- first-time global triplet badges + +Historical caution: + +Slots does not currently require a dedicated All-In button to derive All-In state. A wager equal to the player's committed available balance can still be considered All-In where the metadata logic requires it. + +Run It Back metadata interfaces include compatibility fields such as: + +```text +previousAllIn +previousBet / previousWager compatibility +``` + +Do not remove compatibility reads casually; old/persisted sessions may still rely on them. + +--- + +# 17. Blackjack + +Core logic: + +```text +utils/crumbBlackjack.js +``` + +Important behavior: + +- Hit +- Stand +- Double +- Dealer stands on 17 +- Natural Blackjack pays 3:2 +- Push returns wager +- All-In +- Run It Back +- persistent/owned active hands +- replay metadata +- profile/achievement/badge integration + +## Immutable wager history + +All-In and Run It Back achievements must use immutable wager-time metadata. + +Do not infer a historical All-In from the player's **current** Cookie balance. + +This exists specifically to handle cases such as: + +```text +player goes All-In +→ hand pushes +→ Cookies are gifted to player +→ player uses Run It Back for the original amount +``` + +The sequence must remain valid even though the live balance changed. + +## Resolution safety + +Protect hand resolution against rapid repeated interactions. + +A result must not pay twice. + +--- + +# 18. Roulette + +Core logic: + +```text +utils/crumbRoulette.js +``` + +European roulette uses: + +```text +0–36 +``` + +Current simple bets include: + +- Red +- Black +- Odd +- Even +- Zero + +Zero loses for Red/Black/Odd/Even. + +Roulette uses rate-limit-conscious message edits for pseudo-animation. + +It supports All-In and Run It Back metadata/profile tracking. + +Do not convert the animation into a high-frequency message spam loop. + +--- + +# 19. Vault Heist + +Core logic: + +```text +utils/cookieHeist.js +``` + +The Saucer exposes approaches such as: + +```text +Sneak +Hack +Smash +``` + +All approaches share the same Heist cooldown. + +Heist has risk/reward rules with caps and profile integration. + +Heist is a game against the bot/house vault and is separate from PvP `/cookies steal`. + +Profile tracking includes both: + +- generic game outcome +- vault-specific stolen amount + +Do not collapse those into one event if both semantics are used by achievements/stats. + +--- + +# 20. Bankruptcy + +Core logic: + +```text +utils/bankruptcy.js +``` + +Bankruptcy is a starter/recovery mechanic. + +Profile integration tracks: + +```text +special.bankruptcyCount +special.bankruptcyRecoveryBest +``` + +Recovery progression currently supports achievements around rebuilding after Bankruptcy. + +Important invariant: + +`bankruptcyRecoveryBest` should reflect **actual vault balance after Bankruptcy**, not merely lifetime Cookie gains. + +A new Bankruptcy resets the current recovery run while previously unlocked achievements remain unlocked. + +--- + +# 21. Run It Back + +Run It Back exists across supported Saucer games. + +Profile system tracks per-game consecutive Run It Back use. + +Concept: + +```text +source = "run_it_back" +``` + +A normal/new wager breaks that game's chain. + +Outcomes such as win/loss/push do not inherently break the streak because the streak represents the player's decision to immediately replay. + +Current supported games include: + +```text +slots +blackjack +roulette +``` + +Use canonical `wager` metadata for new events. + +Compatibility reads for old `bet`/`previousBet` may intentionally remain in `profileSystem.js`. + +--- + +# 22. "I Can Do This All Day" + +Cross-game sequence: + +```text +All-In +→ round resolves +→ Run It Back +→ same committed wager +``` + +The previous All-In and previous wager are stored as immutable historical metadata. + +Do not replace this with a check against current vault amount. + +Global first-time badge and normal achievement may both exist for this concept. + +--- + +# 23. Profile System — Central Contract + +`utils/profileSystem.js` is the central profile/progression module. + +It owns: + +```text +stats_profiles +items_achievements +guild_badges +config_achievements +``` + +Major responsibilities include: + +- default stats schema +- profile stats reads/writes +- profile event tracking +- game outcomes +- Run It Back tracking +- activity/day/time tracking +- social relationship tracking +- achievement requirement evaluation +- event-condition achievements +- achievement notifications +- global badge claiming +- profile rendering +- category formatting +- profile indexes/system initialization + +Do not build game-specific alternative achievement storage. + +--- + +# 24. Profile Event Vocabulary + +Prefer adding metadata to established events rather than inventing redundant parallel event systems. + +Common examples: + +```js +{ + type: "game_start", + game: "blackjack", + source: "normal" | "run_it_back", + wager, + allIn, + previousAllIn, + previousBet +} +``` + +```js +{ + type: "game_result", + game, + result: "win" | "loss" | "push", + profit +} +``` + +```js +{ + type: "cookie_gain", + source: "collect" | "give" | "steal" | "bankruptcy", + amount +} +``` + +```js +{ + type: "cookies_given", + source: "give", + amount, + targetUserId +} +``` + +```js +{ + type: "steal_success", + source: "steal", + amount, + targetUserId +} +``` + +```js +{ + type: "stolen_from_me", + source: "steal", + amount, + thiefUserId +} +``` + +Metadata conventions: + +```text +field/property names → camelCase +enum/string values → snake_case where applicable +achievement/badge IDs → snake_case +``` + +For newer wager event metadata: + +```text +wager +``` + +is preferred over: + +```text +bet +``` + +Compatibility fallbacks may remain intentionally. + +--- + +# 25. Activity / Time Tracking + +Profiles track activity using a configured timezone. + +Current default: + +```text +Europe/Berlin +``` + +Activity includes concepts such as: + +```text +currentDailyStreak +bestDailyStreak +lastActiveDate +lastGameDate +currentWeekKey +currentWeekDays +gamesByWeekday +gamesByTimeBucket +``` + +Time buckets: + +```text +overnight +morning +afternoon +evening +``` + +Achievement design preference: + +- normal progression should use broad buckets +- exact late-night hours should not be required for meaningful completion +- optional hidden overnight content is acceptable + +Do not scatter hour-boundary definitions through games; use centralized activity context. + +--- + +# 26. Daily Narrative State + +Profiles also maintain bounded/current-day state for narrative/composite achievements. + +Conceptual fields include: + +```text +daily.dateKey +daily.gamesPlayed +daily.gamesWon +daily.gamesLost +daily.collectedCookies +daily.gambledAfterCollect +daily.wonAnyGame +daily.lostAnyGame +daily.gaveCookies +daily.stoleCookies +``` + +Daily state resets when the profile activity date changes. + +Do not turn this into an unbounded event history. + +--- + +# 27. Social Relationship State + +Profiles track durable relationship sets such as: + +```js +social: { + giftedTo: [], + receivedFrom: [], + successfullyStolenFrom: [], + successfullyStolenBy: [] +} +``` + +Use `$addToSet` semantics because these arrays represent unique users. + +Current social achievement scale is intentionally suited to a small community: + +```text +5 unique players +10 unique players +``` + +Avoid introducing 25/50-user requirements unless the community size changes materially. + +Derived relationship flags include concepts such as: + +```text +Et Tu, Crumb? +No Hard Feelings +Cookie Laundering +``` + +Historical gifting relationships cannot be fabricated if recipient IDs were not stored historically. + +--- + +# 28. Achievement Model + +Achievements are personal progression and can be earned by multiple players. + +Definitions live in: + +```text +config_achievements +``` + +Unlocks live in: + +```text +items_achievements +``` + +## Stat-backed achievements + +Use a stat path and target. + +These can usually be reconciled/backfilled. + +## Composite achievements + +Measures such as: + +```text +all +any +``` + +evaluate condition arrays. + +Supported condition concepts include comparison operators and array membership. + +## Event achievements + +Use: + +```text +measure: "event" +eventType +eventConditions +eventMode +``` + +They depend on the current event context and generally must not be historically invented. + +Event operators include concepts such as: + +```text +eq +neq +gt +gte +lt +lte +in +not_in +truthy +falsy +``` + +## Collector achievements + +`achievementPercentage` achievements should use the same definition of "collectible" everywhere. + +Event-only achievements are intentionally excluded from Completionist-style denominators where they cannot reliably be backfilled. + +Do not let the profile UI denominator disagree with the evaluator denominator. + +--- + +# 29. Achievement Notifications + +Achievement unlock notification is centralized. + +Do not independently announce achievements from each game. + +The notification should mention the user who earned the achievement where supported by the current implementation. + +If an achievement is inserted directly by maintenance reconciliation, public live-game notification is intentionally not required. + +Maintenance writes should log what was backfilled. + +--- + +# 30. Global / Guild-Unique Badges + +Badges are unique guild-wide collectibles. + +Collection: + +```text +guild_badges +``` + +Typical atomic claim pattern: + +```text +guildId +badgeId +ownerId: null +``` + +then atomically set: + +```text +ownerId +unlockedAt +metadata +``` + +This guarantees simultaneous candidates cannot both own the same badge. + +Never implement a guild-first badge as: + +```text +find unclaimed +→ then separate uncontrolled update +``` + +Manual/hidden badges may be intentionally assigned directly in MongoDB. + +Current examples include: + +- game firsts +- Slots triplets +- All-In/Run It Back firsts +- hidden bug-hunter/beta-tester badges +- Poker firsts + +Keep historical badge IDs stable once they may have owners. + +--- + +# 31. Profile Maintenance / Admin + +`utils/profileMaintenance.js` provides reconciliation/audit behavior. + +`commands/applications/profileAdmin.js` exposes administrator tools. + +Admin access uses the shared permission utility. + +Current concepts include: + +```text +/profileadmin reconcile +/profileadmin audit +maintenance reward/status +``` + +## Reconciliation + +Reconciliation: + +- evaluates stat-backed/backfillable achievements +- skips event-only achievements +- can dry-run +- logs actual/dry-run unlocks + +Do not treat a high event-only skip count as an error by itself. + +## Audit + +Audit validates structural consistency such as: + +- game W/L/P totals +- non-negative economy counters +- activity schema +- date/week keys +- achievement definition validity +- composite operators +- event operators +- orphaned achievement docs +- badge counts +- maintenance reward claim arrays +- Poker profile fields where integrated + +## Maintenance rewards + +Maintenance compensation is designed to be idempotent by reward ID. + +It should not inflate lifetime gameplay/economy achievements simply because the bot was offline. + +--- + +# 32. Achievement / Badge ID Stability + +Once users may have unlocked an ID, treat it like a database API key. + +Prefer: + +```text +active: false +legacy: true +``` + +over deleting or renaming a historical definition. + +When replacing tiers, preserve old unlock data and introduce the new live IDs/tier definitions deliberately. + +--- + +# 33. Profile Categories + +Category labels are formatted centrally. + +Common categories include: + +```text +economy +stealing +victim +vault +slots +blackjack +roulette +heist +poker +collector +activity +other +games +``` + +If a new category appears as a generic `🏅 Name`, add an explicit category label only when that category is truly intended. + +Do not create an empty UI category solely for naming aesthetics. + +--- + +# 34. Poker — Architecture + +Poker is a special persistent multiplayer game inside the Crumb Saucer. + +Important files: + +```text +utils/pokerEngine.js +utils/crumbPoker.js +utils/pokerDiscord.js +utils/pokerInteractions.js +utils/pokerProfile.js +handlers/poker.js +buttons/poker/* +selectMenus/poker/* +``` + +See any Poker-specific guide in the repository for deeper details. + +Core product rules: + +```text +2–6 human players +Crumb Saucer entry only +no /poker command +1,000 default buy-in +10 / 20 blinds +90 second turns +single pot only +no side pots +no tournaments +no bots +no rake +no spectators in V1 +``` + +## Poker persistence + +`items_special_games` is temporary authoritative live/recovery state. + +Game records and membership records coexist there using `gameType`/`recordType`. + +Settled games are deleted after all value is safely returned. + +Permanent history goes to profiles—not raw closed game documents. + +## Poker concurrency + +Authoritative game records use optimistic `version` updates. + +Never perform broad unversioned authoritative writes from interaction handlers. + +Mongo `_id` must be stripped from domain objects before replacement. A previous bug demonstrated that cloning BSON `ObjectId` and feeding it into `replaceOne()` can trigger immutable `_id` errors. + +## Poker economy + +Cookie economy crosses the Poker boundary only for: + +```text +buy-in +cash-out/refund +``` + +Betting uses internal stacks. + +Idempotent operation markers protect recovery. + +## Poker privacy + +Never expose opponent hole cards publicly or in another player's private response. + +A live player equity calculator was intentionally deferred for fairness. + +## Poker UI + +Public embed emphasizes: + +- cards dealt +- street +- pot +- community cards +- positions +- current turn +- remaining turn time +- result/hand complete state + +Private Cards view reminds the player whether it is their turn. + +## Poker lifecycle + +Table concepts include: + +```text +WAITING +PREFLOP +FLOP +TURN +RIVER +NEXT_HAND +CLOSING +CLOSED (short transition before cleanup) +``` + +`NEXT_HAND` is rendered as **HAND COMPLETE** for users. + +Lobby timeout is disabled during active hands and refreshed after a hand ends. + +Timeout action uses the same authoritative Fold transition. + +Host cancellation outside active hands cashes out everyone safely and then cleans the game record. + +--- + +# 35. Poker Rules / Single Pot + +ShaiBot Poker is simplified Texas Hold'em. + +Hole cards: + +```text +2 per player +``` + +Community: + +```text +Flop 3 +Turn 1 +River 1 +``` + +Actions: + +```text +Fold +Check +Call +Bet/Raise +All-In +``` + +Hand ranking: + +```text +Royal Flush +Straight Flush +Four of a Kind +Full House +Flush +Straight +Three of a Kind +Two Pair +Pair +High Card +``` + +## Single-pot invariant + +No side pots. + +At hand start: + +```text +handCap = smallest participating starting stack +``` + +Each player can commit at most that cap for the hand. + +Do not remove this cap unless side pots are intentionally designed across engine, persistence, UI, tests, and settlement. + +Heads-up positions: + +```text +Dealer = Small Blind +other player = Big Blind +``` + +So UI `[D/SB]` is correct. + +--- + +# 36. Poker Profile Integration + +Permanent Poker history belongs in: + +```text +stats_profiles.games.poker +``` + +Concepts include: + +- hands played/won/lost/pushed +- Cookies won/lost +- largest pot won +- All-Ins / All-In wins +- showdowns / showdown wins +- action counts +- fold wins +- full-table wins +- pocket Aces wins +- 7-2 wins +- win/loss streaks +- hand-rank counters + +Poker profile writes happen **after authoritative Poker state succeeds**. + +A profile failure must never corrupt/rollback a valid pot settlement. + +Profile processing uses bounded de-duplication markers; do not turn them into permanent unbounded logs. + +Poker achievements/global badges use the existing profile/badge system, not a second Poker-specific achievement database. + +--- + +# 37. Crumble Strike + +Core logic: + +```text +utils/crumbleStrike.js +``` + +Buttons: + +```text +buttons/crumbleStrike/ +``` + +Handler/recovery: + +```text +handlers/crumbleStrike.js +``` + +Crumble Strike is a cooperative multiplayer boss battle with significantly more lifecycle complexity than ordinary Saucer games. + +Core concepts include: + +- encounter spawning +- recruitment +- author-controlled start +- later joining +- encounter lifetime +- committed Cookie stakes +- victory/failure cleanup +- DPS/Tank/Healer roles +- job selections +- tiers +- HP +- Energy +- Limit resource +- revive progress +- boss state +- persistent recovery + +Actions include: + +```text +Strike +Defend +Heal +Revive +Limit Break +``` + +Revive behavior: + +```text +normal roles → +1 progress +Healers → +2 progress +3 progress → revive +``` + +Do not copy Crumble Strike complexity into simpler games merely because it is multiplayer. + +When modifying it, trace encounter expiry/recovery and Cookie settlement carefully. + +--- + +# 38. Raid Planner + +Core utility: + +```text +utils/raidPlanner.js +``` + +Raid planning uses Discord embeds/reactions. + +Participation may count unique participants across reactions without forcibly removing reactions. + +Reaction add/remove events are routed through the event system. + +Be mindful of Discord partials because reaction/message/user partials are enabled. + +--- + +# 39. Records / Presentation Utilities + +The repository includes: + +```text +utils/profileRecords.js +commands/applications/records.js +``` + +and profile display code. + +Before adding a duplicate "records" or statistics rendering path, inspect these modules. + +Keep presentation logic separate from authoritative mutation logic. + +--- + +# 40. Error Handling Conventions + +Expected domain errors often use stable strings: + +```text +INSUFFICIENT_COOKIES +SAME_ACCOUNT +INVALID_COOKIE_AMOUNT +OUT_OF_TURN +STATE_CONFLICT +... +``` + +Some code stores the identifier in: + +```js +error.message +``` + +other code may use: + +```js +error.code +``` + +When mapping expected errors to friendly UI, support the conventions used by that subsystem. + +Do not expose raw internal identifiers when a user-friendly message exists. + +Unexpected errors should be logged/propagated rather than silently converted into misleading user errors. + +--- + +# 41. Concurrency Rules + +Assume users will: + +- double-click +- spam buttons +- act simultaneously +- race timeouts +- change balances between screens +- restart the bot mid-operation + +For any new stateful/economy feature, ask: + +```text +Can two interactions both read the same old state? +Can both pay? +Can both debit? +Can timeout and click both win? +Can a process restart after money changes but before state persists? +``` + +Use the existing pattern appropriate to the subsystem: + +- atomic filtered Mongo update +- unique index +- cooldown claim +- state/version CAS +- idempotent operation ID +- result/resolution marker +- compensating write + +Do not rely on JavaScript process-local flags for correctness that must survive restart. + +--- + +# 42. Embed / Footer State + +Historical ShaiBot flows sometimes encode owner/session information in embed footers. + +Modern rule: + +- footer state may be used for navigation/legacy compatibility +- do not treat embed/footer content as authoritative economy/game state +- prefer persistent DB/session records for critical state + +When editing old footer-based flows, preserve compatibility unless deliberately migrating all buttons that depend on it. + +--- + +# 43. Public vs Private UI + +Public by default: + +- normal game output +- menus +- profile pages +- achievement announcements +- global badge announcements + +Ephemeral/private where appropriate: + +- unauthorized control feedback +- validation errors +- cooldown/debug notices +- Poker hole cards +- admin maintenance replies +- destructive confirmation dialogs + +Do not accidentally make a public `followUp()` where the content contains private game information. + +--- + +# 44. PM2 / Deployment + +Typical production lifecycle: + +```bash +node --check +pm2 restart ShaiBot +pm2 logs ShaiBot --lines 100 +``` + +For multiple changed JS files, syntax-check each important entry/utility before restart. + +A silent `node --check` is success. + +Do not claim a deploy is safe solely because syntax checking passed; state/economy changes require live or controlled testing. + +--- + +# 45. Testing Philosophy + +The repository historically had limited automated testing, while Poker introduced pure-engine/state tests during development. + +General recommendation: + +- pure deterministic engines should have automated tests +- economy/state integration requires concurrency/recovery testing +- existing stable systems without tests should not be broadly rewritten merely to make them test-shaped + +Useful manual race tests: + +```text +double click +two users act simultaneously +balance changes between screens +timeout vs click +restart during state transition +retry after partial write +``` + +When tests exist in the repository, preserve them when modifying the covered module. + +--- + +# 46. Database Migrations + +Prefer idempotent migrations. + +Good migration behavior: + +- `$ifNull` for new profile fields +- `$mergeObjects` when extending nested schemas +- upsert definitions by stable ID +- preserve existing values +- preserve historical unlock IDs +- dry-run reconciliation before bulk unlock writes + +Avoid: + +- overwriting whole profile subdocuments unnecessarily +- blind destructive renames +- dropping historical achievement IDs +- inventing historical data that was never tracked + +If data cannot be reconstructed reliably, start tracking from rollout date. + +--- + +# 47. Indexes + +Indexes are part of correctness, not only performance. + +Examples: + +```text +items_cookies unique userId +cooldown_cookies unique userId + guildId +profile collections indexes established by profile initializer +items_special_games.gameId unique +``` + +Poker also uses indexes for: + +- game type/guild/state +- player membership lookup +- Discord message lookup +- stale/updated game lookup + +Before changing a unique-key assumption, inspect the initializer and production migration commands. + +--- + +# 48. Schema Versioning / Auditing + +Profiles currently use: + +```text +profileSchemaVersion >= 2 +``` + +where expected by the current audit. + +Do not bump a schema version casually without: + +1. default schema update +2. migration +3. audit update +4. compatibility consideration + +The audit is intended to reveal structural drift after rollouts. + +--- + +# 49. Naming Conventions + +General preferred naming: + +```text +JavaScript fields: camelCase +event values: snake_case where appropriate +achievement IDs: snake_case +badge IDs: snake_case +Mongo collections: existing repository convention +``` + +Do not rename IDs that have already escaped into Mongo simply for cosmetic consistency. + +New event wager metadata should use: + +```text +wager +``` + +while existing compatibility fields may continue using: + +```text +previousBet +``` + +until deliberately migrated. + +--- + +# 50. Source-of-Truth Hierarchy + +For any subsystem, prefer this hierarchy: + +```text +authoritative DB / engine state + ↓ +shared utility + ↓ +Discord rendering + ↓ +footer/customId hints +``` + +Never reverse this by reconstructing critical facts from an embed when durable state exists. + +--- + +# 51. Adding a New Crumb Saucer Game + +Preferred process: + +1. Put game rules/shared state in `utils/`. +2. Reuse `cookieEconomy.js`. +3. Add thin buttons under the established directory. +4. Reuse profile events/stats. +5. Define authorization explicitly. +6. Persist immutable wager metadata needed later. +7. Protect resolution against repeated interactions. +8. Check house liquidity for house-funded games. +9. Use internal stacks instead of repeated balance mutation for multiplayer games where appropriate. +10. Keep DB state authoritative. +11. Add recovery if value can be stranded across restarts. +12. Add audit/progression changes only after core economy correctness. + +Do not create a top-level command when the product requirement is Saucer-only. + +--- + +# 52. Adding an Achievement + +Before adding an achievement, determine which type it is. + +## Lifetime/stat-backed + +Use a durable stat. + +Benefits: + +- easy audit +- reconciliation +- progress display + +## Composite + +Use existing durable state paths and `all`/`any`. + +## Event-only + +Use when the accomplishment genuinely depends on the exact event context and cannot be reconstructed later. + +Keep event-only accomplishments out of collector denominators where necessary. + +## Hidden + +Set secret/hidden semantics in the definition rather than hiding it with ad-hoc UI code. + +After inserting definitions: + +```text +/profileadmin audit +/profileadmin reconcile dry-run:true +``` + +--- + +# 53. Adding a Global Badge + +Use the existing badge collection and atomic claim helper. + +Decide: + +- guild-unique? +- hidden? +- gameplay-claimed or manually awarded? +- metadata needed? +- meaningful "first" rather than arbitrary simultaneous completion? + +Do not create "first" badges where multiple players necessarily complete the same event simultaneously unless tie ownership has a defined rule. + +--- + +# 54. Maintenance Reward Design + +Maintenance compensation is not ordinary gameplay income. + +Keep it idempotent using a stable reward ID. + +Do not route it through normal profile gain tracking if doing so would artificially unlock economy achievements. + +Admin maintenance actions should: + +- defer ephemerally +- support dry-run where destructive/bulk +- log results +- be safe to retry + +--- + +# 55. Security / Secrets + +Never commit: + +```text +.env +Discord bot token +Mongo credentials +private administrative secrets +``` + +If credentials are exposed, rotate them. + +Do not print secrets into: + +- public embeds +- user-facing errors +- repository documentation +- screenshots/logs intended for public use + +--- + +# 56. Performance Guidance + +Avoid unnecessary sequential Mongo round-trips. + +However, do not sacrifice correctness for fewer calls. + +Common performance priorities: + +- acknowledge Discord interactions early when work may be slow +- reuse already-fetched documents/definitions where clean +- avoid querying achievement definitions multiple times in one pipeline when the same snapshot can be reused +- avoid high-frequency message edits +- keep arrays bounded when they are only for idempotency/recent history + +Do not perform a broad performance refactor during an unrelated bug fix unless the issue is causing real failures. + +--- + +# 57. Known Historical Failure Modes / Lessons + +These patterns have caused bugs during development and are worth guarding against. + +## Missing/renamed exports + +Buttons/admin commands import central utilities. If a helper is renamed or omitted from `module.exports`, runtime errors look like: + +```text +X is not a function +``` + +When changing shared utility exports, grep consumers first. + +## Embed footer assumptions + +Some older profile/navigation buttons once assumed an embed/footer always existed. Use optional access and/or centralized session helpers. + +## Mongo immutable `_id` + +Do not clone BSON `_id` into replacement domain objects. + +## Internal errors leaking to users + +Expected internal errors such as `INSUFFICIENT_COOKIES` must be mapped to friendly text. + +## Stale interaction confirmation controls + +After a destructive confirmation succeeds or is explicitly cancelled, remove/disable the confirmation controls. + +## Discord timeout + +If state/profile work becomes slow, the interaction may succeed server-side but Discord displays "This interaction failed." New expensive interactions should acknowledge early. + +## Historical state inferred from current balance + +Never determine old All-In/replay facts from current Cookies. + +--- + +# 58. AI Agent Safe-Change Workflow + +Before modifying code: + +1. Read this file. +2. Read `README.md`. +3. If touching Cookies/steal logic, read `COOKIE_LOGIC.md`. +4. Read every utility directly involved. +5. Read its command/button consumers. +6. Search for imports/exports of functions being changed. +7. Identify the authoritative state location. +8. Identify concurrency/restart requirements. +9. Identify profile/achievement side effects. +10. Make the smallest coherent change. + +After modifying: + +1. Syntax-check changed JS. +2. Search for stale imports/field names. +3. Review Mongo update filters for race safety. +4. Verify expected error mapping. +5. Restart in a controlled environment. +6. Inspect PM2 logs. +7. Exercise the affected interaction. +8. Inspect relevant Mongo document(s). +9. Run profile audit/reconcile dry-run if profile schema/definitions changed. +10. Update this guide if an architectural contract changed. + +--- + +# 59. What Not to Do + +Avoid these unless explicitly requested as a coordinated redesign: + +- Mongo transaction dependency +- giant new framework around stable utilities +- duplicate Cookie balance logic +- duplicate profile system +- duplicate achievement/badge storage +- raw balance writes from game buttons +- trusting Discord button visibility for authorization +- storing critical state only in embeds +- global deferral of every interaction +- side-pot infrastructure for Poker +- deleting historical achievement/badge IDs +- unbounded idempotency/history arrays +- broad refactor while fixing one isolated bug +- inventing historical profile data that was never recorded + +--- + +# 60. Repository Documentation Map + +Use these docs together: + +```text +README.md + Broad feature/product overview and contribution conventions. + +COOKIE_LOGIC.md + Detailed /cookies steal/robbery rules and collection contracts. + +AGENTS.md + Cross-repository AI-agent architectural/safety guide. + +Poker-specific agent guide (if present) + Deep Poker state/economy/rules/recovery contract. + +Phase/rollout notes (if retained) + Historical implementation context; current code still wins. +``` + +If an older phase note disagrees with current implementation, do not regress current code to match the note. + +--- + +# 61. Current Stable Design Principles + +ShaiBot generally values: + +```text +shared utilities +thin Discord wrappers +atomic economy operations +explicit metadata +persistent critical state +recoverable multiplayer state +idempotent bulk/admin operations +centralized profile progression +public fun / private sensitive data +simple rules over unnecessary abstraction +``` + +When choosing between a clever abstraction and a small explicit implementation, prefer the latter unless reuse is already proven. + +--- + +# 62. Final Invariants Checklist + +Before shipping any stateful feature change, answer these. + +## Economy + +```text +Can Cookies duplicate? +Can Cookies disappear? +Can a debit/pay happen twice? +Is insufficient balance checked atomically? +If a multi-write operation fails halfway, is it recoverable/compensated? +``` + +## Game state + +```text +What is authoritative? +Can two clicks both win? +What happens after restart? +What happens to stale buttons? +Can someone act on another user's session/turn? +``` + +## Profile + +```text +Is the stat durable? +Should it be backfillable? +Does it affect collector percentage? +Can the same event be counted twice? +Does profile failure compromise game settlement? +``` + +## Discord + +```text +Will the interaction be acknowledged in time? +Is private information public? +Does the UI reflect authoritative state? +Are destructive confirmation controls invalidated afterward? +``` + +## Mongo + +```text +Are update filters race-safe? +Are unique indexes relied upon? +Are new fields migrated safely? +Are arrays bounded where appropriate? +Are historical IDs preserved? +``` + +If any answer is unclear, inspect the owning utility before shipping. + +--- + +# 63. Maintenance of This File + +Update `AGENTS.md` when any of these materially change: + +- collection names +- runtime/handler loading +- economy sequencing +- core cooldown/steal rules +- profile schema/version +- achievement evaluation model +- badge claim model +- Crumb Saucer game list +- Poker state/rules/lifecycle +- Crumble Strike persistence/lifecycle +- authorization/session model +- admin/audit workflow + +Do not update this file with every cosmetic UI tweak. + +The purpose is to preserve architectural knowledge and safety invariants so future maintainers and AI agents can make changes confidently without re-learning the same failure modes.