# Fix Jira Bug Tickets With Pull Requests in a Laravel App

Yes. An agent reads the Jira bug, reproduces it with a PHPUnit test in your Laravel app, fixes it, opens a pull request and comments the link on the ticket.

[Run this on Rush](https://cloud.getrush.ai/run/fix-jira-bugs/laravel/), in a Project that has your repository.

## What the agent does

A Jira automation rule posts each new bug's key and summary to a handler on your server, which sends the request on this page. The agent clones the repository into its own cloud sandbox, then reads the ticket through your Atlassian connection: the description, the steps, the browser or API client the reporter used, and the comments. In a CRM the ticket usually names an action ("moving a contact to another vault loses its labels") rather than a file, so the comments that say which screen or which vault are what the agent uses to find the code.

In a Laravel app organised by domain, like monicahq/monica, that action maps to a small set of classes. Contact behaviour sits in `app/Domains/Contact/`, split into folders such as `ManageContact`, `ManageContactAddresses`, `ManageLabels`, `ManageNotes` and `ManageRelationships`. `ManageContact/Services/` holds one service per action (`CreateContact`, `UpdateContact`, `MoveContactToAnotherVault`, `CopyContactToAnotherVault`, `ToggleArchiveContact` and others), and `ManageContact/Web/Controllers/` the controllers that call them. Every service extends `BaseService`, declares `rules()` for validation and `permissions()` such as `vault_must_belong_to_account`, `contact_must_belong_to_vault` and `author_must_be_vault_editor`, and runs `$this->validate()` before `execute()` does any work. A bug is either in those checks or in what `execute()` does after them, and the agent reads both.

It reproduces the bug with a PHPUnit test where the existing ones live. Monica mirrors the domain folders under `tests/Unit/Domains/`, so a bug in `MoveContactToAnotherVault` gets a case in `tests/Unit/Domains/Contact/ManageContact/Services/MoveContactToAnotherVaultTest.php`. Those tests extend `Tests\TestCase`, use `DatabaseTransactions`, build accounts, vaults and contacts from factories, and assert on `ValidationException`, `ModelNotFoundException` or `NotEnoughPermissionException` for the refusal cases. `phpunit.xml` sets `DB_CONNECTION=testing`, and `config/database.php` points that connection at SQLite in `database/database-test.sqlite` unless `DB_TEST_DRIVER` says otherwise.

With the test failing for the reason the ticket describes, the agent fixes the service or controller and runs `php artisan test` until the suite passes. It opens a pull request with the ticket key in the title and branch name, so Jira's development panel shows it, and comments on the ticket with the link and the cause in one sentence.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "A Jira bug ticket was created; its key and summary follow this prompt. Use the Atlassian connection to read the ticket, its description and all comments. Find the code in the contacts domain under app/Domains/Contact/: the service in the matching Manage* folder (its rules(), permissions() and execute()) and the controller that calls it. Reproduce the bug with a failing PHPUnit test in the mirrored folder under tests/Unit/Domains/Contact/, using DatabaseTransactions and the model factories like the existing tests. Fix the code and run php artisan test until it passes. Open a pull request with the ticket key in its title and branch name, then comment on the ticket with the pull request link and the cause in one sentence. If you cannot reproduce it, comment with what you tried and open no pull request.",
  "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 named after the ticket. In monica it usually changes one service under `app/Domains/Contact/` (a missing permission check, a relation not copied when a contact moves vault, a validation rule that is too strict) and adds a test to the matching `*Test.php` under `tests/Unit/Domains/Contact/`. The description names the service and method at fault, shows the test that failed before the change, and gives the `php artisan test` result.

The Jira ticket gets one comment with the pull request link. The pull request's checks run your workflows; monica's `tests.yml` calls its shared Laravel workflow on PHP 8.3 and 8.4 with SQLite as the default connection, and `static_analysis.yml` and `lint.yml` run beside it, so a fix that passes the tests but breaks a type or the Pint style shows up there. The transcript keeps every command the agent ran and its output.

Before merging, a reviewer checks that the fix sits in the service rather than in the controller (other callers, such as the DAV import, go through the same services), that no permission in `permissions()` was loosened to make the test pass, and that the new test fails on `main`. When the bug could not be reproduced, there is no pull request and the ticket comment says why.

## Run it on every new Jira bug

Add a Jira automation rule with the trigger "Work item created", a condition on issue type Bug, and the action "Send web request" to your handler with `{{issue.key}}` and `{{issue.summary}}` in the body. The handler sends one request per ticket with them appended to `input` and your Atlassian connection named in `connections`. Filter on a component such as Contacts if only part of the app should go to the agent.

Set a webhook URL on the API key and Rush POSTs `run.completed` or `run.failed` with the `session_url` when the turn finishes, which the handler can turn into a Slack message or a Jira transition. A key runs at most `max_concurrent` sessions at once (4 by default) and answers `429` past that.

## Why run it on Rush?

Reproducing a Laravel bug needs PHP 8.3 or later, Composer, the app's extensions and a migrated test database. In a Rush sandbox the agent installs them and runs `composer install` and `php artisan test` on its own machine, so a ticket never waits for someone to set up the right PHP version or reset their local database, and two tickets never share a half-migrated one.

The result is checkable without trusting the agent: the failing and passing test runs are in the transcript, the PHP 8.3 and 8.4 matrix and static analysis run on the pull request, and the Jira comment closes the loop for the reporter. Tickets that arrive together run in parallel, each in its own sandbox.

## Frequently asked questions

### Does it run against MySQL or SQLite?

SQLite by default, because `phpunit.xml` selects the `testing` connection and `config/database.php` gives it the SQLite driver unless `DB_TEST_DRIVER` is set. If the bug only shows on MySQL, say so in the prompt and the agent installs MySQL in its sandbox and sets the `DB_TEST_*` variables.

### Will it change permission checks to make a test pass?

It should not, and the prompt asks for the fix where the bug is. A reviewer still reads the diff to `permissions()` and `rules()` in particular, because loosening one of them is the easy wrong fix for a refusal bug.

### Can the ticket link go in resources?

No. `resources` accepts a GitHub repository or a Linear issue, and a Jira URL is `422 RESOURCE_UNSUPPORTED`. Send the key in the prompt and name your Atlassian connection; the agent reads and comments on the ticket through it.

### Does it also run the front-end build?

Only if the bug is in the Vue pages under `resources/js`. Then it installs with Yarn from `yarn.lock` and builds with Vite before running the tests; for a bug in a service it runs only `php artisan test`.
