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 with this body:
{
"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"
}
]
}- 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
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.
The use case this job follows, with its automation and trigger: Fix a bug from an alert.
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.