All jobs

Turn Labeled GitHub Issues Into Pull Requests for a Rails App

Yes. Label a GitHub issue on a Rails app and an agent reproduces it with an RSpec request spec, fixes the code, runs rspec and opens a pull request that closes it.

Run this on RushIn a Project that has your repository.

What the agent does

The agent starts from the labelled issue, not from a generic Rails layout. In a community site shaped like lobsters/lobsters it reads .ruby-version, which pins Ruby 4.0.0 and which the Gemfile reads through ruby file, then the Gemfile itself: sqlite3 with force_ruby_platform, solid_queue and solid_cache, rack-attack for rate limiting, lograge for JSON request logs, sentry-rails, and rspec-rails with factory_bot_rails for tests. config/database.yml.sample shows four SQLite databases per environment, primary, cache, queue and rack_attack, so a schema change touches db/schema.rb for the primary database only. The issue text decides the file: a story submission or voting bug sits in app/controllers/stories_controller.rb, 517 lines with create, update, upvote, flag, hide, save and check_url_dupe among its actions, and a comment or reply bug sits in app/controllers/comments_controller.rb, 464 lines with create, reply, edit, update, delete, undelete, disown, upvote and flag.

Both controllers guard their actions with before_action filters such as require_logged_in_user_or_400, verify_user_can_submit_stories and find_user_story, so a report that says a user could do something they should not have usually points at a filter list rather than at the action body. The agent reads the filter chain at the top of the controller and the private helpers at the bottom (story_params, update_story_attributes, find_comment, render_created_comment) before it touches the action the report names, because the fix often belongs in the helper.

The test comes before the fix. spec/requests/stories_spec.rb is the pattern: a request spec that builds a user and a story with factory_bot, signs in with sign_in, posts to a path such as /stories/check_url_dupe.json and asserts on the status, the body and, for JSON, the parsed response. spec/requests/comments_spec.rb and spec/controllers/comments_controller_spec.rb cover the other controller the same way, and spec/factories has a factory for story, comment, user, vote, tag and the rest. The agent adds a case that fails with the reported behaviour, in the request spec for the controller it changes, then changes the code until it passes.

It runs what CI runs. .github/workflows/check.yml sets RUBYOPT to enable YJIT and frozen string literals, installs libvips, copies the templated credentials from .github/workflows/check into config, copies database.yml.sample to database.yml, runs ./bin/rails db:schema:load db:migrate and then bundle exec rspec. Separate jobs run bundle exec standardrb, brakeman and database_consistency. The Makefile's test target is the same rspec run followed by brakeman, and its lint target is standardrb --fix-unsafely. The agent follows those steps in the sandbox, runs standardrb on the files it changed, and opens a pull request whose body starts with Closes and the issue number and explains the cause.

Start it with one API call

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

{
  "agent": "claude",
  "input": "Fix GitHub issue #ISSUE in this Rails repository. Read the issue and its comments first. Start from .ruby-version, the Gemfile and config/database.yml.sample, then find the controller the report names, app/controllers/stories_controller.rb or app/controllers/comments_controller.rb, and read its before_action chain and private helpers before changing an action. Add a request spec case under spec/requests that reproduces the reported behaviour with the existing factories and sign_in helper, confirm it fails, then fix the code until bundle exec rspec passes after ./bin/rails db:schema:load db:migrate, as .github/workflows/check.yml runs it, and bundle exec standardrb passes on the files you changed. Do not change .ruby-version, the Gemfile.lock or any workflow file. Open a pull request whose body starts with Closes #ISSUE, explains the cause, lists the files and quotes the rspec summary.",
  "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 a pull request on a new branch. The diff is the controller the issue pointed at, sometimes a model or a helper it calls, plus one case added to the matching request spec. The body starts with Closes and the issue number, names the cause in a sentence, lists the files, and quotes the rspec summary and the standardrb result from the sandbox. The Gemfile.lock, .ruby-version and the workflows are unchanged unless the issue itself asked for them.

The session read names the repository and the pull request URL. The transcript at GET /o/{org}/sessions/{id}/messages keeps each tool call: the issue read, the files opened, the schema load, and each rspec invocation with its output, including the first run where the new case fails. A reviewer does not have to re-run the suite to see what the agent ran.

Before merging, a reviewer checks that the new case fails on the old code and passes on the branch, that a permission fix went into the before_action chain or helper rather than a one-off check inside the action, that brakeman has nothing new to say, and that the GitHub keyword in the body points at the right issue, since it closes the issue on merge.

Run it on every labelled GitHub issue

Add an automation to the Project with this prompt and a github trigger on issues.labeled, with the label you reserve for the agent in its labels. Only a label applied by an owner, member or collaborator fires it. Each labelled issue starts one run on its own branch, and the trigger adds the issue event after the prompt, so the agent reads the number from it. From your own server, the same run starts with this request when your GitHub webhook handler sees the label, with an Idempotency-Key derived from the delivery id so a retried delivery returns the same session.

Give the API key a webhook URL and Rush POSTs run.completed, run.failed or run.cancelled when the turn ends, signed with the whsec_ secret, with data.session_url to read for the pull request. A key runs at most max_concurrent sessions at once, 4 by default, and answers 429 past that.

The use case this job follows, with its automation and trigger: Turn a Linear label into a PR.

Why run it on Rush?

Reproducing a controller bug on a laptop means installing Ruby 4.0 and libvips, templating the credentials the way the check workflow does, loading four SQLite schemas and remembering that the suite wants YJIT and frozen string literals on. The agent does that in its own sandbox with its own clone, and the sandbox parks with its disk when the turn ends, so a follow-up from the review picks up the same environment.

The proof is the pull request plus the transcript, not a claim that the tests passed. The exact rspec output, the failing first run and the spec file the new case landed in are kept on the session. Several labelled issues run at once, each in its own sandbox with its own pull request, up to the key's concurrency limit.

Frequently asked questions

Will it edit the credentials or database templates CI copies in?

No. The files under .github/workflows/check and config/database.yml.sample are copied into place in the sandbox the way check.yml does it, and they stay as they are in the pull request. A fix that needs a new credential is reported as a blocker instead.

Does it run brakeman and standardrb as well as rspec?

It runs standardrb on the files it changed, because CI fails on a style offence. Brakeman and database_consistency run in CI on the pull request; ask for them in the prompt when you want their output in the transcript too.

What if the bug is in a job rather than a controller?

Then the fix goes under app/jobs, where solid_queue jobs such as notify_comment_job and cache_page_job live, with a spec under spec/jobs. Name the job in the issue so the agent starts in the right place.

How does the issue text reach the agent?

A handler you run writes the issue number into input in place of #ISSUE; an automation's github trigger instead adds the issue event after the prompt and replaces nothing, and resources clones the repository through the Rush GitHub App installation. The agent then reads the issue and its comments from the repository's issue tracker with the credentials the installation provides.