veratown-prototype

Casino Wallet And Revised Games

Runtime configuration receipts

Room-scoped heartbeats report the shared configuration revision adopted by the most recently created slots or roulette session. Startup validation, help, entry-grant calculations, repository reads and reusing an existing roulette round do not report session adoption. A known newer shared snapshot is pending until a new session uses it; invalid/missing rules report a fixed safe error identifier without discarding historical last-loaded revision/time. Configured game sessions keep their saved rules, so a loaded receipt never means every active round uses the same revision. A session receipt is not wager success or a BC observation.

The casino uses the shared economy account and ledger. Credits is the canonical unit; casino player-facing text may temporarily call them “chips.” The runtime CASINO_GAMEPLAY_ENABLED gate is true following operator-reported successful live testing and activation approval on 2026-10-15. Slots, roulette, and location access are approved; game recovery starts with the casino plugin. A new member receives a zero-balance shared account; the migration copies only existing prototype casino balances and never imports Ropeybot profiles.

On server-observed room/map entry, players receive the configured entry-grant amount after the configured rolling cooldown. Both values come from validated room-scoped casino rules; the amount is capped at 20 credits (displayed as chips) and the cooldown cannot be shorter than 24 hours or longer than 365 days. This is not a midnight reset or an automatic payment while remaining in the room. Existing occupants are checked on bot ready/reconnect; the bot’s own member is excluded. Duplicate entry/reconnect events cannot bypass the persisted cooldown. A successful grant is privately reported; notification failure does not undo the credit. Missing or invalid rules at runtime disable entry grants rather than substituting defaults. Startup inserts the validated 20-chip / 24-hour casino baseline in rooms.main["casino.rules"] only if the key is missing; it never overwrites an operator value.

lastEntryGrantAt is optional on existing economy accounts and has no TTL. Granting atomically updates this timestamp and shared balance and inserts an entry_grant economy ledger entry with 60-day retention. The grant worker is independent of the gameplay gate and is drained before Mongo closes.

sequenceDiagram
    participant BC as Server-observed room entry / ready
    participant Plugin as CasinoFeaturePlugin
    participant Wallet as CasinoWalletService
    participant Economy as EconomyService
    participant Mongo as Injected MongoEconomyRepository
    BC->>Plugin: Member enters (exclude bot)
    Plugin->>Wallet: grantOnEntry(member, validated entry-grant rules)
    Wallet->>Economy: grantOnce(stable operation ID, validated amount and cooldown)
    Economy->>Mongo: Transaction: load/create shared account
    alt Last grant is within configured cooldown
        Mongo-->>Economy: cooldown; no credit
        Economy-->>Wallet: cooldown
        Wallet-->>Plugin: no credit or notification
    else Eligible
        Mongo->>Mongo: Increment credits + persist lastEntryGrantAt + economy ledger
        Mongo-->>Economy: credited
        Economy-->>Wallet: credited
        Wallet-->>Plugin: credited
        Plugin->>BC: Private whisper (dispatched_unconfirmed)
    end

Player commands

Command Behavior
/bot casino or /bot casino help Privately explains wallet commands and lists only games allowed at the sender’s observed map location.
/bot casino balance Loads or creates a zero-balance wallet and privately reports available and reserved chips.
/bot casino rules Privately describes rules only for games allowed at the sender’s current location.
/bot casino fairness Privately explains cryptographic server-side draws, durable recovery, and lack of commit/reveal.
/bot casino slots <chips> Denied unless the sender is in a configured slot point location and gameplay is enabled.
/bot casino roulette ... Denied unless the sender is in the configured roulette region and gameplay is enabled.

The CasinoCommandSuite is registered by the lifecycle-managed CasinoFeaturePlugin before the command router starts. The first valid, funded bet starts idle roulette for any eligible player. The explicit /bot casino roulette open command remains an optional room-admin operation. Both paths require gameplay activation and a configured roulette-region binding. Replies use the shared CharacterCommunicationApi whisper operation and preserve its dispatched_unconfirmed acknowledgement semantics. Stopping the plug-in disposes its command registration and awaits game-service shutdown when activated. Reverse-order cleanup removes entry and location listeners before draining accepted invitations and stopping the game service, even when another disposer fails.

Chat readability

Casino replies use a leading newline, clear headings, short labelled lines, and blank lines between sections. Help lists one command per line, selection syntax, and working examples; either !casino or /bot casino can be used. Only games available at the sender’s observed location are described, and the optional manual roulette-opening command is listed only for room admins.

