What the agent does
The agent reads the manifest first, because in a Manifest V3 extension the permissions and the contexts are the review. In chibat/chrome-extension-typescript-starter, public/manifest.json declares manifest_version: 3, an action with popup.html and icon.png, an options_ui page at options.html, one content script matching <all_urls> that loads js/vendor.js and js/content_script.js, a background.service_worker at js/background.js, permissions: ["storage"] and host_permissions: ["<all_urls>"]. Every API call in src/ has to be covered by one of those entries, and every new entry widens what the store review and the install prompt show.
Then it reads how the build produces what Chrome loads. webpack/webpack.common.js has four entries, popup, options, background and content_script, writes them to dist/js/[name].js, splits shared code into a vendor chunk for every entry except background, compiles with ts-loader, and copies public/ into dist/ with copy-webpack-plugin. webpack.prod.js sets mode: 'production' and webpack.dev.js adds inline-source-map; package.json maps build to the production config and watch to the development one. popup.html and options.html each load js/vendor.js before their own script, which is why the vendor split must keep excluding background: a service worker cannot load a second script tag. tsconfig.json is strict with jsx: react and moduleResolution: bundler, and @types/chrome is pinned at 0.0.158.
The checks the repository runs are small. .github/workflows/build.yml does npm ci, npm run build --if-present and npm test on Node 20 and 22. jest.config.js roots tests in src and transforms .ts with ts-jest under tsconfig.test.json, and the only test, src/__tests__/sum.ts, checks sum(1, 1). Nothing in the suite touches the chrome namespace, so the agent runs npm ci, npm run build and npm test, then opens dist/manifest.json and the four bundles to confirm the build wrote what the manifest references, and writes a short Jest case with a stubbed chrome global in the sandbox when a finding needs proving, without adding it to the branch.
The diff is read with the two questions the prompt names. Permissions: src/options.tsx uses chrome.storage.sync.get and set, covered by storage; src/popup.tsx calls chrome.action.setBadgeText, chrome.tabs.query({ active: true, currentWindow: true }) and chrome.tabs.sendMessage, which need no tabs permission for the fields it reads today, but reading tabs[0].url in the popup depends on host_permissions covering the page; a diff that adds chrome.scripting, chrome.alarms or chrome.cookies without a matching permission is blocking, and one that widens host_permissions is a question for the author. Message passing: src/content_script.tsx registers chrome.runtime.onMessage.addListener and calls sendResponse synchronously for both branches, so a change that makes the handler async must return true to keep the channel open; src/background.ts is a polling function that re-arms a 30-second setTimeout, which a Manifest V3 service worker will not keep alive once it is idle, so a diff that builds real work on that timer gets a finding pointing at chrome.alarms and the permission it would need. One GitHub review is posted per run, quoting npm test and npm run build, separating blocking issues from suggestions, with file and line on every finding, and nothing is pushed.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "Review this pull request on a Manifest V3 Chrome extension. Check it out, run npm ci, npm run build and npm test, then open dist/manifest.json and the bundles under dist/js to confirm the build wrote every file the manifest references. Read the diff with public/manifest.json, webpack/webpack.common.js, src/background.ts, src/content_script.tsx, src/popup.tsx and src/options.tsx. Look for chrome.* APIs used without the permission or host permission that covers them, host_permissions or content_scripts matches widened without a reason in the description, onMessage listeners that respond asynchronously without returning true, work scheduled with setTimeout in the service worker instead of chrome.alarms, and webpack entries or vendor splitting changed so that background.js would load a second chunk. Confirm a finding with a short Jest case that stubs the chrome global in the sandbox where you can, but do not add it to the branch. Post one GitHub review that quotes the test and build results, 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"
}
]
}- 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.
- 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.
- 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. The summary quotes the npm test result and the webpack output from npm run build, with the list of bundles written under dist/js and whether dist/manifest.json references each of them. Findings follow with file and line, marked blocking or suggestion: an API call with no covering permission names the key to add to manifest.json; a message handler that went async names the return true it needs.
The transcript keeps the install, the build output, the manifest the agent read back from dist/, and any stubbed-chrome test it wrote to confirm a finding, so a reviewer can see the exact assertion that failed rather than a description of it.
Before merging, a reviewer loads dist/ as an unpacked extension, opens the popup and options page, and checks each cited line. A missing permission or a widened host_permissions is blocking by default; a suggestion to move the background timer to chrome.alarms is a follow-up unless the pull request depends on it.
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. The trigger adds the pull request after the prompt, so each pull request and each push to it gets its own sandbox, build and review, and a read_only automation can comment but not push.
The review is the result and appears on the pull request; the run is listed in the Project. max_runs_per_hour, 1 to 30, caps a burst of small pushes, and a fire past it is skipped as rate_limited until the next push. A manifest change is worth a second trigger on pull_request.labeled with a permissions label, so a maintainer can ask for a fresh review after the author answers the permission question.
The use case this job follows, with its automation and trigger: Review every pull request.
Why run it on Rush?
Extension review is mostly about what the manifest lets the code do, and a diff that touches src/popup.tsx says nothing about public/manifest.json or about what webpack writes to dist/. Checking it means building, reading the output manifest, and matching every chrome.* call against the permission list, which is tedious enough that it gets skipped. On Rush the agent does that in its own sandbox and keeps the build output and the manifest it read, so the review rests on the artifact Chrome would load.
One sandbox per pull request keeps reviews parallel and leaves the branch untouched; the author keeps their checkout, and the reviewer gets findings with the bundle list and the test output attached. The same run is cheap to repeat after the author narrows a host permission.
Frequently asked questions
The test suite only tests sum(). What does the agent get from running it?
Proof that ts-jest still compiles under tsconfig.test.json after the change, and a harness to write short cases into. For a message-passing finding the agent stubs chrome.runtime.onMessage and chrome.tabs.sendMessage on globalThis in a throwaway test, runs it in the sandbox, and quotes the result without adding the file to the branch.
Does it install the extension in a browser?
No. The sandbox builds dist/ and reads it back, which is what the repository's CI checks too. Loading dist/ as an unpacked extension is the step a reviewer does before merging, and the review says which findings were read from the built output rather than observed in Chrome.
What does it say about the 30-second polling in background.ts?
That a Manifest V3 service worker is stopped when idle, so the setTimeout loop in src/background.ts is not a reliable schedule. It is left alone in the starter, where the loop does nothing, and becomes a finding only when a pull request attaches real work to it; the finding names chrome.alarms and the alarms permission it would need.
Which Node version runs the build?
build.yml tests Node 20 and 22, so the agent uses 22 and notes if the pull request adds a dependency whose engines field excludes 20. The package.json pins no engines of its own.