fix: reset warm reflection state between calls (#273)#2
Merged
Conversation
The warm container reused one Rector Application across every call but reset nothing between them. Rector's DynamicSourceLocatorProvider caches its AggregateSourceLocator for every non-PHPUnit run, so the second and every later file was analysed with a source locator that only knew the first file — and Rector emitted `System error: "ClassReflection must be resolved for class X"` on test classes whose hierarchy the stale locator could not resolve. A fresh rector CLI process never hit this; the warm daemon did, and the failure was being content-hash-cached and replayed (2100 poisoned entries, papered over by #272). RectorRunner::run() now resets every service tagged ResettableInterface before each warm call — the same flush AbstractRectorTestCase performs between fixtures — so the warm daemon matches a cold CLI boot. The expensive bootstrap stays warm; only per-run reflection state is flushed (~0.4s/warm call, unchanged). Adds env-gated regression testWarmReflectionMatchesColdForSequence + the tools/repro-273.py harness that discovers a triggering sequence. A self-contained fixture cannot reproduce it: in-process PHPUnit disables the locator cache via isPHPUnitRun(), and the trigger needs real framework base classes extended from outside the configured paths. Co-Authored-By: Max <noreply>
8c920e0 to
1df7b5d
Compare
2 tasks
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.
Problem (#273)
The warm daemon reused one Rector
Applicationacross every call but reset nothing between them. Rector'sDynamicSourceLocatorProvidercaches itsAggregateSourceLocatorfor every non-PHPUnit run, so the second (and every later) file was analysed with a source locator that only knew the first file — and Rector emitted:on test classes whose hierarchy the stale locator couldn't resolve. A fresh
rectorCLI never hit this; the warm daemon did, and the failure was content-hash-cached and replayed (2100 poisoned entries, papered over by claude-supertool#272).Root cause for claude-supertool#273.
Fix
RectorRunner::run()now resets every service taggedResettableInterfacebefore each warm call — the same flushAbstractRectorTestCaseperforms between fixtures. Bootstrap stays warm; only per-run reflection state is flushed (~0.4s/warm call, unchanged).Proof (deterministic, real DVSI checkout)
System error: "ClassReflection must be resolved for class AresGlobalModuleTest". Cold (fresh server, same file) → clean. Warm-only.Tests
testWarmReflectionMatchesColdForSequence— proven red→green against DVSI's installed bin. Skipped withoutMCP_RECTOR_WARM_REPRO_DIR/FILES/BIN.isPHPUnitRun(), and the trigger needs real framework base classes extended from outside the configured paths.tools/repro-273.pydiscovers a triggering sequence.CHANGELOG → 0.4.0. After merge: tag
0.4.0so claude-supertool + DVSI can require it.