Skip to content

fix(desktop): cross-machine session-list sync (pins/titles) — focus refresh + 30s focused poll - #283

Merged
Kyzcreig merged 1 commit into
mainfrom
fix/cross-machine-session-list-sync
Jul 11, 2026
Merged

Kyzcreig merged 1 commit into
mainfrom
fix/cross-machine-session-list-sync

Conversation

@Kyzcreig

Copy link
Copy Markdown
Collaborator

Ace repro'd both directions live: pin on Studio ≠ visible on MBP; unpin on MBP ≠ visible on Studio (until app restart).

Root cause: two mechanisms both stop at the machine boundary — session-sync.ts uses BroadcastChannel (same-machine renderer windows only), and gateway session.info events route to the session's OWNING transport only (write_json precedence: per-session transport first). A second desktop connected to the same gateway never hears about the mutation.

Fix: refresh the session list on window focus + a 30s poll while focused (mirrors the shipped livesync poll pattern). Reuses refreshSessions(); no new RPCs; pop-out windows excluded as before. Typecheck clean.

@greptile-apps

greptile-apps Bot commented Jul 11, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR fixes cross-machine session-list sync (pins, titles, new sessions) by adding a window focus listener and a 30-second focused poll that calls refreshSessions() — mirroring the cron and messaging poll patterns already in place. Secondary/pop-out windows are correctly excluded via isSecondaryWindow().

  • Adds a single useEffect to DesktopController that attaches a window focus listener and a setInterval(30s) guarded by document.hasFocus(), both calling refreshSessions() on each tick.
  • Reuses the existing refreshSessions function with silent error handling (catch(() => undefined)), so no new RPC surface is introduced.

Confidence Score: 5/5

Safe to merge; the fix is narrowly scoped and all error paths are swallowed gracefully.

The change is a small, focused addition that reuses a well-exercised code path. The only notable divergence from the established polling pattern — missing the gatewayState guard — causes silent no-op failures rather than visible user impact.

apps/desktop/src/app/desktop-controller.tsx — the new effect is the only changed code; worth a second look for the missing gateway-state guard noted in the inline comment.

Important Files Changed

Filename Overview
apps/desktop/src/app/desktop-controller.tsx Adds a focus-triggered + 30s poll useEffect for cross-machine session-list sync; consistent with existing poll patterns but missing the gatewayState guard used by cron and messaging polls

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant W as Window (Machine B)
    participant DC as DesktopController
    participant RS as refreshSessions()
    participant GW as Gateway API

    Note over W,GW: Machine A pins/renames a session
    Note over W,DC: BroadcastChannel & session.info events never leave Machine A

    W->>DC: window 'focus' event
    DC->>RS: refresh()
    RS->>GW: fetch session list
    GW-->>RS: updated sessions (with Machine A's changes)
    RS-->>DC: sessions store updated

    Note over DC: 30s interval (while focused)
    loop Every 30s while document.hasFocus()
        DC->>RS: refresh()
        RS->>GW: fetch session list
        GW-->>RS: updated sessions
        RS-->>DC: sessions store updated
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant W as Window (Machine B)
    participant DC as DesktopController
    participant RS as refreshSessions()
    participant GW as Gateway API

    Note over W,GW: Machine A pins/renames a session
    Note over W,DC: BroadcastChannel & session.info events never leave Machine A

    W->>DC: window 'focus' event
    DC->>RS: refresh()
    RS->>GW: fetch session list
    GW-->>RS: updated sessions (with Machine A's changes)
    RS-->>DC: sessions store updated

    Note over DC: 30s interval (while focused)
    loop Every 30s while document.hasFocus()
        DC->>RS: refresh()
        RS->>GW: fetch session list
        GW-->>RS: updated sessions
        RS-->>DC: sessions store updated
    end
Loading

Reviews (3): Last reviewed commit: "fix(desktop): cross-machine session-list..." | Re-trigger Greptile

@Kyzcreig
Kyzcreig force-pushed the fix/cross-machine-session-list-sync branch from 191b7e5 to 8fc3b7f Compare July 11, 2026 01:42
…cus + 30s focused poll

BroadcastChannel only reaches windows on the SAME machine, and gateway
session.info events route to the owning transport only, so a pin/unpin/
title/create on one desktop never propagated live to another (visible
only after app restart — reported by Ace pinning on Studio and MBP).
Mirrors the poll-based livesync pattern: refresh on window focus + a
30s interval while focused. No new RPCs; reuses refreshSessions().
@Kyzcreig
Kyzcreig force-pushed the fix/cross-machine-session-list-sync branch from 8fc3b7f to ace3dcb Compare July 11, 2026 01:47
@Kyzcreig
Kyzcreig merged commit d9ce140 into main Jul 11, 2026
24 checks passed
@Kyzcreig
Kyzcreig deleted the fix/cross-machine-session-list-sync branch July 11, 2026 01:51
@Kyzcreig
Kyzcreig restored the fix/cross-machine-session-list-sync branch September 21, 2026 10:32
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.

1 participant