Split The code is public; the app answers only to the crew.

A match you walk out of stops calling itself "in progress" on the leaderboard #50

shipped merged opened this has it
opened this

Leave a match from the pause overlay and its row sits on the front page as · in progress forever. toMenu() in src/client/main.ts sets running = false, drops the world and posts nothing, so the last thing the server heard was a mid-match update with finished: false; recentMatches renders every such row as in progress with no time bound, and src/tty.ts shows the same row as (live). The standings count its matches and its partial kills, so the page's Recent matches list slowly fills with games nobody is playing.

Done looks like:

  • [ ] Leaving a match sends one last result that says it ended without being finished — a third state beside "in progress" and "finished", validated at the boundary in src/result.ts like every other field, defaulting to the behaviour we have today when a client does not send it.
  • [ ] The page says abandoned for those rows instead of in progress, and src/tty.ts says the same word in its match list. Standings keep the kills and the match, and still count no win — a win already requires finished = 1.
  • [ ] A row nobody ever closed — a browser that crashed or a tab that closed mid-round — reads as abandoned too once it has not been updated for a while, so the page cannot accumulate permanent ghosts. The rule lives in one place, not in two renderers.
  • [ ] Tests: result.ts parses the new state and rejects a bad one; db.ts round-trips it; statsHtml shows abandoned for a left match and in progress for one updated seconds ago; standings are unchanged by an abandoned match's win column; and the terminal's match list renders the same word.
  • [ ] bun test green, README says what the three states mean.

Not in this issue: resuming an abandoned match, deleting rows (that is #40), and any change to how often a live match posts.

Files I expect to change: src/result.ts, src/db.ts, src/pages/game.ts, src/tty.ts, src/client/main.ts, the tests beside each, README.md.

Risks: this is the /api/match boundary, which #34 is rewriting to tie a result to a forge identity — hence the dependency; land on top of whatever shape that leaves rather than beside it. A schema change needs CREATE TABLE IF NOT EXISTS to stay true for a database that already has rows: add the column with a default and do not drop anything. #41 is moving serve(); keep out of src/server.ts if you can.

Who: scout — server, page and terminal.

commented

Since this was written, #48 landed GET /api/leaderboard (leaderboardJson in src/pages/game.ts), which hands finished straight out as data. So the third state has to be right in three renderers, not two: the HTML list, the JSON, and src/tty.ts. The rule for a stale row still belongs in one place that all three read.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs