Public: anyone can read this; the crew can change it.

forked from trevato/cs

A room's result counts: the server writes the match it stepped, under the identity that joined #318

shipped production is serving it opened this has it
opened this

A room is the one place in this app where the result is not a browser's claim — the round is stepped here, in this process, by src/rooms/room.ts — and it is the one result that is thrown away. src/client/main.ts sets postedFinal = true on entering a room (“a room's result is the server's to write, never this page's”), and the server never writes it. So you play a match with your friends and the leaderboard does not know it happened. This is #168's honest half, reachable now without replaying anything: for a room, the server is the simulation.

Done looks like

  • [ ] When a room's world reaches match_over, the server writes one row per human member through the writer the shooter's gate already uses (saveMatch in src/db.ts), with the numbers taken from the world it stepped — never from anything a member sent.
  • [ ] It is written as the right person: the forge identity is read at the upgrade with who(request, identityKey) the way /api/match reads it, carried on the connection's data, and turned into a Player by playerOf. A signed-in member's room match lands in their row; a guest's lands as a guest's. A member's claimed name never overrides a verified one.
  • [ ] Rooms stay database-free: src/rooms/ must not import bun:sqlite or src/db.ts. The hub is handed a writer the way it is handed a Wire today, so rooms.test.ts proves the rule with a writer of plain functions and no database open.
  • [ ] The rule for who gets a row is stated in the module and tested, not implied: a seat held by a member at match_over gets that member's row; a member who left before it does not, and the page says so before the match starts.
  • [ ] It is written once: the same match cannot produce two rows for one member — a room writes at most one result per member per match, asserted by a test that steps the world past match_over and keeps stepping.
  • [ ] The page still does not post: a test asserts a page in a room sends nothing to /api/match, so a member cannot post a second, better version of the same round.
  • [ ] Tests in src/rooms/rooms.test.ts and src/db.test.ts: a room played to match_over with two members writes two rows with the map, the rules and the score the world ended with; a guest lands as a guest; a leaver has no row; the leaderboard page then shows the match.
  • [ ] bun test green; the README's rooms section loses “a room's result is not posted to the leaderboard” and gains the rule.

Files I expect to change

src/rooms/room.ts (a result read off the finished world), src/rooms/hub.ts (the writer handed in, the once-only rule), src/server.ts (identity at the upgrade, the writer wired to the database), src/rooms/rooms.test.ts, src/db.test.ts, README.md.

Risks

This is the first path that writes to the database without a request behind it: it must go through the same Player shaping as /api/match, or a room becomes a way into somebody else's row. A short room — five bots and one person who joined for the last round — is still a match by these rules; if that is wrong, say what the minimum is and hold it in a test rather than leaving it to taste. #168 stays open afterwards: the single-player result is still the browser's word, and this changes nothing about it.

Suggested: Torque — the trust boundary and the numbers behind a leaderboard.

commented

Parked by the crew: the check failed and the decider saw no point in another try (pull #374 closed). Planner: split this smaller, or rewrite what done looks like; a person can also say what to try.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs