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

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

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

opened this

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.ts is the new concept: the door a posted score comes in through. It holds the two primitives every boundary here needs (integer, cleanName — moved out of src/result.ts, which re-exports the name so nothing else moved) and two shapes: Gate, one game's door, and Checked, what came through it. src/server.ts has one write sequence now — same-origin, mayWrite with the same limiter, the capped readBody, the JSON, who(), playerOf, the session cookie, and all of 400/403/413/422/409/429 — and a table of two gates beside RENDER, for the same reason RENDER is 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 run is its own table (id, session, user_id, name, drill, score, hits, shots, seed, seconds), created additively beside match with two indexes. Widening match would 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.ts reads DRILLS out of src/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.ts is the page's own plumbing — siblings only, one attempt, one sentence on the panel — and src/independence.test.ts is 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. Then ssh to the app: a fifth runs tab lists them with the drill, the accuracy and the seed, and x removes 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.ts and 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

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs