feat(agent-activity): replace the popup with a full-screen fleet view - #1105
Merged
Merged
Conversation
…l fleet T14. The evidence is the owner's live workspace, read through Agent Code's own control MCP rather than imagined: 33 sessions, 28 of them in one project, 3 with an explicit title, 0 with a spoken name, 11 on a lane. That says what is wrong with the current modal, in order: - it has no attention signal at all, so it cannot answer the only question worth asking of 33 agents — which one needs me; - with 3 titles across 33 sessions, rows fall back to the first user prompt, so the fleet reads as paragraphs, and three rows read "agent-code"; - it lists panes, so the 22 parked sessions most likely to be closable are the hardest to reach; - its vocabulary (tab, Dispatch order, grid, tiled) was retired by #1013; - no search, and actions hidden until hover while the real job is bulk. Six stages, with the reconciliation isolated in one row model that the view and the command are the only importers of. Six unknowns are listed rather than assumed, two of them owner calls: whether this is a modal or a takeover, and whether it replaces Close Old Agents and Close Idle Orchestration Agents. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Stage 1 of docs/decomposition/agent-activity-redesign.md was half done: the control-MCP recordings carry identity and placement for a whole window at one instant, and nothing carried the RUNTIME fields a row's attention bucket is computed from. Agent Code already writes exactly that. Every debug bundle contains a `state-snapshot.json` — the renderer's own SessionRuntime for one real session at the moment the bundle was taken, written by the app with no interpretation in between. So this extracts rather than instruments: 71 snapshots, 55 distinct sessions, three providers, spanning months of real use. What it contains that a capture taken this afternoon would have missed: 61 running / 9 idle / 1 exited, seven distinct stream phases, three live conditions, 15 sessions with a queued prompt, and `activityStatus` strings like "Honking…" that no hand-written fixture would have invented. Two payload classes are recorded as counts instead of kept: the four screen mirrors (~10 KB each, four near-copies, the most privacy-loaded field in the file) and the two unbounded arrays the measurement pointed at — `subAgents[*].toolCalls` (4.4 MB of 11.6 MB) and `workActivity.timeline` + `.recentKeys` (3 MB). The row model branches on status, phase, process state, conditions, queue length and timestamps; it never reads a rendered screen, a subagent's tool call, or a touched-file timeline. Every structural field around them is verbatim, and the counts are kept so an agent with 40 running tool calls cannot look identical to an idle one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tage 1's findings The first cut of this decomposition said sessionDisplayTitle resolves 'explicit title -> spoken agent name -> first prompt -> cwd'. It does not: there is no spoken-name term and no first-prompt term, only title, the terminal's live cwd, the spawn cwd and the raw path. That is not a footnote. The document's headline finding -- rows are unidentifiable -- was written believing a better source existed and was merely being reached for last. With 3 titles and 0 spoken names across 33 sessions there is nothing to fall back to, so Stage 2 cannot fix identity by reordering a chain; it has to introduce a source. Which one is now an owner call in the unknowns. Also recorded, from reading the substrate rather than assuming it: TLDR, Goal and Goal Loop have no renderer store and are deliberately subscribed only while a preview is visible, so the fleet-wide batched reader they need is a stage this document did not have (now 2b); the conditions capability lookup throws for terminals; limitHit never self-clears; terminalProviderFailure is inert without meta; Grok can never read failed; a session can hold more than one lane; and Stage 6's 'computed once' is reachable in the renderer but not across the MCP surfaces, by #1080's own design. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
6 tasks
Full screen, keep the two quick close commands, and name untitled rows by their Goal. The full-screen choice is recorded with its mechanism: a full-viewport Dialog rather than a fourth MainSurface takeover, because every hidden-workspace rule names each takeover by hand. Refs #1170 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The old modal had no attention signal at all. This model sections every agent in the window, lane or pool, into needs you / working / idle / exited, reusing the rules the rest of the app already trusts: condition attention, #1018's provider failure, the limit-idle guard, the goal loop chip's reading and #915's last-active. A second copy of any of them would drift from the surface the user compares it with. Rows are named title, then Goal, then folder, and say which. TLDR, Goal and Goal Loop are read in one batched call per kind, only while the view is open. Bulk close goes through closeGrantedSessions with its own kill tag. Tests replay the recorded corpus from #1105. It disagreed with the first draft: the corpus has no permission prompt or failed turn, but it does hold two prompts the app could not deliver, and those are what needs you. Refs #1170 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Full viewport through the same Radix Dialog every modal uses, rather than a fourth MainSurface takeover: the primitive already owns input ownership, the focus trap, Escape and focus restore, and the workspace stays laid out so no terminal resizes. One keyboard grammar: the list owns Space and Backspace, so typing a filter can never select or close anything; the first Esc leaves the filter, the second dismisses. Close Old Agents and Close Idle Orchestration Agents are unchanged (owner decision). The old AgentActivityModal is deleted; the closed-view derivation test now covers the new view. Refs #1170 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A failed compaction now needs you. The unread rule leaves compaction out, and the policy labels only its failed phase, so the label is read on its own instead of only inside the attention branch. - A start that failed and then exited shows the error, not "Exited": process failures are checked before the exit, as the orchestration lifecycle already orders them. - A focused footer button keeps its native Enter/Space. - Tab from the filter reaches the list with the query kept, so filtered rows can be selected from the keyboard. - Scrolling follows the highlighted session id, not row objects rebuilt on every runtime update, and the fallback highlight is pinned to its session. - A Goal is searchable when a title wins the name. Refs #1170 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Extension views are left out again: no process, nothing to report. The index lists them as pool citizens, which the first cut inherited. - An unconfirmed prompt says it "may not have sent", distinct from one the app knows was not delivered: re-send versus go and look. - Sections are labelled groups inside the listbox rather than region landmarks, so screen readers keep the options attached to the list. Refs #1170 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s busy Grok's review: Claude can sit on its usage-limit banner with the process alive, so processActive and the derived status say running, and the "not working" guard hid the one case the rule exists for. isLimitIdle already drops a hit older than the current turn. Extension views are also no longer asked about goal loops. Refs #1170 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 25, 2026
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.
Fixes #1170
Replaces the Agent Activity popup with a full-screen view that answers three questions for every agent in the window, lane or pool: who needs me, who is working and on what, and what can I clean up. This PR started as Stage 1 (evidence and plan only). It now carries the whole redesign.
The problem
The owner's verdict on the old modal: "that modal is ages and just shit across the board". The evidence recorded in Stage 1, from the owner's own 33–48 agent window:
Owner decisions (2026-09-24)
What it does
closeGrantedSessions. It confirms once, carries its ownbulk.agent-activitykill tag, records no Undo Close entries, and refuses an agent that started working after it was approved. A working agent the user deliberately selected does close.Design decisions
Dialog, not a fourthMainSurfacetakeover. Every hidden-workspace rule (theuseKeybindspicker gate and Escape, lease hygiene, the control observation) names each takeover by hand, so a takeover would mean four more edits, each easy to forget. The Dialog already owns input ownership, the focus trap, Escape and focus restore, and no terminal resizes. The reasoning is also in the decomposition doc.conditionRequiresAttention,terminalProviderFailure,isLimitIdle, the goal-loop chip reading, andsessionActivity. The row model is a pure function, so the bulk close can re-ask it at the kill boundary.closedModalDerivations.Verification
activityRow.test.tsreplays the recorded corpus from Stage 1: 71 real runtimes plus the owner's fleet with titles verbatim.agentActivityClose.renderer.test.tsxdrives the real close executor.AgentActivityView.renderer.test.tsxcovers section order, one read per kind, and the keyboard grammar (typing a filter never selects or closes; a selection survives filtering).tsc -bis clean exceptsrc/main/browserPocket/*. Those errors come from a localnode_modulesmissingplaywright-core, whichorigin/mainadded; CI installs it.check:keybindingsandtest:contractpass.Review round (one round: Codex ×2, Pi, Grok)
None of the four reviewers found a way to close an agent without confirmation, and none found a crash. Fixed:
Each of these has a test.
Limits and follow-ups
🤖 Generated with Claude Code