feat(desktop): report panel freezes and time task open

Merged
CH
4d ago
+897-23

PR 1 of the performance rollout: make freezes and slow task opens visible in Sentry so the later fixes are measured. No loading behaviour changes.

What it does

  • Panel freeze detection. The panel window's renderer SDK adds eventLoopBlockIntegration({ threshold: 1000 }); the voice and call windows don't (mountWindow(surface, { reportsFreezes: true }) from index.tsx only). Main needs no change: @sentry/electron 8.2.0's main defaults already include rendererEventLoopBlockIntegration, and main's integrations: [startupTracingIntegration()] is merged with those defaults.
  • Unresponsive panel. New apps/desktop/src/main/window/renderer-health.ts follows each panel BrowserWindow's unresponsive / responsive / closed as an Effect stream and reports one Sentry event per episode: when the page answers again, or when the window closes while still frozen (recovered: false). The event carries the duration and the page's app.getAppMetrics() working set read as it froze. Repeated unresponsive within an episode counts once. Wired via a new followResponsiveness option on PanelManager. Unlike the voice window, the panel isn't replaced.
  • Memory. sampleWindowMemory reads every window's working set every 60 s on Schedule.spaced, run from the telemetry service's own scope (closed at stop). It leaves a breadcrumb only when a window crosses 1.5 GB: not on every sample while it stays above, and again only after it drops below and crosses back.
  • Spans (sampled at SENTRY_TRACES_SAMPLE_RATE, each started as its own trace):
    • task.open: from the press in openTask until the document region is READY for that task. A refused or replaced ask, a leave, or reopening the task already open records nothing. Tagged task.plan_chars.
    • transcript.first_paint: from opening the follow to the commit after its first page. Tagged transcript.message_count and transcript.page_bytes.
    • transcript.caught_up: from opening the follow to the page with nothing more. Same tags.
    • The panel reaches Sentry through a new door, @luke/panel/panel-spans. If no recorder is installed (tests, web), it records nothing. Attributes are typed as numbers only, so no message text or credential can get in. They are also read lazily, only for a span the sampler keeps, so an unsampled span never serializes a page.
  • Slow frames. enableLongAnimationFrame: true is set explicitly on browserTracingIntegration for desktop and web. INP is already reported by the 11.7.0 SDK's default web vitals. Its old enableInp switch is deprecated, so it isn't set.

Tests

  • renderer-health.test.ts (fake window, TestClock): one report per unresponsive episode with its duration and working set, a second episode is reported separately, a close while frozen is reported as not recovered, and the listeners are removed. The memory sampler leaves nothing under the bound, nothing between samples, one breadcrumb on crossing, none while it stays over, and another after dropping below and crossing again.
  • desktop-sentry.test.ts (real SDK, IPC as the boundary): only the panel registers with main's freeze watchdog, at 1000 ms. A span recorded before the SDK loads is sent with its attributes. An unsampled span never reads its attributes.
  • use-agent-transcript.test.ts / use-tasks-tab.test.ts: the three spans, their durations and attributes. A follow released before its first page, and a refused open, record nothing.

Not in this PR

Spans on the web tab (the door supports it, but web installs no recorder yet), PostHog, the server.

Made with Luke


Devin Review