veratown-prototype

Casino Games And Rewards — Implementation Plan

Status: Phase 0 is complete, Phase 1 wallet/balance foundations are implemented, and Phase 2 rules, persistence, recovery, and gated command paths are implemented, including room-scoped database rules/location bindings, the supplied rule/limit revisions, and entry funding. The documented slot baseline has 95.4383735% theoretical RTP. On 2026-10-15, the operator confirmed successful live qualification of slots, roulette, and location access and approved activation. CASINO_GAMEPLAY_ENABLED is now true for these chips-only games. This records operator-reported evidence; no new live run or production inspection was performed by the activation change.

Shared economy update: the wallet ownership, entry-grant approval, and retention decisions in the historical Phase 0/1 sections below are superseded by the shared economy design. Casino and future games use the shared per-member credits account; finalized economy ledger records are retained for 60 days. Migration and rollback steps are in the shared economy guide.

Purpose

Specify a staged casino feature for the Veratown prototype that offers games with a real in-bot wager and a chip-denominated payout/reward, while preserving the current architecture and its BC, MongoDB, privacy, and action-journal invariants.

The plan uses the legacy casino in ~/repo/ropeybot/bin/games/casino* as behavioral inspiration, not as a source of code, database schema, BC transport, or permission policy. In particular, the existing prototype appearance qualification supports mutations of the sender’s own character only. A third-party clothing or bondage wager is therefore a hard feasibility gate, not an assumed capability.

This document remains the plan for future games and gated casino capabilities. The current balance-only implementation is described in the casino guide.

Goals and non-goals

Goals

Non-goals for the initial delivery

Current architecture constraints

The implementation must follow bot runtime architecture, commands and plug-ins, appearance foundation, and BC acknowledgement rules.

  1. One composition root and one socket. Build the feature dependencies in src/app/start.ts. Inject the existing BcClient, command parser, communication API, ActionCoordinator, and Mongo Db. The casino owns no Socket.IO connection, socket.emit, or Mongo client.
  2. Static plugin lifecycle. Add a statically imported CasinoFeaturePlugin, its dedicated command suite, and explicit subscriptions/disposers. Register it before CommandRouterPlugin. Dispose handlers, event listeners, timers, and in-flight session work in stop().
  3. Layer boundaries. Pure rules belong in src/features/casino/services/ and do not import adapters or other features. Persistence belongs in the feature repository. Only the composition root wires collaborators. The casino may depend on core/ contracts and injected BC/Mongo capabilities; other features must not import the casino.
  4. Honest BC outcomes. Chat/whisper output is not a delivery receipt. socket.emit means only dispatched/dispatched_unconfirmed. Appearance changes require a dedicated action and later matching BC observation; a journal record alone never proves that a restraint was applied.
  5. Observed state remains authoritative. Do not optimistically patch character, room, or appearance snapshots. Do not reuse persisted session state as a substitute for a fresh BC observation.
  6. One journal owner per BC mutation. A future appearance action owns its own action lifecycle. Do not wrap a journaled action in another ActionCoordinator operation, or dispatch generic BC packets from an already-journaled action.
  7. Database isolation. All runtime casino collections live in the selected fixed database: veratown_prototype for production, or veratown_prototype_test for test. Do not accept arbitrary database names and do not use the legacy Ropeybot database as a runtime target or seed.
  8. Privacy. Do not store or log passwords, chat/whisper contents, access codes, full appearance payloads, wardrobe snapshots, or private account data. Use member numbers and bounded, non-sensitive game metadata.
  9. Pinned protocol. Use the vendored BC wire declarations and existing adapter. No bc-bot, browser code, packet emulation, or packet bypass.
  10. Errors and lifecycle. Surface repository and action failures with context. Use explicit outcome types/attempt() at boundaries. Clear timers and listeners on terminal state, disconnect, plugin stop, and startup rollback. Non-process-critical timers must be unref’ed.

Product decisions required before implementation

The architecture below does not require these answers, but production behavior does. They are release gates; do not fill them with undocumented assumptions.

Decision Required choice
Chip source Starting balance, optional daily grants, and whether an operator may grant chips.
Chip sinks and movement Whether transfers, purchases, refunds, and paid restraint removal are allowed.
Limits Minimum/maximum wager, maximum payout, maximum active rounds/players, and any per-day limits.
House rules Roulette wheel variant/odds; blackjack deck, dealer rule, payout, hit/stand and later options; dice/craps wager types; slot symbols/paytable.
Interruption policy What happens on disconnect, bot restart, timeout, table closure, or ambiguous BC outcome for each game.
Consent Whether future clothing/forfeit stakes and rewards are opt-in per game, what the opt-out mechanism is, and exactly which bounded consequences can be offered.
Appearance authority Whether the pinned BC protocol and current access/permission behavior can support a dedicated, journaled mutation of another player’s appearance.
Fairness disclosure Whether server-side cryptographic randomness is sufficient, or a published commit/reveal protocol is required.
Retention Wallet-ledger retention, settled-session retention, and whether a separate operator audit export is needed.

The current Phase 2 approvals and remaining gates are recorded below. Decisions for later games, transfers, purchases, and appearance stakes remain deferred.

Original Phase 0 decisions and evidence (historical)

The following decisions were approved for Phase 1 on 2026-10-12:

Topic Approved decision
Initial wallet Create wallets with zero available and reserved chips.
Grants and chip movement No starting grants, operator grants, daily grants, transfers, purchases, or public wagering in Phase 1. Do not expose grant or wager commands.
Wallet balance The only player-facing casino command in Phase 1 is a private balance/help response.
Ledger retention Apply a 30-day TTL to settled ledger entries. A reservation remains available for recovery while its session is unfinished; set its TTL only when the wager settles.
Session retention Settled session records expire after 30 days. Open, settling, or recovery-pending sessions have no expiry date and must not be TTL-deleted.
House rules Defer slot symbols, paytable, wager limits, and game interruption policies to Phase 2, before any player wager is accepted.
Appearance stakes Clothing and bondage wagers/rewards remain disabled pending a separate feasibility and consent approval.

The user authorized read-only inspection of veratown_prototype_test and prohibited database writes unless absolutely required. A read-only hello command against that database returned an Atlas replica-set topology with a primary and three hosts; this supports Mongo multi-document transactions. No application data was read or written.

The operator confirmed that production and test use the same MongoDB backend and that the test backend qualification applies to production. Combined with the observed test replica-set topology, this satisfies the Phase 0 transaction-topology gate for both runtime databases. Production was not directly inspected, and its credentials, connection, and transaction permissions were not independently tested. This is backend-capability evidence, not a live reserve/settle qualification or authorization to connect to production.

These original Phase 1 decisions did not approve any live wager, game, grant, or appearance mutation. The subsequent Phase 2 approvals below supersede the funding deferral without authorizing gameplay activation or live qualification.

Phase 2 approvals and revisions (2026-10-12)

The operator approved a baseline of 20 chips on room/map entry per rolling 24 hours, the existing refund-before-durable-result / settle-after-result recovery policy, and the existing cryptographic randomness approach. The grant amount and cooldown are room-scoped casino.rules settings (bounded to 1–20 chips and a 24-hour–365-day rolling interval); missing rules fail closed. Grants are implemented independently of gameplay activation, with atomic wallet/ledger writes and a durable optional lastEntryGrantAt. Reconnect/ready checks current occupants without bypassing the cooldown; the bot itself is excluded. No grants accumulate while a player stays in the room or is absent.

The supplied revised game specification is implemented for offline qualification as revised-2026-10-12. The first Option 2 schedule is retained as revised-2026-10-12-option-2 for outcome recovery. Its lower-tier awards were recalibrated in revised-2026-10-13-rtp95; wagers remain 1–100 whole chips.

Calibrated four-reel slots — 95% RTP target

Every reel independently draws a cryptographic integer ticket in 0–99:

Symbol Weight Four-of-kind base Three-of-kind base
Cherry 35 1x 1x
Apple 25 1x 1x
Banana 18 1x 1x
Peach 12 1x 1x
Diamond 8 100x 15x
Seven 2 500x 50x

Special returns are 1.25x for two pairs, 1x for exactly one pair, and 1x for four distinct fruits (cherry/apple/banana/peach, no repeated symbol). Other results return zero. Categories are mutually exclusive: four-of-kind, then three-of-kind, two-pair, pair, and four-fruit result. The reels draw independently. No Lucky Seven or Diamond Booster applies. Multipliers are gross returns including the stake; fractional-chip returns are floored at settlement.

The requested 35/25/18/12/8/2 symbol weights are unchanged. The earlier Option 2 awards produced 128.102798% theoretical RTP, and weight-only adjustments did not approach the target while preserving the jackpot schedule. The calibrated revised-2026-10-13-rtp95 schedule retains 100x four diamonds, 500x four sevens, 15x three diamonds, and 50x three sevens while reducing lower-tier payouts.

Exhaustive RTP validation: all 1,296 ordered outcomes produce 95.438374% theoretical RTP. Whole-chip return rounding gives 92.684432% RTP for a one-chip stake and 95.438374% at 20- and 100-chip stakes. 89.479456% of outcomes return chips, including break-even pushes. Theoretical positive net-profit frequency is 11.211430%; with a one-chip wager and whole-chip rounding, only 0.195664% of outcomes return more than the stake. RTP contributions are pair 53.358912 percentage points, three-kind 21.339192, two-pair 13.769708, four-kind 2.434562, and four distinct fruits 4.536. The operator confirmed successful live qualification and approved activation on 2026-10-15; the gameplay release constant is now true.

Up to 20 players have independent slot rounds. A wager starts that player’s 10-second window; drawing and settlement occur at its deadline. There is one wager per player/round and no player cancellation. The result period lasts five seconds after durable terminal closure, followed by five seconds of renewal. Only then can the player submit a new wager. Slots never automatically repeat stakes. Failed/insufficient-funds reservations do not occupy a seat.

European roulette

The uniform single-zero wheel supports all supplied selections:

Selection Gross multiplier Command selection syntax
Straight-up 36x number 0-36
Split 18x split a b
Street 12x street row (1–12)
Trio 12x trio 0 1 2 or trio 0 2 3
Corner 9x corner a b c d
Four-number 9x four_number (0,1,2,3)
Six-line 6x six_line row (first row 1–11)
Red/black, odd/even, low/high 2x red, black, odd, even, low, high
Dozen / column 3x dozen 1-3 / column 1-3

