Skip to content

fix(coding-agent): make subagent cancellation explicit - #464

Closed
snimu wants to merge 1 commit into
mainfrom
fix/subagent-stop-ux
Closed

snimu wants to merge 1 commit into
mainfrom
fix/subagent-stop-ux

Conversation

@snimu

@snimu snimu commented Jul 17, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Stopping a subagent had four confusing or unreliable outcomes:

  • completion could win the race and be reported as an error (Subagent already finished);
  • repeated stop input could send duplicate requests;
  • a parent awaiting rlm(...) could remain blocked when child work ignored abort;
  • a daemon-hosted child could remain resident after cancellation.

What changes

Session lifecycle

Cancellation is idempotent while the run remains tracked. It rejects the public rlm(...) waiter immediately, aborts the child, emits the authoritative cancelled update, keeps terminal activity clear, and does not let a late provider error replace the cancellation reason/status.

Daemon cleanup

After acknowledging cancel_rlm_child, the daemon asynchronously closes the matching hosted descendant runtime with reason killed. The response does not wait for shutdown, and the ancestor check prevents an unrelated session from being closed.

Interactive UX

The TUI:

  • shows Stopping subagent... immediately;
  • coalesces duplicate stop input while the request is pending;
  • reports Subagent stopped on success;
  • reports Subagent is no longer running when completion wins the race, without treating it as an error;
  • displays the session-owned Cancelled update without stale activity/recap;
  • removes explicit orchestrator deletion tombstones normally.

The UI does not maintain a second optimistic child-state machine. AgentSession remains authoritative for cancellation state, and later daemon roster refreshes may remove the transient cancelled row after runtime release.

Scope and consolidation

This branch is one commit on current main and intentionally excludes the earlier kernel/process-group cleanup work.

Tests follow the consolidation approach from #498, #499, and #500:

  • no new test files or harnesses;
  • session assertions extend the existing RLM lifecycle test;
  • daemon teardown is tested at the command boundary;
  • cancellation rendering is checked through the existing summary/detail components;
  • interactive success, completion-race, failure, coalescing, and tombstone behavior live in the existing InteractiveMode suite.

Validation

  • npm run check
  • 195 focused session/interactive/rendering tests
  • focused daemon cancellation command test

Note

Make subagent cancellation explicit with immediate feedback and race-condition fixes

  • Cancelling a subagent now immediately marks it as cancelled, rejects the public run promise, records durationMs, and emits a terminal UI update before the child finishes unwinding.
  • Fixes a race in AgentSession.runRlmChild where result collection could overwrite a concurrent cancellation with a done status.
  • The daemon now closes the daemon-hosted child session matching the cancelled subagent after the parent acknowledges cancellation.
  • The interactive UI shows a "Cancelled" recap immediately, avoids duplicate stop requests via a stoppingChildAgentIds set, and keeps cancelled snapshots visible unless explicitly deleted by the orchestrator.
  • Behavioral Change: previously cancelled subagents were removed from the inspector; they now remain visible unless marked as deleted by the parent orchestrator.

Macroscope summarized ae977bc.


Note

Medium Risk
Touches RLM child lifecycle, daemon session teardown, and promise rejection paths where races previously caused stuck waiters or wrong terminal status; behavior is well covered by new/extended tests.

Overview
Subagent stop now treats cancellation as authoritative end-to-end: AgentSession rejects the public rlm.run waiter immediately, emits a terminal cancelled update (with duration, no stale activity), idempotently acknowledges repeat cancels, and re-checks cancellation after usage collection so a late finish cannot flip status to done.

Daemon cancel_rlm_child still returns { cancelled: true } right away, then asynchronously closes the matching hosted descendant session (killed) using ancestor checks so unrelated sessions are not torn down.

Interactive TUI shows immediate “Stopping subagent…”, coalesces duplicate stop input, reports success or “no longer running” without erroring on completion races, keeps user-cancelled rows visible (only orchestrator deletion tombstones are removed), and the child inspector shows Cancelled instead of stale recap/activity.

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

@snimu
snimu marked this pull request as ready for review July 17, 2026 18:38
Comment thread packages/coding-agent/src/modes/interactive/interactive-mode.ts
Comment thread packages/coding-agent/src/core/agent-session.ts Outdated
Comment thread packages/coding-agent/src/modes/daemon/daemon-mode.ts Outdated
Comment thread packages/coding-agent/src/modes/interactive/interactive-mode.ts
Comment thread packages/coding-agent/src/modes/interactive/interactive-mode.ts
Comment thread packages/coding-agent/src/core/agent-session.ts
@snimu
snimu requested a review from kevinjosethomas July 20, 2026 09:59
kevinjosethomas added a commit that referenced this pull request Jul 20, 2026
Comment thread packages/coding-agent/src/core/agent-session.ts Outdated
Comment thread packages/coding-agent/src/modes/interactive/components/child-agent-inspector.ts Outdated
kevinjosethomas added a commit that referenced this pull request Jul 20, 2026
Comment thread packages/coding-agent/src/modes/interactive/interactive-mode.ts
kevinjosethomas added a commit that referenced this pull request Jul 20, 2026
kevinjosethomas added a commit that referenced this pull request Jul 20, 2026
Comment thread packages/coding-agent/src/modes/interactive/interactive-mode.ts
kevinjosethomas added a commit that referenced this pull request Jul 20, 2026
Comment thread packages/coding-agent/src/core/kernel/index.ts Outdated
Comment thread packages/coding-agent/src/core/kernel/index.ts Outdated
kevinjosethomas added a commit that referenced this pull request Jul 20, 2026
Comment thread packages/coding-agent/src/core/agent-session.ts
kevinjosethomas added a commit that referenced this pull request Jul 20, 2026
Comment thread packages/coding-agent/src/modes/interactive/interactive-mode.ts
Comment thread packages/coding-agent/src/modes/interactive/interactive-mode.ts Outdated
@snimu
snimu force-pushed the fix/subagent-stop-ux branch from b2d202c to 29bc998 Compare July 22, 2026 09:50
Comment thread packages/coding-agent/src/modes/interactive/interactive-mode.ts
@snimu
snimu force-pushed the fix/subagent-stop-ux branch from 29bc998 to 45c04b9 Compare July 22, 2026 10:59

@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 2 potential issues.

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 45c04b9. Configure here.

Comment thread packages/coding-agent/src/core/agent-session.ts
Comment thread packages/coding-agent/src/core/agent-session.ts
@snimu
snimu force-pushed the fix/subagent-stop-ux branch from 45c04b9 to ae977bc Compare July 22, 2026 11:22
@snimu

snimu commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

Closing as superseded. All four problems this PR addressed have since been solved (or made moot) on main by the subagent-subsystem rework:

  • Completion racing stop shown as an error: the UI this patched (child-agent-inspector, interactive-mode kill path) no longer exists; the agents-view stop flow shows 'Subagent already finished' as a neutral status message, not an error.
  • Duplicate stop requests: stop is now a two-step arm+confirm flow, and a duplicate cancel is a harmless no-op.
  • Parent blocked awaiting rlm(...): rlm() now resolves at admission (fire-and-forget); there is no completion waiter to block, and the internal publication waiter is rejected synchronously on cancel.
  • Daemon child staying resident after cancel: cancellation flows through releaseRlmSubagentRuntime with an ordered teardown (ledger deletion boundary, closeSession, artifact sweep); re-adding this PR's eager close would race that.

Verified per-problem with file:line evidence on current main (details in the review notes). Thanks — the ideas here all made it into the architecture, just via different routes.

@snimu snimu closed this Aug 19, 2026
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