Global help shows only the casino wallet/rules/fairness summary. Scoped casino help recomputes game visibility for the requester and lists slots or roulette only when gameplay, effective room rules, and that member’s observed location allow it. Roulette opening syntax is visible only to a room admin with roulette access. Unknown or inaccessible nested scopes use the shared neutral private response.

Rules use the effective configured values (including a running roulette round’s saved rules), rather than hardcoded baseline odds. Payouts are listed beside each bet type; slot payouts convert internal quarter-chip units to gross multipliers beside each symbol. Timings use seconds/minutes/days, and absolute deadlines explicitly use UTC. Returns include the stake; net profit is explained separately, and long-run averages are not guarantees.

For example, the roulette portion of !casino rules includes:

ROULETTE - European single-zero wheel [0-36]
Winning payouts - stake included; theoretical long-run return:
- Straight-up: 36x; 97.30%
- Split: 18x; 97.30%

Timing and limits appear in their own labelled lines:

Betting window: 45 seconds
Result display: 10 seconds
Wait after result: 5 seconds
Limits: 20 active players; 1000 bets per round including cancelled bets.
Bet size: 1-100 chips; code ceiling: 100 chips.

These are excerpts of the baseline, not overrides of database configuration. Balance, entry bonuses, welcome invitations, fairness, slot confirmations and results, usage errors, access denials, and other command responses follow the same heading/line conventions. The operator-edited roulette round/result format remains unchanged. Long command replies split at line boundaries into private continuation whispers, without truncation, within BC’s 2,000-character limit including its map-wide OOC parentheses. Every part uses the existing action journal and send queue; dispatch still does not prove delivery.

Location-Based Game Access

Casino access is checked per sender against server-observed map location transitions, not against the bot’s location or a location name supplied in a command. Roulette bindings must point to a map location whose shape is region; slot bindings must point to point locations (x/y). The access policy is an explicit supported-game gameId -> [{ key, shape }] registry loaded from validated room-scoped database configuration. Location keys are never compiled into the runtime; only slots and roulette are executable game IDs. Slots accept up to 16 point bindings; roulette accepts one region binding. Each referenced veratownLocations record must be enabled and have type game, and the main bot must observe the sender in the matching shape.

Bindings are stored together with the rules at prototypeGameConfig.rooms.main["casino.rules"] and edited using the existing room-admin !world config / /bot world config commands. Empty bindings deny all location access. Invalid rules or shapes are rejected before an admin write; an invalid document written outside the command surface causes the consumer to fail closed. Startup seeds the complete game rules only; it does not seed casino location documents.

Map runtime entry/exit events are derived from inbound BC positions and room snapshots. Access is removed when a member leaves a point/region, disconnects, the map configuration removes/disables it, or its shape changes. Reconnect snapshots republish current occupancy. Each sender’s /bot casino help and game-specific /bot casino rules details include only games allowed at their current observed locations. Global /bot help retains the general casino wallet command; it does not list game-specific subcommands. Direct game commands are independently checked and denied outside their configured locations. Multi-location access is set-based, and exits from overlapping locations remove only the exited key.

On gaining roulette access, the casino sends one private invitation pointing to /bot casino help, /bot casino rules, first-bet round starting, and the optional room-admin-only round-opening command. Initial observed occupancy can invite too; repeated position/entry events while access remains available do not repeat it. Leaving and re-entering, or a new plugin lifecycle, can invite again. Disabled gameplay, disabled/unbound locations, mismatched types/shapes, and the bot’s own member never trigger an invitation. It does not automatically open a round or place a bet. Notification failures are logged and pending invitations are drained at shutdown, alongside entry grants, before the game service stops. Whisper dispatch is not proof of delivery.

By default, round-open, automatic renewal, and round-closed announcements remain private whispers to all currently observed, authorized roulette-region occupants, not local room chat at the bot’s position. An optional wheel_helper presentation mode delegates the public opening, spin, and settled-result presentation to the configured helper character; see Roulette wheel helper presentation. Consolidated wager results always remain private supervisor whispers to their participants, even if they have left the roulette region.

Roulette messages are grouped into four sections. Each accepted bet sends a private confirmation of only that new bet, not the cumulative reserved wallet balance (sent whispers cannot be edited):


🎰 ROULETTE 🎰
   - Bet of 10 chips on 🟥 accepted

After settlement, default whisper mode sends the aggregate to all eligible roulette-region occupants, including spectators. In helper mode, the helper emotes this same aggregate to the room. Counts refer to bets, not players; the chip total is the combined net result of all settled bets, including participants who left the region:


