Absence is not evidence: the seat wallet trusts only verdicts
The games-list reply pruned any wallet seat the server did not vouch for — so one poll against a restarting (or wrong) server silently deleted live credentials, the second seat-burning path this session's server-squatting exposed. myGames now returns explicit verdicts: a seat is voided only when its room EXISTS and refused that exact token. Absent rooms earn no verdict and absent seats survive; the wallet caps by age at fifty instead of by trust. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
co-authored by
Claude Fable 5
parent
72148dd7c2
commit
3d2bf17585
@@ -491,18 +491,18 @@ class Net {
|
||||
if (g.yourTurn && !was) this.notifyTurn(g);
|
||||
this.lastYourTurn.set(key, g.yourTurn);
|
||||
}
|
||||
// A seat the server answered for but did not list is gone — the
|
||||
// room was deleted (or the seat's token voided). Quietly drop it
|
||||
// from the ledger rather than showing "unreachable" forever.
|
||||
// (The server checks at most MAX_MYGAMES_SEATS = 50 per ask;
|
||||
// never prune blind past that cap.)
|
||||
if (this.seats.length <= 50) {
|
||||
const listed = new Set((msg.games as GameSummary[]).map((g) => `${g.roomId}:${g.name}`));
|
||||
const kept = this.seats.filter((s) => listed.has(`${s.roomId}:${s.name}`));
|
||||
if (kept.length !== this.seats.length) {
|
||||
this.seats = kept;
|
||||
saveSeats(this.seats);
|
||||
}
|
||||
// Prune ONLY seats the server proved dead: the room exists and
|
||||
// refused this exact token. A seat merely ABSENT from the reply
|
||||
// stays — a restarting server, a stale restore, or the wrong
|
||||
// backend all answer with ignorance, and treating ignorance as
|
||||
// deletion once cost a live player his seat mid-game. The
|
||||
// wallet is capped by age instead of by trust.
|
||||
const voided = new Set((msg.voided as string[] | undefined) ?? []);
|
||||
let kept = this.seats.filter((s) => !voided.has(`${s.roomId}:${s.name}`));
|
||||
if (kept.length > 50) kept = kept.slice(kept.length - 50);
|
||||
if (kept.length !== this.seats.length) {
|
||||
this.seats = kept;
|
||||
saveSeats(this.seats);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user