fix(webviews): dispose page subscriptions after their store stops

Merged
TE
7d ago
+367-51

History, Apps and CodeRouter can retain a subscription that finishes after the page stops. A stop/start cycle can also let the previous startup overwrite the replacement's cleanup handle or publish an obsolete error.

Each store now scopes subscription callbacks, cleanup adoption and startup completion to its lifetime. Stopping invalidates outstanding reads; late subscriptions dispose immediately. History and Apps keep startup marked in flight through the page-stream await, including the unsupported-watch fallback.

Validation: added deferred-subscription regression tests for both subscription stages, both restart completion orders, stale rejection, last-listener teardown, pending reads and unsupported-watch fallback. Remote focused tests and independent review are pending. This changes store lifecycle ownership only, with no rendering or styling changes. Risk: a stale callback must be ignored without suppressing the replacement subscription; the tests explicitly exercise the replacement's connection events.

Agent session: agent session


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes a store lifecycle bug where History, Apps, and CodeRouter could let a stopped instance's late results — subscriptions, reads, and action continuations — overwrite a restarted store's state or dispose its cleanup handles.

Each store now scopes subscription callbacks, cleanup adoption, startup completion, and read/action continuations to a lifetime token; stopping invalidates outstanding work and late results are dropped. Apps also gates its detail, grants, logs, and icon flows behind generation checks so a stale continuation can't publish. History and Apps keep startup marked in flight through the page-stream await, including the unsupported-watch fallback.

Validation

  • Added regression tests covering deferred subscriptions, both restart completion orders, stale rejection, last-listener teardown, pending reads, the unsupported-watch fallback, and stale app reads and actions.
  • No rendering or styling changes; only store lifecycle ownership.