Public: anyone can read this; the crew can change it.
forked from trevato/cs
The range gets the board it deserves: a run is posted, checked and ranked per drill — the server's second game #265
Check · Review · Land · Live done — Landed and live in production.
The range now posts what it plays, and it goes through the shooter's door rather than a second one written beside it.
shipped production is serving it deploy ae07c2d
Sign in to join this conversation.
new above
offline
The range now posts what it plays, and it goes through the shooter's door rather than a second one written beside it.
What is shared, and what it is called.
src/gate.tsis the new concept: the door a posted score comes in through. It holds the two primitives every boundary here needs (integer,cleanName— moved out ofsrc/result.ts, which re-exports the name so nothing else moved) and two shapes:Gate, one game's door, andChecked, what came through it.src/server.tshas one write sequence now — same-origin,mayWritewith the same limiter, the cappedreadBody, the JSON,who(),playerOf, the session cookie, and all of 400/403/413/422/409/429 — and a table of two gates besideRENDER, for the same reasonRENDERis there: a gate closes over this process's database and this server's pages, so it is not data in a registry. It is not called "match" because a match is one game's word for one game's thing; what is shared is the door, not the numbers.Two tables, one gate. A match and a run share three columns and no others, so
runis its own table (id, session, user_id, name, drill, score, hits, shots, seed, seconds), created additively besidematchwith two indexes. Wideningmatchwould have been two halves of nullable columns and a game filter nobody may forget. Accuracy is deliberately not a column and not the browser's word: it is hits over shots, computed where it is shown (hitShare).The ceilings are the drill's own.
src/run.tsreadsDRILLSout ofsrc/range/targets.ts— the server may, the range may not — so the most plates a run can raise (atOnce + runTime/gap + 1), the most one plate can pay and the seconds it can last all come from the table that defines the drill. A run claiming more hits than plates could stand up, more score than those hits could pay, more shots than 20/s, or a clock longer than its drill is a 422.src/range/post.tsis the page's own plumbing — siblings only, one attempt, one sentence on the panel — andsrc/independence.test.tsis green unchanged.What to look at in the preview:
/range— the board under the stage, one table per drill with the empty state, the name field in the overlay, and the panel's last line as a run lands. Thensshto the app: a fifthrunstab lists them with the drill, the accuracy and the seed, andxremoves the row under the cursor through one action that says which table it is in — so whatever identity check #40 grows, both games grow it at once.What is not done, and it is the gap I would fix first: the tests I added are the terminal's (the runs tab, the guest, the escape-in-a-name, the two-table delete) and the suite is green at 1168, but I ran out of budget before writing
src/run.test.tsand the server tests the issue asks for by name — an honest run stored and coming back on the page, an impossible one refused, two drills ranking apart end to end. The behaviour is implemented and exercised by hand, not yet pinned by a test that would fail without it. A reviewer should treat/api/run's ceilings as unproven until those land.Budget reached at $8.53; this is where it stopped.
Closes #187