Repository navigation
feat(host): push channel lifecycle through project lobbies - #337
Merged
Merged
Conversation
iamnbutler
force-pushed
the
lobby-presence
branch
from
October 15, 2026 04:19
b8b95fd to
d6330ee
Compare
iamnbutler
force-pushed
the
lobby-lifecycle
branch
from
October 15, 2026 04:19
8d0fb04 to
e92f7ac
Compare
iamnbutler
force-pushed
the
lobby-presence
branch
from
October 15, 2026 10:21
d6330ee to
557f62f
Compare
After each successful publish to the directory, a host diffs its channels against its previous publication and sends each change (created, renamed, summarized, archived, resumed, moved, deleted, or a run's end, with the new listing) to that project's lobby. Each stays queued, latest per channel, until the lobby echoes it back, and is resent after a reconnect, since a socket can die unnoticed. A lobby the host no longer needs stays until the next publication, so a project's last deletion still reaches it. Teammates' hosts apply events to their directory listings at once and rebuild listings once per burst, so a replay of thousands of events reads catalogs a few times, not per event; a directory read already in flight reapplies them, and a resync reads the directory again. While every project a host subscribes to has a live lobby and its last publish succeeded, the 30 s directory poll slows to a 5 min read; changes still publish immediately, and a change that arrives during a sync runs another. Activity alone sends no event and reaches teammates through peers or that read. Without lobbies the poll stays at 30 s. Refs #17, #14. Why: apps/host/src/lobby.ts#function published | events must be sent only after the directory PUT succeeds, so a teammate that reads the directory on receipt agrees with the event Why: apps/host/src/directory.ts#function watch | the poll may slow only while every subscribed project's lobby is live and the last publish succeeded; a 404, dropped lobby, or failed PUT must keep the 30 s poll Why: apps/host/src/directory.ts#function apply | a directory read that started before a lifecycle event must reapply it, or the stale read undoes the push Why: apps/host/src/lobby.ts#function receive | this host's events stay queued until the lobby echoes them (same channel and at), since a socket can die unnoticed for up to SILENT Why: apps/host/src/lobby.ts#function update | a lobby no longer wanted must stay until the next publication, or a project's last deletion is never sent Why: apps/host/src/lobby.ts#function listed | teammates' events rebuild listings once per burst; a replay must never re-read catalogs per event
iamnbutler
force-pushed
the
lobby-lifecycle
branch
from
October 15, 2026 10:22
e92f7ac to
4089ac8
Compare
This was referenced Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fourth of four stacked PRs for project lobbies (base: #336). Refs #17, refs #14.
Hosts push channel lifecycle through project lobbies, so project members stop polling the directory every 30 s.
published(before, after)inapps/host/src/lobby.tsdiffs its own listings into lifecycle events and sends each one to its project's lobby. The kinds arecreated,renamed,summarized,archived,resumed,moved,deleted, andran(a run ended). Listings and catalogs stay the source of truth, and an event only patches the receiving host's copy of the directory (directory.apply) until its next read.activeorbusyand running or idle reach teammates through peers or the directory's next read. That read is every 5 min while covered, anddocs/architecture.mdsays so.at). It is resent after the nextsynced, because a socket can die unnoticed for up to 65 s.deleted, and closes once its queue is echoed.emitopens a lobby if publishing outran the subscription.seq. A host sayshellowith the lastseqit applied. The lobby replays newer events, or answersresyncwhen they are gone, and the host then does a full directory read.ownersChanged(which re-reads catalogs, now async after fix(host): run request-path git off the gateway's event loop #346) runs once per 100 ms burst, not once per event, so a replay of 2,000 events triggers only a few rebuilds.directory.watchtakes an options object. Rebased ontomain's per-URL sync: Refresh, a directory change, and the heartbeat still join a sync under way, while a channel change during a sync queues one more after it, since the running one may have read channels first. Events that arrive during a GET are reapplied to its result.PUT /hostsrewrites the host's row (fix(directory): write only changed channel listings on each publish #391 already skips unchanged channels), so a covered host writes about 288 host rows a day instead of 2,880, plus one per actual change.Smoke test: a source host (port 4391,
ACE_HOMEunder/tmp, Tailscale shimmed off,ACE_DIRECTORYpointed at a localwrangler devof this stack) and a scripted teammate host in the same lobby. Run again after review:createdevents and then 300deletedevents. The app listed all 300 channels, then none, after 2 client updates per burst.projects.json: deleting that channel delivereddeletedto its lobby, and the host then closed the lobby.renamedonce, about 5 s after the restart.main's directory (no Lobby) got 404, retried quietly, and kept the 30 s poll.Not checked end to end: the
ranevent, because a run needs model credentials on the test host. It uses the same path as the other kinds.Deploy:
wrangler deployofservices/directoryfrom the merged stack adds theLOBBYbinding and thev2migration (#335). It needs no new secrets. The live Worker's last applied migration tag isv1. Until it is deployed, every host keeps today's behavior.Open: changes from hosts on an older Ace, and changes made while a host's process was down, reach others only through the 5-minute read or peers.
Rebased onto
mainand run again with the same setup as #336: create and delete reached the teammate's lobby within 0.1 s, a 300-event burst and its 300 deletions each produced 2 client updates, a dropped project's last deletion reached its lobby, and the host's 30 s directory publish paused while its lobbies were live (after the smoke, its next sync came 5 min 6 s later instead of 30 s).Built in Ace
No. This was built with Claude Code outside Ace by the overnight watcher, because this Mac's Ace Canary is a participant in the two-person study and can't be used for development tonight. See #5.