events: SSE nudge stream, Federated and the bell update live (#476) #477

Merged
Cordy merged 3 commits from fix-476 into main 2026-09-10 15:11:38 +00:00
Owner

Per the #476 brief, TDD with red witnessed on the runner (stubs first, behavior tests failing, then the implementation).

  • internal/events: in-process Hub — per-user fanout, buffered channels with drop-on-full (a publish never blocks a request handler), per-user cap of 4 streams, Count for tests. Tests: fanout + unsubscribe cleanup, no cross-user leakage, drop-on-full does not block, cap enforcement.
  • internal/api/events.go: GET /api/v1/events — session-authenticated SSE; text/event-stream, X-Accel-Buffering: no, 25s heartbeat comments, exits on client disconnect. Emits event: change / data: {"topic":"…"} nudges only; the client re-fetches REST endpoints it already trusts. Tests: full httptest round-trip (subscribe → publish → frame on the wire → disconnect frees the hub) and 401 without a session. The server sets no WriteTimeout (deliberate, per its own comment), so no deadline handling is needed.
  • Publish call sites: inbound shares handler nudges the recipient after AddShareIn (via a Service.Notify closure — no events import in package ocm); ocmShareInDecide and dismiss nudge the deciding user, so other tabs/devices sync.
  • Frontend: startEvents() opens the EventSource after boot; on an ocm nudge it runs pollBell() and, when the Federated view is on screen, fedReload(). The 60s poll stays as the fallback; closed streams retry with backoff (EventSource handles transient drops itself).
  • Handbook "Live updates" note + CHANGELOG v0.6.157.

Deferred (recorded here, not built): cairn_sse_connections gauge, bell-topic publishers beyond OCM (peering inbox, licence), and an ocm-package integration test for the inbound nudge — the call site is a nil-guarded one-liner and the hub/handler carry the test load.

Closes #476.

Per the #476 brief, TDD with red witnessed on the runner (stubs first, behavior tests failing, then the implementation). - `internal/events`: in-process `Hub` — per-user fanout, buffered channels with drop-on-full (a publish never blocks a request handler), per-user cap of 4 streams, `Count` for tests. Tests: fanout + unsubscribe cleanup, no cross-user leakage, drop-on-full does not block, cap enforcement. - `internal/api/events.go`: `GET /api/v1/events` — session-authenticated SSE; `text/event-stream`, `X-Accel-Buffering: no`, 25s heartbeat comments, exits on client disconnect. Emits `event: change` / `data: {"topic":"…"}` nudges only; the client re-fetches REST endpoints it already trusts. Tests: full httptest round-trip (subscribe → publish → frame on the wire → disconnect frees the hub) and 401 without a session. The server sets no WriteTimeout (deliberate, per its own comment), so no deadline handling is needed. - Publish call sites: inbound `shares` handler nudges the recipient after `AddShareIn` (via a `Service.Notify` closure — no events import in package ocm); `ocmShareInDecide` and dismiss nudge the deciding user, so other tabs/devices sync. - Frontend: `startEvents()` opens the EventSource after boot; on an `ocm` nudge it runs `pollBell()` and, when the Federated view is on screen, `fedReload()`. The 60s poll stays as the fallback; closed streams retry with backoff (EventSource handles transient drops itself). - Handbook "Live updates" note + CHANGELOG v0.6.157. Deferred (recorded here, not built): `cairn_sse_connections` gauge, `bell`-topic publishers beyond OCM (peering inbox, licence), and an ocm-package integration test for the inbound nudge — the call site is a nil-guarded one-liner and the hub/handler carry the test load. Closes #476.
Cordy merged commit 1768daa0a4 into main 2026-09-10 15:11:38 +00:00
Cordy deleted branch fix-476 2026-09-10 15:11:41 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#477
No description provided.