Skip to content

Fix thread detachment when re-opening live session via new sidebar - #52

Merged
lukemarsden merged 2 commits into
mainfrom
feature/001913-after-merging-latest-2
May 8, 2026
Merged

Fix thread detachment when re-opening live session via new sidebar#52
lukemarsden merged 2 commits into
mainfrom
feature/001913-after-merging-latest-2

Conversation

@lukemarsden

Copy link
Copy Markdown

Summary

After the two upstream-merge PRs (#42 / #43) landed the new agents sidebar, clicking the currently-open thread in the sidebar caused the panel to "detach" from the live Entity<AcpThread>: Zed's panel stopped updating but Helix kept receiving message_added / message_completed events for the same session. Single-thread, single-click was enough to trigger it — no ThreadSwitcher, no restart.

Root cause: two parallel dedup paths in agent_panel.rs use different identity checks. notify_thread_display (Helix bring-up) compares Entity<AcpThread> references; panel.load_agent_thread (UI clicks) compares acp::SessionId. When the session-id check missed, external_thread issued a duplicate connection.load_session(), producing a fresh entity Y. Helix's WebSocket subscription stayed on the original X (events kept flowing) but the panel rebound to Y (silent) — the same split-brain shape as d7be64fad1 (restart variant), different trigger.

Fix

Adds an #[cfg(feature = "external_websocket_sync")]-gated guard at the top of pub fn load_agent_thread in crates/agent_ui/src/agent_panel.rs, before the existing has_session block:

  1. Look up external_websocket_sync::get_thread(session_id).
  2. If a live entity exists → entity-id check against the active CV → no-op.
  3. Else entity-id check against retained CVs → promote the match.
  4. Else wrap the live entity via ConversationView::from_existing_thread(…) (same path notify_thread_display uses).
  5. Else fall through to the unchanged upstream logic.

The new code is purely additive in front of the existing function body. Outside the Helix feature, behaviour is unchanged.

Changes

  • crates/agent_ui/src/agent_panel.rs — entity-identity guard at the top of load_agent_thread.
  • portingguide.md — Critical Fix Upstream merge 2026 02 26 #11 documenting the guard, its rebase requirements, and its relationship to the previous unregister_thread_if_matches fix.

Notes

  • No live repro / E2E in this environment. Host has no Rust toolchain and no Docker access for the run_docker_e2e.sh setup. Build was attempted via ./stack build-zed dev (zed-builder docker image); status noted in the spec task. Reviewer to run the manual repro and the E2E script before merging.
  • Hypothesis-agnostic. The design considered three possible reasons the session-id dedup could miss (root_session_id None/stale, session-id mismatch between paths, BaseView::Uninitialized at click time). The entity-id guard short-circuits all three without needing to confirm which is hit. If a follow-up investigation finds a specific root cause, a smaller upstream fix is also possible — but this guard is the safe Helix-side floor.

Release Notes:

  • Fixed thread becoming detached when clicking the currently-open thread in the new agents sidebar (Helix mode).

🔗 Open in Helix

📋 Spec:

🚀 Built with Helix

After PRs #42/#43 landed the new agents sidebar, clicking the
currently-open thread in the sidebar caused the panel to detach from
the live Entity<AcpThread>: Zed's panel stopped updating but Helix kept
receiving message_added/message_completed events for the same session.
Single-thread, single-click was enough to trigger it.

Root cause: two parallel dedup paths in agent_panel.rs use different
identity checks. notify_thread_display (Helix bring-up) compares
Entity<AcpThread> references; panel.load_agent_thread (UI clicks)
compares acp::SessionId. When the session-id check missed,
external_thread issued a duplicate connection.load_session(), producing
a fresh entity Y. Helix's WebSocket subscription stayed on the original
X (events kept flowing) but the panel rebound to Y (silent) — same
split-brain shape as d7be64f (restart variant), different trigger.

Fix: add an #[cfg(feature = "external_websocket_sync")]-gated guard at
the top of load_agent_thread, before the existing has_session block.
Look up external_websocket_sync::get_thread(session_id); if a live
entity exists, do entity-id matching against active and retained CVs;
if no view observes it, wrap it via ConversationView::from_existing_thread
(the same path notify_thread_display uses). Falls through to the
unchanged upstream logic when no live entity exists.

Also documents this in portingguide.md as Critical Fix #11 with rebase
instructions.

Spec task: 001913_after-merging-latest-2

Release Notes:

- Fixed thread becoming detached when clicking the currently-open thread in the new agents sidebar (Helix mode).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Spec-Ref: helix-specs@77d9c813c:001913_after-merging-latest-2
…merging-latest-2

Spec-Ref: helix-specs@21bb2539d:001913_after-merging-latest-2
@lukemarsden
lukemarsden merged commit 910838a into main May 8, 2026
23 of 24 checks passed
lukemarsden added a commit to helixml/helix that referenced this pull request May 8, 2026
Pulls in the entity-identity guard at the top of agent_panel.rs::load_agent_thread
that fixes the "thread becomes detached when clicking the currently-open thread
in the new agents sidebar" bug. See helix/design/2026-05-06-acp-thread-entity-routing-on-sidebar-click.md
for the full writeup.

Spec task: 001913_after-merging-latest-2

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Spec-Ref: helix-specs@31416ab8e:001913_after-merging-latest-2
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