Current status: :compiler:java-direct:test full suite green, 2839/2839 (100%). No known won't-fix.
Last archived: implDocs/archive/ITERATION_RESULTS_2026_07_13.md (entries through 2026-07-13).
This log is read into the agent's context every session, so entries must stay short.
implDocs/<TOPIC>.md and link to it from the entry — do not inline it here.AGENT_INSTRUCTIONS.md → “Docs Maintenance”): git mv it to implDocs/archive/ITERATION_RESULTS_<last-entry-date>.md, add an archive banner, and reset this file to the template below.### YYYY-MM-DD — <one-line title> - **Change**: what changed and why (1–3 lines). - **Files**: key files touched (+N/−M LoC if useful). - **Tests**: suites run + counts (e.g. box 1178/1178, phased 1513/1513). - **Result**: green / regression fixed / won't-fix — link to a detail doc if there is one.
JavaParsingLightweightScannerTest. Every raw Kotlin assert(...) in the module's unit tests (a no-op without -ea) is now a JUnit assertion — assertEquals/assertTrue/assertFalse/assertNull/assertSame and the contract-carrying org.junit.jupiter.api.assertNotNull, which let the following !! go. Messages that only restated expected/actual are dropped. New scanner tests: a package name split across a line and a block comment (package builder // c \n . /* c */ subpackage;), package com.123;, and class 456 {} with and without a well-formed sibling. extractFileInfoLightweight now joins identifier segments instead of appending identifiers and dots verbatim, so a malformed name degrades to its valid prefix (com) rather than the stray-dot com..util/JavaSourceIndex.kt (+3/−3); all 10 unit test files under test/ (~440 assertions rewritten), JavaParsingLightweightScannerTest.kt +4 tests.FirLazyJavaAnnotationMutableList sit in FirEnumEntryImpl's MutableOrEmptyList slot, and depended on nothing but isEmpty being called before the list materialised. Instead FirEnumEntryImpl.annotations is now a plain var annotations: List<FirAnnotation> whose replace installs the given list as is, so FirJavaFacade can install a FirLazyJavaAnnotationList (same class the members use) via replaceAnnotations after buildEnumEntry. The only cost is transformAnnotations, which maps into a new list instead of transforming in place.fir-jvm/.../FirJavaFacade.kt, fir-jvm/.../enhancement/FirJavaAnnotationList.kt (−29, FirLazyJavaAnnotationMutableList deleted); FIR tree generator: new ListField.isAssignableList + useAssignableList("annotations") on impl(enumEntry); FirEnumEntryImpl/FirEnumEntryBuilder regenerated. The builder-side var-list support in generators/tree-generator-common is reverted — those files are identical to master again.JavaParsingTest and JavaCycleBreakerTest green; both regression gates green (PhasedJvmDiagnosticLightTreeTestGenerated.*, *CompileKotlinAgainstKotlin*).JavaSupertypeGraph: the duplicated supertype-reference resolver was test-onlyresolveSupertypeReference (“we partly repeat type resolution logic here”). JavaSupertypeGraph re-derived import lookup, star-import candidates and package/class splits from the AST, and returned an empty list for any dotted supertype — an unspecified under-approximation. Repo-wide, its two entry points (LeanJavaClassFinder.getDirectSupertypes / collectInheritedInnerClasses) had no production callers: the test fake in JavaParsingTestBase implemented both as empty, and only JavaParsingClassFinderTest invoked the real ones. Production already resolves the first supertype level through the class’s own JavaResolutionContext (resolveInheritedInnerClassToClassId) and deeper levels through directSupertypeClassIds.util/JavaSupertypeGraph.kt (−270, deleted), JavaClassFinderOverAstImpl.kt, resolution/LeanJavaClassFinder.kt, resolution/JavaModelSessionAccess.kt (KDoc), implDocs/ARCHITECTURE.md, test/JavaParsingTestBase.kt, test/JavaParsingClassFinderTest.kt.JavaParsingClassFinderTest re-pointed at the real path — the B<String>.C case is now a positive assertion (the inherited a.B.C.N must resolve; it previously only asserted the wrongly-truncated a.B was absent), plus a new same-level multi-supertype test covering the single-match and ambiguous-decline arms of walkSupertypeClassIds. Two graph-only tests (testGetDirectSupertypesUsesCache, testDiamondInheritanceInnerClasses) were dropped: they asserted the deleted cache, and the transitive/grandparent case they gestured at is already covered end-to-end by qualifiedInheritedNestedClassInOwnImplementsClause.kt.JavaParsing* unit tests green (JavaParsingClassFinderTest 12/12); full box + phased green (2795 executed, 0 failures, 0 errors).DependencyKind.Source), a codegen // MODULE: lib dependency is really compiled: JavaCompilerFacade.compileJavaFiles javac-compiles lib's .java into lib's output dir, and JvmEnvironmentConfigurator.registerModuleDependencies puts that dir on main's classpath — so main reads a.Base as a binary Java class through FirBackedJavaClassAdapter, i.e. the exact shape of the IJ-FP testIntellij_exceptionAnalyzer regression. So a box test can express what a phased test cannot.testData/codegen/box/javaDirect/packagePrivateInheritedNestedClassNotVisibleAcrossPackages.kt (source arm, single module) and testData/codegen/boxJvm/javaDirect/packagePrivateInheritedNestedClassFromBinaryModule.kt (adapter arm, lib → main) — both new; no generator change needed.JavaUsingAstPhasedTestGenerated + JavaUsingAstBoxTestGenerated green (2795 executed, 0 failures, 0 errors); shared-data gate green in Fir{LightTree,Psi}BlackBoxCodegenTestGenerated$Box{,Jvm}$JavaDirect.FirBackedJavaClassAdapter.visibility to Visibilities.Public fails only the binary-module test, while short-circuiting isInheritedNestedClassAccessible to true fails both. Both probes reverted (git diff on src empty). Box data needs no golden file, so rule 5/6 concerns about shared diagnostic data do not apply.// MODULE)FirBackedJavaClassAdapter.visibility arm of isInheritedNestedClassAccessible (2026-07-14, IJ-FP testIntellij_exceptionAnalyzer) had no small-test guard: the adapter is only consulted for classes outside the module‘s own Java source index, and a phased test always gets Java as sources. // MODULE: lib / // MODULE: main(lib) closes that gap — a dependency module’s Java class is not in main's JavaSourceRoot set (JavaDirectFacadeBuilder.kt:40), so classifierAdapterFor returns the adapter exactly as it does for binary Java.testData/diagnostics/tests/jvm/javaDirect/packagePrivateInheritedNestedClassFromOtherModule.kt (new).JavaUsingAstPhasedTestGenerated and in both PSI gates (PhasedJvmDiagnostic{LightTree,Psi}TestGenerated); full JavaUsingAstPhasedTestGenerated re-run green, 0 FAILED.visibility back to Visibilities.Public makes it fail (“Actual data differs”). Note: diagnostics tests hard-code DependencyKind.Source, so this is not a compiled-jar dependency; a genuinely binary Java dependency still needs PROVIDE_JAVA_AS_BINARIES (foreign-annotations roots only) or a box test.KotlinFullPipelineTestsGenerated; not the M1 0.5%JavaClassFinderOverBinaryIndex no longer builds an FqName per lookup: the top-level cache is now two-level, keyed by the classId's own packageFqName + outermost class Name (new FqName.topLevelName()), so the string concatenation, pathSegments() list, FqName/ FqNameUnsafe pair and re-hash are gone from the hot path (378994 lookups, ~1.5M objects per corpus). Paired in-run A/B: 631 -> 465 ns per hit lookup (-26%), but only ~54 ms per corpus.readBinaryJavaClass on 69617 class files — work master does through the same reader — 18% knownClassNamesInPackage (2.05M name strings, 35306 traversals), 5% top-level index traversals. Allocation/GC with -Xjava-direct on vs off is identical (216-221 GB per corpus, 112-119 young GCs, 8-9 full GCs), so extra garbage is not the mechanism. The regression does not reproduce here: two A/B batches disagree on the sign (±4%), i.e. developer-machine wall time cannot resolve 0.5%.JavaClassFinderOverBinaryIndex.kt, review doc §12 (method, tables, and seven ranked strategies for locating the gap on the mini), this file. All harnesses removed.KotlinFullPipelineTestsGenerated (413 modules) green.TopLevelClassFiles shape: measured, holder keptSmartList-style encoded slot vs the pre-merge two maps) on the real workload from §10 (1469/1615 finders, 19k/12k cached keys, 77k/42k lookups per suite). The whole cache costs <1 ms and ~1.4 MB per full suite; the spread between shapes is ~0.15 ms/suite (~0.1 µs per compilation). The array is not faster — it is the slowest single-map variant on a 20× corpus (26.7 ms vs 23.4 ms) and loses the type/names; the encoded slot saves 33% of the allocation but trades the scoped/unscoped distinction for an unchecked encoding. Kept the named holder, added a KDoc pointer to implDocs/BINARY_SOURCE_DIVIDE_REVIEW_2026_07_22.md §11.JavaClassFinderOverBinaryIndex.kt (KDoc +5), review doc §11, this file.JavaClassFinderOverBinaryIndex had two FqName -> VirtualFile? maps differing only in firstOrNull { it in scope } vs firstOrNull(). Measured over both suites (3084 finder instances): 69–75% of scoped keys were also in the all-scope map, every shared key had an identical value, and no lookup ever saw >1 candidate or a candidate outside scope (multiCandidate=0, noCandidateInScope=0) — see implDocs/BINARY_SOURCE_DIVIDE_REVIEW_2026_07_22.md §10. Replaced by one FqName -> TopLevelClassFiles(anywhere, inScope) cache filled in a single index pass, which keeps the two answers distinct while dropping ~12k redundant index lookups per suite (−39%). (2) Reverted FirJvmConflictsChecker + the FirSession.getJavaClassLikeSymbolByClassId forwarder and FirDirectJavaActualDeclarationExtractor to master: master already bypassed the composite provider, so both diffs were pure restyling, and no fixture covers the Kotlin-vs-binary-Java redeclaration case the checker's comment described.JavaClassFinderOverBinaryIndex.kt (−12/+15); reverted FirJvmConflictsChecker.kt, fir-jvm/JavaSymbolProvider.kt, FirDirectJavaActualDeclarationExtractor.kt (shared-file diff vs master for these three is now zero).PhasedJvmDiagnosticLightTreeTestGenerated green, FirLightTreeBlackBoxCodegenTestGenerated*CompileKotlinAgainstKotlin* green.createJavaDirectJavaFacadeBuilder now takes javaSourcesScope instead of librariesScope and dispatches scope === javaSourcesScope -> JavaClassFinderOverAstImpl, every other scope -> JavaClassFinderOverBinaryIndex over that scope. Previously the single whitelisted case was the library scope, so any new binary scope (IC output, HMPP-fragment classpath) would silently have been answered by the source AST finder. Memo map is now an IdentityHashMap keyed by the scope object (was System.identityHashCode + ct.sym flag in a data class — collision-prone with several scopes in play); the CliVirtualFileFinderFactory lookup is hoisted out of the lambda. Also routed the HMPP-common library session (JvmFrontendPipelinePhase.kt:397) through javaDirectFacade: it already asks for context.librariesScope, so it reuses the same memoized binary finder, and it is a literal no-op when useJavaDirect is off. The IC precompiled-binaries provider still constructs the PSI facade deliberately — it has no test coverage (see the 2026-07-29 review notes).JavaDirectFacadeBuilder.kt (−13/+16), JvmFrontendPipelinePhase.kt (2 lines), implDocs/ARCHITECTURE.md §3.PhasedJvmDiagnosticLightTreeTestGenerated 10988/10991 (3 pre-existing skips).isInSourceIndex / hasPackageInSources / sourceClassNamesInPackage from JavaClassFinder + FirJavaFacade; every production finder is already single-sided by construction, so JavaSymbolProvider is back on hasTopLevelClassOf / hasPackage / knownClassNamesInPackage (this restores a real AST-index gate on java-direct, where isInSourceIndex was a constant true, and re-enables the facade caches). (2) Deleted the JvmBinaryClassFinderInputs seam: JvmBinaryClassFinderInputsOverIndex became JavaClassFinderOverBinaryIndex : JavaClassFinder, absorbing LibraryJavaClassFinder / BinaryPackageInfoJavaPackage, so JvmClassFileBasedSymbolProvider has one input (javaFacade), no ?: fallbacks and no flag awareness. The packagePartProvider fallback in hasPackage (for @file:JvmPackageName) is now unconditional.JavaClassFinder.kt, FirJavaFacade.kt, JavaSymbolProvider.kt, JvmClassFileBasedSymbolProvider.kt, FirJvmSessionFactory.kt, JvmFrontendPipelinePhase.kt, JavaDirectFacadeBuilder.kt, +JavaClassFinderOverBinaryIndex.kt, −JvmBinaryClassFinderInputs.kt, −JvmBinaryClassFinderInputsOverIndex.kt.PhasedJvmDiagnosticLightTreeTestGenerated 10991/10991, FirLightTreeBlackBoxCodegenTestGenerated (full) 10643/10643 incl. CompileKotlinAgainstKotlin.implDocs/BINARY_SOURCE_DIVIDE_REVIEW_2026_07_22.md.@Deprecated enum constant, testIntellij_vcs_git), demoting cycleSafeClassLikeSymbol to genuine defense-in-depth, but the cycle class stays reachable — the declarations lazy reads FirJavaClass.typeParameters, whose bound enhancement iterates the class's own annotations via extractDefaultQualifiers (plus a raw outer-class getClassLikeSymbolByClassId); the enum-entry returnTypeRef is still eager because SignatureEnhancement requires a resolved ref; and 3 of 5 guard call sites carry no annotation (const-field values, @Target, type-argument substitution). The other breakers (cycleGuardedSupertypeWalk, supertype memoization, local visited sets) are annotation-agnostic. Docs/comments updated accordingly; also fixed the stale JavaSupertypeLoopChecker name (the code is cycleGuardedSupertypeWalk / JavaModelSupertypeWalkGuard).resolution/JavaModelSessionAccess.kt, test/.../JavaCycleBreakerTest.kt (comments only), AGENT_INSTRUCTIONS.md.git diff that no code line changed.extractFileInfoLightweight against its production ancestor SingleJavaFileRootsIndex.JavaSourceClassIdReader (cli-base). Unmatched closers drove the balances negative, so atTypeDeclaration() (== 0) stopped firing and every top-level type after a stray }/) was lost — for a stray } before the file's namesake class the file is dropped from the index entirely (tryBuildFileEntry requires the base name). Now clamped, mirroring Kotlin.flex's if (lBraceCount == 0) popState() else lBraceCount--; no-op on well-formed input. Also dropped a dangling KDoc, the redundant if (at(SEMICOLON)) advance() (the class-scan loop skips it anyway) and mapped an empty package name to null. Divergences from the ancestor are intentional and stay: the break@loop on a non-package token (upstream appends the first class name to the package on a missing ;), no isPackageInfo arm (JavaPackageIndexer routes package-info.java to JavaPackageInfoIndexer first), and when (lexer.getTokenType()) instead of four at() calls. getTokenType() itself is a cached field read (JavaLexer.locateToken: if (myTokenType != null) return), so repeated calls need no hoisting; getTokenText() is the allocating one, and it is only reached at balance 0.util/JavaSourceIndex.kt (+7/−12); JavaParsingLightweightScannerTest.kt (+47, 2 tests).JavaParsing* 16/16; box+phased 2792 executed / 0 FAILED. Both new tests fail without the clamp (got [Broken], got [Foo]).JavaSourceFileReader abstraction; read via File.readTextVirtualFile→File switch the interface had a single implementation (DefaultJavaSourceFileReader), walkSourceRoots had no callers (it served the deleted eager buildIndex), and no production or test call site ever substituted a reader — the parameter was threaded through four collaborators for nothing. Replaced by one internal readJavaSourceFileText(File): String?; the reader parameter is gone from JavaClassCache, JavaPackageIndexer, JavaPackageInfoIndexer, JavaSupertypeGraph, JavaClassFinderOverAstImpl and extractFileInfoLightweight. Content is now read with File.readText() instead of String(readBytes(), UTF_8): measured over 1503 repo .java files (37 MB, interleaved rounds, 7 samples) the medians are 46 ms vs 48 ms — ~2 ms of pure read time per full corpus, i.e. negligible against lexing/parsing.util/JavaSourceFileReader.kt (−57), added util/javaSourceFileText.kt (+19); JavaClassCache.kt, JavaClassFinderOverAstImpl.kt, JavaPackageIndexer.kt, JavaPackageInfoIndexer.kt, util/JavaSourceIndex.kt, util/JavaSupertypeGraph.kt; tests JavaParsingTestBase.kt, JavaParsingLightweightScannerTest.kt; implDocs/ARCHITECTURE.md.JavaParsing* green; box+phased 2790 executed / 0 FAILED.FirJavaEnumEntry with buildEnumEntry + lazy MutableList annotationsFirJavaEnumEntry duplicated FirEnumEntryImpl (~180 LoC) just to host a lazy annotation slot. The tree generator gained a per-field opt-in (LeafBuilder.listFieldsWithVar + useVarForListField DSL in AbstractBuilderConfigurator; configured only for builder(enumEntry) in the FIR BuilderConfigurator), so default builder generation is unchanged and only FirEnumEntryBuilder.annotations becomes var. The enum-entry arm of convertJavaFieldToFir uses plain buildEnumEntry with a new FirLazyJavaAnnotationMutableList — an AbstractMutableList composing a plain FirLazyJavaAnnotationList (conversion reused via toMutableList() on first mutable access; only the 5 abstract members are overridden; cheap isEmpty keeps toMutableOrEmpty() from forcing conversion) — and FirJavaLazyDeprecationsProvider. FirJavaEnumEntry deleted.generators/tree-generator-common/.../Builder.kt, AbstractBuilderPrinter.kt, config/AbstractBuilderConfigurator.kt, fir/tree/tree-generator/.../BuilderConfigurator.kt, regenerated fir/tree/gen/.../builder/FirEnumEntryBuilder.kt (single-line val→var), fir-jvm/.../FirJavaFacade.kt, FirJavaAnnotationList.kt, javaAnnotationsMapping.kt; deleted FirJavaEnumEntry.kt.CompileKotlinAgainstKotlin gate green (shared fir-jvm + fir-tree edits).@param chatter, Stage/§/implDocs references in shared fir-jvm sources; kept short why-notes, API contracts, and regression/testData guards. Codified the rules in AGENT_INSTRUCTIONS.md → Source Comment Conventions (“default is no comment” gate, ~3% density baseline).cli-base, cli-jvm, fir-jvm, frontend.common.jvm, core/compiler.common.jvm, java-direct/{src,test}; docs: AGENT_INSTRUCTIONS.md (conventions rewrite, reference-table refresh, stale status header).git diff that no code line changed.JavaPackageIndexer‘s per-package directory descent used File(dir, segment).isDirectory (added 2026-07-21 when the source path moved from VirtualFile to java.io.File), which is case-insensitive on macOS/Windows. A sibling source dir (syntax/logger, platform/ml/session) was wrongly accepted as package Logger/Session, so nested-class imports like com.intellij.platform.syntax.Logger.Attachment mis-split into a package prefix and reported UNRESOLVED_IMPORT. Descent now matches against the parent’s real child names via File.list() (case-sensitive), mirroring the binary index / PSI VFS findChild.JavaPackageIndexer.kt (new descendDirectoriesCaseSensitive, used by findPackageDirectories + findPackageDirectoryUnder).IntelliJFullPipelineTestsGenerated.testIntellij_platform_syntax and testIntellij_platform_ml now pass with java-direct on.useJavaDirect; delete dead finder; dedup the ASM binary readerimplDocs/BINARY_SOURCE_DIVIDE_REVIEW_2026_07_22.md §4.1/§4.2/§4.7. §4.1: the binary deserializer seam is now gated on configuration.useJavaDirect in prepareJvmSessions — ON uses JvmBinaryClassFinderInputsOverIndex, OFF returns null so the deserializer falls back to the PSI FirJavaFacade binary reader (both source and binary now share one flag). §4.2: removed dead CombinedJavaClassFinder.kt (no references). §4.7: extracted the shared readBinaryJavaClass core (caching + inner-class dispatch + ClassifierResolutionContext) into frontend.common.jvm; both JvmBinaryClassFinderInputsOverIndex and the binary branch of KotlinCliJavaFileManagerImpl delegate to it. Investigated §4.1's “turn off PSI finder creation”: not doable now — KotlinCliJavaFileManagerImpl still backs JPMS module-info resolution (ClasspathRootsResolver → JavaModuleInfo.read → findClass) regardless of the flag, and its PSI class-loading branch is already inert by default (usePsiClassFilesReading=false).cli-jvm/.../JvmFrontendPipelinePhase.kt, cli-base/.../KotlinCliJavaFileManagerImpl.kt, frontend.common.jvm/.../classFiles/BinaryJavaClassReader.kt (new), JvmBinaryClassFinderInputsOverIndex.kt; deleted CombinedJavaClassFinder.kt.PhasedJvmDiagnosticLightTreeTestGenerated) 0 fail, CompileKotlinAgainstKotlin 0 fail (shared-pipeline + file-manager edits), KotlinCliJavaFileManagerTest 7/7.java.io.File through the Java-source indexing path (drop internal VirtualFile)com.intellij.openapi.vfs.VirtualFile for its own source-file representation/reading; source roots are consumed as java.io.File (the CLI's JavaSourceRoot.file is already a File, so the old VFS-resolution step is removed). Per-package directory descent uses File(dir, segment)/listFiles(); content is read via readBytes() decoded as UTF-8 (unchanged charset). Binary-class-finder/CLI wiring stays on VirtualFile intentionally (external contract).JavaDirectFacadeBuilder.kt, JavaPackageIndexer.kt, JavaPackageInfoIndexer.kt, util/JavaSourceFileReader.kt, util/JavaSourceIndex.kt; tests JavaParsingTestBase.kt, JavaParsingClassFinderTest.kt, JavaParsingLightweightScannerTest.kt.JavaParsing* green.isDirectory/isFile) rather than at VFS resolution — equivalent end behaviour.FirLazyJavaAnnotationList (KT-74097)convertJavaFieldToFir resolved annotations eagerly while materialising FirJavaClass.declarations, which could re-enter the same in-flight ClassId (self-cycle). New FirJavaEnumEntry (mirrors FirJavaField) backs annotations/ deprecationsProvider with FirLazyJavaAnnotationList, so no eager resolution happens while declarations is built. Removed the now-dead setAnnotationsFromJava; the cycleSafeClassLikeSymbol guard is now defense-in-depth, not the sole crash preventer.fir-jvm/.../declarations/FirJavaEnumEntry.kt (new), fir-jvm/.../FirJavaFacade.kt, fir-jvm/.../javaAnnotationsMapping.kt, JavaCycleBreakerTest.kt (comment).PhasedJvmDiagnosticLightTreeTestGenerated) + CompileKotlinAgainstKotlin gates green (shared fir-jvm edit).JavaSupertypeGraph (drop splitCanonicalFqName)splitCanonicalFqName text scan; it reuses the AST-based extractReferenceNameParts (extracted from JavaClassifierTypeOverAst into JavaTypeOverAst), reading JAVA_CODE_REFERENCE identifier segments directly. The generic-argument edge case (a.B<String>.C) is preserved.util/JavaSupertypeGraph.kt, model/JavaTypeOverAst.kt.JavaParsing* green.JavaSourceIndex; exclude comment/bad tokens from the light-tree rootextractFileInfoLightweight scans the Java-lexer token stream (JavaSyntaxDefinition.createLexer) instead of regex/comment-stripping to find the package name and top-level type names, using brace/paren balance for nesting; removed PACKAGE_REGEX/DECLARATION_REGEX, manual comment stripping, and the now-unused reader openLineReader. JavaLightTree synthetic-root children now also exclude comments (root-only, each declaration keeps its DOC_COMMENT for @deprecated) and BAD_CHARACTER.util/JavaSourceIndex.kt, util/JavaSourceFileReader.kt, parse/JavaLightTree.kt.JavaParsing* (incl. lightweight scanner) green; box+phased green (0 FAILED).by lazy(PUBLICATION) (same precedent as supertypes/typeParameters): class keyword flags (isInterface/isEnum/isRecord/isAnnotationType/isSealed), methods/fields/constructors/recordComponents/innerClassNames/annotations; per-member resolutionContext/valueParameters/returnType and field leadingFieldNode/modifierList/type/initializerNode/annotations; type rawTypeNameParts/typeArguments. Memoizing the class collections is the key enabler — member wrappers are now stable, so the per-member lazies actually cache. Behaviour-preserving (pure functions of the immutable AST + already-lazy classifier).model/JavaClassOverAst.kt, model/JavaMemberOverAst.kt, model/JavaTypeOverAst.kt.JavaParsing* 105/105.HashMap concurrency in JvmBinaryClassFinderInputsOverIndex, annotation classId memoization, more per-type/param lazies) in implDocs/PERFORMANCE_REVIEW_2026_07_20.md.package-info.classfindPackage) now materialises a binary <pkg>/package-info.class and exposes its class-level annotations as the package’s JavaPackage.annotations. Previously those were dropped, so JSR-305/JSpecify package defaults (@ParametersAreNonnullByDefault, @TypeQualifierDefault, @NullMarked, …) on a binary Java package were invisible: a type-variable parameter substituted with an explicitly nullable Kotlin type argument stayed nullable instead of becoming definitely-non-null, producing a spurious UNSAFE_CALL in user code (dokka's Property<File?>.map { it.relativeToOrSelf(..) }). The finder reuses the same memoised binary index the deserializer reads through; class/package existence still routes through the deserializer, so only annotations are added.JvmBinaryClassFinderInputsOverIndex.kt (+findPackageInfoClass), JavaDirectFacadeBuilder.kt (NoOpJavaClassFinder → LibraryJavaClassFinder + BinaryPackageInfoJavaPackage), cli-jvm/…/JvmFrontendPipelinePhase.kt (thread the shared binary-inputs builder into the facade builder); new test codegen/boxJvm/javaInterop/foreignAnnotationsTests/tests/dnnParameterFromBinaryPackageAnnotation.kt.UNSAFE_CALL … nullable receiver of type 'String?') and passes with it.probeFqnSplits fallback: commit to the leftmost type like javacresolveQualifiedNameToClassIdFromParts no longer retries a failed name as a plain package.Class split. Like javac (JLS 6.5.4/6.5.5), once a leftmost type is found the interpretation is committed: a failed member-type descent returns the nonexistent nested id of the committed prefix (full resolution), which stays unresolved downstream — red code, exactly as javac reports on a package/type name clash (JLS 6.1). The reentrance-safe flavor returns null instead, so supertype-walk seeding is never poisoned by a dangling id. probeFqnSplits deleted.qualifiedNamePackageClassClash.kt deleted from the shared roots and recreated with javac-strict expectations in the new java-direct-owned testData/diagnostics root (wired into TestGenerator); the pre-existing javac/qualifiedExpression/PackageVsClass2.kt — verified against real javac to be red code (“cannot find symbol: class b, location: class a”) — is skipped via the new SkipTestsPinningPsiJavaModelDeviationsMetaConfigurator and mirrored strictly in the same root. Strict diagnostics: MISSING_DEPENDENCY_CLASS on the call whose Java signature uses the clash name + UNRESOLVED_REFERENCE on members of the unresolved type.resolution/JavaTypeResolver.kt (−32/+18), testFixtures/…/components.kt, testFixtures/…/AbstractJavaUsingAstTest.kt, testFixtures/…/TestGenerator.kt, build.gradle.kts (own testdata root registered); Scenario D refreshed in ReadMe.md and implDocs/RESOLUTION_SCHEMA.md.resolveQualifiedNameToClassIdFromParts as a left-to-right JLS 6.5.4 passprobeFqnSplits fallback survives, but is now reached only when the JLS pass fails — the sole divergence from javac remains the package/type name clash pinned by qualifiedNamePackageClassClash.kt (KT-87813).resolution/JavaTypeResolver.kt (−36/+28); Scenario D refreshed in ReadMe.md and implDocs/RESOLUTION_SCHEMA.md.JavaParsing* unit tests green.JavaToKotlinClassMap disjunct in resolveFromJavaLangresolveFromJavaLang accepted a name when either JavaToKotlinClassMap.mapJavaToKotlin hit or classExists was true; the map disjunct is dead. It only ever probes ClassId(java.lang, X), and classExists resolves those via the symbol provider whose JVM builtins arm answers only kotlin.* ids (StandardClassIds.builtInsPackages), so a java.lang.* lookup never returns a BuiltIns-origin symbol filtered by tryResolve — it hits the JDK library class instead. Every mapped java.lang fqName (Object/String/Number/CharSequence/Comparable/Throwable/Cloneable/Iterable/Enum/wrappers/Deprecated) exists in both full JDK and mockJDK, so classExists already covers exactly what the map would. Collapsed to classExists(classId, fullResolution), matching resolveFromSamePackage; better PSI parity (no divergence in the no-JDK case). Removed the now-unused JavaToKotlinClassMap import.resolution/JavaTypeResolver.kt (−5/+1).classExists alone suffices).testNamelessInJava)JavaClassOverAst.constructors required a constructor METHOD node to have both no return TYPE and an IDENTIFIER, so a malformed nameless declaration like void () {} (its void is an error element, not a return type) was dropped; with no explicit constructor a public default constructor was synthesized, so class K : Nameless() saw a visible constructor and produced no diagnostic. PSI (same syntax parser) treats any no-return-type method as a (package-private) constructor, suppressing the default one → INVISIBLE_REFERENCE. Dropped the IDENTIFIER requirement so constructor detection mirrors PSI's getReturnTypeElement() == null.model/JavaClassOverAst.kt (constructors filter). Test data unchanged (shared golden).JavaParsingTest/JavaLightTreeTest green.testIntellij_exceptionAnalyzer)walkSupertypeClassIds accepted the first inherited nested class of a matching simple name regardless of accessibility, so a package-private nested type in a supertype from another package (e.g. SimpleColoredComponent.TextRenderer) wrongly shadowed a same-named top-level class, producing a spurious RETURN_TYPE_MISMATCH. Now filtered by JLS 6.6/8.2 accessibility: private never inherited, package-private only within the declaring package; inaccessible matches expand deeper instead of resolving. Visibility read cycle-safely via new FirJavaClass.nonEnhancedVisibility (from originalStatus, no lazy status) surfaced through FirBackedJavaClassAdapter.visibility.resolution/JavaInheritedClassResolver.kt, resolution/FirBackedJavaClassAdapter.kt, fir-jvm/.../FirJavaClass.kt (+nonEnhancedVisibility); test codegen/box/javaDirect/packagePrivateInheritedNestedClassNotVisibleAcrossPackages.kt.IntelliJFullPipelineTestsGenerated.testIntellij_exceptionAnalyzer green under java-direct; PSI + CompileKotlinAgainstKotlin gates green (shared FirJavaClass edit).JavaClassOverAst.supertypes (IJ-FP regression)supertypes was a recomputing get() that returned fresh JavaClassifierTypeOverAst instances on every read; FIR forces it from two lazy slots per class (FirJavaClass.superTypeRefs enhancement and directSupertypeClassIdsCache), so each supertype's classifier (a per-instance lazy hitting the symbol provider) resolved from cold twice. Made it by lazy(PUBLICATION) so both reads share instances and each supertype resolves once. List build allocates wrappers only → still resolution-safe.model/JavaClassOverAst.kt (supertypes get()→by lazy).testIntellij_platform_ide_impl warm frontend (isolated bench, 4 iters, same build): java-direct 21.5s wall / 20.8s CPU vs legacy 25.3s / 23.6s — was ~+8% before.