Skip to content

Fix stuck reconnecting banner after pageshow with persisted=false #132

Description

@jeonghun-jj-lee

Important

Problem

After a transient server connection drop (sleep/wake, network blip), the chat panel's "Server connection dropped -- reconnecting..." banner stays visible indefinitely despite the session being live and responsive. The ribbon never recovers to the idle state without a manual window reload.

Approach

Remove the event.persisted guard from resumeStreamAfterPageShow so that start() is called on every pageshow event. The guard was designed for browser bfcache semantics, but VS Code webview iframes never fire pageshow with persisted = true, leaving the SSE stream permanently stopped after a pagehide.

Approaches Considered

  • Always restart on pageshow (chosen) -- minimal one-function change; safe because start() is idempotent (if (started) return)
  • Remove pagehide/pageshow handlers entirely -- simpler but loses the intentional "stop stream on hide" cleanup, which matters if the app is reused in a browser context
  • Switch to document.visibilitychange -- semantically better but equally uncertain about VS Code webview event model, more code

Scope

  • In: fix resumeStreamAfterPageShow logic, update its unit test
  • Out: proactive health-check heartbeat (separate concern, unnecessary for this fix)
  • Out: extension-side status bar changes (the bug is in the web app, not StatusBarManager)

Acceptance Criteria

  • After a simulated pagehide + pageshow (with persisted = false), the SSE stream restarts and streamStatus transitions back to "connected"
  • The existing resumeStreamAfterPageShow unit test passes with the updated expectation (both persisted: true and persisted: false call start())
  • The ConnectionBanner shows the green "Server reconnected" flash and then hides within 3 seconds after recovery
  • No behavior change when pageshow fires without a preceding pagehide (start() is idempotent -- no double streams)

Key Decisions

  • start() idempotency (if (started) return run at line 270) is the safety net -- no risk of duplicate SSE connections from unconditional calls
  • Keep the pagehide -> stop() handler (intentional cleanup for when the page is truly hidden); only the resume side changes
  • The _event parameter becomes unused but is retained for the signature contract (callers pass the event)
Prior Art / Patterns
  • packages/app/src/context/server-sdk.tsx lines 162-165 -- the function to change
  • packages/app/src/context/server-sdk.tsx lines 269-339 -- the reconnect loop (unchanged)
  • packages/app/src/context/server-sdk.tsx lines 347-349 -- the event listener registration (unchanged)
  • packages/app/src/context/server-sdk.test.ts lines 6-16 -- the test to update
  • packages/app/src/components/connection-banner.tsx -- the banner component (unchanged, already handles recovery)

Source

Notes

  • The fix ships with the next engine build (opencode binary) vendored into the Amicode VSIX. After merge, bump the vendor pin in harmoniqs/amicode and cut a new extension build.
  • The pagehide/pageshow lifecycle was added for the opencode browser/terminal app where bfcache is real; in that context persisted = true fires correctly. The VS Code webview is the only embedding where it fails.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions