All jobs

Audit a Django Repository per Customer Request

Your backend sends one API request per customer repo; an agent runs its Django tests and posts JSON on stale packages, test results and risky code to your webhook.

Run this on RushIn a Project that has your repository.

What the agent does

Each audit is one session, started by your backend with the customer's repository in resources. The agent begins with how the project pins its environment. In a Django codebase like django/djangoproject.com it finds pyproject.toml with requires-python = "==3.12.*", Django 6.1.1 and every other dependency pinned with ==, a uv.lock, and three files under requirements/ that uv export generated from it. One dependency, django-push, is pinned to a git commit rather than a release, so the agent lists it separately: it cannot be compared to a version on PyPI. It then runs uv tree --outdated --depth 1 against the lock and keeps only the direct dependencies whose latest release is at least one major version ahead of the pin.

Finding the test command takes more than manage.py test. The Makefile has a test target that runs coverage run --source=. --module manage test --verbosity 2 over an APP_LIST of fifteen apps, from accounts to tracdb. The project's own CI in .github/workflows/tests.yml shows what that needs: Postgres with two databases, djangoproject and code.djangoproject, the second loaded from tracdb/trac.sql, and a conf/secrets.json one directory above the checkout, which djangoproject/settings/common.py reads from DATA_DIR. The agent sets up the same databases in its sandbox, writes the same throwaway secrets file outside the repository, and runs make test. When the Playwright tests need browsers, it installs them the way the workflow does, with python -m playwright install --with-deps chromium firefox.

For risky patterns it reads code rather than grepping and stopping. It searches for the usual Django suspects (mark_safe, csrf_exempt, .raw(, .extra(, DEBUG = True, hard-coded secrets, |safe in templates) and opens each hit to judge it. In this repository the @csrf_exempt on receive_webhook in fundraising/views.py sits above a stripe.Webhook.construct_event call that verifies the signature, so it is a deliberate exemption, not a finding. The mark_safe(output) in docs/templatetags/docs.py and the mark_safe( help text in members/models.py are checked for whether any user-supplied value reaches them. Only patterns the agent can explain go in the list, each with its file, line and a one-sentence reason, up to the ten the prompt allows.

The prompt forbids changing files, and the agent works on a fresh clone in its own sandbox, so nothing it does reaches the customer's repository. It ends the turn with the JSON as its final answer: three arrays, outdated_dependencies, tests and risky_patterns, in the field names the prompt gives.

Start it with one API call

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

{
  "agent": "claude",
  "input": "Audit this Django repository without changing any file. Return only JSON with three arrays. outdated_dependencies: each direct dependency whose latest release is at least one major version ahead of its pin, with name, pinned, latest and the file that pins it; list git-pinned dependencies with latest set to null. tests: each test command the project defines (Makefile, tox, CI workflow), with the command, the setup it needed, passed true or false, and the last 20 lines of output when it failed. risky_patterns: up to ten patterns such as mark_safe on user input, csrf_exempt without another check, raw SQL with string formatting or secrets in settings, each with file, line, pattern and a one-sentence reason. Set up whatever databases and config files the tests need inside the sandbox, outside the repository.",
  "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 the agent's final answer, the JSON, in the session's result. The run.completed webhook carries it in data.result when it fits in 16 KiB; a larger answer is left out of the webhook, and your server reads it from data.session_url with the key. Each tests entry says which command ran and what it needed (for this repository, the two Postgres databases, the Trac dump and the secrets file), so a failure caused by missing setup can be told apart from a failing test. Each risky_patterns entry points at a file and line your customer can open.

Before showing the audit to the customer, your product should validate the JSON against the three arrays it expects and treat anything else as a failed audit. A person reviewing a sample of audits checks that outdated_dependencies lists only direct dependencies, that the git-pinned ones are marked as such, and that each risky pattern has a reason specific to the code rather than a generic warning. The transcript, GET /o/{org}/sessions/{id}/messages, has every command and its output if a customer asks how a result was reached.

Run it on every customer who connects a Django repository

When a customer connects a repository in your product, your backend sends POST /o/{org}/sessions with that repository and this prompt, and stores the execution_id from the 202 next to the customer's id: the request has no metadata field, so that mapping is yours. Send an Idempotency-Key built from the customer and the commit, so a retried request returns the audit already running. A scheduled job can repeat the audit weekly for every connected repository, up to the key's max_concurrent runs at once (4 by default; one more is 429).

Set a webhook_url on the key. Rush POSTs run.completed, run.failed or run.cancelled when the turn ends, signed per Standard Webhooks, and retries three more times with the same webhook-id if your endpoint does not answer 2xx within 5 seconds. Your handler verifies the signature, looks up the customer by execution_id, parses data.result and saves the audit.

The use case this job follows, with its automation and trigger: Run an agent per customer task.

Why run it on Rush?

Running a stranger's Django test suite on your own servers means installing their dependencies, creating their databases and executing their code next to everyone else's. Each Rush session gets its own sandbox, so one customer's make test, Postgres databases and secrets file never share a machine with another's, and a test that does something unexpected stays inside that one sandbox.

An audit is worth what its evidence is worth. The tests entries come from commands that ran, the outdated list from the lockfile and the package index, and the risky patterns from files the agent opened, with every step kept in the transcript. Your product can show the customer the JSON and, when they question a line, the exact command behind it.

Frequently asked questions

How does the agent get access to my customer's repository?

The repository in resources must be one your org's Rush GitHub App installations cover; GET /o/{org}/github lists them, and a repository none covers is refused with 422 RESOURCE_NOT_ACCESSIBLE before a sandbox starts. Installing the App needs a person on github.com, so it fits naturally into your customer's onboarding.

What if the tests need services the repository does not describe?

The agent reads the CI workflow, docker-compose.yml, the Makefile and the settings to find what the suite needs, and sets it up in the sandbox. When it cannot, the tests entry says passed: false with the output, and the setup it tried, so the customer sees a setup gap rather than a false failure.

Can the agent change the customer's code?

The prompt tells it not to, and it works on its own clone in the sandbox. The audit opens no pull request. If you want fixes, send a follow-up to the same session asking for a pull request for one finding; the sandbox still has the clone.

Can I enforce the JSON shape?

The request has no schema field (output_schema is refused with 400 FIELD_REMOVED), so the shape lives in the prompt. Validate the answer on your side and, if it does not parse, send a follow-up to the same session asking for the JSON again.