Repository navigation
Stop remote streaming and chip switches from freezing the app - #357
Merged
Merged
Conversation
Two 2.18 fleet-view regressions behind the beach ball users see while a remote chat streams: - App subscribed to every backend's whole AppState (directly and through useMergedWorktrees), so each streamed token on any host re-rendered the root. It now subscribes through cached projections — the merge, and a per-backend worktree-list selector — and useSyncExternalStore bails when they're unchanged. - The keep-everything-mounted loop rendered from the view-filtered list, so in single mode a chip switch unmounted every other host's worktrees and the return trip remounted them all. Switching back to Local remounted 38 worktrees / 64 chat tabs at once; 58 file listings in one second froze local main for ~5s. The loop now spans the whole fleet, with one merge cache per view mode so both stay reference-stable. Chat tabs also dedupe their in-flight file listing per worktree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Move createSingleFlight to src/shared so the renderer reuses the tested helper instead of a second untested copy. - The chat @-mention file cache and its in-flight dedupe are now per backend. With the whole fleet mounted, two hosts with the same paths would otherwise have shared one listing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Profiled during a repro: local main blocked 8.5s, 8.4s of it in child_process.spawn under runGitRead. perf.log shows those reads were changed-files / branch-commits for /home/mike/personal-assistant-agentjesus, a worktree on the remote, run by the LOCAL backend. On macOS /home is an autofs mount, so each spawn there sits ~1s on the main thread while the automounter gives up. The same lines appear in every incident back to the first report. Cause: App resolved a worktree's owner from the view-filtered merge. Switching the chip to Local drops the remote worktree from that map, so selectedBackendId fell back to the active backend and the right column (changed files, commits, watcher) queried the Mac for a remote path. Every App routing map now comes from the fleet-wide merge — who owns a path doesn't depend on what the sidebar shows. Main also refuses worktree reads for paths it doesn't host (string check against its worktrees, repo roots and pane keys — no filesystem access), so any future misroute fails fast instead of freezing the app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Register worktree-path handlers through onWorktreeRequest / onWorktreeSignal (backed by a tested ownedPathsOnly wrapper) instead of inline checks, and extend coverage to the ones that were missing: file diffs, binary reads, writes, the per-file watcher, tools, editor:open, and the agent session lookups and spawn-args (which probe a login shell in that directory). Store-only handlers are left alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Second 2.18.1 hotfix, following #356. The Cleanup flood is fixed, but a remote chat streaming still froze the app with a beach ball, at its worst when switching back to a local worktree.
Evidence (local
perf.log/debug.logduring the repro)eventloop-spike4847 ms and 1710 ms. Renderer input took 1.8–5.4 s to paint with zero renderer long tasks, so the renderer itself wasn't busy.worktree:listFilescalls in one second. This machine has 64 local chat tabs across 38 worktrees. Each chat tab lists files on mount with no in-flight dedupe, so nearly every local chat had mounted at the same moment.Causes and fixes
useFleetWorktrees), and visibility is keyed on(backendId, path).getMergedWorktreeskeeps one cache per view mode, so reading both views on each render doesn't evict either one.AppState, both directly (the open-worktree prune) and throughuseMergedWorktrees. It now subscribes through cached projections viauseAllBackendStatesSelector: the merge, andgetFleetWorktreeLists.useSyncExternalStoreskips the render when nothing it reads changed.Tests
New cases in
merge-backend-states.test.ts:getFleetWorktreeListsstays stable until some backend's list changes.Typecheck, build and the full vitest suite pass. There is no React render test harness in the repo, so the "no re-render per token" claim is pinned at the projection layer. It has not been verified end to end yet; that needs a manual repro with a build from this branch.
🤖 Generated with Claude Code