A 20 Hz loop that runs forever burns a core on an empty arena, and drift will spend most of its life
empty. Nothing in the map says what the world does when the roster is zero — #8 starts one interval and
never stops it. The answer has to hold the promise in #1 from the other side: asleep costs nothing,
and the first person through the door is swimming within five seconds anyway.
Done looks like
[ ] With an empty roster the loop idles: it stops stepping the world entirely, or steps it slowly
enough to be free. The pull says which and why, with the measured cost of an idle process.
[ ] The first join wakes it in the same tick it arrives, not on the next timer: joining is what starts
the round, and a test asserts the gap between join() and the first frame is one tick, not a
second.
[ ] A sleeping world is an honest world, not a paused one: the clock does not run, no round is
counted, no scores are written for a round nobody played, and the ladder is unaffected.
[ ] The page says the sea is asleep rather than showing a stopped clock that looks broken, and it is
the same region #30 uses to say the loop stalled — the two states are different words, and a
person can tell them apart.
[ ] The field that #18 inherits is saved when the world goes to sleep, so falling asleep is where the
last round's currents are written down rather than a separate code path.
[ ] The last player leaving mid-round ends that round by the rule the pull names — abandoned rather
than completed, and not on the ladder.
[ ] Tests drive it by hand with no sleeping: empty roster stops the stepping, a join resumes it in one
tick, an abandoned round is recorded as abandoned, and a wake after a long sleep does not replay
the elapsed ticks in a burst.
Risks The bug this invites is the catch-up burst: a loop that wakes and tries to make up an hour of
missed ticks. Waking is a reset of the clock, never a replay. The second is a flapping world — one
visitor arriving and leaving repeatedly should not start and record a round each time, so give sleep a
short grace period and say what it is.
Waits on #8, and touches the same seam as #18 and #30 — read both before starting. Fits Vesper's brief.
A 20 Hz loop that runs forever burns a core on an empty arena, and drift will spend most of its life empty. Nothing in the map says what the world does when the roster is zero — #8 starts one interval and never stops it. The answer has to hold the promise in #1 from the other side: asleep costs nothing, and the first person through the door is swimming within five seconds anyway.
Done looks like
enough to be free. The pull says which and why, with the measured cost of an idle process.
the round, and a test asserts the gap between
join()and the first frame is one tick, not a second.counted, no scores are written for a round nobody played, and the ladder is unaffected.
the same region #30 uses to say the loop stalled — the two states are different words, and a person can tell them apart.
last round's currents are written down rather than a separate code path.
than completed, and not on the ladder.
tick, an abandoned round is recorded as abandoned, and a wake after a long sleep does not replay the elapsed ticks in a burst.
Files
src/round.ts,src/round.test.ts,src/page.ts,src/actions.ts.Risks The bug this invites is the catch-up burst: a loop that wakes and tries to make up an hour of missed ticks. Waking is a reset of the clock, never a replay. The second is a flapping world — one visitor arriving and leaving repeatedly should not start and record a round each time, so give sleep a short grace period and say what it is.
Waits on #8, and touches the same seam as #18 and #30 — read both before starting. Fits Vesper's brief.