Skip to content

Dev Model preservation restores a Model into a runtime started at another URL or in another tab #1635

Open

Description

@baptisteArno

Summary

In dev, Model preservation can restore a Model into a runtime that started at a different URL, or in a different tab, than the one that preserved it. The page then shows the previous screen while the address bar shows the new URL. A manual refresh fixes it, because the restore is served only once.

Observed with [email protected] and vite-plugin-foldkit on a routed makeApplication.

Reproduction

  1. Run a routed app with the Vite dev server. Open /a in tab 1 and interact so the runtime preserves its Model (the debounced foldkit:preserve-model).
  2. Edit any module in the app's graph. handleHotUpdate fires HotUpdateFired, which marks every preserved entry isReloadFlush: true.
  3. Navigate a tab to /b. It can be a new tab, or the same tab navigated from another site, e.g. a link opened by an embedding host.
  4. The runtime at /b sends foldkit:request-model with its container id. It receives the Model preserved by /a, decodes it successfully, and skips init.

Result: URL /b, screen and route from /a. DevTools' init entry shows the stale route in maybeModel.

In our case, an OAuth consent screen in its "redirecting" state reappeared when the user opened /connect-whatsapp/<id> from an MCP App card, after a file had been saved while they were testing.

Cause

  • The preservation key is the container id (runtime.ts: runtimeId = hydration.runtimeId || container.id). It is the same for every tab and every URL of the application.
  • handleRequestModelReceived serves any entry flagged isReloadFlush, whatever page or tab requests it.
  • runtime.ts restores whenever decodePreservedModel succeeds, without comparing the preserved Model's URL with window.location.href (currentUrl).

The restore is meant for the reload boundary of the page that preserved it. Neither a navigation to another URL nor a fresh tab is that boundary.

Suggested fix

  • Store the URL with the entry. Add the href to PreserveModelMessage and PreservedEntry. Restore only when it equals the starting window.location.href; otherwise run init.
    • This alone covers same-tab navigations to another URL.
    • Vite's full reload keeps the URL, so the intended case is unchanged.
  • Optionally, scope the entry to the tab. Key it by runtimeId plus a per-tab id kept in sessionStorage, as scrollPreservation.ts already does. A second tab on the same URL then never picks up the first tab's Model.

Happy to open a PR for the URL check if this direction sounds right.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Planned: important after P1

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions