What the agent does
The agent starts from the issue and the repository, cloned into its own sandbox, and checks what is already registered, since Fastify apps often have @fastify/swagger installed and half set up. In fastify/demo (Fastify ^5.10.0, @fastify/autoload, TypeBox schemas through @fastify/type-provider-typebox, Knex on MySQL), package.json already depends on @fastify/swagger ^9.8.1 and @fastify/swagger-ui ^6.1.0, and src/plugins/external/swagger.ts registers both: the document with openapi.info titled "Fastify demo API" and hideUntagged: true, and the UI with routePrefix: '/api/docs', which puts the JSON document at /api/docs/json, not the /documentation/json the issue names. So the work is not "register @fastify/swagger"; it is making the document complete and putting it where the issue says, and the agent writes that in its plan.
hideUntagged: true is why the document is incomplete today. It leaves out every route whose schema has no tags, and in this codebase only src/routes/api/tasks/index.ts tags its routes (tags: ['Tasks'] on ten of them). POST /api/auth/login in src/routes/api/auth/index.ts, PUT /api/users/update-password in src/routes/api/users/index.ts, GET /api and the home route in src/routes/home.ts have none, so they are missing from the document. The agent adds tags such as Auth and Users to those schemas, gives the routes that have no response schema one built from the TypeBox types in src/schemas/, and adds a session cookie security scheme, because src/routes/api/autohooks.ts rejects any request without request.session.user outside /api/auth/login, and a document that hides that is wrong about every route.
On the path, it follows the issue: the document at /documentation/json, by setting the UI's routePrefix to /documentation and saying in the pull request that the old /api/docs link moves. It checks two things the code makes easy to miss. The swagger plugins load from plugins/external through autoload, outside the routes/api folder whose autohooks.ts requires a session, so it confirms with a request that the document is reachable without signing in, or says that it is not if your team wants it private. And @fastify/helmet is registered with its default options, whose Content-Security-Policy can stop the Swagger UI page from loading its scripts, so the agent opens the UI in the sandbox's browser instead of trusting a 200.
The test goes in test/routes/, written with node:test like the rest of the suite and using the build helper in test/helper.ts. It collects every route the app registers with an onRoute hook, fetches /documentation/json with app.inject, converts Fastify's :id parameters to OpenAPI's {id}, and fails with the list of routes missing from paths. It keeps the request count low, because CI sets RATE_LIMIT_MAX: 4 for the global @fastify/rate-limit. Last, it does what .github/workflows/ci.yml does against a mysql:8.4 service started in the sandbox: npm i --ignore-scripts, npm run build -- --noEmit, npm run lint, touch .env, then npm run db:migrate && npm run test with the same environment variables, until everything passes.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "A Linear issue labeled agent says: \"Serve OpenAPI Docs from a Fastify API. Register @fastify/swagger so every route's TypeBox schema appears in a generated OpenAPI document at /documentation/json, and add a test that the document lists every route.\" Before changing anything, check whether @fastify/swagger or @fastify/swagger-ui is already registered, with which options and at which path, and which routes the document leaves out and why (hideUntagged, missing schemas, encapsulation). Say what already exists. Serve the document at /documentation/json and note any old path that moves. Give every route tags and a response schema from the existing TypeBox schemas, and describe the session or token auth the routes require as a security scheme. Confirm with a request whether the document needs a session, and open the UI in the browser to check it loads under the app's security headers. Add a node:test test that collects every registered route with an onRoute hook and fails listing any route missing from the document's paths. Start MySQL in the sandbox, run the build, lint, migrations and npm test the way CI does until they pass, and open a pull request that links back to the issue.",
"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 its own branch that links the Linear issue. In an app shaped like fastify/demo it changes src/plugins/external/swagger.ts (the routePrefix, a components.securitySchemes entry for the session cookie), adds tags and response schemas to the routes in src/routes/api/auth/, src/routes/api/users/ and src/routes/api/index.ts, and adds one test file under test/routes/. The description opens with what was already there: both plugins registered, the document at /api/docs/json, and hideUntagged hiding every route outside tasks. It then lists the routes that were missing and are now present.
The proof is the test and the transcript. The new test fails if a route is added later without making it into the document, so the issue's requirement stays true after this pull request. The session keeps every command with its output: the MySQL start, npm run build -- --noEmit, npm run lint, npm run db:migrate and npm run test, plus the request to /documentation/json and a screenshot of the UI. The repository's CI workflow runs the same steps on the pull request across Node 22 and 24.
Before merging, a reviewer decides whether the document should be public (the agent reports whether it needs a session today), checks that response schemas match what the handlers really return rather than what the agent inferred, and makes sure nothing that linked to /api/docs breaks when the path moves.
Run it on every Linear issue labelled agent
In the console, add an automation to the Project with a Linear trigger on linear.issue.labeled and the label agent. Each issue that gets the label starts one run in the Project's repository with the issue's text as context, and the agent's own updates through the integration do not start another.
From your own server, the same run is a POST /o/{org}/sessions per issue: the repository and the https://linear.app/<workspace>/issue/<ID> URL both go in resources, and Rush reads the issue through your Linear connection. A webhook on the API key posts run.completed or run.failed with the session_url once the turn ends.
The use case this job follows, with its automation and trigger: Turn a Linear label into a PR.
Why run it on Rush?
Proving that a generated OpenAPI document is complete means booting the whole app with every plugin and route loaded, which for this API means MySQL migrated and seeded, a cookie secret, and the same rate limit CI uses. In a Rush sandbox the agent starts MySQL 8.4, runs the migrations and the full npm test, and opens the Swagger UI in its own browser, so the check covers the app as it boots rather than one route file in isolation.
The issue becomes a pull request that explains itself: what was already registered, which routes were missing and why, and a test that keeps the document honest from now on. The transcript keeps each command with its output beside it, and several labelled issues can run at once in separate sandboxes, up to the key's max_concurrent limit.
Frequently asked questions
Why are some of my routes missing from the Swagger document?
Usually one of three things: hideUntagged: true with routes that have no tags, routes registered outside the encapsulation context @fastify/swagger was registered in, or routes registered before the plugin. In fastify/demo it is the first. The agent finds which applies and fixes the routes, not the test.
Will it change my route schemas?
It adds tags, and a response schema where a route has none, built from the TypeBox types already in the codebase. Because Fastify uses response schemas to serialize replies, a new one can drop fields a handler returns, so the agent runs the full test suite and lists every schema it added for review.
Does the document need authentication?
The agent checks with a request and reports it. In fastify/demo the session check lives in src/routes/api/autohooks.ts and applies to routes under routes/api, while the swagger plugins load from plugins/external. If you want the document private, say so in the issue and the agent puts it behind the same check.
How does the test know every route?
It registers an onRoute hook before the app loads, collects each method and URL, and compares them with the paths in /documentation/json after converting :param to {param}. A route added later without a schema in the document fails the test with its method and path.