# Add a Feature to a Flask App from a Customer Spec

Yes. Your product sends the customer's spec in one API call; an agent builds it in their Flask repo with tests and opens a pull request.

[Run this on Rush](https://cloud.getrush.ai/run/add-feature-per-customer/flask/), in a Project that has your repository.

## What the agent does

The request comes from your product, not from a developer at a terminal: a customer writes a short spec, and your server sends it to `POST /o/{org}/sessions` with the customer's repository in `resources`. The agent clones that repository into its own cloud sandbox and reads it before writing anything. In a Flask app like miguelgrinberg/microblog, `requirements.txt` pins `Flask==3.0.0`, `Flask-SQLAlchemy==3.1.1`, `SQLAlchemy==2.0.23`, `Flask-Login==0.6.3` and `Flask-WTF==1.2.1`, the app is built by `create_app` in `app/__init__.py` with blueprints under `app/main`, `app/auth`, `app/api` and `app/errors`, and the tests live in one file, `tests.py`, a `unittest` suite with a `TestConfig` that uses `sqlite://`.

The spec here is: let a user export their own posts as a CSV download from their profile page. The agent finds that something close already exists, and that matters more than any new code. `app/main/routes.py` has an `/export_posts` route that calls `current_user.launch_task('export_posts', ...)`, and `app/tasks.py` runs it on an RQ worker backed by Redis (`rq==1.15.1`, `REDIS_URL` in `config.py`), sleeping five seconds per post and emailing a `posts.json` attachment. `app/templates/user.html` shows the "Export your posts" link only while no export task is in progress. The pull request has to say how the new download relates to that flow instead of quietly adding a second export.

So it builds the download beside the existing pattern. A new route in `app/main/routes.py`, `@bp.route('/export_posts.csv')` with `@login_required`, selects only `current_user`'s posts with `user.posts.select().order_by(Post.timestamp.asc())` (the same query the task uses), writes them with the standard library's `csv` module to a `Response` with `mimetype='text/csv'` and a `Content-Disposition: attachment` header. Columns are the post's `timestamp` in ISO format with a `Z`, as the JSON export writes it, and `body`. A link next to the existing one in `user.html` appears only on the signed-in user's own profile, and the label goes through `_()` like every other string, so Flask-Babel can translate it.

Tests go into `tests.py` in its style: a new `TestCase` that creates two users with posts, signs one in through the test client, requests the CSV, and asserts the status, the content type, the header row, that only that user's posts are present and in order, and that a signed-out request redirects to the login page. The agent runs `python -m unittest tests` for the whole suite and opens the pull request with the three bullets the spec asks for.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Build this feature: let a user export their own posts as a CSV download from their profile page. Read the app first and follow its patterns for routes, blueprints, templates, translations and tests. If the app already has an export of any kind, keep it working and explain in the pull request how the new download relates to it. Only the signed-in user's own posts may be exported; a signed-out request must go to the login page. Add tests in the project's existing test style covering the download's content, ordering, that another user's posts are never included, and the signed-out case. Run the full test suite and open a pull request that explains the change in three bullets.",
  "resources": [
    {
      "url": "https://github.com/your-org/customer-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 a pull request on the customer's repository that changes `app/main/routes.py`, `app/templates/user.html` and `tests.py`, and no other file. Its description is the three bullets the spec asked for: what the route does, how it relates to the existing emailed JSON export (left unchanged), and which tests cover it. Your product can show those bullets to the customer as they are.

The session read gives your server everything it needs to report back: the status, the pull request and the final answer in `result`, with the transcript of every command in the sandbox, including the `python -m unittest tests` run and its output. Any checks the customer's repository runs on pull requests run on this one too.

Before the customer merges, a reviewer on their side checks the one question that matters for a data export: whether the query can ever return another user's posts. They also decide whether the old emailed export should stay, since two exports with different formats is a product choice, not a code one.

## Run it on every customer feature request

Each request from your product starts one run: one customer, one repository in `resources`, one spec in `input`. Send an `Idempotency-Key` built from your own request id, so a retry after a network error returns the run already started instead of building the feature twice.

Set a webhook URL on the API key and Rush posts `run.completed` or `run.failed` for each run with its `execution_id` and `session_url`. Your handler matches the id to the customer's request, reads the session for the pull request, and updates the customer's ticket. A key runs at most `max_concurrent` sessions at once, 4 by default, and answers `429` past that, so queue requests during busy hours.

## Why run it on Rush?

When your product hands customer work to an agent, each customer's code has to stay apart from every other customer's. Each Rush run gets its own sandbox with only the one repository its request names, cloned through your GitHub App installation, so one customer's code never sits on a machine with another's.

Your customer gets a pull request with tests they can run and a transcript of what the agent did, which is the proof an agency or a dev-tools product needs before billing for the work. Your server starts as many runs as customers send, up to the key's limit, without a laptop or a person in the loop.

## Frequently asked questions

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

The customer installs the Rush GitHub App on their repository once, which needs a person on github.com. After that, `GET /o/{org}/github` lists the repositories the key can reach, and any of them can go in `resources`.

### What if the spec conflicts with code that already exists?

The agent reads the repository before building, and the prompt tells it to keep existing features working and explain the overlap. In microblog that means the emailed JSON export stays as it is and the pull request says how the CSV download differs.

### Can I send a follow-up when the customer wants a change?

Yes. `POST /o/{org}/sessions/{id}/messages` with the change as `input` wakes the same sandbox where it left off, with the branch and the work already there, and the agent updates the same pull request.

### Does the customer see the agent's whole transcript?

Only if you show it. The transcript is available to your server through `GET /o/{org}/sessions/{id}/messages`, and your product decides what to pass on.