🎰 ROULETTE 🎰:
Result: 7 🟥
1 out of 1 bets have won.
Players have won a total of 10 chips!!

Each participant then receives one private list of their settled bets and a combined net result instead of separate per-bet and total whispers:

Your bets:
- 10 chips on 🟥
Total: You've won 10 chips

Net means gross return minus stake; zero says You've broken even (or Players have broken even overall!! publicly). Cancelled/refunded bets are excluded. Very long private lists are split at bet boundaries to fit BC’s 2,000-character limit, including OOC wrapping, with the total only on the last part. Other bet types retain selection details; outcomes use 🟥/⬛/🟩. There is no additional technical “delivery is unconfirmed” chat message; dispatch remains unconfirmed in the action journal.

Roulette wheel helper presentation

casino.rules.roulettePresentation is optional and defaults to the current whisper behavior. To opt in, select one enabled helper and two enabled, reviewed Chat or Emote templates whose location key is the configured roulette region:

{
    "roulettePresentation": {
        "mode": "wheel_helper",
        "agentId": "roulette-wheel",
        "openingTemplateId": "roulette-open",
        "spinningTemplateId": "roulette-spin"
    }
}

The helper registry entry must use room key main, the same RUNTIME_TARGET, and the dedicated roulette-wheel capability. Its BC-observed position must be inside the bound, enabled game region at dispatch time. In addition to its own position, a fresh supervisor or peer-helper BC observation of the helper’s registered member can establish region membership; its own uploaded observation is not independent evidence. The helper is an operator-managed BC character; this feature does not create or mutate a map object. See the Helper Bots guide for helper and template documents.

The supervisor still owns every wager, draw, durable result, settlement, access check, deadline, and private participant report. It queues an idempotent session/stage event for opening, spinning, and a successfully settled result. Opening and spinning use the reviewed templates; result text comes only from the existing aggregate formatter and contains no player-level data. These events reuse prototypeHelperTasks, the helper’s existing lease, budget, action journal, and supervisor-observed matching Chat/Emote completion for opening and spinning; settled result announcements remain Emotes. Up to three queued roulette events are retained per helper; a queued opening or spin is expired if a result supersedes it. Ambiguous or possibly dispatched events are never replayed and never paired with an automatic whisper fallback.

Known pre-dispatch unavailability or a confirmed queue-capacity failure uses the existing eligible-occupant whisper path. A database error whose enqueue outcome cannot be confirmed does not fall back, since the helper may already have the event. Switching the mode back to whisper fences queued helper events before dispatch. The whisper default and helper-off behavior require no helper account and preserve the existing casino presentation.

First-bet starting, manual opening, and automatic renewal use the stored round’s betting window:


🎰 ROULETTE 🎰:
A 🆕 round has started. Betting is open for 45 seconds!!
Multiple bets per player; cancel your last active bet before close.

Use /bot casino help for more information

Confirmations, public results, and round-start messages begin with a newline so their roulette header appears below the whisper prefix.

Manual opening does not send a duplicate confirmation to the admin. Payouts, access checks, and queue/rate-limit behavior are unchanged.

Troubleshooting missing roulette access

A map-entry log proves only that the map runtime detected a location. To make roulette available, the location’s key must match the rules binding, its type must be game, and its geometry must be an explicit region, for example:

{
    "key": "casino_game_location",
    "roomKey": "main",
    "type": "game",
    "enabled": true,
    "region": {
        "TopLeft": { "X": 32, "Y": 36 },
        "BottomRight": { "X": 38, "Y": 39 }
    }
}

This is a geometry excerpt, not a complete insert document. Bounds are inclusive. Legacy x/y plus data.bottomRightX/data.bottomRightY do not define a runtime region: without region, the record is treated as a point. The legacy game_region type is not eligible for casino access. Do not broaden runtime permissions or infer bindings from names to compensate for invalid configuration.

On 2026-10-15, approved repairs were applied and read back in both isolated databases, preserving existing location keys and unrelated settings:

Target Roulette location key Repair Game-config revision
Test casino_game_location Added explicit region (32,36)–(38,39); existing rules unchanged 4
Production game_location Set type game, added the same explicit region, and bound roulette to this key 3 (previous revision 2 backed up transactionally)

Both rules values passed the casino schema. Payouts, timings, slot bindings, wallets, and unrelated configuration were preserved; production slot bindings remain empty and production slots remain unavailable. No source database was accessed. The repair’s read-back verified stored configuration and offline access checks, not a live BC observation or invitation delivery.

