mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-08 11:13:44 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Its web UI keeps live views fresh with a live-events websocket plus React Query, and #9627 began replacing polling with event-sourcing (pushing data into the cache) > - After the churn fixes (#9624) and #9627 shipped, a long-lived tab's memory footprint was still climbing, so I re-profiled with the Chrome DevTools MCP > - The dominant remaining churn is React Query re-arming a polled query's `refetchInterval` timer on **every** observer notification — and our live-event handlers `setQueryData(liveRuns(companyId))` on nearly every event, so each pushed update re-arms every `liveRuns` observer's timer (the sidebar is always mounted) > - #9627 event-sourced the live-runs data but left the now-redundant `refetchInterval` in place, so we did half the fix — the poll is pure waste and the thing re-arming timers > - This pull request removes `refetchInterval` from the event-sourced company live-runs queries so the frequent cache writes have no timer to re-arm > - The benefit is that the steady-state timer churn on the most-observed resource collapses, so the off-heap footprint stops climbing ## Linked Issues or Issue Description No public GitHub issue exists; describing inline per CONTRIBUTING.md → "Link Issues or Describe Them In-PR", following the bug report template. Continues #9569 / #9624 / #9627. **What happened?** With the earlier fixes deployed, a browser tab left open on the app kept growing its memory footprint. MCP profiling showed ~100+ `setInterval` create/clear cycles per 5s on an aged tab (vs ~16 fresh), all from React Query's refetch-interval timers being re-armed on every `setQueryData` to the frequently-written `liveRuns(companyId)` query. **Expected behavior** A resource whose data is pushed (event-sourced) should not also poll; cache writes should not repeatedly re-arm interval timers. Idle tabs should hold a bounded footprint. **Steps to reproduce** Open a tab with agents streaming, leave it open, and instrument `setInterval`/`clearInterval`: the churn rate climbs and traces to `QueryObserver.updateTimers` (`refetchInterval`) for `liveRuns`, re-armed by every live-event cache write. **Paperclip version or commit** Branch `perf/drop-live-runs-refetch-interval`, off `master` (after #9627). **Deployment mode** Local dev (`pnpm dev`), web UI. Core UI live-updates plumbing; not adapter-specific. ## What Changed - Set `refetchInterval: false` on every site that polls the **plain** `queryKeys.liveRuns(companyId)` query (event-sourced by #9627): `Sidebar`, `SidebarAgents`, `Issues`, `Inbox`, `IssueDetail` (companyLiveRuns), `ProjectDetail` (×2), `Routines`, `ExecutionWorkspaceDetail`, `bridge-init`. - Removed the now-unused `useVisibilityRefetchInterval` interval vars/imports in `Issues`, `Inbox`, `IssueDetail`. - **Left variant-key sites polling on purpose** — `Agents` page (`[...liveRuns, "agents-page"]`) and `ActiveAgentsPanel` (`[...liveRuns, scope, …]`) are NOT event-sourced by #9627 (different exact cache key), so dropping their poll would make them stale. Those are a later phase. - Freshness for the converted queries now comes from event-sourcing (#9627) + its reconnect reconcile; the initial mount fetch and cross-tab publish (`usePublishSharedQueryData`) still happen. ## Verification - MCP profiling identified the churn: the single churning callback is React Query's `refetchInterval` timer, re-armed by `setQueryData(liveRuns)` on live events. - `vitest`: all affected suites pass (`Sidebar`, `SidebarAgents`, `Issues`, `Inbox`, `IssueDetail`, `ProjectDetail`, `Routines`, `ExecutionWorkspaceDetail`, `LiveUpdatesProvider`) — 153 tests. - Updated two `SidebarAgents` linger-window tests: they advanced fake timers to the *exact* linger-expiry boundary and had relied on poll-induced re-renders to flush. The linger self-schedules its own `setTimeout`, so the tests now cross the boundary with a small margin + an explicit flush (no product change). - `tsc -b` clean. - End-to-end footprint reduction should be re-measured against a rebuilt bundle with the same instrumentation. ## Risks Low, client-only. - `liveRuns(companyId)` freshness now depends entirely on event-sourcing + reconnect reconcile (both from #9627). If an event path is missed, the reconnect handler refetches once; durable replay is a planned later phase. - Variant-key run lists (Agents page, ActiveAgentsPanel) are unchanged and still poll, so they don't regress. - Issue-scoped run queries (`issues.liveRuns/activeRun/runs`) are **not** touched here — they aren't event-sourced yet and are a separate phase. ## Model Used - **Provider:** Anthropic, via the Claude Code CLI. - **Model:** Claude Opus 4.8 (`claude-opus-4-8`). - **Reasoning mode:** Extended thinking enabled. - **Capabilities used:** tool use (shell, file editing), and the Chrome DevTools MCP to re-profile the live instance and pinpoint the `refetchInterval` timer churn. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work (a perf/plumbing change) - [x] I have searched GitHub for duplicate or related PRs and linked them above (continues #9627; no duplicates) - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have considered and documented any risks above - [ ] I have updated relevant documentation to reflect my changes (N/A — no user-facing docs; rationale documented inline) - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code)