What the agent does
The run starts from the repository URL in resources: the agent clones it into its own cloud sandbox and reads the app the way a new maintainer would. In an app like miguelgrinberg/microblog, requirements.txt pins Flask 3.0.0, Flask-Login 0.6.3, Flask-Mail 0.9.1 and Flask-SQLAlchemy 3.1.1, with no Flask-Limiter line today. app/__init__.py builds the app in create_app, registers the auth blueprint at /auth, and wires Redis plus an rq queue for background work.
Password reset already exists without a limit. app/auth/routes.py exposes GET/POST /reset_password_request, looks up the user by email, calls send_password_reset_email from app/auth/email.py, and always flashes the same check-your-email message. Tokens come from User.get_reset_password_token in app/models.py with a 600 second expiry. Nothing counts how often a given email address hits the route.
The agent adds Flask-Limiter to requirements.txt, initializes it in create_app, and limits reset_password_request to 3 requests per hour keyed by the submitted email (falling back to remote address when the form is empty). When the limit trips, the route shows a friendly flash or error page instead of sending another message. The success path and the generic check-your-email flash for unknown emails stay, so the limit does not become an email oracle.
Proving it means extending tests.py, which is a unittest module with TestConfig pointing SQLAlchemy at sqlite:// and disabling Elasticsearch. The agent adds cases that post the reset form four times for one email and assert the fourth is rejected with the friendly message, while a different email still succeeds. The issue says to run pytest; the agent can run pytest tests.py (unittest-compatible) or adjust the suite so that command is the one CI documents.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "A Linear issue labeled agent says: \"Rate Limit Password Reset in a Flask App. Allow at most 3 reset password requests per email per hour with Flask-Limiter and show a friendly message when the limit is hit.\" Do the work in this repository, add tests for it, run pytest, and open a pull request that links back to the issue.",
"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. Its diff updates requirements.txt, app/__init__.py and app/auth/routes.py, and extends tests.py with the per-email limit cases. The description quotes the issue and links back to it.
The checks on the pull request run pytest against the suite. A reviewer reads the 3-per-hour limit, confirms the friendly message on the fourth attempt, and confirms unknown emails still get the generic flash without revealing whether the address exists. Login and register routes stay unlimited unless the issue asked otherwise.
The session read keeps the record behind the pull request: the install of Flask-Limiter, pytest output and the four-request fixture. Anyone who questions the keying by email can see the form posts the agent used.
Run it on every Linear issue labelled agent
The Project's automation turns the label into the run. In the console, add an automation with a Linear trigger on linear.issue.labeled filtered to the agent label and this prompt; each labelled auth hardening issue starts its own run in its own sandbox and opens its own pull request.
If the automation is not wanted, a small server can do the same: receive Linear's issue webhook, put the issue text into input, and POST to /o/{org}/sessions with the microblog repository in resources. A run.completed webhook on the API key posts the pull request when pytest finishes.
The use case this job follows, with its automation and trigger: Turn a Linear label into a PR.
Why run it on Rush?
On a laptop this job needs a virtualenv, the pinned Flask stack from requirements.txt, and Redis if other app factory paths import it at import time. Each run on Rush boots its own sandbox with that already in reach, runs pytest itself, and keeps every command and its output, so the proof of the fourth-request rejection is the run's own record.
Auth rate limits are the kind of fix that waits until after an incident. Labelling the Linear issue starts a sandbox that adds Flask-Limiter and the tests without blocking whoever is mid-migration on the same microblog checkout.
Frequently asked questions
Is the limit per IP or per email?
Per email, as the issue says: at most 3 reset requests per email per hour. The agent keys Flask-Limiter on the submitted address so a shared NAT does not lock out every user.
Does Flask-Limiter need Redis in tests?
The agent can use an in-memory storage backend in TestConfig so tests.py keeps using sqlite without a Redis process. Production can keep Redis, which the app factory already configures for rq.
Will reset emails still hide whether an account exists?
Yes. Under the limit, unknown emails still get the same check-your-email flash. Only the rate-limit response is different, and it does not say whether the address was registered.
The suite is unittest. Why does the prompt say pytest?
pytest runs unittest TestCase modules without a rewrite. The agent runs pytest tests.py (or adds a pytest entry) so the command the issue names is what the session records.
Are login and register limited too?
No. The issue names password reset only. Other auth routes stay as they are in app/auth/routes.py.