# Fix a Failing CI Build in a Vue App

Yes. A failed workflow starts an agent that reads the log, reruns the failing lint, typecheck, Vitest, build or Cypress job in a sandbox and opens a pull request.

[Run this on Rush](https://cloud.getrush.ai/run/fix-failing-ci/vue/), in a Project that has your repository.

## What the agent does

The run starts from a `workflow_run.failure` event with the workflow name, the branch and the head commit. The agent clones that commit and reads the failed job's log with `gh run view <run id> --log-failed`. In a Vue 3 app like antfu-collective/vitesse, `.github/workflows/ci.yml` has four jobs that can fail independently: `lint` runs `pnpm run lint`, `typecheck` runs `pnpm run typecheck`, `test` runs `pnpm run test:unit` then `pnpm run build` on a matrix of Node 22 and 24, and `test-e2e` builds, serves `vite preview --port 3333` and runs Cypress through `cypress-io/github-action@v6`. The job name in the event tells the agent which command to reproduce, and the matrix row tells it which Node to install.

It reproduces with the repository's own toolchain. `package.json` pins `packageManager: pnpm@10.30.2` and an `engines.node` range starting at `^22.22.2`, every dependency is a `catalog:` reference resolved in `pnpm-workspace.yaml` (Vite `^8.3.3`, Vitest `^5.0.3`, `vue-tsc ^3.3.12`, `typescript ^6.0.3`, `@antfu/eslint-config ^9.5.1`, Cypress `^16.1.1`), and CI installs with `pnpm install --frozen-lockfile`. `pnpm-workspace.yaml` also lists `onlyBuiltDependencies` as `cypress`, `esbuild` and `simple-git-hooks`, so a Cypress binary missing after a cached install is a known shape of failure, and `ci.yml` works around it with `install-command: pnpm exec cypress install`. The agent runs the failing script, `pnpm run lint`, `pnpm run typecheck` (`vue-tsc --noEmit`), `pnpm run test:unit --run` or `pnpm run build` (`vite-ssg build`), and reads the first error.

Each job fails for a different kind of reason, and the agent reads the file the error names. A lint failure is usually style under `eslint.config.js`, fixed with `eslint --fix` on the files the commit touched. A typecheck failure often sits in a generated declaration: `src/auto-imports.d.ts`, `src/components.d.ts` and `src/route-map.d.ts` are written by `unplugin-auto-import`, `unplugin-vue-components` and the Vue Router plugin in `vite.config.ts`, so a new component or page that was not committed with its regenerated `.d.ts` fails `vue-tsc` on CI and passes locally. A Vitest failure is one of two files: `test/basic.test.ts` or `test/component.test.ts`, which mounts `TheCounter` with `@vue/test-utils` and compares `wrapper.html()` to `test/__snapshots__/component.test.ts.snap`, so a template change in `src/components/TheCounter.vue` fails until the snapshot is updated on purpose. A build failure in `vite-ssg build` is often a page under `src/pages/` that touches `window` or `document` during server rendering.

The e2e job has its own failure modes, and the agent runs it the way CI does: `pnpm run build`, `npx vite preview --port 3333`, then `cypress run`. `cypress/e2e/basic.spec.ts` asserts exact URLs on `http://localhost:3333`, types into `#input`, which is the id on `src/components/TheInput.vue`, expects the route `/hi/Vitesse` from `src/pages/hi/[name].vue`, and clicks `[data-test-id="about"]` in `src/components/TheFooter.vue`; it then expects `.shiki` blocks and `target="_blank"` links on the `/about` page, which `vite.config.ts` adds through `markdown-it-link-attributes` and Shiki. A renamed id, a changed layout string or a removed attribute is the usual cause. The agent fixes the cause, reruns the job's command and the other three, and opens a pull request that quotes the log line it fixed. It does not skip a test, delete a snapshot, or loosen `eslint.config.js`.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "The GitHub Actions workflow on this branch failed; the workflow_run event follows this prompt. Read the failed job's log with gh run view <run id> --log-failed and quote the job name, the matrix Node version if any, and the first error. Use pnpm 10.30.2 from packageManager and pnpm install --frozen-lockfile. Reproduce with the job's own command: pnpm run lint, pnpm run typecheck, pnpm run test:unit --run then pnpm run build, or for test-e2e pnpm run build, npx vite preview --port 3333 and cypress run. For a typecheck error, regenerate src/auto-imports.d.ts, src/components.d.ts and src/route-map.d.ts by running the dev server once and commit them if they changed. Fix the cause in src, test or cypress; update a Vitest snapshot only when the template change is intended and say so. Rerun all four jobs' commands until they pass. Do not skip a test, delete a snapshot or relax eslint.config.js. Open a pull request against the failing branch that quotes the error it fixed and the passing output.\n\nEvent: <workflow_run payload>",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

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 pull request against the failing branch. Its description names the job and matrix row, quotes the first error from the log, and shows the same command failing and then passing in the sandbox with the Node and pnpm versions. The diff is the fix: a source change, a regenerated declaration file, an intentionally updated snapshot with a sentence saying why, or a selector in `cypress/e2e/basic.spec.ts` when the template changed on purpose.

The push runs `ci.yml` again, so the reviewer sees `lint`, `typecheck`, both `test` rows and `test-e2e` go green on GitHub rather than relying on the sandbox. They check that a snapshot update matches a visible template change in `TheCounter.vue`, that a regenerated `.d.ts` contains only the new component or route, and that `eslint.config.js` and `tsconfig.json` are untouched.

When the failure is outside the code, such as the Cypress binary download or a registry outage during install, the run says that with the log lines it read and opens no pull request.

## Run it on every failed CI run on a branch

In the console, add an automation to the Project for this repository with this prompt and a `github` trigger on `workflow_run.failure`. The trigger appends the event after the prompt, so the run knows the job, branch and commit. Only runs on branches by an owner, member or collaborator fire it, and a failure on a fork never does. When the `test` matrix fails on both Node rows for one commit, each is its own event, and `max_runs_per_hour` on the automation bounds that.

If your own server routes CI failures, it can send this body to `POST /o/{org}/sessions` with the failing branch as `https://github.com/<owner>/<repo>/tree/<head_branch>` in `resources`, the GitHub delivery id as the `Idempotency-Key`, and the event text after the prompt. The key's `webhook_url` then receives `run.completed` with the `session_url` to read the pull request from.

## Why run it on Rush?

A red Vue build in this template is usually invisible on the laptop that caused it: a stale `.d.ts` that the local dev server regenerated silently, a snapshot that `vitest` in watch mode updated with a keypress, or a Cypress binary that is present locally and missing in a cached CI store. The sandbox starts from `pnpm install --frozen-lockfile` and the job's exact command, with every command and its output kept, so the pull request can quote the failure and the pass.

Four jobs on a matrix fail in four different ways, and each run reads the one log that matters and fixes the one cause. When a dependency catalog bump reddens several branches, each branch gets its own run and its own pull request, and the checks on GitHub confirm each fix without anyone rerunning the suite by hand.

## Frequently asked questions

### Which Node version does the sandbox use?

The one the failed matrix row names, 22 or 24, read from the event and `ci.yml`; `lint`, `typecheck` and `test-e2e` use `lts/*`. The pull request says which it ran, and the other row runs on GitHub when the checks start.

### Will it update the Vitest snapshot to make the test pass?

Only when the diff shows an intended change to `src/components/TheCounter.vue`, and then it says so in the pull request. A snapshot that changed because of a bug is a fix in the component, not in `test/__snapshots__/component.test.ts.snap`.

### Why would typecheck fail on CI and pass for me?

Because `src/auto-imports.d.ts`, `src/components.d.ts` and `src/route-map.d.ts` are generated by Vite plugins and committed. Adding a component or page locally regenerates them during `pnpm dev`; if the regenerated file was not committed, `vue-tsc` on a clean checkout cannot find the new name.

### Can it fix the Cypress job?

Yes. It builds, serves `vite preview --port 3333` and runs `cypress run` headless in the sandbox browser, reads which assertion in `cypress/e2e/basic.spec.ts` failed, and fixes the id, attribute or route it expects, or the spec when the template change was intended.

### Does it run the Netlify build too?

No. `netlify.toml` runs `pnpm run build` on Node 22 and is not a GitHub workflow, so it never sends `workflow_run.failure`. The same `build` script is part of the `test` job, so a build fix covers both.
