Skip to content

fix: count busy descendants in the agents-view subagents indicator - #1986

Merged
sethkarten merged 11 commits into
mainfrom
fix/agents-view-busy-descendant-indicator
Sep 3, 2026
Merged

sethkarten merged 11 commits into
mainfrom
fix/agents-view-busy-descendant-indicator

Conversation

@snimu

@snimu snimu commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

The agents view's running-subagents indicator was blind past one level: a collapsed idle parent read "N subagents" with no running count while a grandchild streamed. The row-build tally counted only direct children, and the parent's own hasRunningRlmChildren snapshot freezes between its roster flushes, so busy work deeper in the tree was invisible to every ancestor — and the dimmed summary label made even the counted cases easy to miss. This PR fixes the indicator and, in the same stroke, settles the display policy it was violating.

Policy: Running means the session you enter. A session is Running when it is itself doing work now; delegated work shows as the badge, the count, and the idle ranking — never as the section.

Indicator: runningSubagentCount is now a bottom-up tally over the built rows — the view's live per-descendant truth — so a busy descendant at any depth counts on every ancestor, and the "N subagents running" title on a collapsed group is correct at any nesting level. The pass is iterative (each row has at most one parent, so a deep chain cannot overflow the stack). Within the Idle section, rows with busy descendants rank above plain idle rows, so a working subtree surfaces without lying about the parent's own state. A collapsed group's summary title renders in the success color when its count is non-zero, dim otherwise — existing style vocabulary, no new UI machinery.

Classification: hasRunningRlmChildren loses its section-classification role. The roster classifier composes busy from the session's own activity/isSessionActive, and the worker's activity axis (renamed isActiveSessionBusy to hasLiveSessionWork) no longer holds a settled parent at "working" for its children — a parent whose only activity is a delegated crew lists as Idle, at the top of Idle, with the badge. The "subagents running" status label is removed; a delegating idle session shows normal idle labeling.

Deliberately unchanged — the safety half: hasRunningRlmChildren stays on the wire, and hasLiveSessionWork keeps counting it for every consumer where delegated work is live work: the supervisor's worker-eviction snapshot, empty-draft eviction, busy client-owned session counts, and the daemon stop/update confirmations. Killing a worker mid-delegation would kill the crew, so those gates still treat delegated work as busy; only the display axis changed. (The chat children-existence hint and the agent-observe "busy" status also keep the field; they are not section classification.)

Net src: +41/−20 across five files.

Credit: diagnosis by Vincent Bailly — VincentBailly#9 traced the stale-snapshot mechanism and proposed the ancestor walk over live rows. This PR adopts that mechanism and deliberately rejects the section-promotion half per the display policy above.

Validation: pins for a busy grandchild counting on every idle ancestor, busy-descendant-first idle ranking, and the legible collapsed badge fail on main; a direct-child pin documents the non-promotion contract; one existing expectation updated intentionally (a root above a streaming child and grandchild now titles "2 subagents running"). agents-view (state/mode/roster/inactive-reply/missing-cwd), agent-roster, daemon-agent-roster, subagent-summary-line, daemon-session-list, interactive-mode-status — 371 tests green in a sanitized env; root npm run check green.

Linear: RES-1253 https://linear.app/primeintellect/issue/RES-1253


Note

Medium Risk
Changes roster classification, agents-view destructive actions, and recursive RLM cancel behavior—user-visible status and stop semantics can shift for delegating parents, while worker recovery still treats delegated work as busy.

Overview
Agents view now treats Running as the session’s own work: parents that only delegate show as Idle, with subtree busy state surfaced via runningSubagentCount, collapsed “N subagents running” titles (success styling when non-zero), and idle sort that puts rows with busy descendants first. The count is a bottom-up tally over nested rows so work at any depth rolls up to every ancestor; heartbeats stay out of that tally.

Stop/delete UX gates on hasLiveWork (own running section, descendant count, or hasRunningRlmChildren) so an idle-looking parent with a working crew still stops instead of delete.

Daemon/roster display stops promoting parents to “working” solely because children run (activeActivityForSession, classifySessionRosterStatus); hasLiveSessionWork (renamed from isActiveSessionBusy) still treats delegated children as busy for recovery, draft discard, and similar safety paths.

Cancellation extends cancelRlmChildRun with cancelRunningRlmDescendants so stopping a child also cancels running/queued work under settled or retained sessions in the subtree.

Reviewed by Cursor Bugbot for commit e071ab8. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix agents-view subagents indicator to count busy descendants recursively

  • buildAgentsViewRows now tallies running descendants across the full nested subtree via iterative breadth-first traversal, so collapsed summaries report total busy counts instead of only direct children
  • classifySessionRosterStatus, activeActivityForSession, and getSessionStatusLabel no longer treat delegated child work as the parent's own running state; a resident parent with only running children is now idle for display while still counted as busy for recovery and draft-discard via the renamed hasLiveSessionWork helper
  • Rows with busy descendants sort ahead of plain idle rows within the same non-Running section, and summary titles for busy descendants render in the success color
  • Risk: classifySessionRosterStatus removes the running-child flag from the accepted summary shape in agent-roster.ts; out-of-tree callers passing that flag will no longer influence display classification

Changes since #1986 opened

  • Replaced isRunningSessionSummary helper with new hasLiveWork predicate in AgentsViewMode class [0bd4d19]
  • Changed AgentsViewMode.killSubagent method to use descendant activity when determining termination behavior [0bd4d19]
  • Updated delete confirmation title computation in AgentsViewMode to reflect live descendant work [0bd4d19]
  • Added test case for idle row with busy descendants triggering stop instead of delete [0bd4d19]
  • Reworked AgentSession.cancelRlmChildRun to cancel running or queued descendants when the targeted child run has already settled, and added a new AgentSession.cancelRunningRlmDescendants method that recursively walks the session subtree to cancel all active descendants [0511890]
  • Added test case verifying that cancelling a settled child run cancels its live descendants [0511890]
  • Removed test case verifying display of busy settled child on collapsed idle parent and updated test case for busy grandchild counting to assert parent retains 'completed' status label [5b536e7]
  • Removed inline comments from multiple functions without changing implementation logic [5b536e7]
  • Modified AgentSession.cancelRlmChildRun and AgentSession.cancelRunningRlmDescendants methods in the coding-agent package to always descend into child sessions and cancel running descendants regardless of whether the direct child is successfully cancelled or is currently running or queued [e071ab8]
  • Extended the rlm recursion test in coding-agent package to validate that cancellation from a root session propagates to deeper descendants beneath active child sessions [e071ab8]

Macroscope summarized 65d5d6e.

The "N subagents running" indicator only tallied direct children whose
row classified as running, so a busy grandchild under an idle
intermediate child left every ancestor reading "N subagents" with a zero
count. The tally is now a bottom-up pass over the built rows - the live
per-descendant truth - replacing the direct-child increment.

Indicator only, per policy: a session is Running when it works itself;
idle ancestors of busy subtrees stay in Idle. Within Idle they now rank
above plain idle rows, and a collapsed group's summary row renders its
running count in the success color instead of dimmed so the busy subtree
is discoverable without expanding.

Diagnosis credit: Vincent Bailly (VincentBailly#9) traced
the stale hasRunningRlmChildren snapshot and the row-walk mechanism;
this adopts his walk for the indicator while rejecting the section
promotion.

RES-1253
Comment thread packages/coding-agent/src/modes/agents-view/agents-view-state.ts Outdated
The descendant tally read row sections after
propagateHeartbeatStateToAncestors had promoted idle ancestors to
running, so a heartbeat-active grandchild counted its promoted parent
too. The tally now runs before the promotion pass and counts
intrinsically busy rows only; once that propagation pass is deleted
(#1967) the ordering is a no-op.

RES-1253
Comment thread packages/coding-agent/src/modes/agents-view/agents-view-state.ts Outdated
The recursive tally threw RangeError on a deep child chain just by
opening the agents view. The nesting loop assigns every row at most one
parent, so the tree is a forest: a reverse breadth-first pass over one
work list computes the same bottom-up counts iteratively, with the
pre-promotion ordering kept.

RES-1253
…dren

Second half of the RES-1253 policy, user-approved: Running means the
session you enter is doing work now. Delegated child work no longer
classifies a session as running - the busy-descendant badge, count, and
idle ranking from the first half carry the delegation signal.

hasRunningRlmChildren loses its section-classification role in both
owners: classifySessionRosterStatus composes busy from the session's own
activity/isSessionActive, and isActiveSessionBusy (the worker's activity
axis) no longer holds a settled parent at "working" for its children.
The "subagents running" status label dies with the section it had to
agree with. The field itself stays on the wire, and isSessionSummaryBusy
keeps it on purpose for its residency and shutdown-safety consumers
(worker eviction snapshots, empty-draft eviction, busy client-owned
session counts, daemon stop confirmation).

RES-1253
Comment thread packages/coding-agent/src/modes/daemon/daemon-session-list.ts Outdated
isActiveSessionBusy served two meanings after the classification change:
the display activity axis (session's own work only, correct) and worker
recovery plus draft-discard (where a running RLM child is live work that
dies with the worker). The recovery journal recorded busy:false for a
settled parent with a running child, so a worker death skipped its
interruption record and notice.

The shared predicate is deleted and each meaning gets one named owner:
activeActivityForSession reads the session's own isSessionActive
directly, and hasLiveSessionWork (own turn or running RLM child) backs
recordWorkerRecoveryState and isDiscardableDraft. No caller can grab the
wrong meaning blind.

RES-1253
sethkarten
sethkarten previously approved these changes Sep 3, 2026
Comment thread packages/coding-agent/src/modes/daemon/agent-roster.ts
Comment thread packages/coding-agent/src/modes/agents-view/agents-view-state.ts Outdated
Since Running means the session's own work, an idle parent of a running
crew was branching into the delete path: killSubagent chose stop-vs-
delete from the display section and stopAgentForDeletion treated
activity==='working' as the only live work. Destructive actions are
safety consumers of the display/safety split: one row-level hasLiveWork
(own section running, busy descendants, or the wire running-children
flag) now drives the stop-first branch, the deletion flow, and the
confirmation verb.

Also trimmed the tally comment to current behavior.

RES-1253
Comment thread packages/coding-agent/src/modes/agents-view/agents-view-mode.ts
Stopping a subagent row whose own run already settled reported
'Subagent already finished' while a nested descendant kept running:
cancelRlmChildRun only cancelled the targeted run itself, and a fully
released child (run removed, session retained) was never matched by id
at all. Cancellation now descends where the tree lives: a settled target
stops every running or queued run in its retained session's subtree
(cancelRunningRlmDescendants, mirroring hasRunningRlmChildren's walk),
and the returned flag stays truthful so the UI says stopped only when
something stopped.

RES-1253
Comment pass: multi-line narration cut to one-line invariant guards
(destructive live-work gate, residency-vs-section busy split, worker-
death live work) and code-readable notes deleted. Test pass: the direct-
child indicator pin folded into the grandchild pin (the general case),
which now also carries the idle-label and wire-flag assertions.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5b536e7. Configure here.

Comment thread packages/coding-agent/src/core/agent-session.ts
Cancelling a live child run aborts it, and abort cascades into the
child's ACTIVE runs - but running work retained under a settled
descendant of that child was the end of the line: neither the cascade
nor the cancel walk visited it. cancelRlmChildRun now descends into the
target's session after cancelling it, and cancelRunningRlmDescendants
descends at every node instead of treating running runs as leaves, so
cancellation is exhaustive over the subtree regardless of each node's
run state. The returned flag stays truthful.

RES-1253
@sethkarten
sethkarten self-requested a review September 3, 2026 20:57
@sethkarten
sethkarten merged commit 6165572 into main Sep 3, 2026
23 checks passed
@sethkarten
sethkarten deleted the fix/agents-view-busy-descendant-indicator branch September 3, 2026 21:00
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.

2 participants