Skip to content

acpmux: an adopted host's running subagents keep their tags - #30614

Merged
teamleaderleo merged 2 commits into
feat-cmux-nextfrom
lane/ux-acpmux-adopt-subagents
Nov 1, 2026
Merged

teamleaderleo merged 2 commits into
feat-cmux-nextfrom
lane/ux-acpmux-adopt-subagents

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

After a daemon restart, the new daemon adopts the running agent host (host_adopted). The hub's subagent tree (SubagentTree) started empty, so it no longer knew which subagents the chat was running. Until each subagent's next progress event, that subagent's lines reached clients without a tag (_meta.acpmux.subagent), so they read as the chat's own. The web pane then showed a helper's grep as the chat's own call. Leo saw this after a finished turn: "why does it say you're still running grep". #30416 (cf818b1) made the pane attribute updates by sessionId. This PR fixes the cause on the hub side, so the hub keeps the list.

  • adopt_held (hub/hosts.rs) now calls seed_subagents_from_log (hub/work_below.rs) before the host's replay reaches the tap. It rebuilds two things from the durable log, the same way the tap followed them live:
    • which subagents the chat started, so tagging continues from the first replayed line
    • which subagents still run, so the summary's subagentsRunning and subagentNames and the work-below state are right
  • A harness start, stop or exit ends the background subagents. A turn start ends the foreground ones. Both match live behaviour, and processes.rs read_processes rebuilds the same way.
  • New SubagentTree::adopt merges the rebuilt tree into the live one. A subagent the live tree already knows keeps its own state.
  • The head predicate for "this line may name a subagent" is now shared with open_work_reads (names_subagents). SubagentTree's background tests move to subagents_background_tests.rs, so the god-file-capped subagents.rs and hosts.rs shrink instead of growing.

Builds on #30439, which added the running-subagent counts that are now rebuilt too.

Test

tests/durable_agents.rs a_subagent_running_across_a_restart_keeps_its_tag_after_adoption. A turn starts subagent child-1 and its tool call. The daemon is killed and restarted, then adopts the host. The test checks that the summary still counts the subagent, then that the child's next line (tool_call_update cg-1) is tagged child-1.

Command: cargo test -p acpmux --test durable_agents a_subagent_running_across_a_restart_keeps_its_tag_after_adoption

  • Base plus test only (78a63f7): see the comment below
  • Head: see the comment below

Observed / Not run

Observed: rustfmt, prepush-gate, and the post-edit god-file check (no growth). The test results are in the comments.
Not run: the full acpmux suite locally (it runs through cmux-ci).

Changelog

Fixed: after an acpmux restart, a running subagent's activity no longer shows as the chat's own.

Agent session: agent session

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes running subagents losing their tags after a daemon restart adopts their host. The adopting daemon's subagent tree started empty, so until each subagent's next progress event its lines reached clients untagged and read as the chat's own activity.

Bug Fixes

  • adopt_held now rebuilds the subagent tree from the durable log before the host's replay reaches the tap, restoring which subagents the chat started and which still run.
  • Added SubagentTree::adopt to merge the rebuilt tree into the live one, keeping live state for subagents the tree already knows.
  • names_subagents is now shared between replay seeding and open_work_reads.
  • Moved SubagentTree's background tests to subagents_background_tests.rs so the file-size-capped subagents.rs and hosts.rs don't grow.

Written for commit 5fdcf7b. Summary will update on new commits.

View guided diff Turn on auto-fix

teamleaderleo and others added 2 commits November 1, 2026 09:05
…ts replay

After host_adopted the hub's SubagentTree started empty, so a running
subagent's lines reached clients untagged (as the chat's own) until its
next progress event. adopt_held now seeds the tree from the log (which
subagents the chat started, and which still run) before the tap sees the
replay. The head predicate is shared with open_work_reads; the tree's
background tests move to their own file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-63b9964940c20354b639
@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 5fdcf7b: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr30614 -f ref=lane/ux-acpmux-adopt-subagents -f platform=macos -f backend_mode=local

Shipped in:

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 1, 2026

@cubic-dev-ai cubic-dev-ai 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.

5 issues found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cmux-tui/crates/acpmux/src/subagents.rs">

<violation number="1" location="cmux-tui/crates/acpmux/src/subagents.rs:226">
P2: Adopting a root known by the durable log does not repair a live tree's pre-root phantom state. Apply each adopted root through `remember_root` so its stale `parents`, `background`, and `running` entries are removed before subsequent updates are classified.</violation>
</file>

<file name="cmux-tui/crates/acpmux/src/hub/work_below.rs">

<violation number="1" location="cmux-tui/crates/acpmux/src/hub/work_below.rs:126">
P2: This discards log-scan failures and lets adoption continue with an incomplete subagent tree, so the first post-restart child update can be tagged as the chat and the summary can undercount running subagents. Propagate the scan error or otherwise refuse/delay adoption until reconstruction succeeds.</violation>

<violation number="2" location="cmux-tui/crates/acpmux/src/hub/work_below.rs:130">
P2: The seed leaves foreground subagents running after a logged `turn_result` until another turn starts. Handle `turn_result` here as a foreground boundary, so canceled or otherwise unclosed foreground agents do not remain in `subagentsRunning` after restart.</violation>
</file>

<file name="cmux-tui/crates/acpmux/src/subagents_background_tests.rs">

<violation number="1" location="cmux-tui/crates/acpmux/src/subagents_background_tests.rs:47">
P3: This test never exercises a nested subagent: `annotate` always uses `sessionId: "s1"`, so every spawn is a root child. Add a child update under `a` and assert `running_count` and `running_now` include it; otherwise nested-count regressions can pass.</violation>
</file>

<file name="cmux-tui/crates/acpmux/src/hub/hosts.rs">

<violation number="1" location="cmux-tui/crates/acpmux/src/hub/hosts.rs:487">
P2: `seed_subagents_from_log` runs before adoption proves that the host is compatible. An incompatible or failed attach therefore leaves subagents from the old log permanently reported as running even though this daemon will not receive their updates; move seeding into the `Attached::Ready` path immediately before replay delivery.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

self.running.push((id, label));
}
}
self.root_threads.extend(log.root_threads);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Adopting a root known by the durable log does not repair a live tree's pre-root phantom state. Apply each adopted root through remember_root so its stale parents, background, and running entries are removed before subsequent updates are classified.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/acpmux/src/subagents.rs, line 226:

<comment>Adopting a root known by the durable log does not repair a live tree's pre-root phantom state. Apply each adopted root through `remember_root` so its stale `parents`, `background`, and `running` entries are removed before subsequent updates are classified.</comment>

<file context>
@@ -203,6 +203,29 @@ impl SubagentTree {
+                self.running.push((id, label));
+            }
+        }
+        self.root_threads.extend(log.root_threads);
+    }
+
</file context>
Suggested change
self.root_threads.extend(log.root_threads);
for root in log.root_threads {
self.remember_root(&root);
}

tree.set_roots(
meta.agent_session_id.iter().chain(&meta.retired_agent_session_ids).map(String::as_str),
);
let _ = self.store.scan_heads(&session.id, &mut |head| {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This discards log-scan failures and lets adoption continue with an incomplete subagent tree, so the first post-restart child update can be tagged as the chat and the summary can undercount running subagents. Propagate the scan error or otherwise refuse/delay adoption until reconstruction succeeds.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/acpmux/src/hub/work_below.rs, line 126:

<comment>This discards log-scan failures and lets adoption continue with an incomplete subagent tree, so the first post-restart child update can be tagged as the chat and the summary can undercount running subagents. Propagate the scan error or otherwise refuse/delay adoption until reconstruction succeeds.</comment>

<file context>
@@ -109,6 +109,57 @@ impl Hub {
+        tree.set_roots(
+            meta.agent_session_id.iter().chain(&meta.retired_agent_session_ids).map(String::as_str),
+        );
+        let _ = self.store.scan_heads(&session.id, &mut |head| {
+            if head.dir == "mux" {
+                match head.kind {
</file context>

let resume_after = self.last_host_seq(session, &record.incarnation);
// The subagents the host still runs, before its first replayed line reaches the tap:
// a child's line is tagged with its session from the first one (`work_below.rs`).
self.seed_subagents_from_log(session);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: seed_subagents_from_log runs before adoption proves that the host is compatible. An incompatible or failed attach therefore leaves subagents from the old log permanently reported as running even though this daemon will not receive their updates; move seeding into the Attached::Ready path immediately before replay delivery.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/acpmux/src/hub/hosts.rs, line 487:

<comment>`seed_subagents_from_log` runs before adoption proves that the host is compatible. An incompatible or failed attach therefore leaves subagents from the old log permanently reported as running even though this daemon will not receive their updates; move seeding into the `Attached::Ready` path immediately before replay delivery.</comment>

<file context>
@@ -482,6 +482,9 @@ impl Hub {
         let resume_after = self.last_host_seq(session, &record.incarnation);
+        // The subagents the host still runs, before its first replayed line reaches the tap:
+        // a child's line is tagged with its session from the first one (`work_below.rs`).
+        self.seed_subagents_from_log(session);
         let tap = self.session_tap(session);
         let incarnation = record.incarnation.clone();
</file context>

if head.dir == "mux" {
match head.kind {
"host_started" | "stopped" | "exited" => tree.end_background(),
"turn_started" => tree.end_foreground(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The seed leaves foreground subagents running after a logged turn_result until another turn starts. Handle turn_result here as a foreground boundary, so canceled or otherwise unclosed foreground agents do not remain in subagentsRunning after restart.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/acpmux/src/hub/work_below.rs, line 130:

<comment>The seed leaves foreground subagents running after a logged `turn_result` until another turn starts. Handle `turn_result` here as a foreground boundary, so canceled or otherwise unclosed foreground agents do not remain in `subagentsRunning` after restart.</comment>

<file context>
@@ -109,6 +109,57 @@ impl Hub {
+            if head.dir == "mux" {
+                match head.kind {
+                    "host_started" | "stopped" | "exited" => tree.end_background(),
+                    "turn_started" => tree.end_foreground(),
+                    _ => {}
+                }
</file context>
Suggested change
"turn_started" => tree.end_foreground(),
"turn_started" | "turn_result" => tree.end_foreground(),


/// The hover card's live count (cmux NEXT sidebar, Leo 2026-11-01: "this hover preview being
/// able to see the # of associated subagents"): every running subagent, foreground or
/// background, nested ones too, with its name; a replay counts none, and without a running

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: This test never exercises a nested subagent: annotate always uses sessionId: "s1", so every spawn is a root child. Add a child update under a and assert running_count and running_now include it; otherwise nested-count regressions can pass.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/acpmux/src/subagents_background_tests.rs, line 47:

<comment>This test never exercises a nested subagent: `annotate` always uses `sessionId: "s1"`, so every spawn is a root child. Add a child update under `a` and assert `running_count` and `running_now` include it; otherwise nested-count regressions can pass.</comment>

<file context>
@@ -0,0 +1,123 @@
+
+/// The hover card's live count (cmux NEXT sidebar, Leo 2026-11-01: "this hover preview being
+/// able to see the # of associated subagents"): every running subagent, foreground or
+/// background, nested ones too, with its name; a replay counts none, and without a running
+/// turn only the background ones still run.
+#[test]
</file context>

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex, review-ux-adopt-subagents): SHIP at 5fdcf7b

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Test results at 5fdcf7b:

@teamleaderleo
teamleaderleo merged commit 9288e32 into feat-cmux-next Nov 1, 2026
102 of 129 checks passed
@teamleaderleo
teamleaderleo deleted the lane/ux-acpmux-adopt-subagents branch November 1, 2026 09:35

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 5fdcf7b7 Deployed Nov 1, 2026 by teamleaderleo via cmux-tui tree preflight #95246
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant