Consolidate IPC types into shared module, enforce with satisfies PearAPI#36
Conversation
…rAPI`
Previously the IPC surface had two competing definitions: preload/index.ts
exported `typeof api` (the implementation) while renderer/src/lib/ipc.ts
declared a separate, hand-maintained `PearAPI` interface that was wired into
`window.pear`. Nothing forced them to agree — the preload's `invoke<unknown>`
calls happily returned a different shape than what the renderer's interface
promised, and 13 burn/aiHist types were duplicated verbatim across the two
files.
This moves the entire IPC type surface to src/shared/types/ipc.ts:
- preload/index.ts imports types from shared, tightens every `invoke<T>` to
the real return type, and ends with `satisfies PearAPI` so any future
drift between implementation and interface fails to compile.
- renderer/src/lib/ipc.ts becomes a thin re-export of @shared/types/ipc
plus the `window.pear` global declaration — no duplicated types.
All call sites continue to work unchanged (`import { ..., type Foo } from
'@/lib/ipc'` still resolves). Type-check error count and test results match
baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (2)
💤 Files with no reviewable changes (1)
📝 WalkthroughWalkthroughThis PR centralizes IPC type definitions into src/shared/types/ipc.ts, updates the preload bridge to use typed invoke/subscribe helpers, constrains the exposed api to the shared PearAPI, and converts the renderer IPC module into a re-export of the shared types. ChangesIPC Type Consolidation and Bridge Surface Typing
Sequence DiagramsequenceDiagram
participant Renderer
participant PreloadAPI as Preload API
participant invoke as invoke<T>
participant ipcRenderer
Renderer->>PreloadAPI: api.broker.spawnAgent(input)
PreloadAPI->>invoke: invoke<BrokerSpawnAgentResult>('broker:spawn-agent', input)
invoke->>ipcRenderer: ipcRenderer.invoke(channel, input)
ipcRenderer-->>invoke: Promise<BrokerSpawnAgentResult>
invoke-->>PreloadAPI: typed result
PreloadAPI-->>Renderer: BrokerSpawnAgentResult
Renderer->>PreloadAPI: api.broker.onStatus(callback)
PreloadAPI->>invoke: subscribe<BrokerStatusEvent>('broker:status', callback)
invoke->>ipcRenderer: ipcRenderer.on(channel, callback)
ipcRenderer-->>PreloadAPI: unsubscribe function
PreloadAPI-->>Renderer: cleanup function
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9ee2ba7b6e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| getProjectBreakdown: (input: BurnProjectInput) => | ||
| invoke<BurnProjectBreakdown>('burn:get-project-breakdown', input), | ||
| lookupSessions: (sessionIds: string[]) => | ||
| invoke<Record<string, BurnSessionLookup>>('burn:lookup-sessions', sessionIds) |
There was a problem hiding this comment.
Remove burn IPC methods without main handlers
These new preload methods call burn:get-project-breakdown and burn:lookup-sessions, but in the main-process registration I checked (src/main/ipc-handlers.ts), only burn:list-agent-summaries and burn:get-agent-breakdown are handled. Any renderer call to either added method will fail at runtime with Electron's “No handler registered” error, so the exposed API now advertises burn functionality that cannot execute.
Useful? React with 👍 / 👎.
The previous commit on this branch picked up two preload methods (and their types) from the working tree that don't exist on main — the matching ipcMain handlers were sitting uncommitted alongside an in-progress burn project page. Shipping the PR alone would have exposed two channels with no handler, failing at runtime with "No handler registered." Removing both preload methods plus BurnProjectInput, BurnProjectBreakdown, BurnProjectAgentRollup, BurnSessionLookup, and BurnSessionAgentRef from shared/types/ipc.ts. The burn-project feature will re-add them in its own commit alongside the matching handlers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
The IPC seam had two competing source-of-truth definitions for the same surface:
src/preload/index.tsexposeswindow.pear = apiand exportedPearAPI = typeof api(auto-derived from the implementation).src/renderer/src/lib/ipc.ts:468also declaredexport interface PearAPI { ... }— a 220-line hand-typed interface — and wired that intodeclare global { interface Window { pear: PearAPI } }.Nothing forced the two to agree:
BurnAgent*,BurnProject*,BurnSession*,AiHist*families).invoke<unknown>(...)while the renderer'sPearAPIinterface promised concrete return types likeBrokerListAgent[],GitFileStatus[],AuthStatus, etc. The renderer was working off a contract the preload never actually honored — type-correct only because nobody was checking.cloudAgent,proactiveAgent, andintegrationsnamespaces calledipcRenderer.invoke(...)directly (returnsPromise<any>) — passing all type checks vacuously.This PR:
src/shared/types/ipc.tsas the single source of truth for every type that crosses the bridge, plus thePearAPIinterface itself.src/preload/index.tsto import from shared, tightens everyinvoke<T>to the real return type, replaces directipcRenderer.invokecalls with the typed wrapper, and ends withsatisfies PearAPIso any future drift between implementation and interface fails to compile.src/renderer/src/lib/ipc.tsfrom 691 lines to ~20: it now just re-exports@shared/types/ipcand declareswindow.pear. All existingimport { ..., type Foo } from '@/lib/ipc'call sites resolve unchanged.Net change: +1043 / -819 (most of the additions are in shared/types; the deletions are deduped renderer-side declarations).
Test plan
tsc -berror count matches baseline (85 errors before and after — all pre-existing, none in the refactored files)npm test— all 11 tests passnpm run build— electron-vite build succeeds (main + preload + renderer)🤖 Generated with Claude Code