Skip to content

feat!: remove protocol-1 SSE/bulk-GET compat surface (#224)#247

Open
mgabor3141 wants to merge 1 commit into
mainfrom
feat/remove-protocol-1
Open

feat!: remove protocol-1 SSE/bulk-GET compat surface (#224)#247
mgabor3141 wants to merge 1 commit into
mainfrom
feat/remove-protocol-1

Conversation

@mgabor3141
Copy link
Copy Markdown
Contributor

Closes #224.

Summary

PR #191 introduced protocol 2 (snapshot.sessions / snapshot.world + the ?as=peer hub 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)

  • initial session-upsert hydration dump for non-asPeer subscribers
  • session-upsert / session-remove per-event emission branch
  • peer-status per-event emission branch (browser-only; browsers get peer state from snapshot.world)
  • isOwnedEvent helper (only the deleted branch called it) + TestIsOwnedEvent

Hub decoder (internal/peering/peer.go)

  • sseEvent wire type
  • session-upsert / session-remove handleEvent cases + the two TestHandleEvent_V1Compat tests

Deliberately kept (corrects the issue checklist)

The issue (written 2026-05-17) listed GET /v1/projects and the projects-update handler 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-update SSE event, now gated to ?as=peer subscribers only. A peer hub has no snapshot.world, so it uses this event to trigger Peer.fetchProjects 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 → snapshotPumpRoute).
  • GET /v1/projects, called by apiclient.GetProjectsPeer.fetchProjects on connect and on every projects-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.
  • Web suite passes unmodified (420 tests) — the v2 client never referenced the per-event types.
  • Repo-wide grep: no remaining consumers of the per-event types in apps/, cli/, scripts/, and no references to isOwnedEvent / sseEvent.
  • apps/website/.../architecture.md already had no GET /v1/projects row and already lists only v2 events on /v1/events — no change needed.

Manual smoke (suggested before merge)

With one daemon on main and 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.

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.
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 29, 2026

Greptile Summary

Removes the protocol-1 per-event SSE compat surface (session-upsert, session-remove, peer-status wire emission and the initial hydration dump) and the hub-side v1 decoder now that the fleet is fully on protocol 2. Two items that superficially resemble v1 artifacts — projects-update (now gated to ?as=peer subscribers only) and GET /v1/projects — are deliberately kept because they are load-bearing for peer project propagation in v2.

  • main.go: Strips the non-asPeer session-upsert hydration loop, the session-upsert/session-remove/peer-status SSE emission cases, and isOwnedEvent; inverts the projects-update gate from "send unless peer" to "send only if peer."
  • peer.go: Removes sseEvent wire type and the session-upsert/session-remove handleEvent branches; peer-status falls to the existing default silent-ignore path.
  • Tests and ADR: Matching test cleanup and ADR rewrite documenting both the removal and the two deliberate retentions.

Confidence Score: 5/5

Safe to merge; removes only dead compat code with no in-tree v1 consumers remaining.

Every deletion has a clear mechanical justification: the v1 wire surface had no remaining callers after the fleet moved to protocol 2, the two retained items (projects-update peer trigger and GET /v1/projects) are demonstrably load-bearing in v2, and snapshotPumpRoute still routes projects-update to the world coalescer so browser subscribers continue receiving project changes via snapshot.world. Test coverage is updated to match and the race-tested suite passes.

No files require special attention.

Important Files Changed

Filename Overview
services/gmuxd/cmd/gmuxd/main.go Removes protocol-1 initial session-upsert hydration dump, per-event session-upsert/session-remove/peer-status SSE emission, and the isOwnedEvent helper; gates projects-update to ?as=peer subscribers only (correct: browsers get projects via snapshot.world through the world coalescer)
services/gmuxd/internal/peering/peer.go Removes the sseEvent wire type and session-upsert/session-remove handleEvent cases; peer-status falls to default (silent ignore), snapshot.world case retains its defensive comment; no functional gap in v2 operation
services/gmuxd/cmd/gmuxd/main_test.go Removes TestIsOwnedEvent — correct cleanup matching the deleted isOwnedEvent function
services/gmuxd/internal/peering/peering_test.go Removes TestHandleEvent_V1CompatPerEvent and TestHandleEvent_V1CompatDropsForwardedFromKnownOrigin; drops now-unused context import; remaining test suite is intact
docs/adr/0001-snapshot-push-protocol.md Updates the backwards-compatibility section to document the v1 wire surface removal and the deliberate retention of projects-update (peer trigger) and GET /v1/projects; accurate and matches the code changes

Sequence Diagram

sequenceDiagram
    participant Spoke as Spoke daemon
    participant Browser as Browser (v2)
    participant Hub as Hub daemon (?as=peer)

    Note over Spoke,Hub: Protocol 2 only (post this PR)

    Spoke->>Browser: snapshot.sessions (initial + on change)
    Spoke->>Browser: snapshot.world (initial + on change)
    Note over Spoke,Browser: projects-update no longer sent to browsers

    Spoke->>Hub: snapshot.sessions (initial + on change)
    Spoke->>Hub: projects-update (trigger to re-fetch projects)
    Hub->>Spoke: GET /v1/projects (fetchProjects on connect + on projects-update)
    Note over Spoke,Hub: session-upsert / session-remove / peer-status wire frames removed
Loading

Reviews (1): Last reviewed commit: "feat!: remove protocol-1 SSE/bulk-GET co..." | Re-trigger Greptile

@github-actions
Copy link
Copy Markdown
Contributor

Try this PR

curl -sSfL https://gmux.app/install-pr.sh | sh -s -- 247

Built from 1def784 — feat!: remove protocol-1 SSE/bulk-GET compat surface (#224)
Requires GitHub CLI with auth. Artifacts expire after 7 days.

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.

Drop the protocol-1 SSE and bulk GET surface in the next breaking release

1 participant