Number lists must be ascending and obey standard layout adjacency; splits with zero cover 0–1, 0–2, or 0–3. Zero loses all outside bets. Multipliers include returned stake, so 35:1 profit is 36x gross. Every supported roulette bet has RTP 36/37 (97.2973%) and house edge 1/37 (2.7027%). Maximum gross return per roulette bet is 3,600 chips.

A valid funded first bet starts an idle table; room admins may still open one explicitly. Each round has 45 seconds of betting for up to 20 active participants, then 10 seconds of results and five seconds of renewal after durable terminal closure. Rounds renew automatically while the service runs after a played round, but no stakes are carried into the next round. An empty or fully cancelled round closes quietly without a draw, result, cooldown, or further renewal. A new valid bet wakes it again. Restart recovery restores pending renewal only for durable played results; interrupted no-result rounds refund and remain idle. Historical empty-round cooldown records do not prevent a first bet.

Multiple bets per participant receive distinct stable IDs; reconciliation of a lost reservation response reuses that request’s ID. Cancellation removes only the last active bet, with full refund before the deadline, and allows a new bet. Repeated cancellation undoes active bets in reverse placement order. Cancellation of every active bet frees the participant’s seat. A technical 1,000-entry history bound per round (including refunds) preserves bounded documents; reaching it explicitly rejects new reservations without debiting.

Settlement exposes winning number/color, every winning/losing bet, total stake, stake returned on winning bets, gross return, and net profit/loss. Individual bet details and per-player totals are private; room announcements contain only the round result and opening notice.

Approved recovery and fairness

An interruption before durable outcome persistence refunds reservations; after persistence, recovery settles that exact result without redrawing. A notification failure never reverses a completed economic transaction. resultUntilAt and nextRoundAt are optional terminal-session fields; a room/next-round index supports restoring cooldowns after restart. Existing Phase 1 documents and old provisional-2026-10-12 saved outcomes remain readable and retain their payouts.

Node cryptographic randomInt remains the approved RNG; no commit/reveal system is requested. /bot casino fairness explains this is server-side randomness, not independent draw verification. /bot casino rules exposes the current paytables, odds, limitations, and measured RTP privately.

Activation update (2026-10-15): the operator confirmed successful live qualification of slots, roulette, and location access and approved enabling them. CASINO_GAMEPLAY_ENABLED is true. Existing authorization, location, rules validation, and accounting safeguards remain unchanged. This approval does not enable appearance stakes or other games, or authorize new live tests or production database operations.

Historical Phase 2 provisional rules (recovery compatibility only)

The first offline implementation used provisional-2026-10-12. These rules are superseded for new rounds but remain supported for saved-outcome recovery:

No new rounds use this version. The current revised rules and activation approval are listed above. The game service and recovery now start in the runtime; entry funding is independent.

Game selection and delivery order

Game / service Complexity Main state concerns Planned phase
Slots Low One stake, one random result, one payout. First game; validates the wallet and settlement path.
Roulette Low–moderate Shared betting window, close deadline, one round result. First game; supports shared-table flow and has useful Ropeybot precedent.
Cocktail/cosmetic shop Low Chip debit and a cosmetic appearance reward. Optional chip sink after wallet foundation; not itself a gambling game.
Dice / simple craps Moderate A roll or a point state with repeat rolls and table rules. After roulette; begin with the simplest approved dice rules.
Blackjack Moderate Per-player actions, dealer turn, turn deadlines, disconnect and refund policy. After the shared session framework is proven.
Solo strip poker High Private hand state plus clothing-state dependency and explicit consent. Later, only after the appearance feasibility gate.
Multiplayer strip poker Very high Roster locking, simultaneous stakes, turn order, disconnects, pot settlement, appearance changes for several players. Last; depends on all earlier wallet/session/recovery work and appearance approval.

“Crabs” is interpreted here as craps/dice. If it means a different game, treat it as a separate pure rules module and do not fold it into the dice state machine without specifying its rules.

Target component architecture

The first code-bearing release should introduce only the casino feature and wallet/rules foundation. Later game modules and appearance capabilities are added incrementally; the diagram shows extension points, not already-existing classes.

classDiagram
    direction LR
    class BotPluginHost
    class CasinoFeaturePlugin {
        +start()
        +stop()
        -dispose subscriptions and timers
    }
    class CasinoCommandSuite {
        +register() Dispose
        +balance
        +play
        +join
        +hit / stand
        +help
    }
    class CasinoSessionService {
        +startGame()
        +submitCommand()
        +cancelOrExpire()
        +recover()
    }
    class CasinoRules {
        <<pure functions>>
        +validate()
        +advance()
        +resolve()
    }
    class CasinoRandomSource {
        <<injected>>
        +uniformInt(min, max)
    }
    class CasinoWalletService {
        +reserve()
        +settle()
        +refund()
        +balance()
    }
    class CasinoRepository {
        <<feature port>>
        +loadWallet()
        +applyWalletTransaction()
        +createOrAdvanceSession()
        +loadRecoverableSessions()
    }
    class MongoCasinoRepository
    class CasinoLocationAccess {
        +enter(member, key, shape)
        +leave(member, key)
        +canAccess(member, gameId)
        +accessibleGames(member)
    }
    class MapLocationEventBus {
        <<server-observed transitions>>
        +locationEntered(member, key, shape)
        +locationLeft(member, key, shape)
    }
    class BcClient
    class CharacterCommunicationApi
    class ActionCoordinator
    class CharacterAppearanceApi {
        <<future optional capability>>
        +execute()
    }
    class MongoDB

    BotPluginHost o-- CasinoFeaturePlugin
    CasinoFeaturePlugin --> CasinoCommandSuite
    CasinoFeaturePlugin --> CasinoSessionService
    CasinoFeaturePlugin --> CasinoWalletService
    CasinoFeaturePlugin --> CasinoLocationAccess
    CasinoFeaturePlugin --> MapLocationEventBus : per-member enter/exit
    CasinoFeaturePlugin --> BcClient : observed room and message events
    CasinoFeaturePlugin --> CharacterCommunicationApi : private replies
    CasinoCommandSuite --> CasinoSessionService
    CasinoSessionService --> CasinoRules
    CasinoSessionService --> CasinoRandomSource
    CasinoSessionService --> CasinoWalletService
    CasinoSessionService --> CasinoRepository
    CasinoWalletService --> CasinoRepository
    CasinoRepository <|.. MongoCasinoRepository
    MongoCasinoRepository --> MongoDB
    CasinoFeaturePlugin ..> ActionCoordinator : only for future BC actions
    CasinoFeaturePlugin ..> CharacterAppearanceApi : future gate; injected

