fix/correctness-silent-drops: close nine tier-1 silent-drop bugs on the path to v2.0#31
Merged
vedanthvdev merged 1 commit intomasterfrom Apr 22, 2026
Merged
Conversation
…he path to v2.0 A post-v1.9.19 multi-agent review found a cluster of silent-drop bugs that all shared the same failure mode: a strategy thought it had walked a construct but had actually missed one of its real-world shapes, so the consumer test for a changed class never entered the selected set — breaking the plugin's cardinal invariant (run more tests than needed, never fewer). This release ships a fix for each one with a regression test that fails on revert. - JavaParsers factory centralises `JavaParser` creation at `LanguageLevel.JAVA_25` (highest stable level in the bundled 3.28 build). Previously the default `JAVA_11` made every file using records, sealed types, or pattern matching produce `isSuccessful == false`, silently removing the file from every AST strategy. `parseOrWarn` replaces the four independent `parseOrGet` helpers and surfaces parse failures at WARN instead of DEBUG, closing the invisibility that hid this bug class. - TransitiveStrategy now preserves generic type arguments and walks method bodies via `findAll(ClassOrInterfaceType.class)`, so `List<FooService>` consumers and helper-inside-method-body shapes no longer lose their reverse-dependency edge. - ImplementationStrategy iterates every `TypeDeclaration` subtype, so `record UsdMoney(long cents) implements Money` and `enum Currency implements HasCode` are now first-class implementers. The new `supertypesOf` helper covers class/record/enum/annotation declarations, with a defensive WARN-logged default branch to make the next unknown subtype loud. - GitChangeDetector diffs against the merge-base between `baseRef` and `HEAD` (falling back to `baseId` with a WARN log when no common ancestor exists), so post-divergence master commits no longer leak into a feature branch's diff and inflate the affected-tests set toward "everything". - UsageStrategy adds a Tier 3 AST pass that catches fully-qualified inline references (`new com.example.Foo()`, `Outer.Inner` inline qualification), and the Tier 1b wildcard tier now treats `import pkg.Outer.*` as a class-member wildcard rather than a package wildcard — the shape that made every Outer.java change silently drop all wildcard consumers. - PathToClassMapper routes `module-info.java` and `package-info.java` to the unmapped bucket instead of producing malformed FQNs, letting the UNMAPPED_FILE safety net escalate JPMS and package-annotation changes conservatively. - AffectedTestsEngine explicitly routes diffs that are a mix of ignored + out-of-scope files to `Situation.ALL_FILES_OUT_OF_SCOPE` (SKIPPED) instead of falling through to `DISCOVERY_EMPTY`, which under `mode = CI` would previously escalate a pure docs+api-test MR into a full CI run. All regression tests cover revert-mutation shapes and `./gradlew check` is green end-to-end.
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.
Why
A second, deeper multi-agent review of the plugin source tree as it landed in v1.9.19 surfaced a cluster of tier-1 correctness bugs that all shared the same failure mode: a strategy thought it had walked a construct but had actually missed one of its real-world shapes, so the consumer test for a changed class never entered the selected set. This breaks the plugin's cardinal invariant — run more tests than needed, never fewer — which is the one guarantee v2.0 needs to ship with.
Every fix in this PR ships with at least one regression test that fails when the production fix is reverted.
What
New file:
affected-tests-core/src/main/java/io/affectedtests/core/discovery/JavaParsers.javaJavaParsersfactory centralisesJavaParsercreation atLanguageLevel.JAVA_25(highest stable level the bundled 3.28 build supports).new JavaParser()defaulted toJAVA_11, so every file using records, sealed types, or pattern matching parsed asisSuccessful == falseand silently dropped out of every AST strategy.parseOrWarnnow centralises the parse helper and surfaces failures at WARN instead of DEBUG.ImplementationStrategyTest#findsRecordImplementationOfChangedInterface,…findsEnumImplementationOfChangedInterfaceTransitiveStrategypreserves generic arguments (List<Foo>now registers theFooedge) by walking everyClassOrInterfaceType.TransitiveStrategyTest#preservesGenericArgumentsInReverseDependencyEdgesTransitiveStrategyscans method bodies, not just signatures, sonew PricingCalculator()inside a method body now adds the reverse edge.TransitiveStrategyTest#discoversEdgesFromMethodBodyReferencesImplementationStrategyiterates everyTypeDeclarationsubtype. Records and enums are now first-class implementers; a defensiveWARN-logged default branch guards against the same bug reappearing when JavaParser introduces a new declaration kind.GitChangeDetectornow diffs against the merge-base betweenbaseRefand HEAD viaRevFilter.MERGE_BASE, falling back tobaseIdwith aWARNlog when there is no common ancestor. Previously, any post-divergence master commit showed up as a "change" on the feature branch and dragged unrelated tests into every MR.GitChangeDetectorTest#diffsAgainstMergeBaseNotBaseRefTipUsageStrategyadds a Tier 3 AST pass that catches fully-qualified inline references (new com.example.Foo(),Outer.Innerinline qualification).UsageStrategyTest#findsTestThatUsesChangedClassByFullyQualifiedInlineReference,…findsTestThatInnerClassQualifiesThroughChangedOuterUsageStrategyTier 1b now treatsimport pkg.Outer.*as a class-member wildcard (dependency onpkg.Outer) rather than a package wildcard.UsageStrategyTest#findsTestThatWildcardsClassMembersOfChangedClassPathToClassMapperroutesmodule-info.javaandpackage-info.javato the unmapped bucket instead of minting the malformed FQNsmodule-info/com.example.package-info.PathToClassMapperTest#moduleInfoRoutesToUnmappedNotProduction,…packageInfoRoutesToUnmappedNotProductionAffectedTestsEngineexplicitly routes diffs that are a union of ignored + out-of-scope files toSituation.ALL_FILES_OUT_OF_SCOPE(SKIPPED) instead of falling through toDISCOVERY_EMPTY— which undermode = CIwould escalate a pure docs+api-test MR into a full CI run.AffectedTestsEngineTest#mixedIgnoredAndOutOfScopeDiffRoutesToAllFilesOutOfScopeVerification
./gradlew check— green end-to-end (core + gradle modules, validatePlugins included).LANGUAGE_LEVEL = JAVA_21→ bump toJAVA_25) and 3 Medium findings (parse-failure WARN,supertypesOfdefault branch, merge-base fallback log level). All four applied in this PR. Zero Critical findings remaining.Release
This is batch 4 of 4 on the road to
v2.0. Ships asv1.9.20via the axion-release workflow on merge to master.See the
CHANGELOG.mdtop block "Fixed — post-v1.9.19 multi-reviewer correctness pass (v1.9.20)" for the authoritative per-fix writeup.