A subsequent full read-only runtime load validated all 52 test locations and 48 production locations, plus their door/group/membership documents. All four test slot bindings and both repaired roulette bindings matched an enabled location with the required type and geometry. Repeating the repair dry runs reported no further changes.

Running bots normally receive location/rule changes through existing change streams. If a watcher failed, reload/restart through the usual operator procedure; re-enter the region and inspect /bot casino help. An already-running roulette round retains its saved rules until completion. The new invitation behavior requires deploying this code; the configuration repair itself does not deploy or restart either bot.

flowchart LR
    BC[Server-observed position / room snapshot] --> Map[VeratownMapRuntime]
    Map -->|locationEntered / locationLeft| Events[MapLocationEventBus]
    Events --> Access[CasinoLocationAccess per-member location set]
    Rules[(Validated room-scoped casino.rules)] --> Access
    Access -->|current sender authorized| Help[Private casino help/rules]
    Access -->|new roulette access| Invite[Private entry invitation]
    Access -->|current sender unauthorized| Hide[Omit game commands and rules]
    Access -->|checked again| Handler[Game command handler]
    Handler -->|deny outside location| Hide
    Empty[Missing/empty bindings] -->|deny all| Access

Database-Driven Rules And Locations

Casino behavior reads casino.rules from the explicit room scope main of the revisioned prototypeGameConfig document. On startup, the composition root inserts the full baseline in this guide only when that room key is absent. It preserves all existing values and reports the inserted/existing document revision. Default bindings are empty, so no player receives game-location access from the seed. The 20-chip / 24-hour entry grant is active independently of CASINO_GAMEPLAY_ENABLED; slots and roulette wagers are enabled by that separate static gate but still require valid location access.

The generic world-config repository stores bounded JSON, while the casino schema strictly validates the supported fields, version, location bindings, unique slot symbols, weights totaling 100, timing/participant/history limits, and payout bounds. A global casino.rules value is rejected; room settings never silently inherit global values. The active document revision is included with the immutable rules snapshot stored on each new game session.

Use /bot world config show room main to inspect the current revision. /bot world config set room main casino.rules <full JSON value> creates a missing value or updates the existing value. The complete default values and command sequences are in the feature configuration catalog. Create a slot location with the guided world location commands and bind its key as a point. The current bot location command does not create region geometry; a roulette region must already exist as an operator-managed, enabled game record in veratownLocations before its key is bound.

/bot world config unset room main casino.rules removes the room rules immediately. Grants and game access then fail closed until the next startup, when the default is reinserted if the key remains absent. Invalid rules also fail closed and are never silently replaced. This configuration does not enable or override CASINO_GAMEPLAY_ENABLED. That switch is a code-level release decision, not an environment variable; setting it in .env has no effect.

entryGrant.chips is configurable from 1 to the 20-chip code ceiling; entryGrant.intervalMs is configurable from 24 hours to 365 days. Player help/rules and each grant use the same validated active settings. These bounds preserve the entry economy’s safety floor and ceiling.

New sessions persist both the exact rules object and its source revision. Configuration changes affect future sessions only. An accepting or settling round—including a recovered round—uses the embedded rules snapshot to calculate its payout, validate the durable result, and complete idempotent settlement. Recovery never redraws an outcome or applies newly edited rules to an older session. The current calibrated slot schedule has 95.4383735% gross theoretical return; database tuning can raise it only up to the code-enforced 99% ceiling. Roulette multipliers may be reduced from, but not increased beyond, standard European gross payouts; the standard schedule returns 36/37 (about 97.2973%) for every supported selection. The rules command reports the active slot odds and effective payout/timing rules.

Ownership and persistence

The feature-neutral EconomyPort and EconomyService own shared balance operations; MongoEconomyRepository is the only runtime owner of economy accounts and ledger entries. CasinoWalletService adapts the casino’s temporary “chips” vocabulary and coordinates casino session records with the shared port. MongoCasinoRepository owns casino sessions; legacy wallet and ledger collections remain only for migration rollback. Multi-document economy operations use MongoTransactionRunner. Economy records are not BC action-journal records, and no casino module owns a socket, performs a BC state mutation, or claims an observed outcome.

