fix(app): gate directory-store mutations for remote SSE events (#1539) - #1546
Merged
jeonghun-jj-lee merged 1 commit intoSep 24, 2026
Merged
Conversation
The SSE fan-in relays remote peer events into the app's single /event SSE connection. The fan-in correctly namespaces events via composite cursor id: fields, but the app-layer event handler was applying ALL events to the directory store regardless of origin — contaminating store.session with un-tagged remote sessions that mergePeerSessions then preferred over the correctly-tagged projection entries. Two changes, one invariant (the directory store is local-only): Change 1: Propagate origin namespace through the event queue - New sse-origin.ts module with parseCursorNamespaces() and resolveEventOrigin() — pure functions that diff the composite cursor id: field to determine which SSE namespace (local vs remote peer) originated each event. Fleet-of-one (bare scalar id) returns immediately with zero overhead. - server-sdk.tsx wires origin resolution into the SSE loop: the ServerEvent type gains an optional origin field, set only for remote events. Change 2: Gate directory-store mutations in the listener - server-sync.tsx checks event.origin and skips for remote events: - applyDirectoryEvent (the primary contamination point) - homeSessions.apply (feeds into dedup list) - indexSession (synthetic session.created from session.moved/forked) - queue.push (triggers directory re-fetches) - session.apply/session.applyV2 still process remote events (needed for per-session caches the timeline reads from) Tests: - sse-origin.test.ts: 12 unit tests for namespace extraction and origin resolution (fleet-of-one, composite local, composite remote, multi-peer, edge cases) - session-fleet-peers.test.ts: regression guard for the contamination scenario + badge persistence when projection is sole source - event-reducer.test.ts: gate contract tests documenting what the gate prevents - session-header-provenance.test.ts: badge persistence across repeated dropdown opens
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Gate
applyDirectoryEventand related directory-store mutations inserver-sync.tsxso that SSE events relayed from remote fleet peers do not contaminate the local directory store — preserving fleet machine badges in the sessions dropdown.The bug
The SSE fan-in correctly namespaces remote events via the composite cursor's
id:field, but the app-layer event loop discarded that provenance before queuing. When both machines share the same workspace path, remotesession.created/session.updatedevents matched a local child store'sdirectoryKeyandapplyDirectoryEventinserted the remote session intostore.session— without theamicode_ownertag. On next dropdown open,mergePeerSessionsdeduplicates local-wins, discarding the correctly-tagged projection entry.The fix
New
sse-origin.tsmodule — pure logic to resolve which namespace (machine) originated an SSE event by diffing the composite cursor against its previous state. Fleet-of-one (no=separator in the id) returns immediately withundefinedorigin — zero overhead.server-sdk.tsx— the SSE loop now callsresolveEventOriginand threads anorigin?: stringfield throughQueuedServerEvent→ the emitter → the listener.server-sync.tsx— four contamination points gated for remote-origin events:homeSessions.apply(line 555)indexSessioncalls fromsession.moved/session.forked(lines 593–601)queue.pushdirectory re-fetch triggers (lines 587, 614)applyDirectoryEvent(line 618)session.apply/session.applyV2are NOT gated — the per-session info and message caches the timeline reads from still process remote events.Tests
sse-origin.test.ts— namespace extraction from composite cursor ids (local, remote, fleet-of-one)event-reducer.test.ts— regression: reducer not called for remote-origin eventssession-fleet-peers.test.ts— regression: dedup when session exists in both local store and projectionsession-header-provenance.test.ts— badge persistence after simulated remote SSE eventsCloses #1539