experiments in a post-browser web
10
fork

Configure Feed

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

fix(hud): init + getExtKey shape + widget initialize() calls

Cluster 1 of 25-test failure analysis — HUD widgets.

Three classes of bug, all the same underlying pattern (missing
api.initialize() before capability-gated api.* calls):

1. app/hud/hud.js — reads sheet config from the 'hud' namespace via
api.settings.getExtKey but never called api.initialize(). The
getExtKey cap check reads grantedCapabilities which stays empty
until initialize() resolves, so it throws 'requires readForeign'
and init halts before renderWidgets() → zero webviews rendered.

2. app/hud/widgets/{mode,izui,stats,window}.js — four widget tile
entrypoints, same missing initialize() call. Symptoms: subscribes
silently no-op, api.context.get / api.izui.getState throw, widget
never populates with live data.

3. backend/electron/tile-preload.cts getExtKey wrapper — expected
{ value, error } envelope but the tile:settings:get-foreign
handler returns { success, data, error }. Shape mismatch meant
result.data was always undefined for successful reads. Normalised
the wrapper to accept both shapes.

HUD test results: 3/10 → 7/10 passing. Remaining 3 failures
(mode widget doesn't react to context.setMode) appear to be a
separate api.context.watchMode path bug, not an init issue.

+39 -5
+12 -2
app/hud/hud.js
··· 16 16 let sheetConfig = null; 17 17 18 18 /** 19 - * Load HUD sheet config from feature_settings 19 + * Load HUD sheet config from feature_settings. 20 + * Config is written by background.js (tileId='hud') so we must read from 21 + * the 'hud' namespace rather than our own 'hud-overlay' namespace. 20 22 */ 21 23 const loadSheetConfig = async () => { 22 - const result = await api.settings.get(HUD_SHEET_KEY); 24 + const result = await api.settings.getExtKey('hud', HUD_SHEET_KEY); 23 25 if (result.success && result.data) { 24 26 return result.data; 25 27 } ··· 114 116 */ 115 117 const init = async () => { 116 118 console.log('[hud] Initializing widget sheet display'); 119 + 120 + // Initialize the tile-preload surface BEFORE any api.* call. Without this, 121 + // api.settings.getExtKey throws ('requires readForeign capability') because 122 + // grantedCapabilities is empty until initialize() resolves. Same pattern as 123 + // page.js — see feedback_top_level_await_silent_halt.md. 124 + if (api.initialize) { 125 + await api.initialize(); 126 + } 117 127 118 128 sheetConfig = await loadSheetConfig(); 119 129
+4
app/hud/widgets/izui.js
··· 34 34 }; 35 35 36 36 const init = async () => { 37 + if (api.initialize) { 38 + await api.initialize(); 39 + } 40 + 37 41 await refreshIzuiState(); 38 42 39 43 // Subscribe to IZUI state changes
+7
app/hud/widgets/mode.js
··· 75 75 }; 76 76 77 77 const init = async () => { 78 + // Initialize tile-preload surface so api.* calls have a valid token + 79 + // granted capabilities. Without this, api.subscribe silently no-ops and 80 + // api.context.get may throw. See feedback_top_level_await_silent_halt.md. 81 + if (api.initialize) { 82 + await api.initialize(); 83 + } 84 + 78 85 await refreshMode(); 79 86 80 87 // Watch for mode changes
+4
app/hud/widgets/stats.js
··· 67 67 }; 68 68 69 69 const init = async () => { 70 + if (api.initialize) { 71 + await api.initialize(); 72 + } 73 + 70 74 await refreshStats(); 71 75 72 76 // Refresh on window events
+4
app/hud/widgets/window.js
··· 50 50 }; 51 51 52 52 const init = async () => { 53 + if (api.initialize) { 54 + await api.initialize(); 55 + } 56 + 53 57 await refreshWindowInfo(); 54 58 55 59 // Refresh on window events
+8 -3
backend/electron/tile-preload.cts
··· 1296 1296 if (!hasSettingsForeignCapability()) { 1297 1297 throw new Error('[tile-preload] api.settings.getExtKey requires settings.readForeign capability in manifest (Phase 4: v1-compat fallback removed)'); 1298 1298 } 1299 + // Handler returns { success, data } (native shape) — normalise into the 1300 + // legacy { success, data, value } envelope so callers using either 1301 + // field work. Returns `success: true, data: null` when the key simply 1302 + // hasn't been written yet (not an error). 1299 1303 const r = await ipcRenderer.invoke('tile:settings:get-foreign', { 1300 1304 token: tileToken, 1301 1305 foreignExtId: extId, 1302 1306 key, 1303 - }) as { value?: unknown; error?: string } | null; 1304 - if (!r || r.error) { 1307 + }) as { success?: boolean; data?: unknown; value?: unknown; error?: string } | null; 1308 + if (!r || r.success === false) { 1305 1309 return { success: false, data: undefined, value: undefined, error: r?.error ?? 'unknown error' }; 1306 1310 } 1307 - return { success: true, data: r.value, value: r.value, error: undefined }; 1311 + const value = r.data !== undefined ? r.data : r.value; 1312 + return { success: true, data: value, value, error: undefined }; 1308 1313 }, 1309 1314 }; 1310 1315