All jobs

Find Untested Code in a Wagtail App

Yes. An agent runs your Wagtail tests with coverage in a sandbox and ranks the ten riskiest untested files, with the test that would cover each.

What the agent does

The agent clones the repository into its own cloud sandbox and sets it up the way its CI does. In a Wagtail site like wagtail/bakerydemo it reads requirements/base.txt and finds Django>=6.0,<6.1 and wagtail>=8.0,<8.1, sees Python 3.12 in .python-version, and follows .github/workflows/ci.yml: uv venv, uv pip install -r requirements/development.txt, then ./manage.py check and ./manage.py test with DJANGO_SETTINGS_MODULE=bakerydemo.settings.test. That settings module swaps in an in-memory cache, an immediate task backend and the MD5 password hasher, and with no DATABASE_URL set the site uses SQLite, so the suite needs no services.

Nothing in the requirements measures coverage, so the agent installs coverage in the sandbox only and runs coverage run --source=bakerydemo ./manage.py test, then coverage report -m for the missed lines per file. In bakerydemo the tests are two modules, bakerydemo/base/tests/test_home_page.py and bakerydemo/base/tests/test_seo.py, so most of blog, breads, locations, people, recipes and search is expected to show up as uncovered, and the report says which apps have no tests at all.

Raw missed-line counts are not a ranking, so the agent weighs each file by what it does and how much depends on it. It counts imports of each module across the project and reads the code. In bakerydemo that brings forward base/models.py (671 lines, including FormPage, an AbstractEmailForm that stores submissions and sends mail), base/management/commands/reset_demo.py (deletes every image and document, then calls reset_schema and migrate), reset_admin_password.py (sets the admin user's password from settings), base/api.py, whose PersonApiViewSet is registered as the public persons endpoint in bakerydemo/api.py, the clean() on the location opening hours in locations/models.py, and search/views.py, which paginates results 10 to a page over a hard-coded list of page models.

For each of the ten files it picks, the agent writes one sentence naming the test that would cover it, in the style the existing tests use (WagtailPageTestCase with focused page setup rather than the full demo fixture, as the repository's AGENTS.md asks). It changes no code and opens no pull request.

Start it with one API call

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

{
  "agent": "claude",
  "input": "Clone https://github.com/wagtail/bakerydemo. Set it up as .github/workflows/ci.yml does (uv venv, uv pip install -r requirements/development.txt) and install coverage in the sandbox only. Run coverage run --source=bakerydemo ./manage.py test with DJANGO_SETTINGS_MODULE=bakerydemo.settings.test, then coverage report -m, and keep the output. Write coverage-gaps.md with: overall coverage and per-app coverage; apps with no tests; the ten files with the most uncovered lines that matter (data writes, deletes, auth, public API, form submissions, validation), ranked by risk and by how many modules import them; and for each, one sentence on the test that would cover it, in the style of the existing tests. Change no code, do not push and do not open a pull request."
}
  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

The session returns coverage-gaps.md as an artifact. It starts with the overall line coverage that coverage report printed and a per-app table, then the list of apps with no tests, then the ten ranked files. Each ranked file has its missed line ranges, why it is risky (a destructive management command, a public API endpoint, a form that stores submissions), how many modules import it, and the test to write.

The full coverage report -m output is in the session's transcript with every other command the agent ran, so the percentages can be checked against the tool that produced them. Before acting on the list, a reviewer checks the risk ranking against what the team knows: a management command only an operator runs may matter less than a page type editors use every day. The list is written to be handed to a test-writing run next, one file at a time.

Run it on every release branch cut

Coverage moves slowly, so this report is most useful at a fixed point: when a release branch is cut, or once a month. A GitHub Actions job on the release tag, or an automation with a schedule trigger in a Project, can send the request so each report reads the code as it was released.

When the turn finishes, Rush POSTs run.completed to the key's webhook. Your server can read the session, compare the new overall number with the last report's, and start a second run that writes tests for the top file on the list.

The use case this job follows, with its automation and trigger: Produce a report or file, no repo.

Why run it on Rush?

Running a Django suite under coverage on a laptop means the right Python, a fresh virtualenv and a database that matches the test settings, and measuring a branch you are not working on means switching away from your own work. The Rush sandbox has its own disk, installs what the CI file lists, and runs the suite without touching your checkout.

The ranking is built from a real coverage run and a real read of the imports, and both are kept in the transcript, so it is evidence rather than a guess about which code feels untested. Several apps or repositories can be measured at once, up to the key's concurrent run limit.

Frequently asked questions

Does it need Postgres or Elasticsearch?

Not for bakerydemo. With no DATABASE_URL, its settings fall back to SQLite, and the test settings use an in-memory cache. Elasticsearch is only in requirements/production.txt. If your app's tests need Postgres, say so in the prompt and the agent installs and starts it in the sandbox.

Will it add coverage to my requirements?

No. It installs coverage in the sandbox only and changes no file in the repository. If you want coverage in CI afterwards, ask in a follow-up and it can open a pull request for that.

How does it decide which untested code matters?

It weighs missed lines by what the code does (data writes and deletes, authentication, public API endpoints, form handling, validation) and by how many other modules import it. The report gives the reason for each pick so you can disagree with it.

Can it write the tests too?

Yes, as a separate step. Send a follow-up to the same session asking for tests for the first file and a pull request; the sandbox still has the environment set up. Starting a new run per file keeps each pull request small.

My Wagtail site is in a private repository. What changes?

Install the Rush GitHub App on it and add the repository to the request as a resource, {"url": "https://github.com/your-org/your-app"}, instead of naming a public URL in the prompt. The agent starts with it already cloned and follows the same steps.