What the agent does
The agent clones the repository into its own cloud sandbox and reads how the app is installed and tested before it writes a line of YAML. In a Flask app like miguelgrinberg/microblog there is no .github directory at all. requirements.txt pins everything: Flask 3.0.0, Flask-SQLAlchemy 3.1.1, SQLAlchemy 2.0.23, Flask-Migrate 4.0.5, flask-babel 4.0.0, gunicorn 21.2.0, psycopg2-binary 2.9.9, redis 5.0.1, rq 1.15.1 and elasticsearch 8.11.0. .flaskenv sets FLASK_APP=microblog.py. The only test module is tests.py, a unittest.TestCase named UserModelCase with four tests, and it ends in unittest.main(verbosity=2), so python -m unittest -v tests is the command. There is no .python-version, and the Dockerfile builds from python:slim without a tag, so no file names a Python version.
It then works out what the tests need from the machine. tests.py defines a TestConfig that sets SQLALCHEMY_DATABASE_URI to sqlite:// and ELASTICSEARCH_URL to None, so the suite needs neither PostgreSQL nor Elasticsearch. app/__init__.py creates a Redis connection from REDIS_URL, which defaults to redis:// in config.py, but Redis.from_url opens no socket until a command is sent and no test enqueues work, so the workflow needs no Redis service either. config.py falls back to a SQLite file, app.db, when DATABASE_URL is unset, which means flask db upgrade can run the Alembic migrations in migrations/versions against a throwaway file as a check that they still apply.
With that read, it writes .github/workflows/ci.yml: on push to main and on pull_request, one job on ubuntu-latest that checks out the code, sets up Python with actions/setup-python and cache: pip keyed on requirements.txt, runs pip install -r requirements.txt, then flask translate compile (the step the Dockerfile also runs, because app/cli.py shells out to pybabel compile -d app/translations and a broken .po file should fail here, not at deploy), then flask db upgrade against the SQLite fallback, then python -m unittest -v tests. It pins the Python version in the workflow to the one it installed in the sandbox and says in the pull request why it chose that one, since the repository itself does not pick.
Before opening the pull request it runs every step itself in the sandbox, in the same order the workflow will, and reads the output. If pip install fails on a pinned wheel for the chosen interpreter it changes the version rather than loosening a pin, because the pins are the app's contract. It pushes the branch, waits for the workflow to run on GitHub, and reads the run's log through the GitHub App. A red run means it reads the failing step, fixes the workflow, pushes again and waits again; it does not finish on a red check.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "Set up GitHub Actions CI for this Flask app. Read requirements.txt, .flaskenv, config.py and tests.py to learn how it installs and tests. Add .github/workflows/ci.yml that runs on push to main and on pull requests: set up Python with pip caching keyed on requirements.txt, install the pinned requirements, run `flask translate compile`, apply the migrations with `flask db upgrade` against the SQLite fallback, and run `python -m unittest -v tests`. Add no service containers the tests do not need, and change no application code or pins. Run every step here first, open a pull request, then wait for the workflow run on the pull request and fix the workflow until it is green.",
"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 that adds one file, .github/workflows/ci.yml, with a green check from the workflow it added. The description lists the steps in order, names the Python version it pinned and why, and quotes the lines from tests.py and config.py that justify running without PostgreSQL, Elasticsearch or Redis. Nothing under app/, migrations/ or requirements.txt changes.
A reviewer opens the workflow run on the pull request and reads the test step: four tests from UserModelCase, each named, with OK at the end, because the command uses -v. They check that the flask translate compile step compiled the catalogs under app/translations and that flask db upgrade walked every revision in migrations/versions. The session's transcript holds the same commands run in the sandbox first, with their output, so the reviewer can compare the two.
If a step cannot be made to pass without touching application code, the agent does not touch it. It says so in the pull request and in its final answer, with the output of the failing step, and leaves that step out of the workflow or marks it as allowed to fail, whichever the prompt asked for.
Run it on every repository with no .github directory
A script on your server lists the organization's repositories through GET /o/{org}/github, which returns every repository your Rush GitHub App installation can reach, checks each for a .github/workflows directory, and sends one POST /o/{org}/sessions per repository that has none, with that repository as the resources URL. Each run works in its own sandbox and opens its own pull request, so ten Flask services get ten workflows and ten reviews, not one sweeping change.
Give the API key a webhook_url and Rush POSTs run.completed to it when each turn finishes, signed per Standard Webhooks, with the session_url to read the pull request from. A run.failed event names the run that needs a person.
The use case this job follows, with its automation and trigger: Hand off from your terminal.
Why run it on Rush?
Writing CI for an app you did not write means guessing what the tests need until the workflow goes green, and each guess costs a push and a wait. The agent does the guessing in its own sandbox with the repository in front of it: it installs the pins, runs the suite, compiles the translations and applies the migrations before the first push, so the pull request usually arrives with one workflow run, not twelve.
The result carries its own proof. The pull request's check is the workflow the agent wrote, running on GitHub's machines, and the session keeps every command the agent ran first with its output. A reviewer compares the two and merges; nothing has to be taken on faith, and their own laptop never installed anything.
Frequently asked questions
Will it change my existing workflow files?
In a repository with no .github directory there is nothing to change. Where workflows exist, it adds a new file beside them rather than editing one, unless the prompt names the file to edit, and the pull request lists every file it touched.
The tests import the app, which creates Redis and Elasticsearch clients. Why no service containers?
Because the clients connect lazily. TestConfig in tests.py sets ELASTICSEARCH_URL to None, so create_app never builds an Elasticsearch client, and Redis.from_url opens no connection until a command runs, which no test does. The agent reads that before deciding, and says so in the pull request. If you later add tests that enqueue RQ jobs, a redis service block is the one-line change.
Which Python version does it use?
The repository does not say: there is no .python-version, and the Dockerfile uses python:slim with no tag. The agent pins the version it successfully installed the requirements under in the sandbox, names it in the pull request, and leaves it to you to change if production differs.
Does it need my production secrets?
No. config.py has defaults for everything the tests touch: SECRET_KEY falls back to a placeholder, the database to a SQLite file, and mail is never sent. The workflow sets no secrets, and the agent never sees yours.
Does it wait for the workflow to pass before it finishes?
Yes. After pushing it reads the workflow run on the pull request through the GitHub App, and if the run is red it reads the log, fixes the workflow and pushes again. A run it cannot make green without changing application code ends with that step's output in its final answer rather than a merged lie.