Collection Purpose Retention
prototypeEconomyAccounts Shared per-member available/reserved credits and optional durable lastEntryGrantAt. No TTL.
prototypeEconomyLedger Stable-ID grants, reservations, settlements, refunds, and forfeitures for all approved economy consumers. 60 days after finalization; unsettled reservations remain durable.
prototypeCasinoWallets Legacy casino balances retained for compatibility rollback. No TTL.
prototypeCasinoLedger Legacy casino ledger retained for compatibility rollback. 60 days after grant/settlement; unsettled reservations remain durable.
prototypeCasinoSessions Bounded wager selections, rules version and immutable config snapshot/revision, durable outcome, deadline, and lifecycle/recovery state. A unique sparse tableKey permits only one open roulette round per room. 30 days after terminal closure; accepting, settling, and recovery-pending sessions remain durable.

Each repository validates documents at the Mongo boundary and initializes indexes before the bot connects to BC. Shared economy startup fails closed until migrate:casino-economy -- --apply writes the durable cutover marker. The dry-run-first migration is limited to the selected fixed runtime database, retains legacy casino records for 60 days, and does not touch the source database or configuration-copy allowlist. See the shared economy guide.

Current state and gates

The source includes a pure slot/roulette rules engine and transaction-backed game service. It reads the explicitly versioned casino.rules object from the room-scoped prototypeGameConfig store, and persists that effective rules snapshot and source revision on each configured game session. It persists an outcome before settlement, refunds an interrupted round with no durable outcome, and resumes settlement from a saved outcome without drawing again. Entry funding, the code-enforced wager/payout ceilings, cryptographic RNG, and recovery policy are approved; the documented baseline schedule measures 95.4383735% theoretical RTP. Earlier provisional and Option 2 outcomes remain readable and recover with their original payout. On 2026-10-15, the operator confirmed that live testing qualified slots, roulette, and their location access, and approved activation. This records the operator’s evidence and authorization; this activation change did not perform another live run or inspect production data. Only chips-only slots and roulette are activated. Appearance stakes, other games, operator grants, transfers, and purchases remain outside this release.

Slots

The documented baseline uses four independent weighted reels with cherry/apple/ banana/peach/diamond/seven at 35/25/18/12/8/2%. Its four-of-kind awards are 1/1/1/1/100/500x; three-of-kind awards are 1/1/1/1/15/50x; two pairs pay 1.25x, a pair pays 1x, four distinct fruits pay 1x, and other results return zero. Categories are mutually exclusive, with no Lucky Seven or Diamond Booster. Returns are gross including stake; fractional-chip totals round down once.

Exhaustive enumeration of this baseline validates 95.4383735% theoretical RTP. At a one-chip stake, whole-chip flooring gives 92.684432% RTP; 20- and 100-chip stakes return 95.438374%. 89.479456% of outcomes return chips, including break-even pushes; the theoretical positive-net-profit frequency is 11.211430%. Configured symbols, reel weights, awards, and timing/seat settings may be edited using room-scoped casino.rules. Each candidate rules object is exhaustively enumerated before saving and is rejected above the code-enforced 99% gross RTP ceiling. Slot returns use exact quarter-chip integer units. The 100x diamond and 500x seven four-of-kind awards remain in the baseline, not hardcoded runtime rules.

The documented baseline has 20 active seats, an individual 10-second wager window, five-second result and renewal periods, and a 100-chip per-wager limit. The database may shorten those windows, reduce the player cap, or lower the per-wager limit, but cannot exceed the code-level timing, seat, wager, and session-history ceilings. No player refund/cancellation or automatic repeat stake is allowed. Insufficient-funds attempts do not occupy seats.

Roulette

A first valid, funded bet opens an idle shared European single-zero table and is placed in that round. Invalid selections/stakes, insufficient funds, or denied location access never start play. If funds change between the balance check and reservation, a rejected first bet closes its provisional empty session without a start announcement. Concurrent first bets are serialized into one round, and later bets do not extend its deadline. Room admins may still explicitly open a round. Multiple bets may be placed during the configured betting window by the configured participant cap. Supported selections after /bot casino roulette bet <chips> are:

Selection Syntax Gross return
Straight-up number 0-36 36x
Split split a b (adjacent, ascending, including 0–1/2/3) 18x
Street street row (1–12) 12x
Trio trio 0 1 2 or trio 0 2 3 12x
Corner corner a b c d (ascending layout square) 9x
Four-number four_number (0,1,2,3) 9x
Six-line six_line row (first row 1–11) 6x
Even-money red, black, odd, even, low, high 2x
Dozen / column dozen 1-3 / column 1-3 3x

