Skip to content

fix(tui): harden disposal recovery — bounded retry, session re-sync, fresh context, conditional event stream#16544

Open
sjawhar wants to merge 2 commits intoanomalyco:devfrom
sjawhar:fix/tui-disposal-recovery
Open

fix(tui): harden disposal recovery — bounded retry, session re-sync, fresh context, conditional event stream#16544
sjawhar wants to merge 2 commits intoanomalyco:devfrom
sjawhar:fix/tui-disposal-recovery

Conversation

@sjawhar
Copy link

@sjawhar sjawhar commented Mar 8, 2026

Issue for this PR

Closes #16545

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

When the backend Instance is disposed while the TUI is active, several recovery bugs can cause a blank screen or infinite retry loops. This PR fixes all six identified issues:

sync.tsx — Disposal recovery hardening:

  • Saves active session IDs before clearing fullSyncedSessions, then re-syncs them after successful re-bootstrap so the user's current view refreshes automatically instead of showing stale data
  • Caps re-bootstrap retries at 5 with exponential backoff (2s → 4s → 8s → 16s → 30s) — previously retried infinitely with fixed 2s delay
  • Adds a generation counter so that when a newer disposal arrives, stale retry chains from earlier disposals cancel themselves instead of racing
  • Replaces manual new Promise(r => setTimeout(r, 2000)) with sleep() from node:timers/promises to match the rest of the codebase

worker.ts — Event stream lifecycle:

  • Moves Instance.provide() inside the while loop so each reconnect iteration gets fresh context — previously a single Instance.provide() wrapped the entire loop, so after disposal the reconnect used a stale context
  • Removes the unconditional startEventStream(process.cwd()) at module load and adds an events RPC method so the thread controls when and where to start the stream
  • Tracks eventStream.directory so reload() can abort the stream and conditionally restart it, rather than relying on timing-sensitive SSE reconnect

thread.ts — Transport-aware event stream startup:

  • Calls worker.events({ directory }) only when not in external transport mode (!external guard) — previously the event stream started regardless of transport
  • Adds proper .catch() handler on the checkUpgrade call (was silently swallowing errors with empty .catch(() => {}))

How did you verify your code works?

  • bun run typecheck passes from packages/opencode
  • Structural grep verification of all six fixes (Instance.provide position, events RPC existence, transport guard, sleep import, generation counter, resync logic)
  • Reviewed the diff to confirm no changes to instance.ts or server.ts (those belong to the separate idle timeout PR)

Checklist

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

@github-actions github-actions bot added the needs:compliance This means the issue will auto-close after 2 hours. label Mar 8, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions github-actions bot added needs:issue and removed needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels Mar 8, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@sjawhar sjawhar force-pushed the fix/tui-disposal-recovery branch 4 times, most recently from f5450b0 to 1fc5100 Compare March 9, 2026 16:22
@sjawhar sjawhar force-pushed the fix/tui-disposal-recovery branch from 1fc5100 to bdaeaef Compare March 9, 2026 16:28
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.

TUI blank screen after backend Instance disposal — unbounded retry, stale context, no session re-sync

2 participants