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