fix: prevent feedback loss from stale cached widget sessions (#46)#47
Open
yepzdk wants to merge 1 commit into
Open
fix: prevent feedback loss from stale cached widget sessions (#46)#47yepzdk wants to merge 1 commit into
yepzdk wants to merge 1 commit into
Conversation
The widget bundle had the session ID baked into its WebSocket URL at serve time, so a cached bundle would keep reconnecting under the old session after the MCP server restarted. The server filed feedback under the orphan bucket and `get_pending_feedback` returned "No pending feedback." Because the queues lived only in RAM, the data was lost. Fixed across four layers so the failure mode is impossible going forward: - The widget reads the session from `document.currentScript.src` at runtime and `/widget.js` is served with `Cache-Control: no-store`, so a cached bundle can never carry a stale session. - WebSocket connections with an unknown session ID are rebound to the live session when exactly one MCP session is registered, or rejected with a `session_invalid` message (and a visible reload banner in the widget) when the server can't safely guess. - Pending and ready queues are persisted to disk under `os.tmpdir()/claude-browser-feedback/<sessionId>.json` and rehydrated on boot, so feedback survives crashes and restarts. - `get_pending_feedback`, `get_connection_status`, and `/status` surface orphan buckets and auto-rescue them into the live session when it is the only one registered. Closes #46 Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
no-store, server-side rebind/rejection of unknown sessions, on-disk queues, and orphan-bucket surfacing/auto-rescue.Closes #46.
Changes
src/server.js: drop session from the__WEBSOCKET_URL__injection; serve/widget.jswithCache-Control: no-store.src/widget.js: read the session at boot fromdocument.currentScript.src(or the tagged script element).src/utils.js: newparseSessionFromScriptSrchelper.src/server.jsWSconnectionhandler: if?session=isn't insessionRegistry, rebind to the sole live session (with areboundfield inconnected), or send{type:'session_invalid', knownSessions}+ close.src/widget.js: adopt rebound IDs, handlesession_invalidby re-resolving from the script src and otherwise showing a visible reload banner.src/storage.js(debounced, atomic, per-session JSON files underos.tmpdir()/claude-browser-feedback/).src/server.js: write-through accessors, rehydrate on owner boot, flush pending writes on shutdown.get_pending_feedback,get_connection_status,/status, and/feedbackexpose orphan buckets.Test plan
npm test— 67 tests pass (3 new test files / suites: storage round-trip + debounce,parseSessionFromScriptSrc, WS rebind +session_invalid+ orphan reporting).Cache-Control: no-storeis set,__WEBSOCKET_URL__placeholder is gone, only the base WS URL is injected, rehydrate-on-boot reads persisted sessions, WS connection with a stale UUID is rebound with arebound:{from,to}payload.get_pending_feedback.?session=DEADBEEFinto the script src and reload — confirm the visible reload banner appears.🤖 Generated with Claude Code