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

forked from trevato/cs

The second game's plumbing lands first: /range is served, bundled and proven engine-only #24

shipped production is serving it opened this has it
opened this

#13 is the only thing this direction is really about and it is blocked behind the renderer and the overlay; everything around it — the route, the page, the entry, the bundle and the rule that forbids reaching into src/client/ — depends on nothing that is still in flight, so land that half now and leave #13 the game itself.

What ships is a live page at /range that boots the engine's loop and controls and draws what it reads: the tick count, the frame time, which actions are held and where you are looking. No room, no targets, no physics, no WebGL — those are #13's, and this issue must not pre-empt them.

Done looks like

  • [ ] GET /range answers 200 with a server-rendered page built from src/pages/shell.ts — its own title, heading, description and sections, not cs's — carrying the same security floor as /; HEAD /range answers like GET; an unknown path is still the same 404. / links to it and it links back.
  • [ ] scripts/build.ts gains one row in ENTRIES ({ name: "range", entry: "src/range/main.ts" }), the committed static/range.<hash>.js is fresh, and the freshness test in scripts/build.test.ts covers it without being rewritten.
  • [ ] src/range/main.ts boots on the shell's ids, creates createControls with its own bindings table in src/range/binding.ts (not CS_KEYS) and createLoop at its own tick rate, and draws a readout on #hud with plain 2D calls: ticks run, ms a frame, held actions, yaw and pitch. It degrades to a sentence, not an exception, when #scene has no context.
  • [ ] src/range/independence.test.ts reads the sources and fails if any module under src/range/ imports anything but src/engine/, src/pages/shell.ts and its own siblings — reusing the exported sources/specifiersOf helpers from src/engine/boundary.test.ts rather than a second copy of the regex, and with a fixture proving the rule catches ../client/world.ts.
  • [ ] src/range/binding.test.ts pins the bindings table and the held-actions-to-intent mapping headlessly, the way src/client/binding.test.ts does.
  • [ ] src/server.test.ts gains: /range 200 and same headers as /, HEAD /range matching, the page's script being static/range.<hash>.js and never cs's bundle, and every id src/range/main.ts looks up present in the served page.
  • [ ] A phone can use it: 360px wide with no horizontal scroll and any control at least 44px, through the shell's touch pads and the engine's controls.
  • [ ] bun test green, both bundles rebuilt and committed, /range measured in single-digit milliseconds, and the README's shape section says what /range is today and what #13 will make it.

Files I expect to change

src/range/main.ts, src/range/binding.ts, src/range/binding.test.ts, src/range/independence.test.ts, src/pages/range.ts (all new), src/server.ts, src/server.test.ts, scripts/build.ts, src/pages/game.ts (the link), README.md, static/range.<hash>.js.

Risks

The failure mode is building #13 by accident: stop at the readout. The second is the route landing in src/server.ts as a third if — if the dispatch stops fitting in one read while you are in there, lift the table into src/routes.ts and say so, but do not both add the game and refactor the server in one pull. Nothing generated at boot may block the first paint, no new endpoint, no new table, no new cookie, and the CSP stays default-src 'self' — no inline script.

Suggested: Torque — this is a route, an entry, a bundle and a rule that reads the sources, which is the brief.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs