# Fix Failing GitHub Actions Checks in a Symfony App

Yes. When a PHPUnit job goes red, an agent reruns the failing matrix entry on the same PHP and database in its sandbox, fixes the cause and opens a pull request.

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

## What the agent does

The agent reads the failed job before anything else. From the `workflow_run` event it runs `gh run view <run id> --log-failed` and names the matrix entry. In a Symfony app like wallabag/wallabag that matters, because `.github/workflows/continuous-integration.yml` runs PHP 8.2, 8.3 and 8.4 against SQLite, MySQL and PostgreSQL, nine combinations, plus a second job that repeats the run without the `wallabag_` table prefix, and each entry runs three PHPUnit suites in turn: `php bin/phpunit --testsuite unit`, then `--testsuite integration`, then `--testsuite functional`, each with a `SYMFONY_DEPRECATIONS_HELPER` that logs deprecations to a file the next step prints. A test that fails only on `pgsql` is usually a query that relied on MySQL ordering or casting; one that fails only on PHP 8.4 is usually a deprecation turned fatal. The agent quotes the failing test and names the entry.

Then it builds that entry. `composer.json` requires PHP 8.2 or later with `symfony/framework-bundle` 5.4, `doctrine/orm` 2.20, `friendsofsymfony/rest-bundle` 3.8 and `phpunit/phpunit` 9.6; the workflow installs the matching PHP with the `pdo_sqlite`, `pdo_mysql`, `pdo_pgsql`, `imagick`, `gd` and `tidy` extensions, Node from `.nvmrc` (24), and runs `yarn install` and `yarn build:dev` because the functional tests render pages that need built assets. `DATABASE_URL` selects the database; `.env.test` defaults to `sqlite:///%kernel.project_dir%/data/db/wallabag_test.sqlite`. `tests/bootstrap.php` does the rest: for the integration and functional suites it drops and recreates the database, runs `doctrine:migrations:migrate`, validates the schema and loads the fixtures with `doctrine:fixtures:load`, unless the run is a partial one. The `DAMA\DoctrineTestBundle` extension in `phpunit.xml.dist` wraps each test in a transaction.

With that in place the agent runs the failing test alone, such as `php bin/phpunit --testsuite functional --filter testGetEntriesByDomainName`, and then reads what it covers. The entry API is `src/Controller/Api/EntryRestController.php`: `getEntriesAction` on `GET /api/entries.{_format}` with its filters, `getEntryAction` on `/api/entries/{entry}.{_format}` behind `#[IsGranted('VIEW', subject: 'entry')]`, which rejects any `detail` other than `full` or `metadata` with a `BadRequestHttpException`, `postEntriesAction`, `patchEntriesAction`, `deleteEntriesAction` and the tag routes beneath them. The matching tests in `tests/functional/Controller/Api/EntryRestControllerTest.php` extend `WallabagApiTestCase`, which logs the fixture user `admin` in through `fos_user.user_manager` and `loginUser`. The agent reads the commit that broke the job as well, since the fix belongs where that commit changed behavior.

The fix goes in `src/`. A test is changed only when the test is wrong, and the pull request says so. Before pushing, the agent reruns the three suites on the failing database, then the functional suite on SQLite if the failure was elsewhere, and `make check-cs` and `make phpstan`, which the `coding-standards.yml` workflow runs, so one green job does not come at the cost of another.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "The GitHub Actions workflow on this branch failed; the workflow_run event is at the end of this prompt. Read the failed job's log with gh run view <run id> --log-failed, name the PHP version, database and PHPUnit suite of the failing matrix entry, and quote the failing test. Install that PHP version with the extensions the workflow lists, install with composer from the lockfile, run yarn install and yarn build:dev, set DATABASE_URL for the failing database (start MySQL or PostgreSQL in the sandbox if that is the one), and reproduce the failure with php bin/phpunit --testsuite <suite> --filter <test> so tests/bootstrap.php migrates and loads the fixtures. Fix the cause in src/ (change a test only if the test is wrong, and say so), rerun the three suites on that database, run make check-cs and make phpstan, and open a pull request against the failing branch that quotes the error it fixed.\n\nEvent: <workflow_run payload>",
  "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

A pull request against the failing branch, with the fix under `src/` and the PHP version, database, suite and quoted failure in the description. It shows the single test failing and then passing, then the three suites on the failing database, `make check-cs` and `make phpstan`, and lists every command the agent ran; the session's trajectory keeps each with its output. The workflow then reruns all nine entries and the no-prefix job on the pull request.

When the failure is a Symfony deprecation that only PHP 8.4 turns fatal, the description names the deprecated call and the replacement. When the failure belongs to a database the sandbox could not start, the agent says what it reproduced on SQLite and marks the other result as pending the workflow rerun. When there is no code cause, it opens nothing and its answer explains what the log shows.

A reviewer checks that a fix for one database does not change behavior on the other two, that an edited test was really wrong, and that `phpunit.xml.dist`, `.env.test` and the workflow are untouched.

## Run it on every failed Symfony test matrix

Add an automation to the Project with this prompt and a `github` trigger on `workflow_run.failure`, and each failed run of the `CI` workflow starts one run with the event under the prompt. A matrix produces one `workflow_run` for all nine entries, so one run reads every failed entry and the prompt asks it to name each. Or send GitHub's `workflow_run` webhook to your own server and send this request with `https://github.com/<owner>/<repo>/tree/<head_branch>` as the resource.

Exclude branches the agent opened so its own pull request's failure does not start a second run, keep each `workflow_run.id` with the returned `execution_id` to drop redeliveries, and use the `run.completed` webhook from Rush to comment the pull request link on the contributor's pull request.

## Why run it on Rush?

A nine-entry PHP matrix is the kind of failure nobody reproduces locally: the wrong PHP minor, a missing `pdo_pgsql`, assets not built, fixtures not loaded. The agent builds the exact entry from the workflow file in its own sandbox, with the database the entry used, and runs the suite the way the runner did.

What it hands back is checkable: the quoted failure, the trajectory with every `phpunit`, `check-cs` and `phpstan` run and its output, and the full matrix rerunning on the pull request. Failures on several branches each get their own run in parallel, and nothing merges without a reviewer.

## Frequently asked questions

### Can it reproduce a MySQL-only or PostgreSQL-only failure?

Yes. It starts that server in the sandbox, creates the `wallabag_test` database the way the workflow's setup steps do, sets `DATABASE_URL` to match, and lets `tests/bootstrap.php` migrate and load the fixtures. The pull request names the database it reproduced on.

### Why does it run yarn before PHPUnit?

Because the workflow does: the functional suite renders pages through the Symfony kernel, and those templates reference built assets. `yarn install` and `yarn build:dev` on the Node from `.nvmrc` make the sandbox match the runner.

### Will it silence a deprecation to make PHP 8.4 pass?

No. The prompt rules that out, and a change to `SYMFONY_DEPRECATIONS_HELPER` or `phpunit.xml.dist` would show in the diff. It replaces the deprecated call and the description names both the old and the new one.

### Does it run the whole functional suite every time?

It runs the failing test first with `--filter`, then the three suites on the failing database before it pushes. The second, no-prefix job is left to the workflow rerun, and the pull request says so.
