Skip to content

Rebuild realtime on one RealtimeSession; callable bool.room (named rooms) - #27

Merged
HomemadeToast57 merged 2 commits into
jack/room-batteryfrom
jack/realtime-v2
Jul 29, 2026
Merged

Rebuild realtime on one RealtimeSession; callable bool.room (named rooms)#27
HomemadeToast57 merged 2 commits into
jack/room-batteryfrom
jack/realtime-v2

Conversation

@HomemadeToast57

Copy link
Copy Markdown
Collaborator

What this is

The rewrite: same public API, same tests, new internals. Stacked on jack/room-battery so the diff shows only the rewrite.

Why a rewrite

The doorbell and the room each carried a private copy of the connection machine. Both of the 2026-07-29 production fires (sync-CLOSED stack overflow, missing wake reconnect) had to be diagnosed and fixed twice — once per copy. That's structural, not incidental.

Architecture

  • session.ts (new) — THE connection machine: mint (10s timeout race) → setAuth → open channel groups → refresh at 75% TTL; backoff ladder; wake reconnect; generation counter; identity-guarded teardown. Every scar written once, each comment naming the production failure that earned it. session.test.ts pins one test per scar.
  • realtime.ts 280 → 118 — the doorbell is pure fan-out.
  • room.ts 722 → ~530 — pure membership ∪ state (throttle, frame-coalescing, stable identities, hydration), zero connection code.
  • client.ts — ONE session, one socket, one wristband refresh under everything.

The API addition (decided with Jack against the Liveblocks/PartyKit/Yjs survey)

bool.room is now callable: bool.room stays the app-wide default room (every existing example works verbatim); bool.room("game:4") returns a scoped room with the identical surface, memoized per id, topic bool:<schema>:room:<id>, ids validated loudly. Rejected on purpose (recorded in room.ts's header): providers, join/leave ceremony, selectors, useUpdateMyPresence-weight names.

Behavior changes

None, except one deliberate re-pin: the old "any failed track() forces a reconnect" rule is gone — it's what amplified the presence rate-limit into a reconnect storm. The join beacon is once-per-join; failure warns; genuinely dead channels heal via terminal states.

Verification

  • 216 unit tests (all pre-rewrite tests pass with only harness-internal edits), tsc clean
  • Real-infra probe (real Supabase sockets, real mint, policy v4 on dev): default-room state flow PASS · scoped rooms live PASS · same-id shares PASS · cross-room isolation PASS · hard socket close → recovery → state flows PASS

Pairs with platform PR (policy v4). Published as 0.4.0-next.7.

🤖 Generated with Claude Code

HomemadeToast57 and others added 2 commits July 29, 2026 11:56
…oms)

The rewrite Jack asked for, built as a structured refactor rather than a
blank page: the public API and the behavior tests are the spec (both
survived five emergency releases unchanged), and the internals reshape
around one RealtimeSession.

Architecture:
- session.ts (new, ~300 lines): THE connection machine — mint with a
  timeout race, setAuth, refresh at 75% TTL, backoff ladder, wake
  reconnect, generation counter, identity-guarded teardown. Every scar
  from the July 2026 fires is written exactly once, each with a comment
  naming the production failure that earned it; session.test.ts pins one
  test per scar. Consumers register ChannelGroups and contain zero
  connection code.
- realtime.ts (280 → 118): the doorbell is now pure fan-out.
- room.ts (722 → ~530 incl. the new API): pure membership ∪ state +
  throttle/coalescing/hydration/stable-identities logic.
- client.ts: ONE session shared by the doorbell and every room — one
  socket, one wristband refresh, no more duplicate lifecycles.

API — the one addition, decided with Jack against the convention survey
(Liveblocks/PartyKit/Yjs): bool.room is now CALLABLE. `bool.room` stays
the app-wide default room (every existing example works verbatim);
`bool.room("game:4")` returns a scoped room with the identical surface,
memoized per id, topic bool:<schema>:room:<id>. Ids are validated loudly
([A-Za-z0-9][A-Za-z0-9:_.-]{0,63}) — silent normalization would put two
"different" ids in one room. Rejected on purpose: provider components,
join()/leave() ceremony, useUpdateMyPresence-weight names, selectors —
recorded in room.ts's header.

Behavior changes beyond structure: NONE. All 214 pre-rewrite tests pass
with only harness-internal edits, except one test that pinned the old
"any failed track() reconnects" rule — deliberately re-pinned to the
rate-shaped join-beacon contract (that rule is what amplified the
presence meltdown into a reconnect storm).

Not published yet: next.7 waits for the platform policy v4 (scoped-topic
send/recv) and a real two-browser verification, per the proof gate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@HomemadeToast57
HomemadeToast57 merged commit 3cad150 into jack/room-battery Jul 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant