fix(app): a saved tab's pane mounts when it first shows, not all at launch
Summary
The Mac app's window (WKWebView) grew to 8–9 GB within a minute of launch and stopped answering: chats took ~45 s to open or failed with "the box didn't answer". The boxes answered in milliseconds the whole time. The window itself was the bottleneck.
The cause is the saved tabs. reconcile gives every session started in an open worktree a tab of its own, and the tab stays after the session ends so it can be started again. A main checkout that agents are started in collects hundreds of these tabs. The report's save had 658 tabs in the worktree shown at launch, 637 of them for sessions that had ended. PaneLayer mounts every pane of every mounted workspace, including the current one at launch. So all 658 panes mounted at once, each with its TerminalView and, with chat as the view, its ConversationPane.
PaneLayer, per pane of a mounted workspace
- <PaneGuard><Pane …/></PaneGuard> // every pane, at launch
+ wake(leaf.id, visible) && <PaneGuard><Pane …/></PaneGuard>
+ // mounts the first time it shows, then stays mounted as before
A pane's content now mounts the first time it shows. After that it stays mounted exactly as before: switching tabs or worktrees, splitting and moving never remount it, and a terminal keeps its screen and connection. A pane that has never been shown is the same positioned, empty data-testid="pane" box. Compare sides and parked panes follow the same rule. No Go code changes, and the saved format doesn't change.
Not changed here: the tabs still pile up, and the strip still draws all of them. Closing or folding tabs whose session ended and that were never opened would be the next step. It's a product decision, so it's left out of this PR.
Evidence
A real box and a real save (~650 tabs), Playwright's WebKit on Linux (the engine WKWebView uses). The production build, against a laptop agent of this checkout paired with the box, read-only (writes refused, no terminals attached). Web process RSS and DOM nodes:
| at ~5 s | at ~150 s | DOM nodes | requests | |
|---|---|---|---|---|
| before | 0.8 GB | 6.1 GB, still climbing | 22k → 59k, climbing | stopped after ~20 s (1.1 MB) |
| after | 0.7 GB | 1.0 GB, flat | ~10k, flat | kept polling |
It scales with the tab count. Before, at ~80 s: 25 tabs 0.8 GB, 100 tabs 1.7 GB, 300 tabs 3.7 GB, all 4.1 GB. Chromium on the same save, before: 1.3 GB, 47k nodes, 15.7k listeners, and the page stopped answering (a screenshot timed out after 30 s). With no save, the same box sits at ~200 MB in Chromium in every view.
Mock (drive.mjs many-tabs-launch, 300 saved tabs of ended sessions):
| build | engine | panes drawn at launch | DOM nodes |
|---|---|---|---|
| main | WebKit | 302 of 302 | 20,462 |
| main | Chromium | page didn't attach a pane within 30 s | — |
| this branch | WebKit | 1 of 302 | 4,732 |
| this branch | Chromium | 1 of 302 | 4,732 |
Launch, only the agent's tab drawn:

An old tab opened: it draws (its session has ended), and the agent's pane stays mounted behind it:

Tests
app/e2e/many-tabs.spec.ts(new): 150 saved tabs, one pane drawn at launch, two and three after opening old tabs, and going back finds the first pane's same element (not remounted). It fails on main ("expected 1 drawn pane").pnpm test:e2e(548 tests): 534 passed on the first run. The 14 failures were rerun with 2 workers, on this branch and on the same tree with onlypane-layer.tsxreverted. The same 6 fail on both and are not touched by this change:home-triage.spec.ts:41,keyboard.spec.ts:173,sidebar-fleet.spec.ts:25and:123,sidebar-quiet.spec.ts:6,team-setup.spec.ts:331.a11y"database-review in berth-dark" is flaky under load on both, failing 4 of 12 with 3 workers on each build. The rest pass.pnpm test,typecheck:plugins,check:titles,check:type,check:grid,check:csp,check:themes,tsc -p e2e: pass.- The verify skill gets a feature file (
features/workspace-tabs.md) and a scenario (many-tabs-launch).
🤖 Generated with Claude Code
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.