Credibility pass: the accretion sanded smooth
Blind reviews over d2b40ec..HEAD (engine / web / server+deploy). Orphaned doc comments rejoined their functions; the swap-meet tradables collapsed from four pasted deriveds to one (killing the "the's treasure" label); FEAR's aura and banner now share the engine's own dreadDistance; the long-press peek got one home; rulebook.ts stopped wearing JSON quotes; process-named tests and war-story comments now state the constraints they pin; the protocol doc caught up to its handlers; verify-ledgers.sh can actually count failures; the runbook learned about the determinism gate and the nightly backups. No behavior changes — 255 tests and all 48 production ledgers replay identically. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
3445d12206
commit
6adcbe23b5
@@ -9,6 +9,8 @@
|
||||
// {type:"claimTransfer", code} claim a seat on a new device
|
||||
// {type:"catchUp", sinceSeq} replay of moves missed while away
|
||||
// {type:"chat", text} table talk to the room
|
||||
// {type:"rollDie"} the tabletop D4, published as talk
|
||||
// {type:"addBot", style?, tier?} host seats an automaton
|
||||
// {type:"watch", roomId} join the Peanut Gallery: nameless, read-only
|
||||
// {type:"leave"} detach this socket from table or gallery
|
||||
// {type:"myGames", seats} summaries for held seats
|
||||
@@ -17,8 +19,8 @@
|
||||
// server -> client:
|
||||
// {type:"welcome"} on connect
|
||||
// {type:"seat", playerId, token} your seat secret — keep it
|
||||
// {type:"room", roomId, players, hostId, started, colors}
|
||||
// {type:"events", events} redacted for this recipient
|
||||
// {type:"room", roomId, players, hostId, started, audience, colors, bots}
|
||||
// {type:"events", events, replayed?} redacted for this recipient
|
||||
// {type:"state", view, seq} redacted full view (after every change)
|
||||
// {type:"chat", player, text, at} one line of table talk
|
||||
// {type:"watching", roomId} you are seated in the gallery
|
||||
@@ -81,7 +83,7 @@ loadPersistedRooms();
|
||||
// current actor is an automaton waits forever for a human to poke it.
|
||||
setTimeout(() => {
|
||||
for (const room of runningRooms()) runBots(room);
|
||||
}, 2000);
|
||||
}, 2000); // a beat for clients to reconnect before the clockwork stirs
|
||||
|
||||
// One process serves both the built client and the websocket, so production
|
||||
// needs only a TLS proxy in front (or nothing, on a LAN).
|
||||
@@ -150,7 +152,8 @@ interface Session {
|
||||
socket: WebSocket;
|
||||
playerId: PlayerId | null;
|
||||
roomId: string | null;
|
||||
/** In the Peanut Gallery: nameless, read-only, counted but never listed. */
|
||||
/** In the Peanut Gallery: spectator implies playerId === null, so every
|
||||
* handler that requires a seat refuses this session by construction. */
|
||||
spectator: boolean;
|
||||
/** The raw seat token this connection authenticated with (memory only). */
|
||||
token: string | null;
|
||||
@@ -380,7 +383,7 @@ wss.on("connection", (socket) => {
|
||||
case "watch": {
|
||||
// The Peanut Gallery: no name, no seat, no ledger line — a pure
|
||||
// reader of the public broadcast, counted but never identified.
|
||||
const roomId = String(msg.roomId ?? "").trim().toUpperCase().slice(0, 8);
|
||||
const roomId = String(msg.roomId ?? "").trim().slice(0, 8);
|
||||
if (!roomId) return send(socket, { type: "error", message: "roomId required" });
|
||||
const room = getRoom(roomId);
|
||||
if (!room) return send(socket, { type: "error", message: "no such room" });
|
||||
@@ -444,6 +447,7 @@ wss.on("connection", (socket) => {
|
||||
if ("error" in result) return send(socket, { type: "error", message: result.error });
|
||||
broadcast(room, (playerId) => ({ type: "events", events: redactFor(result.events, playerId) }));
|
||||
broadcast(room, (playerId) => ({ type: "state", view: viewForPlayer(room, playerId), seq: room.log.length }));
|
||||
// The game's end unmasks the mystery machines in the roster.
|
||||
if (room.state?.phase === "finished") broadcast(room, () => roomInfo(room));
|
||||
botRemark(room, session.playerId, result.events as { type: string }[]);
|
||||
runBots(room);
|
||||
|
||||
Reference in New Issue
Block a user