With #BUS landed the app has two live paths — the SSE stream in src/live.ts, where an event makes static/live.js refetch / and swap a data-live region, and the websocket carrying 10 Hz frames. #1 asks for live over "server-sent events or a websocket on one bus", and nothing says which owns which fact. Left undecided, #CANVAS, #LADDER and #TTY each answer it differently and the page ends up showing the same clock from two sources, one of them a second behind.
Done looks like
[ ] The split is decided and written into AGENTS.md under "Something live": the socket carries everything that changes at frame rate — motes, field, bloom, the round clock, the roster while a round runs — and /events carries what changes at human rate — a round started, a round ended, the ladder. No fact travels on both.
[ ] The page never shows one fact from two sources: a region the canvas draws from frames does not also carry data-live, and a test walks the data-live regions on / and fails if one of them names something the frame already carries.
[ ] One visitor holds one socket and at most one stream, and the two rations agree: the pull states what a single visitor costs against bucketOf's stream cap and SOCKET_MAX_SHARED, and whether the same person can be admitted twice.
[ ] A visitor whose upgrade is refused — a proxy that strips Origin, a corporate network, the cap — still gets the human-rate regions over /events and a page that says the world is running but unreachable, with what to try. Not an empty arena that looks broken; #LOOP and #SLEEP own the other two ways the arena can be still, and this is the third, so the three read differently.
[ ] With JavaScript off, / is still the whole arena in words — the rule, the clock, the roster count — and the README says in one line what such a visitor cannot do.
[ ] Tests: a frame fact that is also a data-live region fails the check; a refused upgrade leaves the page live over /events; a round.ended still swaps the ladder with the socket open.
Risks The temptation is to delete the SSE path because the socket exists. Do not: the server-rendered first paint and its refresh are what a crawler, a JavaScript-off visitor and a blocked upgrade all depend on, and #TTY watches the same human-rate facts from a terminal with no browser at all. One bus is about what the facts travel on, not about how few connections there are.
Fits Vesper's brief on the bus, with Quill on the page's half and the words for an unreachable world.
With #BUS landed the app has two live paths — the SSE stream in
src/live.ts, where an event makesstatic/live.jsrefetch/and swap adata-liveregion, and the websocket carrying 10 Hz frames. #1 asks for live over "server-sent events or a websocket on one bus", and nothing says which owns which fact. Left undecided, #CANVAS, #LADDER and #TTY each answer it differently and the page ends up showing the same clock from two sources, one of them a second behind.Done looks like
AGENTS.mdunder "Something live": the socket carries everything that changes at frame rate — motes, field, bloom, the round clock, the roster while a round runs — and/eventscarries what changes at human rate — a round started, a round ended, the ladder. No fact travels on both.data-live, and a test walks thedata-liveregions on/and fails if one of them names something the frame already carries.bucketOf's stream cap andSOCKET_MAX_SHARED, and whether the same person can be admitted twice.Origin, a corporate network, the cap — still gets the human-rate regions over/eventsand a page that says the world is running but unreachable, with what to try. Not an empty arena that looks broken; #LOOP and #SLEEP own the other two ways the arena can be still, and this is the third, so the three read differently./is still the whole arena in words — the rule, the clock, the roster count — and the README says in one line what such a visitor cannot do.data-liveregion fails the check; a refused upgrade leaves the page live over/events; around.endedstill swaps the ladder with the socket open.Files
AGENTS.md,static/live.js,src/page.ts,src/live.ts,src/socket.ts,src/server.test.ts,README.md.Risks The temptation is to delete the SSE path because the socket exists. Do not: the server-rendered first paint and its refresh are what a crawler, a JavaScript-off visitor and a blocked upgrade all depend on, and #TTY watches the same human-rate facts from a terminal with no browser at all. One bus is about what the facts travel on, not about how few connections there are.
Fits Vesper's brief on the bus, with Quill on the page's half and the words for an unreachable world.