Returns include stake. Zero loses outside bets. The documented standard payouts produce 36/37 (about 97.2973%) theoretical gross return for every selection; configured payouts may be reduced but cannot exceed the standard schedule. Individual wager results and per-player totals include winning pocket/color, winning/losing bets, total stake, stake returned, gross return, and net profit/loss; player economics are never announced to the room. Cancellation refunds only the last active bet before deadline, allowing replacement bets or further reverse-order cancellations. Refunding all active bets frees that participant’s seat.

After a round with settled bets closes, the table uses the configured result and renewal windows, then automatically opens one new betting round. No stakes are carried forward, and bets during the result/renewal delay are rejected with the next-round time. If a betting window ends with no active bets (including when all bets were cancelled), the round closes quietly: no draw, result message, cooldown, or automatic renewal. Roulette is then idle until another valid bet or optional admin opening. An arriving bet can close an expired empty window and start its new window in the same request if its close timer was delayed. An occupied but non-betting room therefore does not keep the game running. The technical history cap is at most 1,000 wagers per round, including cancelled wagers; reaching it rejects new bets explicitly. The configured per-wager ceiling cannot exceed the code-enforced 100-chip maximum.

Terminal sessions persist resultUntilAt / nextRoundAt; an indexed query restores cooldowns after restart. Only rounds with a durable roulette result and settled bets restore a pending renewal; results are not reannounced. Empty/cancelled historical cooldown records are ignored, without a database migration. Interrupted tables without a saved result are refunded and left idle; the next valid bet can start again. Expired terminal cooldowns do not reactivate old tables on startup. Timers are unref’ed, disposed on stop, and tracked work drains before the shared Mongo client closes.

flowchart LR
    Command[Casino command] --> Gate{CASINO_GAMEPLAY_ENABLED?}
    Gate -->|false| DisabledReply[Private disabled notice; no game service call]
    Gate -->|true: approved slots and roulette| Game[CasinoGameService]
    Game --> Rules[Versioned slot / roulette rules]
    Game --> Durable[Persist result before settlement]
    Durable --> Recover[Recover from result or refund before result]
    Command -->|balance| Service[CasinoWalletService]
    Service --> Economy[EconomyPort]
    Economy --> Repository[MongoEconomyRepository]
    Repository -->|read shared account| Wallet{Account exists?}
    Wallet -->|yes| Reply[Whisper balance]
    Wallet -->|no| Zero[Create zero-balance wallet]
    Zero --> Reply
    Game --> Economy
    Entry[Observed entry / ready] --> Grant[20 chips if rolling 24-hour cooldown elapsed]
    Grant --> Service
    Disabled[Operator grants, transfers, purchases] -. not implemented .-> Command
stateDiagram-v2
    [*] --> Idle
    Idle --> Betting: first valid funded bet / admin open
    Betting --> Betting: accepted bet / last-active-bet refund
    Betting --> Settling: betting deadline with active bets; persist outcome
    Betting --> Idle: deadline with no active bets; quiet closure
    Betting --> Refunding: restart or failure before durable outcome
    Settling --> Settled: idempotent wallet/ledger settlement
    Settling --> Settling: restart; reuse persisted outcome
    Refunding --> Cancelled: every reservation refunded once
    Settled --> Results: 10 seconds
    Results --> Renewal: 5 seconds
    Renewal --> Betting: next empty round
    Cancelled --> Idle
stateDiagram-v2
    [*] --> Ready
    Ready --> Reserved: one accepted slot wager; seat held
    Reserved --> Drawing: individual 10-second deadline
    Drawing --> Settling: four weighted reels; persist outcome
    Drawing --> Refunding: interruption before durable outcome
    Settling --> Results: commit payout/loss once; private result
    Results --> Renewal: after 5 seconds
    Renewal --> Ready: after 5 seconds; no automatic wager
    Refunding --> Ready: interrupted stake refunded/reconciled

Phase 0 is complete: a read-only test inspection established a transaction-capable replica-set topology, and the operator confirmed that production shares that backend. This satisfies the topology gate for both runtime databases without accessing production. Production credentials and transaction permissions were not independently tested, and no live reserve/settle qualification was performed. The operator subsequently confirmed successful live qualification and approved slots, roulette, and location access on 2026-10-15; runtime game commands now invoke the engine after their normal access checks. Clothing or bondage stakes/rewards remain subject to the separate BC permission, observation, consent, and recovery feasibility gates in the implementation plan.

See the casino games and rewards implementation plan for phase status, approved decisions, failure/recovery requirements, and future game state diagrams.