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

The match you just played has no page: `GET /m/<id>`, linked from every row on the board and reachable from the terminal #173

open told opened this
opened this

Give one match a page of its own, so the end of a match is something you can come back to and point at instead of a row that scrolls off the board.

src/server.ts has four routes — POST /api/match, GET /api/leaderboard, / and the bundle — and the board on / renders a match as one line: de_forge · 12k 7d · 3400 damage, with the score and the state. There is no way to look at a single match. Everything a page would want is already a column on match (src/db.ts:80): map, kills, deaths, headshots, damage, rounds, score_mine, score_theirs, finished, seconds, name, identity, created_at, updated_at, and the one-word state src/db.ts already computes (live / finished / abandoned).

Done looks like

  • [ ] GET /m/<id> server-renders one match: who played it (the same signed/guest distinction the board draws, never a name that could be faked), the map, the score, kills/deaths/headshots, damage, rounds, how long it took, the state word, and when. HEAD answers like GET.
  • [ ] The id is validated at the boundary before it reaches the database — a match id is a uuid the browser generated, so check its shape and answer 404 for anything else. An id that is well-formed but unknown is the same 404.
  • [ ] Every response carries the security floor in src/server.ts unchanged, and the page gets cache-control: private, no-cache for the same reason / does. Every field is escaped through the helper src/pages/game.ts already uses.
  • [ ] It shows nothing the board does not already show about that match. No session id, no identity string, no route that lets someone walk the table — one id in, one row out.
  • [ ] Each row on the board links to it, and the match-over card links to the match you just finished.
  • [ ] The terminal can reach the same thing: Enter on a row in the matches tab shows the match, Esc goes back. AGENTS.md's rule — what a person can do on the page they can do here too.
  • [ ] Rendering lives in src/pages/match.ts beside game.ts, and the query is one function in src/db.ts. src/routes.ts is worth making if the table of paths in server.ts stops fitting in one read; say which you chose.
  • [ ] Tests that fail without it: a known id renders its numbers; an unknown one is 404; a malformed one is 404 without touching the database; HEAD matches GET; the headers are the floor plus private, no-cache; the terminal's detail view shows the same numbers as the page.
  • [ ] README: one line under what the server serves.

Files I expect to change: src/server.ts, src/pages/match.ts (new), src/pages/game.ts, src/db.ts, src/tty.ts, src/server.test.ts, src/tty.test.ts, src/db.test.ts, README.md.

Risks: the board is public, so this page is too — keep it to the fields already on the board or it becomes a way to learn something about another player. The / route's cookie-setting path must not grow a second copy: a match page does not need to mint a session.

Brief that fits: scout.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs