Workflows/agents view hydration relaunch can attach the TUI to a stale session under tmux; agent-name/ai-title stamped onto unrelated sessions

Status Open
Reported on v2.1.201
Maintainer reply None cached
Activity 3 comments · opened Jul 5, 2026

Environment

  • Claude Code v2.1.201 (native installer, ~/.local/share/claude/versions/2.1.201)
  • macOS Darwin 25.5.0 (Apple Silicon)
  • tmux 3.7 (claude launched as the tmux pane command, one session per terminal tab)
  • Shared per-user daemon active (claude daemon run), background/fleet features enabled

Summary

Opening the workflow/agents view (/workflows) triggers a one-time TUI self-relaunch to hydrate the fleet-gated UI. The relaunched instance re-resolves which session it belongs to — and when claude runs inside tmux (where pane ttys and pane ids are recycled rapidly across short-lived sessions), that resolution can land on a stale, settled session from a different window. The user-visible effect: the live conversation is replaced by the welcome splash, MCP servers restart, and the tab comes back attached to an old session. The stale session's transcript receives new writes.

A second, likely-related bug: the daemon's naming flow stamps ai-title / agent-name entries onto unrelated sessions that have no name yet.

What happened (production observation)

  1. Session A (project X) ran a dynamic-workflow-heavy task inside a tmux pane; it settled: daemon log bg settled 1eb3e5bd (done).
  2. Later, a fresh session B was launched in a different directory (project Y), also in a tmux pane, and the workflow view was opened.
  3. Daemon log at that moment:

``
[...] [bg] bg claimed-spare ca4bf33f (spare)
[...] [bg] bg spawned a1591ab7 (slash)
[...] [bg] bg claimed-spare 1eb3e5bd (fleet) <-- the SETTLED session A resumed for "fleet"
``

  1. Session A's transcript (1eb3e5bd-….jsonl) received new attachment/system entries minutes after it had settled.
  2. Session B's tab ended up showing/attached to the stale session A content instead of its own conversation. The original TUI process never exited (same pid, same start time) — this is a TUI/identity-level swap, not a crash.

Related naming bug (same identity-confusion family)

When session A's agent name was generated (rust-terminal-rendering-recon), the same {"type":"ai-title"...} + {"type":"agent-name"...} entries were also appended to other sessions that had no name yet — each stamped with the receiving session's own sessionId:

  • a brand-new session in another project dir (transcript contained ONLY those two entries, zero conversation),
  • an hours-old idle scratch session (entries appended long after it went idle).

Sessions that already had their own ai-title were untouched. Diagnostic used:

grep -o '"type":"ai-title"[^}]*\|"type":"agent-name"[^}]*' <transcript>.jsonl | sort | uniq -c

The "donor" session had 863 stamps of its own name; the two victims had 1–2 foreign stamps each.

Reproduction (scripted, isolated tmux socket)

W=/tmp/avr; mkdir -p "$W" /tmp/avrepro
TMUX_TMPDIR=$W tmux new-session -d -x 100 -y 30 -s repro -c /tmp/avrepro claude
# accept folder trust, wait for composer, then:
TMUX_TMPDIR=$W tmux send-keys -t repro "/workflows"; sleep 1
TMUX_TMPDIR=$W tmux send-keys -t repro Enter; sleep 5
TMUX_TMPDIR=$W tmux capture-pane -t repro -p

Observed on first /workflows in a session: the full TUI re-renders from the welcome splash, MCP servers are torn down and restarted (⚠ N MCP servers need authentication · run /mcp appears), then the "Dynamic workflows" panel opens. Subsequent open/close round-trips in the same TUI preserve the conversation — the damage window is the one-time hydration relaunch, when competing session identities exist (which tmux makes far more likely by recycling ttys/pane ids).

Mechanism notes (from the shipped binary)

  • The hydration handoff passes state via env: CLAUDE_CODE_AGENT_VIEW_RELAUNCH (AGENT_VIEW_RELAUNCH_ENV_KEY), consumed at startup by consumeAgentViewRelaunchMarker() (reads env, deletes it, returns parsed marker), with session start type agents_view.
  • The stale attach correlates with the daemon resuming a settled session for the fleet view (bg claimed-spare <id> (fleet)).

Mitigation that works (suggests the fix)

Launching with an explicit --session-id <uuid> makes the post-relaunch resolution unambiguous: a pinned session survives the /workflows round-trip with its conversation intact, transcript stays at the pinned id, no stray sessions are created (verified via the scripted repro). Suggests the relaunch/resume path should always prefer an explicit inherited session identity over tty/pane-keyed lookup — and the naming flow should verify the target sessionId before appending ai-title/agent-name entries.

🤖 Generated with Claude Code

View original on GitHub ↗

3 Comments

mchampanis · 1 month ago

I am seeing a variant of the naming-flow half of this bug with a much simpler repro - without tmux, and only one agent session.

Environment

  • Claude Code v2.1.202
  • macOS Darwin 25.5.0 (Apple Silicon)
  • iTerm2, no tmux
  • A single interactive session; no other Claude Code instances required (but does reproduce with two running)

Repro

  1. One Claude Code session, mid-conversation.
  2. Press left arrow (accidentally, in my case) - agent view opens and auto-generates an AI title for the session.
  3. Press Esc/right arrow to return.
  4. The session now shows a teal task-title banner above the input box (e.g. "Determine presentation file format") that was never there before entering agent view. This title never updates to anything more currently relevant.

The banner survives redraws, so the title appears to be written to the session rather than being stale paint. The conversation underneath stays correct - only the unwanted title banner is the problem. It doesn't appear if I don't enter agent view.

Workaround: disabled leftArrowOpensAgents in /config.

TheMizeGuy · 1 month ago

@bogini seems resolved with .206 👍

0xbrainkid · 1 month ago

One invariant I would add to the fix here: treat the relaunch/hydration handoff as an identity-bearing transition, not just a UI state restore.

At minimum, the handoff record should bind:

session_id
transcript_id
cwd / project key
start_type = agents_view / fleet / slash / repl
parent_session_id, if any
daemon actor that claimed the session
monotonic handoff nonce

Then the post-relaunch attach path should fail closed if the inherited session id and the daemon-selected session do not match the active transcript/session registry. That would make the --session-id workaround the normal path: explicit identity beats tty/pane inference.

The naming bug seems like the same class: ai-title / agent-name writes should be scoped to a target session id and rejected if the target does not equal the active session being named. A title write is small, but it is still an identity mutation; if it lands on the wrong transcript, later coordination layers may treat the wrong session as the named actor.

This is also the boundary we have been using when thinking about AgentFolio/SATP-style agent receipts: reputation or profile history only means anything if every action receipt is bound to the actor/session that actually produced it, not inferred from a recycled terminal, pane, or ambient daemon state. The useful primitive is a durable session_identity -> action_receipt chain that survives relaunch, compaction, and fleet handoff without silently reattaching to a nearby stale session.