PR #191 introduced protocol 2 (snapshot.sessions / snapshot.world +
the ?as=peer hub negotiation) and kept the protocol-1 per-event SSE
surface alive alongside it for a staggered fleet upgrade. The fleet is
now on v2, so the compat surface is dead weight: every snapshot fan-out
also fanned out per-event frames to browser subscribers, and every hub
carried a second event-decode path it never exercised against a v2 spoke.
Removed:
* Daemon SSE handler (cmd/gmuxd/main.go):
- the initial session-upsert hydration dump for non-asPeer subscribers
- the session-upsert / session-remove per-event emission branch
- the peer-status per-event emission branch (browser-only; browsers
get peer state from snapshot.world)
- the isOwnedEvent helper (only the deleted branch called it)
* cmd/gmuxd/main_test.go: TestIsOwnedEvent
* Hub decoder (internal/peering/peer.go):
- the sseEvent wire type
- the session-upsert / session-remove handleEvent cases (a v2 hub
consumes snapshot.sessions; v2 spokes never send per-event to
?as=peer subscribers)
* internal/peering/peering_test.go: the two TestHandleEvent_V1Compat tests
Deliberately KEPT (verified load-bearing in v2, not protocol-1 compat;
the issue checklist predated this code and is corrected here):
* projects-update SSE event, now gated to ?as=peer subscribers only.
A peer hub has no snapshot.world, so it uses this event to trigger a
re-fetch of the spoke project list and refresh the peer_projects it
surfaces upstream. Browsers no longer receive it (redundant with
snapshot.world, which projects-update already fires via the pump).
* GET /v1/projects, called by apiclient.GetProjects -> Peer.fetchProjects
on connect and on every projects-update. The browser does not use it.
The internal broadcast events (session-upsert, session-remove,
projects-update, peer-status) stay on the in-process bus as the
canonical "something changed" signal driving the snapshot pump; only
the legacy wire emission and decoders were removed.
Verification: go test ./services/gmuxd/... passes under -race; the web
suite passes unmodified (420 tests); repo-wide grep confirms no
remaining consumers of the per-event types or isOwnedEvent/sseEvent.
BREAKING CHANGE: The protocol-1 per-event SSE stream (session-upsert,
session-remove, peer-status) and the initial session-upsert hydration
dump are removed from GET /v1/events. A pre-2.0 hub can no longer source
session state from a 2.0 spoke, and any client relying on the per-event
SSE frames (rather than snapshot.sessions / snapshot.world) will receive
no session updates. All first-party consumers (web build, CLI, peering
hubs) already use protocol 2.
Closes #224.
Summary
PR #191 introduced protocol 2 (
snapshot.sessions/snapshot.world+ the?as=peerhub negotiation) and kept the protocol-1 per-event SSE surface alive for a staggered fleet upgrade. The fleet is on v2 now, so the compat surface is dead weight: every snapshot fan-out also shipped per-event frames to browser subscribers, and every hub carried a second event-decode path it never exercised against a v2 spoke.This is a breaking change (
feat!) → major bump to 2.0. It pairs with the other breaking changes slated for this release (e.g. the project-on-peer schema restructure), keeping the number of major bumps down.Removed
Daemon SSE handler (
cmd/gmuxd/main.go)session-upserthydration dump for non-asPeersubscriberssession-upsert/session-removeper-event emission branchpeer-statusper-event emission branch (browser-only; browsers get peer state fromsnapshot.world)isOwnedEventhelper (only the deleted branch called it) +TestIsOwnedEventHub decoder (
internal/peering/peer.go)sseEventwire typesession-upsert/session-removehandleEventcases + the twoTestHandleEvent_V1CompattestsDeliberately kept (corrects the issue checklist)
The issue (written 2026-05-17) listed
GET /v1/projectsand theprojects-updatehandler for removal. The code has since evolved so that both are load-bearing in v2, not protocol-1 compat — removing them would silently break peer project/session propagation. Verified and kept:projects-updateSSE event, now gated to?as=peersubscribers only. A peer hub has nosnapshot.world, so it uses this event to triggerPeer.fetchProjectsand refresh thepeer_projectsit surfaces upstream. Browsers no longer receive it (redundant withsnapshot.world, whichprojects-updatealready fires via the pump →snapshotPumpRoute).GET /v1/projects, called byapiclient.GetProjects→Peer.fetchProjectson connect and on everyprojects-update. The browser doesn't use it.The internal broadcast events stay on the in-process bus as the canonical "something changed" signal driving the snapshot pump; only the legacy wire emission and decoders were removed. ADR 0001's "Backwards compatibility" section is rewritten to document the removal and the two deliberate retentions.
Verification
go test ./services/gmuxd/...passes under-race.apps/,cli/,scripts/, and no references toisOwnedEvent/sseEvent.apps/website/.../architecture.mdalready had noGET /v1/projectsrow and already lists only v2 events on/v1/events— no change needed.Manual smoke (suggested before merge)
With one daemon on
mainand one on this branch, confirm sessions from a pre-2.0 spoke no longer reach a 2.0 hub via the per-event path (proves the compat path is gone), while two 2.0 daemons peer normally.