Module responsibilities

Wallet foundation

Wallet rules

  1. Chips are safe, non-negative integers. Reject fractions, unsafe integers, zero/negative stake values, and amounts above a separately approved cap.
  2. A wager is accepted only after the wallet atomically moves its stake from availableChips to reservedChips.
  3. For a win, settlement removes the reservation and credits the approved gross return. For a loss, settlement removes the reservation without a payout. A push/refund removes the reservation and returns the reserved stake.
  4. Do not debit by reading a balance, computing a new value in feature code, then saving an overwrite. Enforce sufficient balance in the atomic database predicate.
  5. Every economic transition has a stable operation ID and unique ledger key. Retrying a transition cannot debit twice, pay twice, or apply a grant twice.
  6. Game outcome persistence and economic settlement are distinct durable steps. Persist the outcome before applying the payout. A restart resumes settlement from the persisted result instead of rerolling.
  7. The initial design does not promise cross-member transfers. Add them only after explicit product approval and an atomicity design.
  8. Wallet and game records are not BC action journal records. Do not put ordinary chip movements in prototypeActions; that collection records BC action lifecycles.

Wallet UML state

stateDiagram-v2
    [*] --> Uninitialized
    Uninitialized --> Available: create zero-balance wallet
    Available --> Reserved: reserve accepted stake atomically
    Reserved --> SettledLoss: outcome is loss
    Reserved --> SettledWin: outcome is win and payout applied
    Reserved --> Refunded: push, approved cancel, or pre-outcome abort
    SettledLoss --> Available
    SettledWin --> Available
    Refunded --> Available
    Available --> Available: idempotent grant or cosmetic purchase
    Reserved --> RecoveryPending: outcome or transaction status is uncertain
    RecoveryPending --> SettledLoss: durable outcome says loss
    RecoveryPending --> SettledWin: durable outcome says win
    RecoveryPending --> Refunded: approved interruption policy says refund
    RecoveryPending --> RecoveryPending: no sufficient evidence; do not guess

Wallet operation flow

flowchart TD
    Input[Validated player command] --> Auth{Authorized sender and valid player?}
    Auth -- no --> Reject[Private usage or denial reply]
    Auth -- yes --> Parse{Amount is safe integer and within approved limits?}
    Parse -- no --> Reject
    Parse -- yes --> Id[Create stable operation or wager ID]
    Id --> Txn[Begin repository atomic operation]
    Txn --> Existing{Operation already recorded?}
    Existing -- yes --> ReturnPrior[Return recorded result; no second mutation]
    Existing -- no --> Balance{Available chips cover stake?}
    Balance -- no --> NoFunds[Insufficient funds; no ledger entry]
    Balance -- yes --> Reserve[Decrease available; increase reserved]
    Reserve --> Ledger[Insert unique ledger entry in same atomic unit]
    Ledger --> Commit{Commit succeeds?}
    Commit -- no --> Error[Return explicit persistence failure]
    Commit -- yes --> Reply[Private result; whisper delivery remains unconfirmed]

Wallet persistence design

Use the prototype prefix and the fixed runtime-selected database. Do not add casino player data to the allowlisted source-configuration copy script, migrate Ropeybot profiles, or accept a configurable database name.

Collection Purpose and principal fields Index / constraints
prototypeCasinoWallets One wallet per BC memberNumber: _id, schemaVersion, availableChips, reservedChips, createdAt, updatedAt. No unbounded transaction array. _id is the unique member identity. Validate non-negative safe integers at reads and writes.
prototypeCasinoLedger Immutable economic entries: _id stable idempotency key, memberNumber, operationId, optional sessionId, kind, integer availableDelta, integer reservedDelta, bounded reason code, and createdAt. Store no command/chat text. Unique _id; indexes { memberNumber: 1, createdAt: -1 } and { sessionId: 1, memberNumber: 1 } for approved query patterns.
prototypeCasinoSessions Bounded session/round state: _id, schemaVersion, gameId, roomName, phase, status, revision, bounded participant IDs, bounded game-specific state, createdAt, updatedAt, optional deadlineAt, optional durable outcome, and stable settlementId. Never store wardrobe snapshots. _id; { status: 1, updatedAt: 1 }; { roomName: 1, status: 1, deadlineAt: 1 }. Add indexes only for actual production queries.
prototypeCasinoForfeits (later, gated) Active managed consequence metadata only: stable ID, target member, session ID, approved item/lock identifiers, expiry, observed application/release status, and safe reason code. No full appearance payload. { memberNumber: 1, status: 1 }; { status: 1, expiresAt: 1 }. Do not TTL-delete active records before reconciliation.

