# Review Pull Requests on an Electron App

Yes. An agent checks out the pull request, runs package, lint, tsc, jest and the Electron smoke test in its sandbox, and posts one review on IPC and preload.

[Run this on Rush](https://cloud.getrush.ai/run/review-pull-requests/electron/), in a Project that has your repository.

## What the agent does

The agent reads the two-package layout before the diff, because in electron-react-boilerplate most review mistakes come from putting a dependency or a file on the wrong side of it. The root `package.json` has `main` set to `./release/app/dist/main/main.js`, and `build.directories.app` points electron-builder at `release/app`, whose own `package.json` is at version 4.6.0 with an empty `dependencies` map that `electron.vite.config.ts` reads to decide which native modules to externalize. `postinstall` runs `npm --prefix .erb/tooling ci`, `install-electron` and `electron-builder install-app-deps`; `.erb/tooling/package.json` holds `typescript`, `ts-jest`, `ts-node` and the TypeScript ESLint plugins apart from the app, and both `jest.config.js` and `eslint.config.js` resolve them through `createRequire` on that package. `devEngines` and `engines` require Node 24 and npm 10 or newer.

The checks the repository runs are in `.github/workflows/test.yml`: on macOS, Windows and Ubuntu with Node 24 it does `npm ci`, then `npm run package`, `npm run lint`, `npm exec tsc -- --noEmit` and `npm test`, and on Linux `xvfb-run --auto-servernum npm run test:smoke`. `package` is `npm run build && electron-builder build --publish never`, with `build` being `electron-vite build` into `release/app/dist/{main,preload,renderer}`. `test` is Jest 30 with `testEnvironment: 'jsdom'`, `identity-obj-proxy` for stylesheets, mocks for `.svg?react` and binary assets, and `setupFiles` pointing at `.erb/scripts/check-build-exists.ts`. The two tests are `src/__tests__/App.test.tsx`, which renders `App` and expects the `electron-react-boilerplate` heading and the docs and donate links, and `src/__tests__/updates.test.ts`, which mocks `electron`, `electron-updater` and `electron-log` under a `node` environment and checks that `startAutoUpdates` does nothing unpackaged, runs once when packaged and logs a rejected `checkForUpdatesAndNotify`. The agent runs that whole sequence in the sandbox, with `xvfb-run` for the smoke test.

Then it reads the diff with the questions the prompt names. IPC boundaries: `src/main/main.ts` registers one handler, `ipcMain.on('ipc-example')`, which replies `IPC test: pong`, and `src/main/preload.ts` exposes `electron.ipcRenderer` through `contextBridge.exposeInMainWorld` with `sendMessage`, `on` and `once` typed against `Channels = 'ipc-example'`; `src/renderer/preload.d.ts` declares `window.electron` from that type. A new channel must be added to `Channels`, handled in `main.ts` and reached only through the bridge, and a `removeListener` must still be returned from `on`. Context isolation and preload exposure: the `BrowserWindow` in `createWindow` sets only `preload` under `webPreferences`, so it relies on Electron's defaults for `contextIsolation` and `nodeIntegration`; a diff that adds `nodeIntegration: true`, turns `contextIsolation` off, or exposes `ipcRenderer` itself instead of a wrapper is blocking. The agent also checks `setWindowOpenHandler`, which denies new windows and hands the URL to `shell.openExternal`, and the `activate` handler, which only creates a window when `mainWindow` is `null` so the updater is not started twice.

The smoke test is the strongest evidence the repository offers, so the agent reads `.erb/scripts/smoke-test.cjs` before quoting it. It starts `npm start` with `--remoteDebuggingPort`, finds the renderer page over the DevTools protocol, waits for the `h1` and a loaded image, asserts there is no `vite-error-overlay`, that exactly two anchors exist, that `window.electron.ipcRenderer.sendMessage('ipc-example', ...)` round-trips to `IPC test: pong`, and that no `Runtime.exceptionThrown` arrived. A pull request that changes the home route, the preload surface or the IPC channel usually shows up here first. One GitHub review is posted per run: the summary quotes `package`, `lint`, `tsc`, `jest` and the smoke test, separates blocking issues from suggestions, and every finding cites file and line. The agent pushes nothing.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Review this pull request on an Electron app built on electron-react-boilerplate. Check it out, run npm ci, then npm run package, npm run lint, npm exec tsc -- --noEmit, npm test and xvfb-run --auto-servernum npm run test:smoke, the same sequence as .github/workflows/test.yml. Read the diff with src/main/main.ts, src/main/preload.ts, src/renderer/preload.d.ts, electron.vite.config.ts and release/app/package.json. Look for IPC channels used in the renderer that are not in the Channels type or not handled in main.ts, anything exposed through contextBridge that hands the renderer a raw ipcRenderer or Node API, webPreferences that enable nodeIntegration or disable contextIsolation, native modules added to the root package.json instead of release/app/package.json, and listeners registered in on() without a matching removal. Post one GitHub review that quotes each command's result including the smoke test, separates blocking issues from suggestions, and cites file and line for every finding. Do not push commits.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app/pull/42"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a review on the pull request. Its summary quotes the result of `npm run package`, `npm run lint`, `tsc --noEmit`, the Jest run with its two suites, and the smoke test's final line or the assertion it failed on. Findings follow with file and line in `src/main`, `src/renderer` or the config files, each marked blocking or suggestion, and a finding about the preload names the property that was exposed and what the renderer could reach through it.

The transcript keeps the install with its `postinstall` steps, every command and its output, and the smoke test's console including anything Electron printed on the way up, so when the review says the renderer threw on load, the exception text is in the record.

Before merging, a reviewer runs `npm run test:smoke` on the branch on their own platform, since the sandbox covers Linux only, opens each cited line, and dismisses suggestions that do not hold. A new `webPreferences` flag or an unguarded IPC handler is blocking by default; an ESLint warning about import order is not.

## Run it on every pull request opened

Create an automation in the Project with a `github` trigger on `pull_request.opened` and `pull_request.synchronize`, `branch_patterns` set to `main`, and this prompt. Each fire adds the pull request after the prompt, so every push gets its own sandbox, its own `npm ci` and electron-builder run, and its own review. Set `read_only` so the runs can comment but never push.

The review appears on the pull request as the result, and the run is listed in the Project. The automation's `max_runs_per_hour`, 1 to 30, keeps a day of Dependabot bumps to a bounded number of packaging runs; a fire past the cap is skipped as `rate_limited` and that pull request is reviewed on its next push.

## Why run it on Rush?

An Electron review that stops at the diff misses the failures that matter: a preload that exposes too much, a channel the main process never handles, a native module that electron-builder cannot rebuild. Proving those means installing Electron, packaging the app and starting it under a virtual display, which is why most reviews skip it. On Rush the agent does that in its own sandbox and keeps every command and its output, so the review quotes the smoke test's IPC round-trip rather than asserting that IPC still works.

One sandbox per pull request means several contributors' changes are packaged and reviewed at the same time, each with its own `release/build` output, and none of it touches a developer's machine or the branch. The reviewer gets findings with the evidence attached and the author gets the same sequence CI would run, before CI runs it.

## Frequently asked questions

### Does the sandbox cover macOS and Windows too?

No. `test.yml` runs on all three, and the sandbox runs the Linux leg, including the smoke test under `xvfb-run`. The review says so, and a change to `build.mac`, `build.win`, `assets/entitlements.mac.plist` or the notarize script in `.erb/scripts` is reviewed by reading, labelled as such.

### Will it catch a native module in the wrong package.json?

Yes. `electron.vite.config.ts` externalizes exactly the names in `release/app/package.json` `dependencies`, and `postinstall` rebuilds them with `electron-builder install-app-deps`. A native module added to the root `package.json` gets bundled instead, which the agent confirms by reading the build output and flags with the line to move.

### What does it make of the updater code?

`src/main/updates.ts` returns early unless `app.isPackaged`, guards against a second start, sets `log.transports.file.level` and catches a rejected `checkForUpdatesAndNotify`. `updates.test.ts` pins all of that, so a diff that changes it is checked against those tests, and a change that lets the updater run in development is a finding.

### Does it run the lint with the repository's ESLint config?

Yes, `npm run lint` uses `eslint.config.js`, a flat config with the react, react-hooks, jsx-a11y, import, promise, compat and prettier plugins, loading the TypeScript parser from `.erb/tooling`. The review quotes the error count and treats a new error as blocking only if the pull request introduced it.
