fix(server): bound cursorless SSE replay (#882) - #888
blabbler78 wants to merge 1 commit into
Conversation
|
🤖 The configured labels ( |
🤖
|
daec5a6 to
0f2f284
Compare
📏 Live tunnel verificationI activated the reversible patch against the installed 0.9.3 server and repeated the original long-run measurement through both localhost and the authenticated ngrok tunnel.
This validates the two relevant protections under the reported conditions: a bounded first replay prevents the browser/tunnel abort, and sequence resume prevents amplification if the transport later reconnects. The persisted NDJSON files were not changed. |
Closes #882
🎯 Goal
Keep long run streams usable in Safari and over tunnels by preventing a cursorless EventSource connection from replaying an unbounded multi-megabyte transcript.
🔍 Problem
The released 0.9.2 and 0.9.3 servers replay the complete NDJSON transcript on every connection. Measured runs returned between 3.49 MB and 28.6 MB per connection, and individual tool events reached 1.24 MB. Safari aborted during replay, immediately reconnected, and repeated the same transfer until the cockpit stopped accepting input.
🔍 Root Cause
PR #739 added progressive history, frame ids,
Last-Event-ID, and cursor resume onmain, but intentionally retainedstore.readEvents(id)for the protected no-query path. The history hook also uses that path as its compatibility fallback. Consequently the first cursorless connection still has no network or per-frame bound, and a single oversized persisted tool result can reproduce the reconnect amplification even after resume support ships.What Changed
🧪 Tests
run-event-replay.test.tsfailed because the bounded replay implementation did not exist.npm test --workspace @open-mercato/cezar -- --run src/server/run-event-replay.test.ts src/server/route-parity.test.ts— 12 tests passed.npm run typecheck— passed for contract, API client, server, and web.npm run test:unit— 35 passed, 1 platform-specific test skipped.npm run build— passed; package check found 478 files and 85 web assets.npm run test:package— 15 tests passed.npm testruns reached 6,060/6,061 passing tests. The only failures were pre-existing parallel timing flakes inhealth-topic.test.ts/automations-gate.test.ts; both affected files pass together in isolation (31/31), and the base commit's exact GitHub CI is green: https://github.com/open-mercato/cezar/actions/runs/31816677977. PR CI remains authoritative for this head.💥 Breaking Changes
The no-query
/api/v1/runs/:id/eventsconnection now receives a recent byte-bounded tail instead of the complete transcript. Consumers requiring the complete archive must use/api/v1/runs/:id/history. Event names, required event envelope fields, route aliases, persisted NDJSON, cursor semantics, sequence ids, and event ordering are unchanged. Oversized live SSE payloads can receive the same bounded wire projection while their full persisted form remains available through history.