The template's lost-seat search counts the tables it cannot give back rather than naming them, leaves a seat a bot stands in for to the host, and the keeper's door looks a name up across tables

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CCLa2PaEeCj8njQZSWGqHx
This commit is contained in:
Eric WagonerandClaude Fable 5.1 committed 2026-11-10 21:20:27 -05:00
1 parent dcb18b2b7e
commit e3f8a03215
7 files changed
+69 -35

No files matched your search

+31 -17
View File
@@ -337,17 +337,12 @@ export class Rooms<State, Input> {
this.apply(room, line);
}
/**
* The seats a name holds at tables not yet over, for a browser that lost them. A seat last used from the
* asker's own address, at a table where no other person shares that address, is theirs again with a new
* token, the old one dead; the rest are named, so the asker can get a phrase from the host there.
*/
findSeats(rawName: string, addr: string, only?: string): { seated: { roomId: string; seat: SeatId; token: string }[]; elsewhere: { roomId: string; seat: SeatId; players: string[] }[] } {
const name = cleanName(rawName).toLowerCase();
const seated: { roomId: string; seat: SeatId; token: string }[] = [];
const elsewhere: { roomId: string; seat: SeatId; players: string[] }[] = [];
/** The tables not yet over, written to lately, where a person of this name sits; one table alone when it is named. */
private tablesOf(name: string, only?: string): { room: Room<State>; seat: Seat }[] {
const wanted = cleanName(name).toLowerCase();
const since = Date.now() - LOST_SEAT_LOOKBACK_MS;
const ids = only ? [normalizeCode(String(only))] : this.store.roomIds().filter((id) => this.store.updatedAt(id) > since);
const found: { room: Room<State>; seat: Seat }[] = [];
for (const id of ids) {
let room: Room<State>;
try {
@@ -356,18 +351,37 @@ export class Rooms<State, Input> {
continue;
}
if (room.over) continue;
const person = (s: Seat) => !s.bot || s.standIn;
const mine = room.seats.find((s) => person(s) && s.name.toLowerCase() === name);
if (!mine) continue;
const shared = room.seats.some((s) => s !== mine && person(s) && s.addr === addr);
if (mine.addr === addr && !shared) {
const { token } = this.reseat(room, mine.id);
seated.push({ roomId: room.id, seat: mine.id, token });
} else elsewhere.push({ roomId: room.id, seat: mine.id, players: room.seats.filter((s) => s !== mine).map((s) => s.name) });
const seat = room.seats.find((s) => (!s.bot || s.standIn) && s.name.toLowerCase() === wanted);
if (seat) found.push({ room, seat });
}
return found;
}
/**
* The seats a name holds at tables not yet over, for a browser that lost them. A seat last used from the
* asker's own address, at a table where no other person shares that address, is theirs again with a new
* token, the old one dead. The rest are only counted: a name proves nothing, so neither the tables nor the
* company at them are told to the asker, who can get a phrase from the host, or a link from the keeper. A
* seat the host gave to a bot is the host's to give back, by a phrase, never taken back by name.
*/
findSeats(rawName: string, addr: string, only?: string): { seated: { roomId: string; seat: SeatId; token: string }[]; elsewhere: number } {
const seated: { roomId: string; seat: SeatId; token: string }[] = [];
let elsewhere = 0;
for (const { room, seat } of this.tablesOf(rawName, only)) {
const shared = room.seats.some((s) => s !== seat && (!s.bot || s.standIn) && s.addr === addr);
if (seat.addr === addr && !shared && !seat.standIn) {
const { token } = this.reseat(room, seat.id);
seated.push({ roomId: room.id, seat: seat.id, token });
} else elsewhere += 1;
}
return { seated, elsewhere };
}
/** For the keeper: every table not yet over where a person of this name sits, with the company there, to re-seat them by. */
tablesForKeeper(name: string): { roomId: string; seat: SeatId; standIn: boolean; players: string[]; updatedAt: number }[] {
return this.tablesOf(name).map(({ room, seat }) => ({ roomId: room.id, seat: seat.id, standIn: !!seat.standIn, players: room.seats.filter((s) => s !== seat).map((s) => s.name), updatedAt: room.updatedAt }));
}
/** Claim a phrase: the seat and its token, once. Too many misses void every pending phrase. */
claim(raw: unknown): { roomId: string; seat: SeatId; token: string } {
const now = Date.now();