contradiction
Two things found by playing v0.8.0.1, neither in the mechanism itself.
?pace= never worked. index.html's doors are play.html?lobby and
play.html?solitaire, so arriving through the splash replaces the query string and
the play page only ever saw ?lobby — a whole game was played at 1x while believing
it was at 7x. v0.8.0 shipped that parameter as the only way to change speed and the
game's own front door destroyed it. There is a control on the play screen now,
beside zoom, persisted per viewer; the doors carry pace through as well, so the URL
lever is honest for handing two playtesters different speeds. PACE_LEVELS moved to
sim/pacing.ts with DWELL and MAX_PACE — the tuning surface in one file, and
testable. The committed default is unchanged: what it should be is a question for a
game played at a speed that took effect.
And the Day-end dialog said "0 today, 2 in all". advance.ts increments the Day and
then zeroes collisionsToday, and noteDayEnd() fires when the Day goes up — so the
dialog reporting the Day that just finished was drawn from the very frame in which
that Day's count was reset. Reproduced on four of five seeds before changing
anything. The count is captured at the rollover now; it is not derivable on the
client, because in multiplayer the push announcing the new Day is the same push
that carries the reset. And "today" was the wrong word regardless: it names the Day
instead — "Collisions: 2 on Day 1, 2 in all".
Unrelated to v0.8.0 — that one has been wrong since the dialog was built for
Gitea#10, and needed somebody to play a Day with a collision in it and then read
the summary.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6cF1iYvJ1kNmzYBzu4QX6
The "Play multiplayer" door on index.html had sat disabled, labelled
"Coming soon", since before the server existed — Phases 2 through 4 built
a working lobby and nothing ever linked to it. Loading the site landed on
the same solitaire splash whether a real multiplayer server was behind it
or not, with no visible way in. Found packaging Phase 6 for StartOS.
The door is now a live link to ./play.html?lobby, and main.ts's start()
routes ?lobby straight to the lobby screen — the same showScreen('lobby');
runLobby(beginRemote) the in-game Multiplayer button already used —
instead of dealing a solitaire game first.
GET /api/health is new, and exists to be failed. The same dist/ ships both
served by src/server/ and uploaded as flat files by deploy-web.ts, and the
bundle is identical either way (D4), so the page cannot know from its own
build which it is; every other route 404s an unknown path exactly as a
static host does, so nothing distinguished them. The splash probes it on
load and closes the door when nothing names itself in reply.
The door starts open and only ever closes, deliberately: a wrong "no
server" is the bug above again — invisible, and it strands a player who
does have one — while a wrong "there is one" costs a click and a lobby
that says it cannot connect. The reply must name itself rather than merely
return 200, or a host answering every path with its index page would pass.
Verified: tsc clean; 659 tests pass (656 + 3); /api/health exercised live
against a running server — 200 with the right body, unauthenticated, while
an unknown path and a wrong method both still 404, which is what makes the
probe discriminate at all.
The probe's own test was vacuous on the first attempt — both its "closes"
cases reached close() through the .catch arm, so deleting the body-naming
check outright still passed. Caught by mutating splash.ts and re-running;
the test now covers all three closing routes and fails without the check.
Splash page only: the box art thumbnail opens a full-size lightbox on click, a
new "Play multiplayer" (coming soon) card balances the bottom row to three
cards, and the footer's "runs entirely in your browser" claim is now scoped
to solitaire.
Adds SVG board rendering shared by the game and the replay, a splash page with a
shareable replay directory, hover tooltips for reference detail, and makes curves
two-port arcs so sidings and run-arounds can finally be built.