Skip to content

fix: Stabilize terminal restore, worker shutdown, and E2E tests#200

Merged
trustin merged 1 commit intomainfrom
fix-e2e-2
Apr 25, 2026
Merged

fix: Stabilize terminal restore, worker shutdown, and E2E tests#200
trustin merged 1 commit intomainfrom
fix-e2e-2

Conversation

@trustin
Copy link
Copy Markdown
Contributor

@trustin trustin commented Apr 25, 2026

Motivation

Several interrelated bugs were causing terminal state to be lost or incorrectly restored after hub and worker restarts, and E2E tests were flaky due to brittle selectors and race conditions.

  1. A race condition in worker graceful shutdown caused disconnect-notice broadcasts to fail: the bidirectional stream was torn down before svcCtx.Shutdown() completed, so watchers never received the disconnect notice.
  2. Terminal titles were throttled at 10-second intervals, meaning a title set just before shell exit would not be flushed to the worker before the close handler wrote metadata to the database, causing stale titles to be restored on reconnect.
  3. Terminal screen snapshot hydration ran at mount time, but the snapshot often arrives after mount (queued behind a reconnect message), so the initial screen content was silently dropped.
  4. The workspace restore hook used title as the discriminator for detecting missing worker-side data. Shells that never emit OSC title sequences would trigger an infinite retry loop.
  5. E2E tests used text-based selectors (getByText, button:has-text) that broke when UI text or styling changed, and a streaming auto-scroll race caused flaky hover-state assertions.

Modifications

  • Replaced signal.NotifyContext with manual context management and a goroutine signal handler in backend/cmd/leapmux/worker.go so svcCtx.Shutdown() runs before the bidi stream closes. Wrapped all shutdown paths in sync.Once to prevent duplicate invocations.
  • Changed terminal title flush throttle from 10 s to 500 ms in frontend/src/components/shell/useTerminalOperations.ts.
  • Converted TerminalView.tsx to apply the initial screen snapshot via a reactive effect that triggers when the snapshot arrives, rather than unconditionally on mount, with a per-instance latch to prevent double-painting.
  • Updated useWorkspaceRestore.ts to use cols (not title) as the discriminator for missing worker-side data, and to distinguish DISCONNECTED status from genuinely absent data.
  • Added data-testid attributes to EditorToolbar.tsx, DirectoryTree.tsx, and WorkerSectionContent.tsx to give E2E tests stable anchors.
  • Added waitForAgentIdle() in the sendAndWaitForReply() E2E helper to eliminate the auto-scroll/hover race.
  • Updated E2E tests in 09-worker-management, 14a-workspace-archive, 20-markdown-editor-input, 25-chat-message-rendering, and 25-tunnel-ui to use data-testid selectors.
  • Added 108-line unit test suite for TerminalView covering deferred hydration and one-shot latch behavior.
  • Added 155-line unit test expansion for useWorkspaceRestore covering the retry mechanism when worker-side payload (cols, rows, screen) arrives after status-change events.

Result

  • Workers now broadcast disconnect notices reliably to all watchers on graceful shutdown.
  • Terminal titles and screen content are correctly restored after hub and worker restarts, including for shells that do not emit OSC title sequences.
  • Workspace restore no longer loops infinitely for non-OSC-title shells.
  • Previously flaky E2E tests pass consistently.

@trustin trustin merged commit 658524d into main Apr 25, 2026
5 checks passed
@trustin trustin deleted the fix-e2e-2 branch April 25, 2026 06:51
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