(sidebar): cap subagent lists and build the remainder on demand - #154
Merged
Conversation
Both subagent lists built every row on every render and then hid most of them: appendSubagentChildren filled the children container whatever the caret state, and the project-level orphan bucket appended all its orphans behind a CSS collapse. At 1300 children plus 1300 orphans that is 18 200 DOM elements per render, 99% of them never looked at. Each list now renders the union of the active subagents and the 10 most recent, and emits a "+ N more" toggle for the rest. The remainder is not built until that toggle is clicked; the click records the list in the expandedSubagentRest localStorage set so the expansion survives morphdom. The union is deliberate: a long-running subagent whose transcript stopped growing must keep its running dot, which a recency-only cut would put out. An active search bypasses the cap so no hit hides behind a click. Same fixture after the change: 217 elements, 20 subagent rows.
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.
Both subagent lists built every row on every render, then hid most of what they had just built:
appendSubagentChildrenfilled the children container whatever the caret state and setdisplay:noneon it, and the project-level orphan bucket appended every orphan behind a CSS collapse.buildSubagentItemproduces 7 elements per row, so a project with 1300 children and 1300 orphans built 18 200 elements per render — 99% of them never looked at. Neither the+ N olderpattern nor the collapsed-group CSS helps here: they save screen space, not construction.Each list now renders the union of
isSubagentActive()holds, andmodifiedand emits a
+ N moretoggle for the rest. The remainder is not built until that toggle is clicked.The union is deliberate rather than a truncated sort: a long-running subagent whose transcript stopped growing must keep its running dot, and a recency-only cut would silently extinguish it — the exact class of bug this sidebar has been chasing all week.
Clicking the toggle builds the remainder, removes the toggle, and records the list in the
expandedSubagentRestlocalStorage set (p:<parentSessionId>,o:<projectPath>). The next render reads that set before splitting, so what the user expanded survives morphdom — same mechanism asexpandedSubagentsandorphanExpanded:<projectPath>, GC included. The toggle's payload lives in a module-level map keyed by its id rather than in its closure, because morphdom keeps the old element whenever ids match and a build-time closure would pin the first render's data forever. An active search bypasses the cap, for the same reason the orphan bucket already auto-expands during a search.Measured on the jsdom harness, 1300 children + 1300 orphans:
.sidebar-subagentrows builtNo information is lost silently: the caret still announces the total ("54 subagents") and the toggle says "+ 44 more".
Tests
test/dom-subagent-list-cap.test.js, 11 cases. The load-bearing ones assert that a capped-out row has no element in the document at all, not merely a hidden one. Six mutations were applied and reverted; each failed with its own message:698 tests, 691 pass, 0 fail, 7 pre-existing skips. ESLint: 0 errors, 265 warnings, unchanged.
Deliberately out of scope
expandedSubagentRest; only the GC removes entries whose session is gone. Symmetry with the orphan bucket's collapse would be a second click, easy but not asked for.Rationale in
.ai/contexts/subagent-observability.md("Capped subagent lists").