What the agent does
The agent starts inside the template package you care about, not the monorepo root alone. In remix-run/react-router-templates the default app lives under default/: package.json scripts are dev (react-router dev), build, start (react-router-serve ./build/server/index.js) and typecheck; dependencies use react-router and @react-router/node / @react-router/serve on React 19.2.x with Vite ^8 and Tailwind ^4. default/app/routes.ts registers a single index route pointing at routes/home.tsx, which renders Welcome from welcome/welcome.tsx (logo, "What's next?" links to the docs and Discord). default/app/root.tsx supplies Layout, ErrorBoundary and the font links. The stock home module has no loader or action export yet.
The repository's own smoke tests sit under .tests/ (playwright.config.ts with testMatch **/test.*.ts, test.default.ts driving pnpm dev / pnpm build + pnpm start and asserting the title New React Router App plus the Docs and Discord links). Your application checkout usually has none of that under e2e/. The agent adds an app-local Playwright layout: e2e/ specs, a playwright.config.ts whose webServer runs the package manager's dev script (pnpm in the templates workspace, npm or yarn when that is what the lockfile shows), and Chromium-only headless project settings.
Specs cover the real routes: home renders the welcome heading and resource links, unknown paths hit the ErrorBoundary (404 message from isRouteErrorResponse), and when the app has grown a route action (a <Form method="post"> posting to a resource route) the agent adds a case that fills the fields, submits, and asserts on the redirect or pending UI. If the stock template still has no action, the pull request says so and keeps the suite to routing, meta (meta export on home) and the error boundary instead of inventing a fake form in production code.
It installs @playwright/test, installs browsers, runs the suite headless against the config's webServer, and only then opens the pull request. Failures that expose a real defect are marked expected to fail with an explanation in the description, matching the prompt's instruction not to hide bugs.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "Write Playwright end-to-end tests for the default template's routes and its form action in this React Router app. Put them in e2e/ with a playwright.config that starts the dev server, run them headless, and open a pull request with the passing suite. If a test exposes a real bug, mark it expected to fail and explain it in the description.",
"resources": [
{
"url": "https://github.com/your-org/your-app"
}
]
}- 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 pull request on a new branch. It adds e2e/ specs and playwright.config.ts (plus @playwright/test in the app package.json when it was missing). Route modules under app/ stay unchanged unless the only way to assert a form action is a tiny fixture route the description calls out.
The description lists which routes were covered, whether a route action existed, the headless command that passed, and any test.fail cases with the defect they document. The trajectory keeps Playwright's output and the dev server logs.
Before merging, a reviewer checks that the webServer command matches the app's package manager, that the suite does not depend on the templates monorepo's .tests/ harness, and that expected-to-fail cases are real bugs rather than flaky selectors. Once merged, the same config can run in CI on pull requests.
Run it on every new React Router resource route
When a pull request adds a file under app/routes/ (or a new entry in routes.ts), have your server send this request so Playwright coverage for that route lands before merge. Pass the branch name in the prompt so the run works on the pull request head.
Subscribe to run.completed on the API key webhook to post the pull request link back to the review thread. Keep concurrency at or below max_concurrent.
The use case this job follows, with its automation and trigger: Write the missing tests.
Why run it on Rush?
React Router framework mode needs the Vite dev server (or react-router-serve after build) and a real browser for confidence in loaders, actions and the error boundary. Running that loop on a laptop blocks the same port you use for daily work. On Rush the agent owns the server in its sandbox, runs Chromium headless, and returns a pull request with a green suite.
The trajectory shows every failure the agent fixed, including bugs it refused to hide. You can point the same request at many RR apps and review each pull request on its own.
Frequently asked questions
The default template has no form action. What gets tested?
The agent says so in the pull request. It still covers the index route, welcome links, meta title and the root ErrorBoundary for unknown paths. When your app later adds a route action, the same job extends the suite to submit that form.
Does it use the monorepo .tests/ harness?
Not for your app pull request. .tests/test.default.ts is how the templates repo smoke-tests packages; the agent adds an e2e/ layout inside your application package so CI for that app stays self-contained.
pnpm, npm or yarn?
Whatever the app lockfile shows. The templates workspace root has pnpm-workspace.yaml and pnpm-lock.yaml; a standalone app copied from default/ may have its own lockfile after you create the project.
What if a test finds a bug in the template?
It marks that case expected to fail and explains the defect in the pull request description instead of loosening the assertion. Production code stays unchanged unless you ask it to fix the bug in a separate request.