Public: anyone can read this; the crew can change it.
forked from trevato/cs
The shooter's entry sheds its second concept: posting a result becomes its own module #262
Check · Review · Land · Live done — Landed and live in production.
`src/client/report.ts` now owns a posted round end to end: the world turned into the body `src/result.ts` accepts, the one pending result (never a queue — a newer round replaces an older one), the single attempt in flight, the retry count and the backoff it waits out. It takes a `fetch`-shaped call, a clock, and the two things it has to say out loud — the sentence by the leaderboard and the board…
shipped production is serving it deploy ae07c2d
Sign in to join this conversation.
new above
offline
src/client/report.tsnow owns a posted round end to end: the world turned into the bodysrc/result.tsaccepts, the one pending result (never a queue — a newer round replaces an older one), the single attempt in flight, the retry count and the backoff it waits out. It takes afetch-shaped call, a clock, and the two things it has to say out loud — the sentence by the leaderboard and the board a 200 carries back — so it touches no document and no global;pending.tsstays the policy it asks about each answer, with its header pointed at its new owner.main.tskeeps only the wiring:createReporter({ fetch, now, say, board }),reporter.send()once a frame, andreporter.post(world, playId, name, final)on the frame a round is scored.Behaviour is unchanged by construction — it is the same code, the same
nextStep, the same/api/matchbody, the same map recorded with it, and the same "not posted" sentences in the same two places. The tests moved with it:report.test.tsdrives the queue against a scripted fetch and a clock it moves itself (a fetch that fails once and then succeeds lands exactly one result, a 429 waits out its backoff, a 422 is never retried, the round retried is the newest one, four attempts is the cap, andstanding()never claims a match is on the board while bytes are still in hand), andmain.test.tskeeps the wiring tests — one POST per round scored in the shape the server accepts, the map it was played on, the retry ridden by the frame loop, and the summary's line.What I chose not to do: fold
pending.tsinto the new module. They are two concepts — what a status means is arithmetic, what to do about it is a round, a network and a clock — andpending.test.tsis the contract for the first of them, so it stayed its own file and only its doc comment changed.Bundle: 98,864 → 98,981 raw and 38,966 → 39,049 gzipped (+117 / +83, the seam itself: a module boundary the minifier will not inline across, the object the frame lends the reporter, and the endpoint named rather than inline). That fits the 103,000 / 41,000 ceiling with 4,019 raw and 1,951 gzipped to spare, so no ceiling moved, and neither other bundle changed a byte.
bun run testis green (1,150 pass). To try it in the preview: play a round with the network throttled or offline and watch the line under the leaderboard go from retrying to saved or lost, and the summary's line refuse to say "saved" until the last post is answered.🤖 Generated with Claude Code
Closes #251