fix: respawn worker on stale dependency (cross-file false negatives)#4
Merged
Merged
Conversation
…ives The warm worker memoises a class's reflection for its whole life and re-analysing a file only re-reads that file's own AST — never its dependencies'. So after a dependency was edited (a method removed), re-analysing a dependent that calls it returned 0 errors while a cold phpstan run reported the now-undefined call. Silent cross-file false negative — the quiet sibling of the loud rector ClassReflection failure (claude-supertool#273). The same-file edit case was already fine (phpstan re-reads the analysed file). PHPStan's worker exposes no per-class invalidation, so PhpstanRunner now respawns the worker when a non-target file it has analysed changed since the worker booted (workerBootedAt + analysed-file set, checked before ensureWorker). Scoped: the analysis target is excluded, so iterating on a single file never respawns and stays fully warm — the ~20s cold boot is paid only when switching to a different file after editing a dependency. The check stats only the analysed working set, not the whole --paths tree. Adds testStaleDependencyIsCaughtWhenDependentReanalysed (proven red without the respawn, green with it). Full suite 14/14. CHANGELOG -> 0.6.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.
Problem
The warm worker memoises a class's reflection for its whole life. Re-analysing a file only re-reads that file's AST — never its dependencies'. So after a dependency was edited (e.g. a method removed), re-analysing a dependent that calls it returned 0 errors, while a cold
phpstanrun reports the now-undefined call. A silent cross-file false negative — the quiet sibling of the loud rectorClassReflectionfailure (claude-supertool#273). The same-file edit case was already fine (phpstan re-reads the analysed file).Proven against a real DVSI checkout: edit
Dep(removevalue()), validateDep(clean — it's fine on its own), then validateUserwhich callsDep::value()→ warm returns 0 errors; cold returnsCall to an undefined method Dep::value().Fix
PHPStan's worker exposes no per-class invalidation, so the only cure is a fresh worker.
PhpstanRunnernow respawns the worker when a non-target file it has analysed changed since the worker booted (workerBootedAt+ an analysed-file set, checked beforeensureWorker()).Scoped so speed survives:
--pathstree.Measured on real DVSI: cold 20.3s vs warm 2.8s — which is why respawn-on-every-edit was rejected in favour of target-excluded scoping.
Tests
testStaleDependencyIsCaughtWhenDependentReanalysed— edit a dependency, re-analyse a dependent through the same warm worker, assert the undefined call is reported. Proven red without the respawn, green with it.CHANGELOG → 0.6.0. After merge: tag 0.6.0 so DVSI can require it.
Honest residual cost
Ping-ponging between two files you've both edited can trigger a 20s respawn per switch; same-file iteration stays warm; CI full phpstan is the backstop. No cheaper correct option exists — the worker has no per-class invalidation.