Atomicity and transaction gate

The preferred wallet operation updates the wallet, inserts its unique ledger entry, and advances the related session settlement status in one MongoDB transaction. Before choosing this design, verify that the deployed MongoDB topology supports transactions in both test and production. Do not infer transaction support merely from a URI or from connecting successfully. The Phase 0 evidence satisfies this topology gate through the read-only test inspection and operator confirmation of the shared production/test backend; it does not establish production credential or transaction-permission testing.

If transactions are unavailable, stop before implementing multi-document settlement and approve a different design. A separate ledger insert and wallet update without an atomic/recoverable protocol can either double-pay or leave wallet and ledger disagreeing. Do not silently fall back to non-transactional read/modify/write logic.

Wallet/ledger records are economic source-of-truth data, not short-lived diagnostic logs. Shared economy finalized entries use the approved 60-day retention, distinct from the 30-day prototypeActions TTL. Settled game sessions may be archived or TTL-cleaned only under their own retention policy. Active/recoverable sessions must never expire automatically.

Rules and session foundation

Shared round lifecycle

flowchart LR
    Command[Command adapter] --> Validate[Parse and authorize]
    Validate --> Session[Session service serializes by session and revision]
    Session --> Rules[Pure game-rule reducer]
    Rules --> Reserve[Wallet reserves stake]
    Reserve --> Active[Persist accepted phase and deadline]
    Active --> Actions[Validate player game actions]
    Actions --> Result[Resolve outcome using injected secure random source]
    Result --> Persist[Persist outcome and settlement ID before payout]
    Persist --> Settle[Atomic wallet and ledger settlement]
    Settle --> Complete[Persist settled state]
    Complete --> Reply[Private summary; optional safe public summary]
    Active --> Abort[Timeout, disconnect, shutdown, or cancel]
    Abort --> Policy[Apply approved phase-specific refund/recovery policy]
    Policy --> Complete

Each persisted transition uses a revision compare-and-set. In-process operations that can affect the same session are serialized. A timer is only a trigger to re-evaluate durable phase/deadline state; it is not proof a round settled. Timers are disposed on completion and unref’ed when appropriate.

Session persistence and recovery rules

Shared round state machine

stateDiagram-v2
    [*] --> Created
    Created --> AcceptingWagers: game opened
    AcceptingWagers --> WagerReserved: atomic reserve succeeds
    AcceptingWagers --> Cancelled: no accepted stakes / admin closes
    WagerReserved --> Playing: game-specific start condition met
    WagerReserved --> Refunding: cancel before outcome per approved policy
    Playing --> OutcomePersisted: rules resolve and durable outcome saved
    Playing --> Refunding: approved timeout or interruption before outcome
    OutcomePersisted --> Settling: stable settlement begins
    Settling --> Settled: wallet, ledger, and session commit
    Settling --> RecoveryPending: transaction status unknown
    RecoveryPending --> Settling: ledger/revision evidence allows safe retry
    RecoveryPending --> RecoveryPending: unresolved; report and do not guess
    Refunding --> Settled: refund committed idempotently
    Settled --> [*]

Game state and flow diagrams

All game reducers are pure, typed, and versioned. They return explicit invalid action, already-complete, result, and continuation outcomes. Player commands are not trusted rule inputs; the command adapter validates member identity, session membership, turn, phase, amount, and deadline before calling a reducer.

Slots

Current game: weighted four-reel solo rounds, up to 20 active players. No appearance wager or interactive BC animation is required. The calibrated RTP schedule was operator-qualified and approved for activation on 2026-10-15.

stateDiagram-v2
    [*] --> Ready
    Ready --> Validating: play command
    Validating --> Ready: invalid wager or insufficient balance
    Validating --> Reserved: wallet reservation committed
    Reserved --> OutcomePending: individual 10-second deadline; secure draw
    OutcomePending --> OutcomeSaved: result and rules version persisted
    OutcomeSaved --> Settling: apply payout/loss once
    Settling --> Settled: wallet transaction committed
    OutcomePending --> RecoveryPending: interrupted before result persisted
    OutcomeSaved --> RecoveryPending: payout transaction uncertain
    RecoveryPending --> Settling: resume from persisted result
    Settled --> Results: 5-second result period
    Results --> Renewal: 5-second renewal
    Renewal --> Ready: player may submit a new wager
flowchart TD
    A[Player chooses chip stake] --> B[Validate limits and reserve chips]
    B -->|Rejected| X[Reply privately; no spin]
    B -->|Committed| Window[Individual 10-second window; no cancellation]
    Window --> C[Draw four independent weighted reels using secure RNG]
    C --> D[Persist symbols, paytable version, and gross return]
    D --> E[Idempotently settle wallet and ledger]
    E --> F[Privately report result, whole-chip return, net and balance]
    F --> Cooldown[5 seconds results + 5 seconds renewal; persisted cooldown]

Roulette

Current game: one shared European table, multiple bets for up to 20 active players. The supplied bet types, profit-plus-stake payouts, and 45/10/5-second pacing are implemented and approved for runtime activation.

