Skip to content

fix(app): a saved tab's pane mounts when it first shows, not all at launch - #223

Draft
Stener1 wants to merge 2 commits into
cosscom:mainfrom
Stener1:bra-3335-mount-panes-when-shown
Draft

Stener1 wants to merge 2 commits into
cosscom:mainfrom
Stener1:bra-3335-mount-panes-when-shown

Conversation

@Stener1

@Stener1 Stener1 commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

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:
launch
An old tab opened: it draws (its session has ended), and the agent's pane stays mounted behind it:
old tab

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 only pane-layer.tsx reverted. 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:25 and :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
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.

View guided diff

…aunch

Every session started in an open worktree gets a tab, and the tab outlives
the session. A main checkout that agents start in collects hundreds. At
launch PaneLayer mounted every one of them, each with its terminal and its
chat: on a save of ~650 tabs the Mac app's window reached 8-9 GB within a
minute and stopped answering. A pane's content now mounts the first time
it shows and stays mounted after, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Nov 6, 2026

Copy link
Copy Markdown

@Stener1 is attempting to deploy a commit to the seanbrydonio's projects Team on Vercel.

A member of the Team first needs to authorize it.

…x its changelog line

Seeding 150 tabs takes 12-36 s on a loaded runner, which timed out at the
default 30 s in 2 of 5 runs with every assertion passing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Stener1

Stener1 commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

Re-verified at 2b040e01. That commit adds only the changelog line (AGENTS.md asks for one) and test.slow() on many-tabs.spec.ts. pane-layer.tsx is unchanged.

  • drive.mjs many-tabs-launch (production build, mock, 300 saved tabs): 1 of 302 panes drawn at launch (4,640 DOM nodes), 2 after opening "Old task 7", still 2 after going back (4,771 nodes). No console errors.
  • many-tabs.spec.ts: every assertion passed in every run, including that the first pane is the same connected element after the round trip. On a runner at load 30–43 the test took 12–36 s, so 2 of 5 runs hit the default 30 s timeout. With test.slow(), 6 of 6 pass, the same way sidebar-fleet and artifacts handle their heavy seeds.
  • Pane persistence neighbours restore-tabs, helper-tabs, compare and home-terminal: 16 of 16 pass.
  • tsc -p e2e and the docs build pass.

The draft is waiting for one of my ready-PR slots. I can't add run-ci here (no permission), so a maintainer will need to add it.

🤖 Generated with Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant