Repository navigation
fix(plugins): keep session follow alive across renderer reloads and inside scope - #1756
SeanLiu326 wants to merge 3 commits into
Conversation
…nside scope Two defects in the vastsa#1745 follow feature: - The out-of-order guard kept a main-side high-water mark over a counter the renderer owns in a useRef. A renderer reload (the automatic recovery for render-process-gone restarts the counter) left the mark stale, so every push afterwards was silently ignored and plugins saw the pre-reload session forever (vastsa#1753). Pushes now carry a per-load boot id; a changed boot id resets the mark, and the out-of-order guard still applies within a boot. - session:activated fanned out to every loaded plugin process and docked view without the activation-scope check that session.getActive and ui.revealView enforce, so a project-scoped plugin received other projects' session identity its pull API refuses to give (vastsa#1754). The fan-out now scope-filters selection events per recipient, using the session's project from the live map and the workspace-relative check when the session has none.
|
Reviewed both halves; they match what #1753 and #1754 asked for, and the scope fix picks the semantics I flagged as the risky one — correctly. #1754 scope parity — right predicate. The filter keys on the announced session's project ( #1753 boot id — the guard still does its job. Reading the gate: a changed Two notes, neither blocking:
Tests: the added |
|
I confirmed both reported paths on current I can't merge this head yet: the required JS CI job fails in |
|
Confirmed both of your points, and the CI failure is narrower than it looks — it is two source-shape assertions that pin the old call signature, not a behavior problem.
:207 assert.match(fanOut, /plugins\.broadcastEvent\(event, \[payload\]\)/);
:231 assert.match(services, /plugins\.broadcastEvent\(event, \[payload\]\)/);Both assert the literal two-argument call. Your change passes a third argument ( Two things about your head that I verified while reading it:
Non-blocking, from earlier: |
…ontracts Review follow-ups on vastsa#1756: make pluginActiveInProject a required dependency of plugin services (an optional one let a future caller forget the wiring and silently reintroduce vastsa#1754), document that the boot-id out-of-order guarantee is per renderer load, and widen the two main-wiring contracts to accept the optional scope-filter argument on the selection-event fan-out instead of pinning the old two-argument call shape.
|
Both asks are in ce8ffae:
On the E2E ask: there is no existing scenario covering the reload/scope user path — |
|
One more assertion of the same shape — this is why the job is still red after
assert.match(viewHostSource, /broadcast\(event: string, payload: unknown\)/);Your Everything else in |
|
Still the same single assertion on
// before
assert.match(viewHostSource, /broadcast\(event: string, payload: unknown\)/);
// after — accepts the optional filter your view host now takes
assert.match(viewHostSource, /broadcast\(event: string, payload: unknown(?:, filter\?:[^)]*)?\)/);If the exact parameter name differs, Nothing else fails: |
vastsa
left a comment
There was a problem hiding this comment.
Both reported failures (#1753 and #1754) are real, and the implementation is aimed at the right causes. I can’t merge this head yet:
- The JS CI lane still fails at
apps/desktop/test/plugin-work-panel-views.test.mjs:285: its source assertion expects the old two-argumentbroadcastsignature and rejects the new optional filter. Update that assertion to accept the optional argument while preserving the event/payload and channel assertions. - The new behavioral test exercises
PluginRuntime.broadcastEventdirectly, but not the production path that derives the filter fromsessionProjectsincreatePluginServicesand passes it to both process and docked-view delivery. Please add a regression test for asession:activatedevent on an out-of-scope session, proving that an in-scope plugin receives it and an out-of-scope plugin/view does not. The boot-id behavior currently has source-shape assertions only; add an observable test that accepts a new renderer boot and still rejects an older sequence within that boot. - The PR still targets
589e580; currentmainis87a48b9. Refresh it, then run the relevant E2E for E2E-267 (or add the missing automated scenario). The current E2E report says NOT RUN, which leaves the renderer-to-main session-follow path unverified.
The direct filter and boot-id approach look sound, but the current tests do not yet prove the production wiring or the affected user path, and CI is red. Thanks for addressing the earlier contract assertions and making the scope dependency required.
|
I applied my suggested patch to your head locally and ran the two affected suites — so this is now a verified fix, not a suggestion: Result on That is the whole remaining fix; the other five checks are already green. For reference, the view host signature it needs to match is (One note if you run these locally: the suites read source files relative to the repo root, so run them from |
… view contract Review follow-ups on vastsa#1756: the session-push gate moves into the dependency-free plugin-session-push-gate.ts so the reload acceptance and within-boot rejection are asserted directly (new boot resets the mark and low sequences flow; older sequences within a boot stay ignored); the scope-filter derivation moves into plugin-session-scope.ts and gains a unit test over the announced session's project (in-scope passes, out-of-scope fails, null project passes through null, other session events fan out unconditionally); and the work-panel-views contract accepts the optional filter argument on the view-host broadcast signature.
|
All three points addressed in c419e99 (rebased on
|
Fixes #1753 Fixes #1754
Fixes #1753
Fixes #1754
Summary
bootId(crypto.randomUUID()inuseAppShellRuntime), and the main-side gate resets its high-water mark whenever the boot id changes. Within one boot, the out-of-order guard behaves exactly as before.broadcastSessionEventbuilds a per-recipient scope filter for selection events (SESSION_SCOPED_EVENTS) —pluginActiveInProject(pluginId, sessionProjects.get(sessionId)), falling back to the existing workspace-relative check for sessions without a project — and passes it toplugins.broadcastEventandpluginViews.broadcast(both gained an optional filter parameter). Project-scoped plugins and views no longer receive other projects' session identity.Rationale
As reported: the high-water mark sat over a counter that a renderer reload (the automatic
render-process-gonerecovery) restarts from one, permanently freezingpi.session.getActiveon the pre-reload session (#1753); and the push side skipped the activation-scope check the pull side (session.getActive,ui.revealView) enforces, leaking other projects' session ids to project-scoped plugins (#1754).Affected specs / ADRs / E2E scenarios
docs/spec/07-plugins/03-*§session describes the snapshot/selection surface; the scope rule now covers pushes as well — spec wording, if wanted, is a one-line follow-up for the maintainers (happy to add).plugin-session-*: NOT RUN — the new coverage is a real-process behavioral test plus source contracts (below).Validation (on latest
origin/main)plugin-session-follow.test.mjsover the realPluginRuntimewith two forked plugin processes and message spies: with the scope filter,session:activatedreaches exactly the in-scope plugin process — out-of-scope receives nothing. Full file: 10 pass / 0 fail.tsc --noEmitinapps/desktop: pass.Compatibility / remaining risk
bootId(none exist — renderer and main ship together) keep the old strict behavior: the reset branch requires a boot id.session:activatedpushes — that is the point; plugins that legitimately followed multiple projects must declare the corresponding scope.