Agent Teams: No distinction between idle-but-alive and dead teammates — lead spawns duplicates, destroying context

Resolved 💬 3 comments Opened Feb 27, 2026 by SNRose07 Closed Mar 2, 2026

Environment

  • Claude Code version: 2.1.59 (regression observed starting ~v2.1.47)
  • OS: macOS (Darwin 25.2.0)
  • Terminal: iTerm2
  • Agent Teams mode: in-process
  • Feature flag: CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
  • Team size: 5 teammates + 1 lead

Bug Description

After teammates complete tasks and go idle, the lead agent has no mechanism to distinguish idle-but-alive teammates from dead/crashed teammates. Both display identically (grey) in the UI. The lead cannot determine which teammates are reachable, so it spawns duplicate agents with "-2" suffixes — permanently destroying the original agent's accumulated context.

This is particularly damaging for knowledge-intensive workflows where agents accumulate domain context (Jira queries, analysis, file reads) over a session and need to be available for follow-up work.

Four Independent Issues

1. No visual or programmatic distinction between idle and dead agents

Idle agents (process alive, waiting for messages) and dead agents (process gone from prior session) both render as "grey" in the in-process UI. There is:

  • No color difference (e.g., amber/dim for idle vs grey/strikethrough for dead)
  • No isTeammateAlive() or getTeammateStatus() tool
  • No process liveness check injected into the lead's context
  • No way for the lead LLM to determine which teammates are reachable

The docs state "idle teammates can receive messages" but the lead has no way to KNOW a teammate is idle vs dead. The TeammateIdle hook addresses whether agents GO idle, but doesn't help the lead distinguish idle from dead after the fact.

Expected: Lead should be able to determine teammate liveness before deciding to spawn vs message.

2. Memory GC destroys team membership records (v2.1.47 / v2.1.50 / v2.1.59 regression)

Three changelog entries progressively garbage-collect the lead's evidence that teammates exist:

  • v2.1.47: "Improved memory usage by trimming agent task message history after tasks complete" + "releasing API stream buffers, agent context, and skill state after use"
  • v2.1.50: "Fixed memory leak where completed teammate tasks were never garbage collected from session state"
  • v2.1.59: "Improved memory usage in multi-agent sessions by releasing completed subagent task state"

The GC doesn't distinguish "task content" (100MB, safe to GC) from "team membership records" (2KB, must never be GC'd). After GC, the lead forgets teammates exist — even if they're still alive and idle.

The memory leak fix was needed (#23239: 4.4GB in 17 minutes), but it's over-aggressive. Team membership records should be exempt from GC.

Expected: GC should clean up task output content while preserving team membership metadata.

3. Team config not preserved across context compaction (#23620)

When the lead's context window fills and compaction triggers, team membership information is lost. CLAUDE.md is re-injected after compaction. Team config is not. This has been open as #23620 since Feb 6, 2026 with 7 thumbs up.

Expected: Team config should be treated like CLAUDE.md and re-injected after compaction.

4. SendMessage creates orphan inboxes without recipient validation (#25135)

SendMessage with a non-existent recipient name silently creates an inbox file and "delivers" the message to a file nobody polls. No error, no warning. Messages are permanently lost.

In our case, agents were instructed to message "product-mgmt" but the lead's config name was "team-lead". 14 agent messages were silently delivered to product-mgmt.json — a file the lead never reads.

Expected: SendMessage should validate recipient names against the team config and return an error for unknown recipients.

Reproduction Steps (Issue 1 — idle vs dead)

  1. Enable Agent Teams: CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
  2. Create a team, spawn 5 teammates, let them complete tasks and go idle
  3. All teammates show as "grey" in in-process mode
  4. End the session (Ctrl+C or close terminal)
  5. Start a new session, create the same team name
  6. config.json now has 10 members: 5 dead from prior session + 5 new with "-2" suffixes
  7. All 10 show identically as grey — humans and LLM cannot distinguish alive from dead
  8. Send a message to one of the "-2" agents → it responds (proving it's alive)
  9. Send a message to one of the original agents → no response ever (dead process)
  10. Lead has no way to determine this without trial-and-error messaging

Live Evidence

Our config.json contained 11 members: 5 dead processes from yesterday's session and 5 alive-but-idle "-2" agents from today. All displayed identically. The lead spawned duplicates because it couldn't tell which were reachable.

We proved an idle agent was alive by sending it a test message — it responded immediately. But the lead had no programmatic way to discover this.

Impact

  • Knowledge-intensive multi-agent workflows (our use case: 5 domain-specific Jira analysts + 1 PM synthesizer) become unusable
  • Every task completion → grey-out → lead spawns duplicate → original context destroyed
  • The TeammateIdle hook mitigates idle timing but does NOT address the lead's inability to distinguish idle from dead
  • #23620 (compaction) compounds this — even if the lead remembered teammates, compaction erases that memory

Related Issues

  • #23620 — Team lost after compaction (OPEN, Feb 6, no response)
  • #25131 — 23 agent incarnations, 35% productive (OPEN, STALE)
  • #23884 — "-2" suffix duplicates claim wrong tasks (OPEN, STALE)
  • #29163 — Agents idle without responding (OPEN)
  • #28075 — Idle nudges expire in 8 sec (OPEN)
  • #28676 — Stale team state blocks cleanup (OPEN)
  • #25383 — Messages marked read before delivery (OPEN)
  • #25135 — SendMessage creates orphan inboxes (OPEN)
  • #27128 — Idle floods create ghost messages (OPEN)

Proposed Fixes

  1. Add teammate liveness detection: process check via PID or heartbeat, injected into lead context or available as a tool
  2. Distinct visual states: idle (amber/dim) vs dead (grey/strikethrough)
  3. Exempt team membership from GC: keep the 2KB membership records, GC the 100MB task output
  4. Re-inject team config after compaction (like CLAUDE.md)
  5. Validate SendMessage recipients against team config, return error for unknown names
  6. Clean up dead members on TeamCreate: check process liveness, remove dead entries, reuse names instead of appending "-2"

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