All jobs

Turn GitHub Issues into Pull Requests for a Python Project

Yes. Label a GitHub issue on a Python project and an agent reads it, adds a pytest, runs the suite, and opens a pull request that links the issue.

Run this on RushIn a Project that has your repository.

What the agent does

The agent reads the issue, then the project metadata, before it picks a file. In a library shaped like encode/httpx, pyproject.toml says requires-python >=3.9, the runtime dependencies are certifi, httpcore==1.*, anyio and idna, and the version is not in that file: [tool.hatch.version] points at httpx/version.py, which sets version = "0.28.1". Optional extras split the edges of the client: http2 pulls in h2, cli pulls in click, pygments and rich, and brotli, socks and zstd are separate. An issue about HTTP/2 decoding is not a change to the CLI extra, and the agent says which extra the test needs.

The code it is most likely to open is httpx/_client.py. That module defines BaseClient, then Client at the sync path and AsyncClient at the async path. tests/client/test_client.py already shows the house style: test_get builds httpx.Client(http2=True) against the server fixture and asserts status_code, url, content and http_version. The server fixture lives in tests/conftest.py. It is session-scoped, builds a uvicorn Config with lifespan off, and serves it on a background thread via serve_in_thread. A new behaviour gets a test next to test_get that talks to that fixture, not a test that calls the public internet.

The command the repository runs is scripts/test, not a bare pytest typed from memory. That script sets PREFIX to venv/bin/ when a venv directory exists, and outside GitHub Actions it runs scripts/check and then scripts/coverage around coverage run -m pytest. scripts/check is stricter than the tests: it runs scripts/sync-version, ruff format on httpx and tests with --diff, mypy on the same trees, and ruff check. [tool.mypy] sets strict = true, with tests.* allowed to skip untyped defs. [tool.pytest.ini_options] sets addopts = "-rxXs", turns warnings into errors, and defines two markers, copied_from and network. A test that needs the network is marked network. The agent does not clear filterwarnings to hide a failure.

CI in .github/workflows/test-suite.yml is the picture of done. On push to master and on pull requests it matrices Python 3.9, 3.10, 3.11, 3.12 and 3.13, then runs scripts/install, scripts/check, scripts/build, scripts/test and scripts/coverage. The sandbox run uses one interpreter that satisfies >=3.9 and runs scripts/test. It does not pretend to have executed the full matrix. The pull request says which interpreter ran, and it does not edit the workflow's python-version list unless the issue is about that list.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "codex",
  "input": "Resolve GitHub issue #ISSUE in this repository. Read the issue and its comments. In a project laid out like encode/httpx, read pyproject.toml and httpx/__version__.py first, then httpx/_client.py (BaseClient, Client, AsyncClient). Add a pytest next to the code you change, using the session-scoped server fixture in tests/conftest.py rather than the public network, and mark it network only if it truly needs the network. Run scripts/test (coverage run -m pytest, with scripts/check outside GitHub Actions). Do not weaken [tool.mypy] strict or pytest filterwarnings. Open a pull request whose body says Fixes and the issue number, names the Python version you ran, and pastes the pytest summary.",
  "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. For an httpx-shaped library the diff touches a module under httpx/, often _client.py, _models.py or a transport under httpx/_transports/, and a file under tests/client or tests/models. The description says Fixes and the issue number, names the interpreter, and includes the pytest line from scripts/test. pyproject.toml's requires-python and the mypy strict flag stay as they were unless the issue asked to change them.

The session records the repository and the pull request. GET /o/{org}/sessions/{id}/messages holds the transcript: the files read, the scripts/test invocation, and the pytest summary line. Artifacts are empty unless the agent also wrote a note. The result to review is the pull request.

A reviewer looks at three things before merging. The new test uses the server fixture rather than a host on the internet. The change respects the split between Client and AsyncClient, so a sync fix is not left unapplied on the async class that shares BaseClient. And the pull request does not drop a warning filter or a ruff rule to get scripts/check to pass. Merging closes the issue through the Fixes line.

Run it on every labelled GitHub issue

Subscribe your server to the issues webhook. When the action is labeled and the label is the one you reserve, POST this request to https://api.prix.dev/o/{org}/sessions. Write the issue number into input and set resources to that repository. Use an Idempotency-Key of the webhook delivery id so GitHub's retry does not start a second run. The repository must be on a Rush GitHub App installation, or the create answers 422 RESOURCE_NOT_ACCESSIBLE.

Give the API key a public https webhook_url. Rush then POSTs run.completed, run.failed or run.cancelled within about 30 seconds of the turn ending, with session_url pointing at the session whose pull request you want. Verify the webhook-signature with the whsec_ secret. Past max_concurrent in-progress runs, the default is 4, the create answers 429, so apply the label to a large backlog through a queue.

The use case this job follows, with its automation and trigger: Turn a Linear label into a PR.

Why run it on Rush?

An httpx issue often fails only when both the sync Client and the AsyncClient are exercised against the in-process server, and scripts/check adds mypy --strict and ruff on top of pytest. Doing that on a laptop means a venv, the right Python, and a long feedback loop. The agent runs scripts/test in a sandbox that has the clone and nothing else of yours, and the sandbox parks when the turn ends.

You review a pull request whose test you can re-run, and a transcript that shows the pytest summary the agent actually got. The run does not claim the 3.9-through-3.13 matrix passed unless that matrix ran. Each labelled issue is its own session, so five issues do not share one working tree.

Frequently asked questions

Does the sandbox run the Python 3.9 to 3.13 matrix?

It runs scripts/test on one interpreter that satisfies requires-python >=3.9, and the pull request names that version. The matrix in test-suite.yml is what GitHub Actions runs on the pull request after it is opened. The agent reads that workflow's result when you ask it to, and it does not edit the matrix to go green.

Will it relax mypy strict or the pytest warning filter?

The prompt forbids that. [tool.mypy] strict and filterwarnings = error are how this repository fails closed. If strict mode rejects a type the issue did not mention, the agent reports the diagnostic in the pull request instead of setting ignore_missing_imports more widely.

What about tests marked network?

The marker exists for third-party builds that disable the network. New tests use the server fixture in tests/conftest.py, which is a uvicorn app on a local thread. A test is marked network only when the issue is about real DNS, proxies or TLS to an outside host, and the pull request says why.

Does it change both Client and AsyncClient?

When the bug is in BaseClient, both subclasses inherit it and the agent says so. When the bug is only on Client or only on AsyncClient, the test constructs that class. The pull request names which one changed so a reviewer can check the other path was considered.

How is the issue attached to the run?

resources clones the repository through your GitHub App installation. The issue number and body are written into input by the server that received issues.labeled. The agent reads the issue thread from that repository. A repeated delivery with the same Idempotency-Key and body returns the session already started.