Summary
MemoryEventsHandler.SSEHandler does not flush response headers until it writes its first event. This means an SSE client connecting with a scope/pattern filter that has no immediately-matching events will hang on http.Client.Do until either:
- A matching event arrives (eventually flushes headers via the first
c.SSEvent + c.Writer.Flush), or
- The client disconnects, which the handler currently detects via the deprecated
c.Writer.CloseNotify()
This produces a flaky CI failure for TestMemoryEventsHandler_SSEHappyPathHonorsScopeFilter (currently t.Skip'd in PR #352) and is also a real-world UX issue: any UI that opens an SSE connection appears stuck until the first event arrives.
Where
- File:
control-plane/internal/handlers/memory_events.go
- Function:
MemoryEventsHandler.SSEHandler
- Skipped test:
control-plane/internal/handlers/memory_events_test.go::TestMemoryEventsHandler_SSEHappyPathHonorsScopeFilter (skip message: flaky in CI: SSE handler defers header flush; covered by WS + cleanup tests)
Expected behavior
Standard SSE handler shape:
- Set headers (
Content-Type: text/event-stream, etc.)
- Call
c.Writer.WriteHeader(http.StatusOK) and c.Writer.Flush() immediately so the client receives headers and can begin reading the body
- Optionally write a
: connected\n\n comment frame as a heartbeat / keepalive marker
- Then enter the event loop
Replacing c.Writer.CloseNotify() with c.Request.Context().Done() is also worth doing — CloseNotifier is deprecated in net/http and behaves inconsistently across httptest vs production servers.
Acceptance criteria
Discovered via
PR #352 (test coverage improvements). Added during the SSE end-to-end test pass; the test was racy in CI and is currently t.Skip'd pending this fix.
Summary
MemoryEventsHandler.SSEHandlerdoes not flush response headers until it writes its first event. This means an SSE client connecting with a scope/pattern filter that has no immediately-matching events will hang onhttp.Client.Dountil either:c.SSEvent+c.Writer.Flush), orc.Writer.CloseNotify()This produces a flaky CI failure for
TestMemoryEventsHandler_SSEHappyPathHonorsScopeFilter(currentlyt.Skip'd in PR #352) and is also a real-world UX issue: any UI that opens an SSE connection appears stuck until the first event arrives.Where
control-plane/internal/handlers/memory_events.goMemoryEventsHandler.SSEHandlercontrol-plane/internal/handlers/memory_events_test.go::TestMemoryEventsHandler_SSEHappyPathHonorsScopeFilter(skip message:flaky in CI: SSE handler defers header flush; covered by WS + cleanup tests)Expected behavior
Standard SSE handler shape:
Content-Type: text/event-stream, etc.)c.Writer.WriteHeader(http.StatusOK)andc.Writer.Flush()immediately so the client receives headers and can begin reading the body: connected\n\ncomment frame as a heartbeat / keepalive markerReplacing
c.Writer.CloseNotify()withc.Request.Context().Done()is also worth doing —CloseNotifieris deprecated innet/httpand behaves inconsistently acrosshttptestvs production servers.Acceptance criteria
SSEHandlerflushes headers (and ideally a comment frame) before entering the event loopSSEHandlerusesc.Request.Context().Done()instead ofCloseNotify()for disconnect detectionmemory_events_test.go::TestMemoryEventsHandler_SSEHappyPathHonorsScopeFilteris unskipped and passes deterministically (locally and in CI)Discovered via
PR #352 (test coverage improvements). Added during the SSE end-to-end test pass; the test was racy in CI and is currently
t.Skip'd pending this fix.