Files
ShaiBot/AGENTS.md
T
2026-09-13 22:10:34 +02:00

44 KiB
Raw Blame History

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:

ShaiBot/
├── assets/
├── buttons/
│   ├── _global/
│   ├── crumbSaucer/
│   ├── crumbleStrike/
│   ├── poker/
│   └── profile/
├── commands/
│   ├── applications/
│   └── miscellaneous/
├── events/
├── handlers/
├── modals/
├── selectMenus/
│   └── poker/
├── utils/
├── index.js
├── README.md
├── package.json
└── .env                # never commit

Important rule:

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:

/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:

D_TOKEN
M_URI
M_DB
BOT_AUTHOR_ID
PROFILE_TIMEZONE

Current profile timezone fallback is:

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:

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:

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:

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:

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:

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:

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:

{
    userId: "...",
    cookies: 2500
}

Some systems may temporarily add fields such as:

appliedCookieOperations
stealable

These fields have lifecycle semantics; do not remove them blindly.

cooldown_cookies

Per-user/per-guild cooldown document.

Conceptual shape:

{
    userId,
    guildId,

    collect: <unix timestamp>,
    steal: <unix timestamp>,
    heist: <unix timestamp>
}

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:

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:

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:

cookies >= amount

If the debit cannot be applied, the utility throws:

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:

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:

appliedCookieOperations: [
    "poker_buyin:...",
    "poker_cashout:..."
]

Purpose:

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:

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:

type: "cookies_given"
source: "give"
targetUserId

and recipient gain may carry:

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 docs/ECONOMY.md.

Key current balance values:

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:

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:

{
    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:

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:

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:

utils/crumbSlots.js

UI buttons:

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:

previousAllIn
previousBet / previousWager compatibility

Do not remove compatibility reads casually; old/persisted sessions may still rely on them.


17. Blackjack

Core logic:

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:

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:

utils/crumbRoulette.js

European roulette uses:

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:

utils/cookieHeist.js

The Saucer exposes approaches such as:

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:

utils/bankruptcy.js

Bankruptcy is a starter/recovery mechanic.

Profile integration tracks:

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:

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:

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:

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:

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:

{
    type: "game_start",
    game: "blackjack",
    source: "normal" | "run_it_back",
    wager,
    allIn,
    previousAllIn,
    previousBet
}
{
    type: "game_result",
    game,
    result: "win" | "loss" | "push",
    profit
}
{
    type: "cookie_gain",
    source: "collect" | "give" | "steal" | "bankruptcy",
    amount
}
{
    type: "cookies_given",
    source: "give",
    amount,
    targetUserId
}
{
    type: "steal_success",
    source: "steal",
    amount,
    targetUserId
}
{
    type: "stolen_from_me",
    source: "steal",
    amount,
    thiefUserId
}

Metadata conventions:

field/property names → camelCase
enum/string values   → snake_case where applicable
achievement/badge IDs → snake_case

For newer wager event metadata:

wager

is preferred over:

bet

Compatibility fallbacks may remain intentionally.


25. Activity / Time Tracking

Profiles track activity using a configured timezone.

Current default:

Europe/Berlin

Activity includes concepts such as:

currentDailyStreak
bestDailyStreak
lastActiveDate
lastGameDate
currentWeekKey
currentWeekDays
gamesByWeekday
gamesByTimeBucket

Time buckets:

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:

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:

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:

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:

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:

config_achievements

Unlocks live in:

items_achievements

Stat-backed achievements

Use a stat path and target.

These can usually be reconciled/backfilled.

Composite achievements

Measures such as:

all
any

evaluate condition arrays.

Supported condition concepts include comparison operators and array membership.

Event achievements

Use:

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:

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:

guild_badges

Typical atomic claim pattern:

guildId
badgeId
ownerId: null

then atomically set:

ownerId
unlockedAt
metadata

This guarantees simultaneous candidates cannot both own the same badge.

Never implement a guild-first badge as:

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:

/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:

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:

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:

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:

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:

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:

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:

2 per player

Community:

Flop  3
Turn  1
River 1

Actions:

Fold
Check
Call
Bet/Raise
All-In

Hand ranking:

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:

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:

Dealer = Small Blind
other player = Big Blind

So UI [D/SB] is correct.


36. Poker Profile Integration

Permanent Poker history belongs in:

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:

utils/crumbleStrike.js

Buttons:

buttons/crumbleStrike/

Handler/recovery:

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:

Strike
Defend
Heal
Revive
Limit Break

Revive behavior:

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:

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:

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:

INSUFFICIENT_COOKIES
SAME_ACCOUNT
INVALID_COOKIE_AMOUNT
OUT_OF_TURN
STATE_CONFLICT
...

Some code stores the identifier in:

error.message

other code may use:

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:

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:

node --check <modified-file.js>
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:

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:

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:

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:

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:

wager

while existing compatibility fields may continue using:

previousBet

until deliberately migrated.


50. Source-of-Truth Hierarchy

For any subsystem, prefer this hierarchy:

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:

/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:

.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:

X is not a function

When changing shared utility exports, grep consumers first.

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 Cookie balances, transfers, stealing, cooldowns, settlement, or economy idempotency, read docs/ECONOMY.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:

README.md
    Human-facing project overview, features, setup, and documentation index.

AGENTS.md
    Repository-wide architecture, invariants, and safety rules for AI agents.

docs/ARCHITECTURE.md
    Runtime, handlers, persistence, concurrency, and recovery.

docs/ECONOMY.md
    Complete Cookie economy manual, including PvP stealing mechanics,
    formulas, heat, exposure budgets, transfers, settlement, and idempotency.

docs/GAMES.md
    Crumb Saucer, Poker, Crumble Strike, and game mechanics.

docs/PROGRESSION.md
    Profiles, achievements, badges, activity, social progression,
    reconciliation, and maintenance.

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:

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

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

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

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

Will the interaction be acknowledged in time?
Is private information public?
Does the UI reflect authoritative state?
Are destructive confirmation controls invalidated afterward?

Mongo

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.