# Fix a Failing CI Build in a Python Project

Yes. A failed workflow run starts an agent that reads the log, reruns the same pytest command in a sandbox and opens a pull request that turns it green.

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

## What the agent does

The run starts from a GitHub `workflow_run.failure` event on the default branch. The event the agent reads names the workflow, the head branch, the head commit and the check runs that failed, so it knows whether it is looking at the test matrix, the lint job or the type check before it opens a single file. It clones the repository at that commit into its own sandbox and reads the failed job's log with `gh run view --log-failed`.

In a library like `psf/requests`, the agent finds three workflows that can go red for different reasons. `run-tests.yml` runs `make` and then `make ci`, which is `python -m pytest tests --junitxml=report.xml`, across Python 3.10 to 3.14, 3.14t, 3.15-dev and PyPy 3.11 on Ubuntu, macOS and Windows, plus a `no_chardet` job that uninstalls `charset_normalizer` and `chardet` and a job pinned to `urllib3<2`. `lint.yml` runs `pre-commit run --all-files`. `typecheck.yml` runs `pyright src/requests/` in strict mode on 3.10 and 3.14.

It reproduces the failing job, not a nearby one. For a test failure it installs `requirements-dev.txt` (an editable install with the `socks` extra, `pytest-httpbin==2.1.0`, `httpbin` and `trustme`) on the same Python minor the log names and runs the one failing test by node id, such as `pytest tests/test_requests.py::TestRequests::test_...`. `pyproject.toml` adds `--doctest-modules`, so a docstring example in `src/requests/` can fail the suite too. A failure only in the `urllib3<2` or `no_chardet` job is reproduced by making the same change to the environment first.

With the failure on screen, the agent fixes the cause: the code in `src/requests/`, a test that depended on something the commit changed, or the workflow itself when the break is an action or a Python version that moved. It reruns the failing test, then `make ci` and `pre-commit run --all-files`, so the fix does not trade a test failure for a lint failure.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "The GitHub Actions run on the default branch failed; the event follows this prompt. Read the failed job's log and name the job, the Python version and the failing test or step. Recreate that job's environment in the sandbox, reproduce the failure with the same command, fix the cause, and run the full test suite and the pre-commit hooks. Open a pull request that quotes the failing log lines and explains the fix. Do not skip, mark flaky or delete a failing test.",
  "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 default branch. Its description names the job that failed (for example the 3.12 Ubuntu leg of `run-tests.yml`), quotes the traceback from the log, and shows the same test failing and then passing in the sandbox. The diff is the fix; a change to a workflow file or to `requirements-dev.txt` is listed apart from the code change.

GitHub runs the full matrix on the pull request, so the checks a reviewer reads are the real ones, including the macOS and Windows legs the sandbox cannot run. They check that the failing job is green, that no test gained a `skip` or `xfail` marker, and that a fix in `src/requests/` still satisfies `pyright` in strict mode.

When the failure only reproduces on a platform the sandbox does not have, such as a Windows path issue, the run says so plainly. It reports what it read in the log, what it tried on Linux and its best reading of the cause, so the person who takes it over starts from the evidence.

## Run it on every failed workflow run on main

In the console, add an automation to the Project for this repository with the prompt above and a `github` trigger on `workflow_run.failure`, with `main` in its branch patterns. Every workflow run that ends in failure on that branch then starts one run, with the workflow name, head commit and failing checks given to the agent as data under the prompt.

A redelivered GitHub event does not start a second run, and the automation's runs-per-hour limit (1 to 30) caps a burst when one bad merge breaks every workflow at once. Runs from forks are never picked up.

## Why run it on Rush?

A red matrix on a Python library usually means someone downloads the log, works out which of a dozen interpreter and OS legs failed, builds a matching virtualenv and reruns one test. A Rush run does that in its own sandbox with the commands and their output kept, so the reviewer sees the traceback before the fix and the passing test after it, not a summary.

Failures come in groups: a dependency release can break the `urllib3<2` job on one repository and the strict type check on another in the same hour. Each failure gets its own run on its own branch, they run at the same time, and the result is a pull request whose own checks prove the fix or a report that says what is still unknown.

## Frequently asked questions

### Which Python version does it use?

The one the failing job used, read from the log or the workflow matrix. For `psf/requests` that can be anything from 3.10 to 3.15-dev or PyPy, and a fix that works on one minor is rerun on the oldest supported one before the pull request opens.

### Will it mark the test as flaky to make CI pass?

No. The prompt forbids skipping, marking or deleting a failing test, and the diff shows any change to a test file. When a test really is nondeterministic, the run says what makes it so and fixes that cause instead.

### Does it work with tox, nox, uv or Poetry?

Yes. It runs whatever the failing workflow step runs, whether that is `make ci`, `tox -e py312`, `uv run pytest` or `poetry run pytest`, so the sandbox installs dependencies the way CI does.

### What if the failure is in the workflow file, not the code?

Then the fix is in `.github/workflows/`, such as a Python version that is no longer available or an action pinned to a removed tag. The pull request says the change is to CI, and its own checks show whether the new workflow runs.
