What the agent does
The agent starts from the labelled issue, not from a generic Django layout. In a document manager shaped like paperless-ngx/paperless-ngx it reads pyproject.toml first: the project is at version 3.3, requires Python 3.11 or newer, pins django to the 5.2 patch series and djangorestframework to 3.16, and resolves everything through uv with a uv.lock. The testing dependency group names pytest 9.1, pytest-django, pytest-cov and factory-boy, and the tool.pytest table sets DJANGO_SETTINGS_MODULE to paperless.settings, turns on coverage and runs the suite across processes with loadscope distribution. The markers table matters for a bug report: tests tagged live, gotenberg, tika or greenmail need the containers the CI starts, and the agent keeps a new test out of those markers unless the bug needs them.
The issue text decides the file. A report about a file that was consumed with the wrong title, date or tags leads to src/documents/consumer.py, where ConsumerPreflightPlugin checks that the file exists, is not a duplicate and that the directories are writable, AsnCheckPlugin validates the archive serial number, and ConsumerPlugin.run does the parsing, storage and overrides, with ConsumeFileDuplicateError raised for a duplicate already in the trash or not. A report about an API response leads to src/documents/views.py, a 5,718-line module where DocumentViewSet carries the detail actions metadata, suggestions, preview, thumb, download, notes, share_links and history, PostDocumentView accepts uploads, and BulkEditView and its siblings handle selections. The agent reads the view or plugin the report names end to end before changing a line, because permissions are layered on through PassUserMixin and the bulk permission mixins.
The test comes before the fix. For the consumer, src/documents/tests/test_consumer.py is the pattern: it mixes in GetConsumerMixin from documents/tests/utils.py, which builds the preflight, ASN check and consumer plugins around a ConsumableDocument with a fake progress manager, and DirectoriesMixin from paperless_testing gives the test its own media directories. For the API, src/documents/tests/test_api_documents.py extends rest_framework's APITestCase and creates documents with DocumentFactory. The agent writes a test that fails with the reported behaviour, then changes the code until it passes and the rest of the file still does.
It runs what CI runs. .github/workflows/ci-backend.yml tests on Python 3.11 through 3.14, starts Gotenberg, Tika, Greenmail and nginx from docker/compose/docker-compose.ci-test.yml, installs unpaper, tesseract-ocr, imagemagick, ghostscript and poppler-utils, syncs with uv sync --group testing --frozen and runs uv run --dev --frozen pytest with PAPERLESS_CI_TEST set. The agent installs those system packages in the sandbox and runs the same pytest command, narrowed to the documents app first and then the whole suite. The frontend under src-ui has its own workflow and is left alone unless the issue is about it. The pull request body starts with Closes and the issue number and explains the cause.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "Fix GitHub issue #ISSUE in this Django repository. Read the issue and its comments first. Start from pyproject.toml for the Python and Django versions, the uv lockfile and the pytest configuration, then find the code the report names: src/documents/consumer.py for consumption problems, src/documents/views.py for API problems, with the matching module under src/documents/tests. Write a test that reproduces the reported behaviour, using the mixins and factories the neighbouring tests use, confirm it fails, then fix the code until uv run --dev --frozen pytest passes for src/documents and then for the whole suite, as .github/workflows/ci-backend.yml runs it. Do not change pinned versions, uv.lock or any workflow file. Open a pull request whose body starts with Closes #ISSUE, explains the cause, lists the files and quotes the pytest summary.",
"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. The diff is the production file the issue pointed at, most often one module under src/documents, plus one test added to the matching file under src/documents/tests. The body starts with Closes and the issue number, names the cause in a sentence, lists the files, and quotes the pytest summary from the sandbox. pyproject.toml, uv.lock and the workflows are unchanged unless the issue itself asked for them.
The session read names the repository and the pull request URL. The transcript at GET /o/{org}/sessions/{id}/messages keeps each tool call: the issue read, the files opened, the container and package setup, and each pytest invocation with its output, including the first run where the new test fails. A reviewer does not have to re-run the suite to see what the agent ran.
Before merging, a reviewer checks that the new test fails on the old code and passes on the branch, that the fix sits in the plugin or view the report named rather than in a serializer or signal that happens to be nearby, that no marker such as live or tika was added to dodge a container, and that the GitHub keyword in the body points at the right issue, since it closes the issue on merge.
Run it on every labelled GitHub issue
Add an automation to the Project with this prompt and a github trigger on issues.labeled, with the label you reserve for the agent in its labels. Only a label applied by an owner, member or collaborator fires it, so a stranger's label starts nothing. Each labelled issue starts one run on its own branch, and the trigger adds the issue event after the prompt, so the agent reads the number from it. From your own server, the same run starts with this request when your GitHub webhook handler sees the label, with an Idempotency-Key derived from the delivery id so a retried delivery returns the same session.
Give the API key a webhook URL and Rush POSTs run.completed, run.failed or run.cancelled when the turn ends, signed with the whsec_ secret, with data.session_url to read for the pull request. A key runs at most max_concurrent sessions at once, 4 by default, and answers 429 past that, so a label applied across an old backlog should be queued rather than fired all at once.
The use case this job follows, with its automation and trigger: Turn a Linear label into a PR.
Why run it on Rush?
Reproducing a consumer bug on a laptop means installing tesseract, ghostscript and poppler, starting Gotenberg and Tika, syncing a lockfile with torch in it, and waiting for a suite that runs across every core. The agent does that in its own sandbox with its own clone, and the sandbox parks with its disk when the turn ends, so a follow-up from the review does not start from zero.
The proof is the pull request plus the transcript, not a claim that the tests passed. The exact pytest output, the failing first run and the file the new test landed in are kept on the session. Several labelled issues run at once, each in its own sandbox with its own pull request, up to the key's concurrency limit.
Frequently asked questions
Will it run the tests that need Gotenberg and Tika?
Yes, when the sandbox can start the containers from docker/compose/docker-compose.ci-test.yml. If it cannot, the pull request says which marked tests were skipped and why, instead of reporting a green run that left them out.
Does it touch the Angular frontend under src-ui?
Not unless the issue is about it. The frontend has its own workflow, ci-frontend.yml, and a backend fix does not belong there. Say so in the issue when both sides need to change.
Which Python version does the sandbox use?
One the project supports: pyproject.toml requires 3.11 or newer and CI tests 3.11 through 3.14. The agent does not edit the requires-python line or uv.lock to make a test pass.
How does the issue text reach the agent?
A handler you run writes the issue number into input in place of #ISSUE; an automation's github trigger instead adds the issue event after the prompt and replaces nothing, and resources clones the repository through the Rush GitHub App installation. The agent then reads the issue and its comments from the repository's issue tracker with the credentials the installation provides.