stateDiagram-v2
    [*] --> Idle
    Idle --> BettingOpen: first valid funded bet / admin open
    BettingOpen --> BettingOpen: reserve valid bet
    BettingOpen --> BettingOpen: cancel last active bet before close; may bet again
    BettingOpen --> Closing: deadline with active bets
    BettingOpen --> Idle: empty or fully cancelled at deadline
    Closing --> OutcomeSaved: draw once; persist number and rules version
    Closing --> Refunding: approved abort before result
    OutcomeSaved --> Settling: calculate each participant's return
    Settling --> Settling: apply one idempotent settlement per wager
    Settling --> Settled: every payout/refund reconciled
    Settling --> RecoveryPending: any write status uncertain
    RecoveryPending --> Settling: recover from saved outcome and ledger
    Settled --> Results: private details and totals; 10 seconds
    Results --> Renewal: 5 seconds
    Renewal --> BettingOpen: automatically open next round; no repeated stakes
    Refunding --> Idle: refund each reservation exactly once
flowchart TD
    A[Open round with betting deadline] --> B[Accept and reserve each valid bet]
    B --> C{Betting deadline reached?}
    C -- no --> B
    C -- yes --> D[Close bets; reject late/cancel commands]
    D --> Active{Any active bets?}
    Active -->|no| Idle[Close quietly; wait for a valid bet]
    Active -->|yes| E[Draw one outcome and persist it before payout]
    E --> F[Resolve each bet using versioned rules]
    F --> G[Settle each player idempotently]
    G --> H[Publish safe round summary and mark settled]

Blackjack

Start with a bounded ruleset such as hit/stand only. Deck count, dealer rule, blackjack payout, push behavior, seat count, and turn deadline are release decisions. Do not implement split/double/insurance until their reservation and settlement semantics have their own tests.

stateDiagram-v2
    [*] --> Waiting
    Waiting --> Betting: open hand
    Betting --> Betting: reserve accepted player stake
    Betting --> InitialDeal: betting closes and at least one player joined
    InitialDeal --> PlayerTurns: persist hands and turn order
    PlayerTurns --> PlayerTurns: valid hit below 21
    PlayerTurns --> NextPlayer: stand, bust, or approved turn timeout
    NextPlayer --> PlayerTurns: another active player
    NextPlayer --> DealerTurn: all players complete
    InitialDeal --> DealerTurn: no player actions required by rules
    DealerTurn --> OutcomeSaved: dealer resolves; persist all results
    OutcomeSaved --> Settling: calculate returns per player
    Settling --> Settled: all reservations reconciled
    PlayerTurns --> Refunding: approved abort before outcome
    DealerTurn --> RecoveryPending: interrupted/uncertain dealer result
    OutcomeSaved --> RecoveryPending: transaction uncertain
    RecoveryPending --> Settling: resume durable results
    Refunding --> Settled: refunds committed
    Settled --> Waiting: next hand
