A lobby may challenge the keeper, and the table can talk before the boards flip

"Challenge Kestrel, the keeper" holds a seat under the keeper's name,
writes the call to the ledger, says so at the table, and raises a
Sentry issue fingerprinted to the room — one ring per call, with the
room's link in the message — which is the alarm the keeper's phone
listens for. The keeper answers by the link: taking the seat, and
leaving a word if the game must wait. For that word, and for any table
to settle when to start, the lobby now shows its talk and carries a
composer; a joiner sees what was said before they sat. Three calls per
address per hour, one per room.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
Eric Wagoner
2026-09-17 00:30:49 -04:00
co-authored by Claude Fable 5.1
parent de279364ce
commit 1be1cf7d1b
5 changed files with 110 additions and 3 deletions
+12
View File
@@ -369,6 +369,10 @@ class Net {
rematchCall = $state<{ roomId: string; by: string } | null>(null);
/** A rematch lobby: the last table's wizards not yet seated. */
expected = $state<string[]>([]);
/** The table called the keeper of this site, and by whom. */
challenge = $state<{ by: string; at: string } | null>(null);
/** The keeper's name, as the server knows it. */
keeper = $state("Kestrel");
view = $state<GameView | null>(null);
log = $state<LogLine[]>([]);
error = $state<string | null>(null);
@@ -523,6 +527,8 @@ class Net {
this.audience = msg.audience ?? 0;
this.rematchCall = msg.rematch ?? null;
this.expected = msg.expected ?? [];
this.challenge = msg.challenge ?? null;
if (typeof msg.keeper === "string") this.keeper = msg.keeper;
if (this.you && this.token) {
const seat: Seat = { name: this.you, roomId: msg.roomId, token: this.token };
localStorage.setItem(SEAT_KEY, JSON.stringify(seat));
@@ -796,6 +802,11 @@ class Net {
return true;
}
/** Call the keeper of this site to the table: a seat is held, and their phone rings. */
challengeKeeper(): void {
this.send({ type: "challengeKeeper" });
}
/** Call for a rematch from a finished table, or join the one already called. */
callRematch(): void {
this.send({ type: "rematch" });
@@ -886,6 +897,7 @@ class Net {
this.chatPending = null;
this.rematchCall = null;
this.expected = [];
this.challenge = null;
this.roomId = null;
this.roomIdPending = null;
this.view = null;