Skip to content

fix(tui): ignore stale Zed poll when switching session directories - #26

Open
detail-app[bot] wants to merge 1 commit into
devfrom
detail/bug-fix/fix-tui-ignore-stale-zed-poll-when-switching-sessi-e1e626
Open

detail-app[bot] wants to merge 1 commit into
devfrom
detail/bug-fix/fix-tui-ignore-stale-zed-poll-when-switching-sessi-e1e626

Conversation

@detail-app

@detail-app detail-app Bot commented Sep 12, 2026

Copy link
Copy Markdown

Detail bug report: View on Detail

Issue for this PR

No separate issue was opened for this transient TUI editor-context bug.

Type of change

  • Bug fix

What does this PR do?

Bug: In Zed-terminal fallback mode (no editor WebSocket), the EditorContext polls Zed's state DB scoped to the current session directory via editor.selection(directory). When the user navigates to a session in a different project, reconnectWithDirectory switches directory, resets the selection, and calls connect() — but an already-started editor.selection(oldDir) promise is still in flight. Its .then handler was guarded only with if (closed || socket) return, which is a no-op in Zed mode (socket is always undefined), so when the stale poll resolved it wrote the old project's selection into the new session's store. Because reconnectWithDirectory had reset lastZedSelectionKey to undefined, the stale selection passed the "new key" check and contaminated the new session until the next 1s poll overwrote it.

Fix (packages/tui/src/context/editor.ts):

  • The Zed poll .then now captures const pollDirectory = directory at poll start and adds || directory !== pollDirectory to the guard, so a resolution belonging to a previous directory is dropped. This is the necessary-and-sufficient fix.
  • reconnectWithDirectory now sets zedSelection = undefined before connect(), so the new directory's poll starts immediately instead of waiting for the old poll's .then/.finally to release the ??= memo slot (a recovery-latency improvement, not the stale-write guard — clearing the reference alone does not cancel the already-attached .then, since JS promises aren't cancellable).

How did you verify your code works?

The Zed-poll path previously had no test coverage at all; I added three tests in packages/opencode/test/cli/tui/editor-context.test.tsx that mount the context in Zed-terminal mode with a fake EditorIntegration (connection: () => undefined, a controllable deferred for selection(directory)):

  1. An immediately-resolving poll populates the store and reports connected/pending (baseline Zed path).
  2. After reconnect(newDir) while an oldDir poll is in flight, resolving the old poll leaves selection() undefined, labelState() "none", connected() false — the stale poll is ignored.
  3. After ignoring the stale poll, resolving the new-directory poll populates the store with the new selection and flips connected to true — the new directory's poll starts promptly.

I also mutation-verified each fix line is load-bearing: reverting only the directory guard (keeping zedSelection = undefined) makes test 2 fail with the stale oldDir selection leaking into the new session — confirming zedSelection = undefined alone does not fix the bug, matching the report. Re-applying the guard and reverting only the zedSelection = undefined line makes test 3 fail with the new-directory selection never arriving — confirming the belt-and-braces line delivers the prompt recovery.

Routine checks all pass: the focused suite (8 pass), the existing Zed-helper suite editor-context-zed.test.ts (16 pass), the full test/cli/tui/ suite (70 pass), the packages/tui suite (191 pass, 1 skip), tsgo --noEmit on both packages/tui and packages/opencode, oxlint on both changed files (0 errors), and prettier --check.

Not verified: the two manual end-to-end smoke procedures (running the live TUI against a real Zed install in Zed-terminal mode, navigating between sessions in different project directories, and the cross-mode Zed→WebSocket reconnect case with an IDE lock file). These require a local Zed installation and a running editor IDE that this environment doesn't have, so I relied on the unit/behavioral tests above, which exercise the same editor.selection(directory) poll and reconnectWithDirectory code paths against a controllable deferred.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Automatic Fixes PRs can be configured here.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

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.

0 participants