Players see their reports answered in the lobby ledger

Reports gain ids; replies live in the same JSONL, folding onto their
report when read (legacy reports answer to their timestamp). The lobby
asks with the same seat-token proof as the games ledger — wake-free —
and lists each report with its reply and status, or 'the wizards are
studying the moment'. deploy/feedback-reply.sh answers one from here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
This commit is contained in:
Eric Wagoner
2026-09-01 09:34:20 -04:00
co-authored by Claude Fable 5
parent eabc10f18c
commit 5abd0ba60e
5 changed files with 153 additions and 3 deletions
+17
View File
@@ -501,6 +501,12 @@ class Net {
this.stats = msg.stats;
break;
}
case "feedbackList":
this.feedbackReports = msg.reports ?? [];
break;
case "feedbackReceived":
this.refreshFeedback();
break;
case "games": {
this.games = msg.games;
for (const g of msg.games as GameSummary[]) {
@@ -600,6 +606,16 @@ class Net {
this.send({ type: "feedback", happened, expected });
}
/** Your reports and the wizards' replies, proven by seat tokens. */
feedbackReports = $state<{
id: string; at: string; roomId: string; seq: number; round: number | null;
happened: string; expected: string;
reply?: { at: string; text: string; status: string };
}[]>([]);
refreshFeedback(): void {
if (this.seats.length > 0) this.send({ type: "myFeedback", seats: $state.snapshot(this.seats) });
}
requestTransferCode(): void {
this.send({ type: "makeTransfer" });
}
@@ -661,6 +677,7 @@ class Net {
startGamePolling(): void {
if (this.pollTimer) return;
this.refreshGames();
this.refreshFeedback();
this.pollTimer = setInterval(() => this.refreshGames(), 45_000);
}
stopGamePolling(): void {