···15671567Each agent follows the 25-min ceiling; if any package overruns,
15681568commit-what-you-have + report blocker. Splits noted inline (3.6g, 3.8b,
156915693.10, 3.11b) are pre-authorised.
15701570+15711571+---
15721572+15731573+### phase3.8a — context audit (2026-04-18)
15741574+15751575+#### Channel inventory in `app/context/*.js`
15761576+15771577+| File | IPC channel(s) invoked | Where called |
15781578+|------|------------------------|--------------|
15791579+| `app/context/index.js` | `context-set` (in `set()`, line 80) | indirectly via `history.js` import; `set()` is the only IPC path |
15801580+| `app/context/history.js` | `context-set` (in `save()`, line 56) | called by `index.js::set()` when `persist=true` |
15811581+| `app/context/history.js` | `context-history` (in `query()`, line 77) | called by `index.js::getHistory()` |
15821582+| `app/context/history.js` | `context-snapshot` (in `getSnapshot()`, line 110) | called by `index.js::getSnapshot()` |
15831583+| `app/context/store.js` | _none_ — pure in-memory; no IPC | internal only |
15841584+15851585+Channels **not** present in `app/context/*.js`: `context-get`,
15861586+`context-windows-with-value`, `context-windows-in-space`. Those are
15871587+exposed by `preload.js::api.context` but are not called directly inside
15881588+these modules.
15891589+15901590+#### Who actually calls `app/context/*.js`?
15911591+15921592+**Nobody.** A full-codebase grep finds zero `import … from
15931593+'peek://app/context/…'`, zero `import … from './context/…'` or similar
15941594+in any `.js`, `.ts`, `.cts`, or `.html` file outside the three
15951595+`app/context/` files themselves. The JSDoc comment at the top of
15961596+`index.js` advertises a `peek://app/context/index.js` URL but no
15971597+renderer has ever wired it up. `context.init(renderer)` is never called
15981598+from anywhere — `ipcRenderer` stays `null` in every live session, so
15991599+the three IPC invocations are dead code today.
16001600+16011601+#### Who calls `api.context.*` (tile-preload surface)?
16021602+16031603+All live context consumers go through `api.context` on `window.app`,
16041604+which tile-preload provides. Confirmed callers:
16051605+16061606+| File | Methods used |
16071607+|------|--------------|
16081608+| `app/cmd/panel.js` | `get`, `setMode`, `watchMode` |
16091609+| `app/page/page.js` | `get`, `setMode`, `watchMode` |
16101610+| `app/hud/widgets/mode.js` | `get`, `watchMode` |
16111611+| `features/spaces/background.js` | `get`, `setMode`, `getWindowsInSpace` |
16121612+| `features/groups/background.js` | `get`, `setMode`, `getWindowsInSpace` |
16131613+| `features/groups/home.js` | `setMode` |
16141614+| `features/editor/home.js` | `setMode` |
16151615+| `features/entities/background.js` | `set` |
16161616+16171617+All renderers listed above load `tile-preload.cjs` (cmd, hud, page via
16181618+their glue files; spaces, groups, editor, entities as v2 tiles). Every
16191619+feature tile has a `context` capability declared in its manifest, so
16201620+they already use `contextStrict` (`tile:context:*` channels). The core
16211621+builtins (cmd, hud, page) are `trustedBuiltin` grants with no `context`
16221622+capability key, so they fall through to `contextCompat` (`context-get /
16231623+context-set / …`) — those six compat branches in tile-preload are the
16241624+ones that still depend on `preload.js`.
16251625+16261626+#### Six `contextCompat` fallback branches in tile-preload
16271627+16281628+```
16291629+contextCompat.get → ipcRenderer.invoke('context-get', …)
16301630+contextCompat.set → ipcRenderer.invoke('context-set', …)
16311631+contextCompat.history → ipcRenderer.invoke('context-history', …)
16321632+contextCompat.snapshot → ipcRenderer.invoke('context-snapshot', …)
16331633+contextCompat.windowsWithValue → ipcRenderer.invoke('context-windows-with-value', …)
16341634+contextCompat.windowsInSpace → ipcRenderer.invoke('context-windows-in-space', …)
16351635+```
16361636+16371637+The compat path is taken when `grantedCapabilities.context` is falsy.
16381638+For cmd/hud/page, the token grant carries `trustedBuiltin: true` but no
16391639+`context` key, so `hasContextCapability()` returns `false` and every
16401640+`api.context.*` call in those three renderers hits the compat channels.
16411641+16421642+#### Migration path recommendation
16431643+16441644+**Option (ii) — add `context` capability to the trustedBuiltin grant
16451645+for cmd, hud, and page** is the correct migration path.
16461646+16471647+`app/context/*.js` is dead code and does not need to be ported or moved;
16481648+it can be deleted as part of 3.8b. The real migration work is entirely
16491649+in the backend: `cmd-glue.ts`, `hud-glue.ts`, and `page-glue.ts` (or
16501650+wherever the trustedBuiltin capability set is assembled) need `context:
16511651+true` added to the grant so `hasContextCapability()` returns `true` for
16521652+those windows. Once that is done, the six `contextCompat` fallback
16531653+branches in `tile-preload.cts` are unreachable and can be deleted along
16541654+with the matching `ipc.ts` legacy `context-get / context-set /
16551655+context-history / context-snapshot / context-windows-*` handlers. No
16561656+consumer renderer needs code changes — they already call `api.context.*`
16571657+and tile-preload's `api.context` will silently switch to strict channels
16581658+once the capability is present. Option (i) (a new tile) introduces
16591659+unnecessary indirection; option (iii) (inline per-renderer) is the same
16601660+work as option (ii) but harder to validate. Option (ii) is a two-line
16611661+change to the grant payload and unblocks both the compat-branch delete
16621662+and the eventual `preload.js` delete.
16631663+16641664+**Suggested 3.8b scope update:** delete `app/context/*.js` (three files,
16651665+currently unreferenced dead code), add `context: true` to the cmd/hud/
16661666+page trustedBuiltin capability grant in the relevant glue files, delete
16671667+the six `contextCompat` compat branches from `tile-preload.cts`, and
16681668+delete the five legacy `context-*` handlers from `ipc.ts`.