What the agent does
In a Django project like healthchecks/healthchecks, every dependency is pinned with ==. requirements.txt has 15 runtime pins, among them Django[argon2]==6.1.2, psycopg==3.3.6, pydantic==2.14.1, PyJWT[crypto]==2.15.1, fido2==2.2.1, pycurl==7.48.0, whitenoise==6.12.0 and django-compressor==4.6. requirements-dev.txt adds apprise, django-stubs==6.1.2, minio, mypy==2.4.0, time-machine and others. The agent reads both files and checks PyPI for each pin's latest release with pip index versions, then pairs the packages that must move together: django-stubs and django-stubs-ext follow the Django version, and mypy is checked against both.
The test setup comes from .github/workflows/tests.yml. It installs libcurl4-openssl-dev, libpython3-dev and libssl-dev with apt before pip, because pycurl builds against libcurl, and runs python manage.py test with SECRET_KEY set to a dummy value. The matrix covers sqlite, postgres, mysql and mariadb across Python 3.12 to 3.15, and hc/settings.py defaults to SQLite unless the DB variable says otherwise. The agent installs the same system packages, runs the suite on SQLite after each change, and also runs mypy --strict hc, the command mypy.yml runs, because a new django-stubs often changes types.
It bumps one package or one linked pair at a time. A bump that fails the tests or mypy is reverted in the requirements file, and the agent keeps the failing test's name or the mypy error with the file and line. For a Django minor or major it reads the release notes' backwards-incompatible changes and deprecations, and fixes a removed setting or import only when the fix is small and the suite passes.
When every bump is settled, the agent runs the full suite once more with every kept pin installed together, since two upgrades that pass alone can still conflict.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "Upgrade the pinned packages in requirements.txt and requirements-dev.txt to their latest releases, one package at a time, moving django-stubs and django-stubs-ext with Django. Install libcurl4-openssl-dev, libpython3-dev and libssl-dev first so pycurl builds. After each bump run SECRET_KEY=dummy-key python manage.py test and mypy --strict hc. Revert any bump that fails either and record the test or mypy error. Run the full suite once more with every kept bump. Open one pull request listing each bump with old and new version and each skipped upgrade with the error that blocked it.",
"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
The pull request changes requirements.txt and requirements-dev.txt, plus any small code fix a Django or library release required, called out in its own section. The description lists every pin that moved with its old and new version, then each pin left where it was with the test or type error that held it back. tests.yml runs the four-database matrix on the pull request and mypy.yml runs strict type checking, so the backends the sandbox did not run are covered there.
A reviewer reads the release notes for the Django version and any package that crossed a major, checks that the MySQL and MariaDB jobs pass, and merges. The output of every manage.py test and mypy run is in the session transcript.
Run it on every Monday morning
Pinned requirements never move on their own, which is the point of pinning and also why they fall behind. A cron job or a scheduled workflow can send this request once a week, so the week's pull request holds a handful of tested bumps instead of a year's worth.
A webhook on the API key tells your own service when the turn ends; read the session from session_url and link the pull request in the channel that reviews dependency changes.
The use case this job follows, with its automation and trigger: Upgrade dependencies weekly.
Why run it on Rush?
A Django dependency upgrade needs the right system libraries for pycurl and psycopg, a clean virtualenv and a full test run per bump. In a Rush sandbox the agent sets that up from scratch each time, without the leftovers a laptop's site-packages carries, and every apt, pip, manage.py and mypy command is kept with its output.
Pinned projects get one pull request per package from most bots, each needing its own CI run and review. This gives one pull request a week that has already passed the tests, with the reasons for anything held back. Several Django services can run the same request at the same time.
Frequently asked questions
Will it switch my pins to ranges?
No. Pins stay exact with ==, moved to the new version. The project chose pinning, and the agent keeps that choice.
Does it test against Postgres and MySQL too?
In the sandbox it runs the suite on SQLite, the default in hc/settings.py. The tests.yml matrix runs Postgres, MySQL and MariaDB on the pull request, so those backends are checked before merge.
What if I use Poetry or uv instead of requirements files?
The agent works with the lockfile the project has. Name the tool in the request, for example uv lock --upgrade-package, and it updates pyproject.toml and the lock instead.
Will it upgrade Django across a major version?
It tries, reading the release notes and fixing small incompatibilities. If the suite still fails, Django stays at its current version and the pull request says what failed.