perf(agent-pane): skip 300 hidden row elements in folded running tray (SW-04)
SW-04: closed running tray stops building hundreds of invisible row elements
The default agent-pane SubagentTray starts folded when more than three agents/commands are running. Its render nevertheless unconditionally maps every live and completed item through line(item) into React <li> children before the return statement. The returned JSX already gates the fold body using useFold(...).present. At 300 active agents, a fully collapsed tray builds 300 AgentLine trees on every render (including its elapsed-time header tick), discarding those trees because the fold body is absent.
Change: construct cards only while fold.present is true. This exact existing hook stays true while a close animation is playing, so open/close animation and focus semantics are preserved. Once it has finished closing, no hidden row creation. Header count, oldest elapsed time, stop semantics, arrival identity tracking, View all and full open tray are unchanged. The shared AgentsPill.nestAgents helper also now appends to indexed sibling groups instead of cloning the prior array for every sibling, preserving order and the existing fallback for cyclic parent links.
Regression: webviews/test/subagent-tray.test.tsx covers 300 siblings and a historical parent cycle, plus adds 300 agents with observable name reads. Before opening, the collapsed tray must have zero AgentLine DOM rows and zero name reads during row construction; expanding must show all 300 and read their names. This tests the render cost that DOM-count-only coverage missed (the old implementation also had zero mounted rows but created 300 hidden element trees).
Source: feat-cmux-next@38550c7743b27f346ae45b75fbd72cbda80af737; branch sw04-skip-collapsed-tray-rows, head 01a768bbfdd335dc2cd30c3869aee865b8b1316b. Three files changed (tray, shared pill nesting helper, focused tray tests). No daemon, Chief or wire contracts changed. A separate active #27886 owns workflow progress and is not modified.
Validation: source-only from this chat; new webview test was committed but the repository bun suite has not been executed here. Gate on cd webviews && bun test test/subagent-tray.test.tsx, bun run check, and a narrow Mac live tray open/close capture if needed.
Performance
| Metric | Before | After | Delta | Harness or run link |
|---|---|---|---|---|
| Hidden row name reads, first render of a folded 300-agent tray | 1200 | 0 | -1200 | cmux-ci job ef23bb758f5d739c562c2785 |
| Hidden row name reads per header rerender (folded, 300 agents) | 600 | 0 | -600 | same job |
| Median folded rerender, ms (jsdom, 50 renders) | 0.601 | 0.119 | -80.2% | same job |
Harness: webviews/test/sw04-bench.test.tsx (uncommitted bench; before = feat-cmux-next's code, after = this PR's, same process), run on the fleet Linux pool via cmux-ci test, job ef23bb758f5d739c562c2785, source 4f1a46121bd7. No new timer, observer or animation loop; the fold keeps its existing cleanup.
Capture
No capture: the closed tray is visually the same header; open/close motion and results are unchanged. The user-visible difference is lower invisible work, to be validated under a signed NEXT build.
Related SW-04 results and other narrow PRs: HQ #5599.
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Stops the folded SubagentTray from building hundreds of invisible row elements on every render. Previously the tray mapped every live and completed agent through line(item) into <li> children regardless of fold state, so 300 collapsed agents constructed 300 AgentLine trees per header tick and discarded them. Rows are now built only while fold.present is true, which stays true during close animation so open/close motion and focus behavior are preserved.
The shared AgentsPill.nestAgents helper now appends to indexed sibling groups instead of cloning the prior array for every sibling, preserving order and the fallback for cyclic parent links.
- Adds regression tests for 300 agents: zero row name reads while collapsed (including a repeated collapsed render), 300 rows after expanding, and nesting order with a cyclic parent fallback.