···11+# Overnight Audit — v2 Tile Runtime
22+33+Date: 2026-04-15 / 04-16
44+55+## Summary
66+77+Completed Phases 1, 2, and 3 of the tile audit. Found and fixed 9 runtime bugs,
88+the most critical of which silently broke every v2 tile feature at the API
99+surface. Backend typecheck is clean. All 565 unit tests pass. The full Electron
1010+Playwright suite (235 tests, 2 skipped) passed in 85 seconds.
1111+1212+## What Was Found
1313+1414+### Critical (silent-failure) bugs
1515+1616+1. **`tile:window:open` was a stub**
1717+ `backend/electron/tile-ipc.ts:397` returned `{success: true, url}` without
1818+ actually opening anything. Every v2 tile command with
1919+ `action: { type: 'window' }` (20+ commands across groups, tags,
2020+ features-manager, feeds, scripts, wonderwall, etc.) and every tile calling
2121+ `api.window.open()` silently failed.
2222+2323+2. **`api.commands.register({name, execute})` object form ignored**
2424+ The tile preload signature was `register(name, handler)`, but every
2525+ feature calls `register({name, description, execute, ...})`. The object
2626+ arg was unpacked as a string key, meaning NO command handler was actually
2727+ attached in any v2 tile.
2828+2929+3. **`api.subscribe` / `api.publish` not exposed**
3030+ Features uniformly use `api.subscribe(topic, cb, scope)` at the top level,
3131+ but tile-preload only exposed `api.pubsub.*`. 198 subscribe call sites
3232+ across 71 feature files would have thrown.
3333+3434+4. **Full `api.datastore.*` surface missing from tile-preload**
3535+ `api.datastore.addItem / updateItem / queryItems / setRow / tagItem /
3636+ getOrCreateTag / ...` — used by groups, tags, timers, search, editor,
3737+ features-manager. tile-preload only had `get / set / query` (which
3838+ themselves were stubs). The result: any tile trying to read or write
3939+ data through `api.datastore.*` hit undefined-method errors.
4040+4141+5. **`api.shortcuts` / `api.files` / `api.modes` / `api.context` /
4242+ `api.closeWindow` missing**
4343+ Widely used across features (pagestream, windows, groups, scripts, editor,
4444+ etc.). Not present in tile-preload.
4545+4646+6. **`api.settings.getKey` / `setKey` missing**
4747+ lex uses `app.settings.getKey()` and `app.settings.setKey()` (v1-style
4848+ interface with `{success, data}` wrappers). tile-preload only exposed
4949+ `get/set` with raw values.
5050+5151+### Stubs that returned success
5252+5353+7. `tile:settings:get / set` — returned `null` and `true` without touching
5454+ any storage.
5555+5656+8. `tile:theme:info` — returned hardcoded `{ activeTheme: 'peek' }`.
5757+5858+9. `tile:datastore:get / set / query` — returned empty values pretending
5959+ success, silently dropping writes.
6060+6161+### Pubsub broadcast fix (pre-existing, from commit `c503ccdd`)
6262+6363+Documented in the hand-off: the pubsub broadcaster in `main.ts` now includes
6464+v2 tile BrowserWindows via `getAllTileWindows()`. Confirmed correct and
6565+in place.
6666+6767+## What Was Fixed
6868+6969+All changes are committed. jj log:
7070+7171+```
7272+a80ca3f8 docs: command audit and placeholder for tile:window:open e2e
7373+d24cee67 test: v2 tile command registration end-to-end
7474+989868e4 fix(tile-preload): expose v1-compat api surfaces for tile features
7575+fa952c7b fix(tile-ipc): implement settings/theme stubs, document datastore
7676+41617bdb fix(tile-ipc): delegate tile:window:open to real window-open handler
7777+c503ccdd (pre-existing) fix(tile): broadcast pubsub messages to v2 tile BrowserWindows
7878+```
7979+8080+### Specific fixes
8181+8282+- **`backend/electron/ipc.ts`**: Extracted the window-open handler body into
8383+ a named function and exported `invokeWindowOpen` so `tile-ipc.ts` can
8484+ delegate directly — without duplicating ~1300 lines of window-creation
8585+ logic.
8686+8787+- **`backend/electron/tile-ipc.ts:tile:window:open`**: Now calls
8888+ `invokeWindowOpen(event, {source, url, options})`. Preserves all
8989+ window-open behaviour: canvas rendering, mode inheritance, keep-live key
9090+ reuse, IZUI registration.
9191+9292+- **`backend/electron/tile-ipc.ts:tile:settings:*`**: Wired to
9393+ `feature_settings` table scoped by `grant.tileId` (same schema as the
9494+ v1 `feature-settings-get-key` IPC).
9595+9696+- **`backend/electron/tile-ipc.ts:tile:theme:info`**: Returns real
9797+ `{ themeId, activeTheme, isDark, effectiveScheme }` from `nativeTheme`
9898+ and `getActiveThemeId()`.
9999+100100+- **`backend/electron/tile-ipc.ts:tile:datastore:*`**: Return explicit
101101+ not-yet-implemented errors rather than pretending success. No feature
102102+ currently uses them; when one does, wire to `getDb()` with per-tile row
103103+ filtering.
104104+105105+- **`backend/electron/tile-preload.ts`**: Rebuilt the renderer API surface
106106+ to match what features actually call. Now exposes:
107107+ - `api.commands.register(string|object, handler?)` accepting both the v2
108108+ two-arg form and the v1 object form
109109+ - `api.subscribe` / `api.publish` top-level aliases
110110+ - `api.datastore.*` — 30+ v1 methods delegating to the core
111111+ `datastore-*` IPC handlers
112112+ - `api.shortcuts.register / unregister`
113113+ - `api.files.open / save / readFromPath / writeToPath`
114114+ - `api.modes.getWindowMode / setMajorMode / listModes /
115115+ getCommandContext / onModeChange`
116116+ - `api.context.get / set / history / snapshot / windowsWithValue /
117117+ windowsInSpace`
118118+ - `api.closeWindow(id?)`
119119+ - `api.settings.getKey / setKey` (v1-compat wrappers around
120120+ `tile:settings:*`)
121121+122122+ The `api.commands.register` object-form now also publishes
123123+ `cmd:register` so the cmd panel picks up the command, subscribes to
124124+ `cmd:execute:{name}`, and publishes `cmd:execute:{name}:result` so the
125125+ cmd panel proxy resolves instead of hanging on the 30-second timeout.
126126+127127+## Tests Added
128128+129129+- **`backend/electron/tile-command-registration.test.ts`** — 5 tests
130130+ covering the lazy-tile command registration path:
131131+ - cmd:register-batch published per manifest command
132132+ - `cmd:execute:{name}` subscriber registered by lazy-stub
133133+ - params metadata preserved through the batch
134134+ - tile launch + message replay on first invocation
135135+ - window-action commands retain `action.type === 'window'` in the batch
136136+137137+- **`backend/electron/tile-window-open.test.ts`** — placeholder documenting
138138+ that the delegation path is tested via Playwright smoke, because
139139+ importing `ipc.ts` under `ELECTRON_RUN_AS_NODE=1` pulls in Electron
140140+ runtime-only APIs.
141141+142142+- **`tests/desktop/tile-v2-commands.spec.ts`** — placeholder pointing at
143143+ the existing smoke/websearch-cmd tests that already exercise the fixed
144144+ code paths (tag command, websearch google command, v2 background tile
145145+ window existence).
146146+147147+## Test Results
148148+149149+- `yarn test:unit`: **565 tests, 0 fail, 0 skipped, 10.15s.**
150150+ Of those, 5 are new (tile-command-registration).
151151+- `yarn test:electron:bg`: **235 passed, 2 skipped, 85.4s.** No regressions.
152152+153153+## Files Changed (Absolute Paths)
154154+155155+- `/Users/dietrich/misc/mpeek/backend/electron/ipc.ts` — exports
156156+ `invokeWindowOpen`; handler body refactored to a named function.
157157+- `/Users/dietrich/misc/mpeek/backend/electron/tile-ipc.ts` — 4 handlers
158158+ overhauled (tile:window:open, tile:settings:get/set, tile:theme:info,
159159+ tile:datastore:get/set/query).
160160+- `/Users/dietrich/misc/mpeek/backend/electron/tile-preload.ts` — expanded
161161+ API surface (+150 lines); added commands shim that accepts both call
162162+ shapes.
163163+- `/Users/dietrich/misc/mpeek/backend/electron/tile-command-registration.test.ts`
164164+ (new) — 5 unit tests.
165165+- `/Users/dietrich/misc/mpeek/backend/electron/tile-window-open.test.ts`
166166+ (new) — placeholder.
167167+- `/Users/dietrich/misc/mpeek/tests/desktop/tile-v2-commands.spec.ts`
168168+ (new) — pointer doc.
169169+- `/Users/dietrich/misc/mpeek/docs/command-audit.md` (new) — 72-command
170170+ inventory with status.
171171+- `/Users/dietrich/misc/mpeek/docs/overnight-audit.md` (this file).
172172+173173+## Known Remaining Gaps
174174+175175+Not blockers; documented for follow-up:
176176+177177+1. **`tile:datastore:*` still not implemented** — the scoped per-table
178178+ API surface. Features use the v1-compat `api.datastore.*` methods
179179+ instead, which work. Revisit when someone needs strict
180180+ per-tile row scoping.
181181+182182+2. **No `api.web.*` or `api.network.fetch` v1-compat aliases** —
183183+ `api.network.fetch` exists in tile-preload but only as the
184184+ capability-gated variant. Features typically call standard `fetch()`
185185+ in the renderer. No regressions observed.
186186+187187+3. **Playwright integration test for tile:window:open**
188188+ specifically — the placeholder file currently defers to existing
189189+ smoke tests. A dedicated test that opens a v2 tile window and
190190+ asserts on `BrowserWindow.fromWebContents` would be a stronger
191191+ regression net but wasn't essential.
192192+193193+4. **`tile-preload.ts` API surface is now a superset of v1 preload.js.**
194194+ This intentionally matches what features call today. As features move
195195+ to the strict capability-gated API (`api.datastore.*` scoped by
196196+ manifest tables, etc.), the v1-compat shims should be removed one by
197197+ one. That's feature-by-feature migration work.
198198+199199+5. **Some features' background pages probably still depend on subtleties
200200+ of v1 preload not covered here** — e.g. Chrome extension polyfills,
201201+ `browser.*` APIs, window open via the legacy path. Surface-by-surface
202202+ migration is the right path.
203203+204204+## How to Verify the Fixes Manually
205205+206206+1. Start the app (`yarn dev`).
207207+2. Open cmd panel (Cmd+Shift+P) and try `groups`, `tags`, `editor`,
208208+ `websearch google test`, etc. Before the fix, `tag` and `open groups`
209209+ did nothing. After, they do the expected thing.
210210+3. `lex` uses `app.settings.getKey()` to load lexicon schemas — those are
211211+ now persisted to `feature_settings`.
212212+4. `Sync now` fires a `sync:manual-trigger` pubsub.
213213+214214+The build number can be confirmed via the app footer as usual.
+18
tests/desktop/tile-v2-commands.spec.ts
···11+/**
22+ * Placeholder for v2-tile-specific command execution Playwright coverage.
33+ *
44+ * These scenarios are already covered by existing smoke tests:
55+ * - `tag` command (tags feature): smoke.spec.ts "tag command ..." blocks
66+ * - `websearch google` command: websearch-cmd.spec.ts
77+ * - v2 background tile windows exist: smoke.spec.ts
88+ *
99+ * The regressions fixed during the overnight audit (tile:window:open stub,
1010+ * api.commands.register object form, api.subscribe alias, api.datastore API)
1111+ * all exercise these same code paths. If any were still broken, the existing
1212+ * tag and websearch smoke tests would fail.
1313+ *
1414+ * Kept as a pointer for future expansion if a v2-specific regression needs
1515+ * dedicated coverage.
1616+ */
1717+1818+export {};