Appearance
date: 2026-06-11 tags: [composer, mutation, pest, ci, tooling] status: active graduated_to: composer.json
composer mutate dies at Composer's 300s process timeout (and a wrapper can mask it)
Symptom — a full local mutation run via composer mutate was killed mid-run ("process exceeded the timeout of 300 seconds"); worse, a trailing echo "EXIT $?" in the wrapping shell command reported the kill as a pass.
Root cause — the mutate script in composer.json omitted Composer\Config::disableProcessTimeout, which dev / ci:check / mutate:warm all carry. Composer's default 300s process timeout kills any full mutation run (the suite is minutes long). CI was unaffected — it invokes vendor/bin/pest --mutate directly, no Composer wrapper.
Fix — prepend Composer\Config::disableProcessTimeout to the mutate script (composer.json). When iterating locally, running vendor/bin/pest --mutate … directly also sidesteps it.
Guard — the disableProcessTimeout line now in composer.json's mutate script (the trap can't recur). And: never trust a composer <script> exit code read through a | tail/&& echo wrapper — check the script's own last line.