Skip to content

fix: reset warm reflection state between calls (#273)#2

Merged
fdaviddpt merged 1 commit into
mainfrom
fix/bump-rector-and-staleness-test
Jun 21, 2026
Merged

fix: reset warm reflection state between calls (#273)#2
fdaviddpt merged 1 commit into
mainfrom
fix/bump-rector-and-staleness-test

Conversation

@fdaviddpt

@fdaviddpt fdaviddpt commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Problem (#273)

The warm daemon 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 couldn't resolve. A fresh rector CLI 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 tagged ResettableInterface before each warm call — the same flush AbstractRectorTestCase performs between fixtures. Bootstrap stays warm; only per-run reflection state is flushed (~0.4s/warm call, unchanged).

Proof (deterministic, real DVSI checkout)

Tests

  • Env-gated regression testWarmReflectionMatchesColdForSequence — proven red→green against DVSI's installed bin. Skipped without MCP_RECTOR_WARM_REPRO_DIR/FILES/BIN.
  • A self-contained fixture can't 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. tools/repro-273.py discovers a triggering sequence.
  • Full suite green (1 gated test skips).

CHANGELOG → 0.4.0. After merge: tag 0.4.0 so claude-supertool + DVSI can require it.

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>
@fdaviddpt fdaviddpt force-pushed the fix/bump-rector-and-staleness-test branch from 8c920e0 to 1df7b5d Compare June 21, 2026 18:44
@fdaviddpt fdaviddpt changed the title fix: reset warm reflection state between calls (#273) + re-land edit-staleness regression fix: reset warm reflection state between calls (#273) Jun 21, 2026
@fdaviddpt fdaviddpt merged commit 0ab077d into main Jun 21, 2026
3 checks passed
@fdaviddpt fdaviddpt deleted the fix/bump-rector-and-staleness-test branch June 21, 2026 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant