experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(tile-broadcaster): register v2 window-entries opened via api.window.open

Split responsibility bug in pubsub delivery. A v2 feature tile has
two registration paths depending on how the tile entry is launched:

- launchTile() — eager / lazy-loaded bg entries — registers the
BrowserWindow with tileWindows via the tile registry.
- registerTrustedBuiltinWindow() — ad-hoc special cases (diagnostic,
settings, hud-overlay, cmd-ui, about:blank, datastore-viewer,
page canvas) — explicit register in ipc.ts window-open.

Ordinary v2 window-type entries opened via api.window.open(peek://
tile/home.html) fell into the gap. getTileWebPreferencesForUrl
attached the tile preload + minted a capability token, but the
resulting BrowserWindow was NOT added to tileWindows. The broadcaster
in main.ts iterates getAllTileWindows() plus webview guests, so the
unregistered window never received inbound pubsub messages even
though its own publishes worked fine.

Consequence: any feature with a bg entry + window entry using pubsub
round-trips silently lost the home-receives-bg-response leg. The
pattern affects several real features (entities, groups, lex,
tag-actions per Explore agent audit) and is the reason websearch
tests failed before the single-tile merge landed.

Fix (narrow, 27 lines across 3 files):

- tile-launcher.ts: getTileWebPreferencesForUrl now also returns
the minted token so callers can register. registerTrustedBuiltinWindow
on-close cleanup narrowed to unsubscribeAll(exact-source) rather
than unsubscribeAllByPrefix(tile-prefix) — closing one window-entry
must not wipe a sibling bg-entry's subscriptions.
- ipc.ts: after new BrowserWindow(...) for ordinary v2 tile URLs,
call registerTrustedBuiltinWindow so getAllTileWindows includes it.
- tests/desktop/pubsub-repro.spec.ts: promoted from test.fail to
normal test — it now passes.

Tests: Pubsub Repro 1/1 (was expected-fail). HUD 10/10, Websearch
10/10, no regressions.

Long-term: three registration paths is one too many. Consolidating
so every tile-preload-getting BrowserWindow always lands in
tileWindows would close this gap by construction.

+27 -9
+12
backend/electron/ipc.ts
··· 1376 1376 if (specialCaseToken && tileWebPrefs?.tileId && tileWebPrefs?.entryId) { 1377 1377 registerTrustedBuiltinWindow(tileWebPrefs.tileId, tileWebPrefs.entryId, win, specialCaseToken); 1378 1378 DEBUG && console.log(`[window-open] registered ${tileWebPrefs.tileId}:${tileWebPrefs.entryId} with tile broadcaster`); 1379 + } else if (tileWebPrefs?.tileId && tileWebPrefs?.entryId && tileWebPrefs?.token) { 1380 + // Feature tile window-entry opened via api.window.open(): register the 1381 + // BrowserWindow with the tile broadcaster so GLOBAL-scope pubsub messages 1382 + // reach it. Without this, the window gets tile-preload + a valid token 1383 + // (so publish works) but inbound pubsub messages are silently dropped 1384 + // because getAllTileWindows() only knows about windows launched directly 1385 + // by launchTile() (i.e. background entries). Symptom: feature's background 1386 + // tile subscribes to a topic the window publishes (or vice versa), and 1387 + // the subscribe-side handler never fires. See pubsub-repro.spec.ts for 1388 + // a minimal repro. 1389 + registerTrustedBuiltinWindow(tileWebPrefs.tileId, tileWebPrefs.entryId, win, tileWebPrefs.token); 1390 + DEBUG && console.log(`[window-open] registered v2 tile window ${tileWebPrefs.tileId}:${tileWebPrefs.entryId} with tile broadcaster`); 1379 1391 } 1380 1392 1381 1393 // Track window position for display-watcher home display restoration
+8 -3
backend/electron/tile-launcher.ts
··· 44 44 revokeTokensForTile, 45 45 clearAllTokens, 46 46 } from './tile-tokens.js'; 47 - import { scopes, publish, getSystemAddress, unsubscribeAllByPrefix } from './pubsub.js'; 47 + import { scopes, publish, getSystemAddress, unsubscribeAll, unsubscribeAllByPrefix } from './pubsub.js'; 48 48 import { DEBUG, getTilePreloadPath } from './config.js'; 49 49 import { loadSchemaDefaults } from './tile-settings-defaults.js'; 50 50 import { getExtensionPath } from './protocol.js'; ··· 397 397 readyTiles.delete(key); 398 398 revokeToken(token); 399 399 try { 400 - unsubscribeAllByPrefix(`peek://${tileId}/`); 400 + // Scope the unsubscribe to THIS entry's source address only — otherwise a 401 + // window-entry closing would wipe out a sibling background-entry's pubsub 402 + // subscriptions, since the tile registry keys subscriptions by full source 403 + // address `peek://{tileId}/{entryId}`. 404 + unsubscribeAll(`peek://${tileId}/${entryId}`); 401 405 } catch (err) { 402 406 console.error(`[tile-launcher:${tileId}:${entryId}] Failed to unsubscribe on close:`, err); 403 407 } ··· 428 432 url: string, 429 433 tilePreloadPath: string, 430 434 lookupLazyManifest: (tileId: string) => TileManifestV2 | null, 431 - ): { preload: string; additionalArguments: string[]; tileId: string; entryId: string } | null { 435 + ): { preload: string; additionalArguments: string[]; tileId: string; entryId: string; token?: string } | null { 432 436 let host: string; 433 437 let pathPart: string; 434 438 try { ··· 472 476 ], 473 477 tileId: manifest.id, 474 478 entryId: entry.id, 479 + token, 475 480 }; 476 481 } 477 482
+7 -6
tests/desktop/pubsub-repro.spec.ts
··· 28 28 29 29 test.describe('Pubsub Repro @desktop', () => { 30 30 31 - // EXPECTED FAILURE: the home-publish → bg-subscribe leg of a cross-tile 32 - // GLOBAL pubsub round-trip is currently broken at the broadcaster level. 33 - // Proven architectural (not websearch-specific) by this minimal repro. 34 - // When the fix lands, flip `test.fail()` to `test()` — Playwright will 35 - // report a surprise pass if the test starts working before we expect. 36 - test.fail('bg responds to home via GLOBAL pubsub round-trip', async () => { 31 + // Cross-tile GLOBAL pubsub round-trip: home-entry publishes, bg-entry 32 + // subscribes + responds, home-entry receives the response. Previously 33 + // broken: v2 tile window-entries opened via api.window.open() weren't 34 + // registered with the tile broadcaster, so inbound pubsub messages were 35 + // silently dropped. Fixed by registering such windows with 36 + // registerTrustedBuiltinWindow() in the window-open IPC handler. 37 + test('bg responds to home via GLOBAL pubsub round-trip', async () => { 37 38 // Force-load the pubsub-repro extension by invoking its command. 38 39 // Mirrors the websearch pattern at websearch.spec.ts:91-126. 39 40 const extensionLoaded = await sharedBgWindow.evaluate(async () => {