fix: recover from warm-state scope corruption (reboot + retry)#3
Merged
Conversation
The warm Rector container can carry stale PHPStan scope/reflection state
across edits — a class whose shape changed on disk between calls yields a
null scope deep in PHPStanNodeScopeResolver ("Call to a member function
toMutatingScope() on null"). It is not a real finding: a cold run on the
same file passes. resetReflectionState() only flushes ResettableInterface
services (source locators); PHPStan's scope/reflection caches are not
resettable, so it cannot clear this.
RectorTool::process now detects these internal corruption errors
(toMutatingScope / "must be resolved" / "System error"), reboots the
container (RectorRunner::reboot()) and retries once — a fresh container is
the only guaranteed reset. Result is cold-quality instead of a false
rector.error surfacing to the caller.
Extract RunnerInterface so the recovery path is unit-testable with a double
that simulates the corruption; RectorTool::withRunner() keeps __construct's
concrete type for MCP SDK autowiring.
Bump 0.3.0 -> 0.4.0.
Co-Authored-By: Max <noreply>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
The warm Rector daemon intermittently surfaced a false
rector.error—Call to a member function toMutatingScope() on null(PHPStanNodeScopeResolver:177) — after an edit, while a coldrector processon the same file passes. Root cause: stale PHPStan scope/reflection state across warm calls.resetReflectionState()(the #273 fix) only flushesResettableInterfaceservices (source locators); PHPStan's scope/reflection caches aren't resettable, so it can't clear this.Change
RectorTool::processdetects internal warm-corruption errors (toMutatingScope/must be resolved/System error), callsRectorRunner::reboot()(drop container) and retries once on a fresh container — cold-quality result instead of a false error to the caller.RunnerInterface+RectorTool::withRunner()test seam (keeps__construct's concreteRectorRunnertype so MCP SDK autowiring is unaffected — widening it to the interface breaks autowiring with-32603).0.3.0 → 0.4.0.Tests
3 new unit tests (TDD red→green): recovers from corruption / does not retry unrelated errors / surfaces error if retry also fails. Unit 6/6, integration back to baseline (25 assertions, 1 skip).
Could not reproduce the original NPE in a fresh harness (56+ warm calls) — it's cross-session accumulation in the long-lived daemon. The fallback is the robust, version-independent safety net regardless of the exact trigger.
🤖 Generated by Max — a fresh container forgets all sins.