The extension tarballs ship no composer.lock, so `composer install --no-dev`
degrades to a full solve that still resolves require-dev, as composer states
itself. Both configured addons pull mediawiki-codesniffer 50.0.0 there, which
pins php_codesniffer 3.13.5, and once an advisory landed against that version
composer refused to load it and the solve became unsatisfiable. The extension
install failed with rc 2 without a single line of this repository changing.
Stripping require-dev from the manifest before composer sees it keeps the
solve on the runtime constraints, which is what --no-dev was asking for. The
stripper decodes to objects rather than associative arrays, because an empty
JSON object would otherwise round-trip into an empty array and composer
rejects `[]` where its schema wants `{}`.
The work that runs inside the container moved out of the embedded shell string
into install/helpers/, which removes three levels of quoting from the calling
script and makes both helpers runnable by hand for debugging. The whole helper
directory is copied in, so adding a helper means dropping a file in there and
nothing else.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>