flowchart TD
    A[Open table and accept bounded seats] --> B[Reserve each player's stake]
    B --> C[Close betting and deal from injected deck source]
    C --> D[Persist hands and ordered active turn]
    D --> E{Current player's action}
    E -->|Hit| F[Validate turn; deal and persist next state]
    F -->|Below 21| E
    F -->|Bust or 21| G[Advance turn]
    E -->|Stand| G
    G -->|More active players| E
    G -->|No active players| H[Dealer resolves by approved rule]
    H --> I[Persist full outcome before any payout]
    I --> J[Settle every reserved stake idempotently]
    J --> K[Privately send each player's result]

Private hand details must be sent only to the participant using a private whisper. A whisper is still reported as unconfirmed delivery. Public messages must not reveal hidden cards or sensitive text.

Dice / craps

Implement the simplest approved dice game first. If full craps is selected, the come-out/point resolution and eligible bet types must be specified before coding. Do not call a single-roll game “craps” if its settlement rules differ.

stateDiagram-v2
    [*] --> Idle
    Idle --> Betting: start round
    Betting --> ComeOut: bets reserved and betting closed
    ComeOut --> OutcomeSaved: immediate win/loss result persisted
    ComeOut --> PointEstablished: point established by approved rules
    PointEstablished --> PointEstablished: roll neither point nor seven
    PointEstablished --> OutcomeSaved: point made or seven-out
    OutcomeSaved --> Settling: resolve eligible bets
    Settling --> Settled: idempotent settlement complete
    Betting --> Refunding: approved abort before first roll
    OutcomeSaved --> RecoveryPending: settlement uncertain
    RecoveryPending --> Settling: resume saved rolls/outcome
    Refunding --> Settled: reservations refunded
    Settled --> Idle
flowchart TD
    A[Open betting window] --> B[Reserve each approved dice bet]
    B --> C[Close betting]
    C --> D[Roll with injected secure random source]
    D --> E{Approved rules resolve now?}
    E -- yes --> F[Persist final outcome]
    E -- no --> G[Persist point and round state]
    G --> H[Roll again on the next permitted step]
    H --> E
    F --> I[Calculate and idempotently settle all bets]
    I --> J[Mark round settled and report result]

Solo strip poker (appearance-gated)

The planned separation is between pure card rules and an optional consequence adapter. A no-appearance card game may be tested first, but must not be advertised as a clothing-changing game. Any clothing consequence requires explicit opt-in and the third-party appearance feasibility gate below.

stateDiagram-v2
    [*] --> Idle
    Idle --> Consent: player requests a clothing-stakes game
    Consent --> Idle: decline, timeout, or capability unavailable
    Consent --> Reserved: explicit opt-in and approved stake reserved
    Reserved --> Deal: round starts
    Deal --> PlayerTurn: deal and privately expose player hand
    PlayerTurn --> PlayerTurn: valid poker action
    PlayerTurn --> Evaluate: hand complete
    Evaluate --> OutcomeSaved: compare hands and persist winner
    OutcomeSaved --> ChipSettlement: settle chip component
    ChipSettlement --> ConsequencePending: approved clothing/forfeit outcome
    ConsequencePending --> AwaitBCObservation: dedicated action dispatched
    AwaitBCObservation --> Completed: matching later BC observation
    AwaitBCObservation --> Unconfirmed: timeout, disconnect, or no matching state
    Unconfirmed --> Reconcile: wait for fresh authoritative observation
    Reconcile --> Completed: observed state reconciled
    Reconcile --> RecoveryPending: still ambiguous; do not retry blindly
    Deal --> Refunding: approved interruption before outcome
    PlayerTurn --> Refunding: approved interruption before outcome
    Refunding --> Idle: refund committed; no consequence claimed
flowchart TD
    A[Player asks to play] --> B{Approved game and appearance capability enabled?}
    B -- no --> X[Decline consequence; no wager accepted]
    B -- yes --> C[Explain bounded rule and obtain explicit opt-in]
    C --> D{Consent and stake valid?}
    D -- no --> X
    D -- yes --> E[Reserve chip stake or record approved stake intent]
    E --> F[Play pure card rules; keep private hand private]
    F --> G[Persist outcome before economic settlement]
    G --> H[Settle chips idempotently]
    H --> I[Request only approved dedicated appearance action]
    I --> J{Later matching BC observation?}
    J -- yes --> K[Record observed outcome]
    J -- no --> L[Unconfirmed; stop and reconcile after fresh sync]

The player must not be labeled a cheater solely because clothing changes. BC state observations can show that an item/slot changed but do not necessarily prove why it changed or who caused it. Until authorized third-party mutations and safe interruption are proven, this game may use chips only or remain disabled.

Multiplayer strip poker (last phase)

stateDiagram-v2
    [*] --> Lobby
    Lobby --> ConsentAndBuyIn: player joins
    ConsentAndBuyIn --> Lobby: decline, leave, or invalid stake
    ConsentAndBuyIn --> Lobby: reserve each buy-in atomically
    ConsentAndBuyIn --> RosterLocked: host starts with valid minimum roster
    RosterLocked --> Deal: freeze rules version and turn order
    Deal --> PlayerTurn: expose only each player's private hand
    PlayerTurn --> PlayerTurn: valid current-player action
    PlayerTurn --> Evaluate: round/hand completes
    Evaluate --> OutcomeSaved: persist result before payouts/consequences
    OutcomeSaved --> PotSettlement: distribute chips idempotently
    PotSettlement --> ConsequencePending: if explicitly opted-in
    ConsequencePending --> AwaitBCObservation: journaled action per target
    AwaitBCObservation --> NextRound: all required outcomes observed or policy resolved
    AwaitBCObservation --> RecoveryPending: ambiguous/unconfirmed appearance result
    PlayerTurn --> DisconnectPolicy: participant leaves or deadline expires
    DisconnectPolicy --> PlayerTurn: approved forfeit/stand rule
    DisconnectPolicy --> Refunding: approved cancel/refund rule
    RecoveryPending --> RecoveryPending: do not continue with uncertain appearance state
    NextRound --> Deal: next hand if enough players remain
    NextRound --> Settled: table closes
    Refunding --> Settled: return all eligible reservations once

This game is not ready until its exact poker variant, private-hand delivery, turn/disconnect rules, multi-member pot transaction, consent lifecycle, appearance feasibility, and restart policy are individually approved and tested. Do not lock a roster or accept buy-ins before all required capabilities are available.

Cosmetic shop and rewards

Treat cocktails, outfit props, and other approved cosmetic items as a separate shop flow, not as a gambling result. The shop uses a bounded catalog of validated cosmetic reward definitions. A purchase debits chips once and then applies any appearance effect only through the existing permission-aware appearance action. A cosmetic purchase is complete only when its economic debit and item-application outcome are both represented honestly.

For the first shop release, prefer rewards that do not mutate another player’s appearance and do not imply an unsupported BC effect. If application fails after debit, follow an explicit idempotent refund/retry policy; do not claim the item was granted from a chat reply alone.

flowchart TD
    A[Player selects catalog reward] --> B[Validate enabled definition and price]
    B --> C[Atomically debit chips and record purchase ID]
    C --> D{Purchase already processed?}
    D -- yes --> E[Return prior recorded status]
    D -- no --> F[Apply cosmetic through approved appearance API if needed]
    F --> G{Required BC observation received?}
    G -- yes --> H[Mark reward observed]
    G -- no --> I[Mark unconfirmed and reconcile; no blind repeat]
    C -->|debit failed| J[No reward request]

Future clothing and bondage stakes: non-negotiable feasibility gates

The current appearance qualification surface is room-admin-only and self-targeted: the sender may mutate only their own character. This is not authority to change another member. A player opting in does not by itself prove that BC permits the bot to perform the mutation.

Before implementing any clothing/bondage stake or reward:

  1. Verify the exact behavior against the pinned BC protocol and the live BC server version. The source pin alone does not certify the server version.
  2. Prove that an authorized action against another participant can be made using the current permission query and dedicated appearance action or an explicitly approved extension. Do not bypass item-access checks, broaden self-targeted qualification, or emit a raw packet.
  3. Use a dedicated prototype bot account and a test room with a separate participant account. Test allowed, denied, occupied-slot, malformed, timeout, disconnect, reconnect, and safeword cases.
  4. Confirm which configured timer-lock behaviors are observed on this BC server. Existing native timer qualification elsewhere is not evidence that casino forfeits have passed.
  5. Keep the appearance snapshot in bounded memory only. Do not persist or journal full appearance payloads or an outfit to restore after restart.
  6. A dispatched write with no matching observation stays timed_out_unobserved/unconfirmed. Reconcile against a later authoritative snapshot before any retry. Do not optimistically patch or automatically reapply an outfit.
  7. If safe third-party mutation or interruption recovery cannot be proven, keep clothing/bondage stakes disabled. The compliant alternative is a chips-only game and, if approved, narration without appearance mutation.

The casino must never promise to detect intent from a state change. It can detect that observed appearance no longer matches an in-memory rule condition; it cannot infer that the player cheated rather than responding to another event. Consequences for that state must be specified as a neutral round interruption/settlement rule, not an automatic accusation.

Commands and authorization

The eventual command surface should use the shared parser and a dedicated command suite (for example /bot casino balance, /bot casino play slots 10, /bot casino join, /bot casino hit, /bot casino stand, and help). Exact syntax is a UX decision and must be documented with the implementation.

Failure, restart, and shutdown handling

Situation Required behavior
Invalid command or insufficient chips Reject before reservation; no session mutation or payout.
Wallet transaction fails Return explicit failure. Do not report an accepted bet.
Duplicate economic operation Read the stable ledger key and return/reconcile its result; never apply a second delta.
Bot stops before outcome persistence Apply only that game’s approved interruption/refund policy.
Bot stops after outcome persistence Resume settlement from the saved outcome; never reroll.
Wallet/ledger transaction result uncertain Check transaction/ledger/session evidence. Stay recovery-pending if evidence is insufficient.
BC disconnect or target leaves during a future appearance action Stop subsequent actions; keep the action unconfirmed/failed as supported by observed evidence; do not retry from stale snapshots.
Plugin stop Stop accepting commands, cancel timers/listeners, await safe in-flight persistence/action work, then allow normal host shutdown order to continue.
Startup recovery Load only validated casino records after DB indexes/repositories initialize; wait for BcClient room readiness before using live participant observations.

Persisted casino state is a recovery aid for economy and game rules; it is not an authority for current room membership, character appearance, lock ownership, or BC action completion.

Implementation roadmap and gates

Phase 0 — approve contracts and qualify infrastructure

Exit gate passed: Phase 0 contracts and backend topology are qualified. The read-only test topology evidence and operator confirmation establish shared backend transaction capability, not independent production credential or live transaction testing. Do not enable player wagering until the remaining Phase 2 product approvals are obtained; live qualification requires separate authorization.

Phase 1 — wallet, ledger, and pure rules harness

Exit gate passed: tests cover overdraw prevention, idempotent reserve and settlement, transaction rollback, and recovery-record durability. Only the zero-balance wallet is player-facing; no player data is copied from Ropeybot. Phase 1 does not itself authorize wagering.

Phase 2 — slots and roulette

The documented baseline is offline-qualified at 95.4383735% theoretical RTP. Configured slot changes are exhaustively checked against a code-level 99% ceiling. The baseline preserves the 100x diamond and 500x seven jackpots; roulette payouts cannot exceed standard European gross returns. The operator confirmed successful live qualification and approved activation on 2026-10-15. Funding, RNG, recovery, supplied wager limits, and shared-backend topology are no longer unresolved decisions. CASINO_GAMEPLAY_ENABLED is true; offline tests alone do not qualify live BC or production Mongo credentials/transaction permissions.

Phase 3 — dice and blackjack

Exit gate: rule tests cover edge cases and state transitions; concurrent commands cannot advance the same turn twice; hidden cards are never posted to room chat.

Phase 4 — simple cosmetic shop

Exit gate: purchase debit/reward status is idempotent and any unobserved appearance effect is surfaced without automatic retry.

Phase 5 — clothing/forfeit feasibility spike (not a promise to ship)

Exit gate: if any required permission/recovery case cannot be safely represented, stop this phase and keep clothing/bondage wagers disabled.

Phase 6 — solo then multiplayer strip poker

Exit gate: separate review of appearance authority, privacy, no-double-pay settlement, and all player-exit paths.

Verification plan

Offline automated tests

Use the repository’s built-in node:test + node:assert/strict patterns and the existing tsx runner. Add each new test file explicitly to the npm test script.

Test-room qualification

Follow temporal test notes. In particular:

Release validation

For each code-bearing phase, run the standard required gates:

npm run lint
npm run format:check
npm run check
npm test
npm run build

The build is required for source/API changes. Update the feature plan into an implemented feature guide only when runtime behavior is actually wired and qualified; keep documentation status honest.

Documentation and wiring when implementation begins

When an approved phase is implemented: