Skip to content

perf(desktop): stop idle-session event-health polling from re-rendering the whole shell #1979

Description

@Astro-Han

Summary

useSessionEventHealthPolling re-renders the whole AppShell every 5 seconds for the entire lifetime of any open session, even when fully idle and no IPC is performed. This is the only periodic work the app does while idle.

Evidence

  • apps/desktop/src/renderer/app-shell-effects.ts:533-560 arms window.setInterval(evaluate, 5_000) whenever activeId is set; deps [activeId, activeSession?.status, activeStreamingLive, hasInFlightLiveTools, activeInteraction?.requestId] are stable for an idle session, so the interval lives for the whole session.
  • evaluate() unconditionally writes a fresh snapshot object { ...current, [activeId]: result.snapshot! } (app-shell-effects.ts:557-565). result.snapshot always carries a new checkedAt (renderer/session-event-health.ts:71-78).
  • app-shell-session-ui-state.ts:105-116 replaceState only short-circuits on reference equality — the fresh object always passes → onChangeforceRender(). useAppShellSessionWorkspace is called inside AppShell (app-shell.tsx:267,1876), and ChatView/Composer/SessionListPanel are not memoized (only TurnView/MessageBody in packages/ui/src/chat-turn.tsx:137,301).
  • The map is read nowhere for rendering: the only consumer is sessionEventHealthBySessionRef inside the polling hook itself (app-shell-effects.ts:544).

Fix

Gate evaluate itself, not just the interval: skip when !sessionExpectsEventStream(status, hasLiveActivity) && previous?.status === 'closed' (both booleans are already effect deps, so a session turning running re-arms automatically). Do not "skip unchanged writes" — the ref is only synced in replaceState (app-shell-session-ui-state.ts:109-113), so skipping writes would break the refreshRequestedAt throttle (every tick would refresh instead of the 10s cooldown).

Add a fake-timer regression test: an idle active session registers no interval and triggers no controller onChange; a running/live session still transitions to stale and refreshes after the threshold.

Severity

P1 (guaranteed, continuous, avoidable re-render while idle). The per-tick cost is bounded (a few ms), so P2 is defensible if the team prefers — the fix is the same either way.

Owner

apps/desktop renderer (app-shell-effects.ts), with sessionExpectsEventStream in @maka/core.

Acceptance

  • Idle session: zero interval, zero forced renders.
  • Running/live session: stale detection and refresh still work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions