Status: Implemented as an independently wired, disabled-by-default feature. See the Hot Seat guide for runtime behavior, commands, operator prompt moderation, persistence, and current release status.
Archived: 2026-10-16 (UTC). The feature guide is the current runtime reference.
An independent, room-scoped social bluffing game. The bot chooses one opted-in player, presents an approved database prompt, and lets that player answer truthfully, bluff, or refuse. Other opted-in players vote whether the answer is true or a lie. The game evaluates only the player’s declared mode against the room vote; it cannot determine whether a personal statement is factually true.
The optional double/forfeit rules use the proposed shared economy from the economy proposal, not a separate Hot Seat wallet or game-to-game imports.
prototypeHotSeatPrompts. The respondent posts
their own answer in room chat; the bot does not copy or retain that answer.truth or bluff through a targeted
command, or chooses refuse. The declaration is intent, not a verified
claim about real-world facts./bot hotseat vote true|lie
prediction each. Voters cannot vote on their own response.lie majority among the configured minimum number
of valid votes exposes a declared bluff. Otherwise a bluff fools the room.
Insufficient votes void the round without an economic outcome.The chat vote is a social judgment, not a truth detector. The bot must never state that it verified the response, diagnose a player, or store a behavioral profile.
Use one typed document per (roomKey, gameId) in a room-scoped
prototypePartyGameConfigs collection. The proposed ID for this game is
<roomKey>:hot-seat. The document has a revision, enabled (false when
missing), typed settings, and update timestamp. It is loaded from MongoDB;
absence or invalid data disables the game. Admin updates use strict bounds
and revision checks. A settings revision is pinned to each session; changes
apply to the next round, not an active vote.
| Setting | Proposed validation / intent |
|---|---|
minPlayers, maxPlayers |
Safe bounded integers; respondent plus voters fit the configured cap. |
answerWindowMs, voteWindowMs |
Bounded positive durations; persisted absolute deadlines drive recovery. |
minVotes |
At least one and no greater than eligible voters. Below quorum, void. |
bluffMultiplier |
Initially 2; positive bounded multiplier, still subject to economy ceiling. |
refusalPolicy, timeoutPolicy |
Typed enums, default to no economic penalty. |
enabled |
Explicit room-level opt-in; absent means disabled. |
The operator-managed prototypeHotSeatPrompts collection contains validated
prompt IDs, category, locale, bounded prompt text, moderation status, content
revision, and timestamps. Only approved/active records can be selected. Do not
put example adult prompts in code or this proposal. Prompt content must be
curated for an adults-only room and must not ask for identifying, non-consensual,
or otherwise unsafe personal disclosures.
prototypeHotSeatSessions stores a bounded roster, respondent member number,
prompt ID/revision, config revision, phase/revision, respondent’s truth /
bluff intent, bounded vote choices, deadlines, and outcome/settlement IDs.
It does not store the respondent’s free-form answer or a transcript.
Terminal sessions may expire after the approved retention period. Active
sessions have no TTL and are recovered or explicitly voided.
Only one active Hot Seat session per room is allowed. Use a unique active key and revision-checked updates so concurrent starts, duplicate votes, and stale timer callbacks cannot advance the same round twice. Deadlines are persisted; timers are in-memory wakeups, unref’ed, canceled on stop, and rebuilt from durable state after restart. Economic rewards/forfeitures use stable session-derived operation IDs.
If the respondent leaves before submitting/refusing, void the round and make no score change. A departed voter remains in the frozen roster but cannot add or change a vote; new arrivals are eligible next round. If the bot shuts down before a vote is closed, resume from the persisted phase and deadline. If the outcome was saved but economy settlement was interrupted, retry the same idempotent settlement.
flowchart LR
Root["start.ts"] --> Plugin["HotSeatPlugin"]
Plugin --> Commands["HotSeatCommandSuite"]
Plugin --> Service["HotSeatService"]
Commands --> Parser["BotCommandParser"]
Commands --> Service
Service --> Rules["HotSeatRules"]
Service --> Repo["HotSeatRepository"]
Service --> Economy["Injected EconomyPort"]
Service --> Comms["Injected CharacterCommunicationApi"]
Repo --> Config[("Room-scoped game config")]
Repo --> Prompts[("prototypeHotSeatPrompts")]
Repo --> Sessions[("prototypeHotSeatSessions")]
Comms --> BC["Existing BcClient actions"]
classDiagram
class HotSeatPlugin {
+start()
+stop()
}
class HotSeatCommandSuite {
+register()
-vote()
-declareMode()
-refuse()
}
class HotSeatService {
+startRound(roomKey)
+declareMode(sessionId, member, mode)
+castVote(sessionId, member, vote)
+refuse(sessionId, member)
+recover(roomKey)
+stop()
}
class HotSeatRules {
+resolve(mode, votes, minVotes)
+validateConfig(config)
}
class HotSeatRepository {
<<port>>
+createActiveSession()
+transition(expectedRevision, nextState)
+listRecoverable(roomKey)
}
class EconomyPort {
<<injected shared contract>>
+forfeitAvailable(operationId, member)
+grantOnce(operationId, member, amount, reason)
}
HotSeatPlugin --> HotSeatCommandSuite
HotSeatPlugin --> HotSeatService
HotSeatCommandSuite --> HotSeatService
HotSeatService --> HotSeatRules
HotSeatService --> HotSeatRepository
HotSeatService --> EconomyPort
flowchart LR
Admin((Room admin)) --> Enable([Enable and configure Hot Seat])
Player((Opted-in player)) --> Join([Join a round])
Player --> Respond([Answer, declare intent, or refuse])
Voter((Opted-in voter)) --> Vote([Vote true or lie])
Timer((Deadline worker)) --> Resolve([Resolve quorum and outcome])
Enable --> Config[(Room config)]
Respond --> Session[(Active round)]
Vote --> Session
Resolve --> Economy([Apply one bounded settlement])
flowchart TD
Start([Start requested]) --> Enabled{Room config valid and enabled?}
Enabled -->|No| Deny[Reply disabled; no session]
Enabled -->|Yes| Join{Minimum opted-in players present?}
Join -->|No| Deny
Join -->|Yes| Create[Select respondent and approved prompt]
Create --> Ask[Post prompt; persist deadline]
Ask --> Reply{Respondent answers, refuses, leaves, or times out}
Reply -->|Refuse / leave| Void[Persist terminal no-penalty outcome]
Reply -->|Timeout| Policy[Apply configured timeout policy]
Reply -->|Answer + private intent| Vote[Open vote; accept one vote per eligible member]
Vote --> Deadline{Deadline reached?}
Deadline -->|No| Vote
Deadline -->|Yes| Quorum{Vote quorum met?}
Quorum -->|No| Void
Quorum -->|Yes| Resolve[Resolve plurality and persist outcome]
Resolve --> Economy[Idempotent credit double / available-credit forfeit / no change]
Policy --> End([Close round])
Economy --> End
Void --> End
stateDiagram-v2
[*] --> Lobby
Lobby --> Question: enabled + quorum of joined players
Question --> Response: prompt published
Response --> Voting: answer posted + private truth/bluff intent
Response --> Refused: respondent declines
Response --> Voided: respondent leaves / timeout policy voids
Voting --> Voting: valid vote before deadline
Voting --> Voided: deadline with insufficient votes
Voting --> Settling: quorum; persist outcome
Settling --> Complete: idempotent economy result recorded
Settling --> Settling: restart; retry same operation ID
Refused --> Complete
Voided --> Complete
Complete --> [*]
sequenceDiagram
participant Player
participant Cmd as HotSeatCommandSuite
participant Service as HotSeatService
participant DB as HotSeatRepository
participant BC as CharacterCommunicationApi
participant Economy as EconomyPort
Player->>Cmd: start / join
Cmd->>Service: startRound(roomKey)
Service->>DB: create session + persist prompt/config revision
Service->>BC: publish prompt and respondent
Player->>Cmd: private truth/bluff intent
Cmd->>Service: declareMode(sessionId, member, mode)
Service->>DB: CAS response -> voting
Service->>BC: announce vote window (unconfirmed dispatch)
loop Before persisted deadline
Player->>Cmd: vote true|lie
Cmd->>Service: castVote(member, enum)
Service->>DB: CAS insert/replace allowed vote
end
Service->>DB: persist resolved outcome
Service->>Economy: idempotent settle/forfeit if applicable
Service->>BC: announce result (dispatch status remains honest)
Room administrators must explicitly confirm the adults-only room policy before
enabling. Trusted operators manually moderate prompt records and set them
approved; no prompt text is seeded. The implementation uses the strict lie
majority rule (ties fool a declared bluff), defaults to a multiplier of two,
sets a hard available-credit ceiling of 100,000, defaults refusal and timeout to
no penalty, and accepts mode/vote commands only through native /bot or private
Whisper paths. Settings remain bounded and room-configurable; active rounds pin
their settings revision.
Acceptance requires independent disablement; strict private command parsing; vote/round CAS tests under duplicate and concurrent commands; timeout, departure, restart, and settlement-retry tests; zero raw response text in Mongo/logs; and no economic mutation when the round is disabled, refused, or below quorum. Live room qualification remains separate from implementation; missing configuration is disabled and no prompt content is included by default.