ci: pin symfony/console to ^7 — Infection mutation gate broken by Symfony Console 8#29
Conversation
symfony/console v8.1.0 (2026-05-29) crashes Infection 0.33.x's mutation runner with 'Unknown service Symfony\Component\Console\Helper\QuestionHelper', failing composer mutation:ci on every fresh resolve. The lockfile is gitignored so CI always resolves latest; illuminate v13 permits Symfony 8. Pin holds the dev toolchain at symfony/console v7.4.x. Verified mutation gate green (Covered Code MSI 81% >= 75). Revisit when Infection supports Symfony Console 8. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Goosterhof
left a comment
There was a problem hiding this comment.
No blockers, no concerns, one nit. This is a correctly-diagnosed, minimal dependency pin and the diagnosis checks out against the repo.
Verified the claims directly against origin/main:
composer.lockis gitignored (/.gitignorecarries/composer.lock), so CI does resolve fresh on every run — the "green yesterday, red today with no source change" failure mode is real, not hypothetical.- The
requireblock permitsilluminate/* ^13, and Laravel 13 permits Symfony 8, so the fresh-resolve path tosymfony/consolev8.1.0 is the documented one. symfony/consoleis the only Symfony component pinned, which is the right call: the break is specifically Infection's DI container resolvingSymfony\Component\Console\Helper\QuestionHelperas a service, and that's asymfony/console8 change. Pinning the whole Symfony surface would be over-broad blast radius for a single-component break.
Praise
The CHANGELOG entry is load-bearing, not ceremonial — it captures the gitignored-lock mechanism, the illuminate v13 → Symfony 8 resolution path, the exact Unknown service failure signature, and an explicit removal trigger (Infection ships Symfony Console 8 support). Anyone hitting a confusing transitive-dep CI failure on a sibling Infection-gated repo can grep this and self-serve. That's the right place for incident provenance.
Nit
The pin's only removal reminder lives in the CHANGELOG. A pin keyed to an upstream fix tends to outlive its cause silently — the gate goes green and nobody revisits. Consider a short inline tracking marker (a one-line // symfony/console ^7: Infection 0.33 QuestionHelper break — widen when Infection supports Symfony Console 8 comment isn't possible in JSON, so a tracked issue or a deferred.md Resolve By is the practical equivalent). Not blocking — the CHANGELOG is honest about the temporary nature.
Verdict: approve-worthy. The fix is one line, the diagnosis is verified, and the constraint widening (^7.2 rather than =7.4.13) leaves room for 7.x patch uptake. Merge once CI confirms the mutation gate is green on this head.
jasperboerhof
left a comment
There was a problem hiding this comment.
Approved — reviewed, no blockers.
New PHPStan rule banning Eloquent persistence-API method calls (save/update/delete/create/destroy/forceDelete/forceFill/push/restore/touch and their *OrFail / *Quietly / *OrCreate variants — 24-method blocklist) on `Illuminate\Database\Eloquent\Model` subclasses and `Illuminate\Database\Eloquent\Builder` chains when the call site is inside an `App\Http\Controllers\*` class (including sub-namespaces via `str_starts_with`). Reads (`find`, `where`, `get`, `first`, `paginate`, `pluck`, `count`, `exists`, `query`) deliberately permitted — controllers reading Models is necessary for route-model binding, ResourceData hydration, and policy checks. Identifier: `forbidEloquentMutationInControllers.eloquentMutationInController`. Doctrine source: ADR-0011 (Action Class Architecture) + ADR-0019 (Explicit Model Hydration). Algorithm: - Namespace gate (`App\Http\Controllers` prefix) - Recursively walk every `ClassMethod` body collecting `MethodCall`/`StaticCall` - For `MethodCall`: type-aware `ObjectType::isSuperTypeOf()` against `Model` OR `Builder` matches the receiver; method name in blocklist fires - For `StaticCall`: `Scope::resolveName()` resolves to Model subclass FQCN; method name in blocklist fires Builder coverage uses resolution (a) per order §A7 — type-aware `ObjectType::isSuperTypeOf()` handles `Builder<User>` as a subtype of the unparameterized `Builder` cleanly, no brittle generic introspection needed. Verified empirically by `ViolationBuilderUpdate.php` fixture firing at the `->update([...])` line of a `User::query()->where(...)->update([...])` chain. Supersedes consumer-side string-match Pest arch tests in kendo, ublgenie, entreezuil, and the ISMS bridge subset from PR #10. Cross-territory cascade is the General's follow-up dispatch after this lands; emmie + BIO pick up coverage automatically on next composer update. 14 tests, 14 assertions; PHPStan max self-analysis clean (10 services); Pint clean; line coverage 87.98% (new rule 91.01%) ≥83 gate; MSI 82%, Covered Code MSI 82% (both ≥75 gate). The 7 escaped mutants on the new rule are all in the recognizable Standing Concern #29 walkNodes() parity family + MBString- equivalent + defensive `??` defaults — same shape as the precedent on the other four rules. Closes war-room enforcement queue #87. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
What
Pins
symfony/consoleto^7.2inrequire-dev.Why — repo-wide CI breakage, no source change
symfony/consolev8.1.0 (released 2026-05-29) crashes Infection 0.33.x's mutation runner:Infection's DI container references
QuestionHelperas a service Symfony Console 8 no longer registers that way, socomposer mutation:ciaborts and exits 1 — failing thecheckjob.The package's
composer.lockis gitignored, so CI resolves dependencies fresh on every run.illuminate/*v13 permits Symfony 8, so the resolver started pulling v8.1.0. Result: every fresh CI run went red on the mutation step fleet-wide — PRs green on 2026-05-28 turned red on 2026-05-29 with zero source change (this branch,main, and the three open rule PRs #26/#27/#28 all affected).Fix + verification
Pinning
symfony/console: ^7.2resolves the dev toolchain down to v7.4.13 (illuminate v13 accepts it). Verified locally:composer mutation:ci→ 419 mutants, 340 killed, Covered Code MSI 81% ≥ 75, exit 0.composer phpstan/composer test(90) /composer format:check/composer coverage:check(87.56%) all green.Lifespan
Temporary. Remove or widen the constraint once Infection ships Symfony Console 8 support.
🤖 Generated with Claude Code