#BUDGET measures the page; nothing measures the world. A 20 Hz loop that cannot finish a step inside its 50 ms slice is the one failure that makes drift unplayable while every door still answers 200 in 7 ms — #LOOP catches a tick that throws, and this is the tick that is merely too slow. Measure it against the pure simulation, before a round loop, a socket and 24 people are standing on it.
Done looks like
[ ] src/world.budget.test.ts (new, its own file so it survives tuning in src/world.ts and the round loop in #ROUND) measures step() at the roster cap — 24 motes, the bloom, the full 32×32 field — over enough steps to take a median, warm, and asserts a stated ceiling that is a fraction of the 50 ms slice rather than the slice itself. A tenth is a suggestion; the pull states the ceiling, the measured median and the headroom between them.
[ ] The measured number is printed, so a regression names itself instead of only failing.
[ ] The frame is measured too, through encodeFrame from src/protocol.ts: a frame at 24 motes stays under the 4 KB #ROUND promises, and the pull states what 10 Hz of that to a full roster is in bytes per second, so a person can judge the cost of a full sea.
[ ] Allocation, not only time: the pull says whether a step allocates per tick — a fresh field of 1024 cells every step at 20 Hz is 72 000 arrays an hour — and whether that is deliberate or worth a reused buffer.
[ ] Nothing sleeps and no timer runs: the test drives step() in a loop the way src/world.test.ts does, so it is a check and not a wait.
[ ] If a measurement says something must change, the change is in src/world.ts and the pull says which number moved and what it bought.
Filessrc/world.budget.test.ts (new), src/world.ts only if a measurement demands it, README.md.
Risks A timing assertion on shared hardware is flaky when the ceiling is tight; choose the ceiling for what it protects against — a tick that cannot hold 20 Hz — not for the best number the box ever showed. If step turns out to be far under the ceiling, say so and keep the test anyway: it exists to catch the change that makes it slow, and the field is the part most likely to grow.
#BUDGET measures the page; nothing measures the world. A 20 Hz loop that cannot finish a step inside its 50 ms slice is the one failure that makes drift unplayable while every door still answers 200 in 7 ms — #LOOP catches a tick that throws, and this is the tick that is merely too slow. Measure it against the pure simulation, before a round loop, a socket and 24 people are standing on it.
Done looks like
src/world.budget.test.ts(new, its own file so it survives tuning insrc/world.tsand the round loop in #ROUND) measuresstep()at the roster cap — 24 motes, the bloom, the full 32×32 field — over enough steps to take a median, warm, and asserts a stated ceiling that is a fraction of the 50 ms slice rather than the slice itself. A tenth is a suggestion; the pull states the ceiling, the measured median and the headroom between them.encodeFramefromsrc/protocol.ts: a frame at 24 motes stays under the 4 KB #ROUND promises, and the pull states what 10 Hz of that to a full roster is in bytes per second, so a person can judge the cost of a full sea.step()in a loop the waysrc/world.test.tsdoes, so it is a check and not a wait.src/world.tsand the pull says which number moved and what it bought.Files
src/world.budget.test.ts(new),src/world.tsonly if a measurement demands it,README.md.Risks A timing assertion on shared hardware is flaky when the ceiling is tight; choose the ceiling for what it protects against — a tick that cannot hold 20 Hz — not for the best number the box ever showed. If
stepturns out to be far under the ceiling, say so and keep the test anyway: it exists to catch the change that makes it slow, and the field is the part most likely to grow.Fits Vesper's brief.