Tell the player when their match did not land on the leaderboard, and try the last post again once.
postResult in src/client/main.ts:564-593 posts after every round and again at match_over. On response.ok it swallows the server's HTML into #stats; on anything else — 429 slow down, 409 not yours, 413, 422, 500 — and on a thrown fetch it does nothing at all. The comment says a leaderboard that cannot be reached is not a reason to stop playing, which is right, but the player is left reading a board that silently disagrees with the match they just finished, and the retry-after the server carefully sends on a 429 is read by nobody.
Done looks like:
[ ] A new module, src/client/posting.ts: plain data in, plain data out — (status: number | null, final: boolean) => { note: string | null; retry: boolean }, where null status is a network failure. It owns the wording and the retry rule and touches no DOM.
[ ] The rule: a failed final post retries once, after retry-after seconds when the server sent one (clamped to something sane, 1-10s) and about a second otherwise; a failed per-round post is not retried, since the next round posts again anyway.
[ ] A line in the page for it: one server-rendered, hidden element in the leaderboard section of src/pages/game.ts, written with textContent and never with markup, shown when note is non-null and hidden again the moment a post succeeds.
[ ] The words are honest and specific: not recorded because you are offline, because the server is rate-limiting, or because this match belongs to another browser. Nothing claims a save that did not happen.
[ ] Tests that fail without the change: posting.test.ts over every status the server can answer with (200, 409, 413, 422, 429 with and without retry-after, 500, network null) for both final values.
[ ] bun run check green.
Files I expect to change: new src/client/posting.ts and src/client/posting.test.ts, src/client/main.ts, src/pages/game.ts, src/pages/game.test.ts.
Risks: main.ts has no DOM harness (#114 is about exactly that), so keep the wiring there to a handful of lines and put every decision in the module the test can drive. One retry, never a queue: a match that failed twice stays failed and says so. Do not let the retry double-post a result the server accepted.
Brief that fits: scout — the server, the page and what it says.
Tell the player when their match did not land on the leaderboard, and try the last post again once.
postResultinsrc/client/main.ts:564-593posts after every round and again atmatch_over. Onresponse.okit swallows the server's HTML into#stats; on anything else — 429slow down, 409not yours, 413, 422, 500 — and on a thrownfetchit does nothing at all. The comment says a leaderboard that cannot be reached is not a reason to stop playing, which is right, but the player is left reading a board that silently disagrees with the match they just finished, and theretry-afterthe server carefully sends on a 429 is read by nobody.Done looks like:
src/client/posting.ts: plain data in, plain data out —(status: number | null, final: boolean) => { note: string | null; retry: boolean }, wherenullstatus is a network failure. It owns the wording and the retry rule and touches no DOM.retry-afterseconds when the server sent one (clamped to something sane, 1-10s) and about a second otherwise; a failed per-round post is not retried, since the next round posts again anyway.hiddenelement in the leaderboard section ofsrc/pages/game.ts, written withtextContentand never with markup, shown whennoteis non-null and hidden again the moment a post succeeds.posting.test.tsover every status the server can answer with (200, 409, 413, 422, 429 with and withoutretry-after, 500, networknull) for bothfinalvalues.bun run checkgreen.Files I expect to change: new
src/client/posting.tsandsrc/client/posting.test.ts,src/client/main.ts,src/pages/game.ts,src/pages/game.test.ts.Risks:
main.tshas no DOM harness (#114 is about exactly that), so keep the wiring there to a handful of lines and put every decision in the module the test can drive. One retry, never a queue: a match that failed twice stays failed and says so. Do not let the retry double-post a result the server accepted.Brief that fits: scout — the server, the page and what it says.