A reference whose hierarchy escapes into an unanalyzed class is counted as
unverified rather than silently ignored. A dump
names the application's runtime classpath, never the JDK's own classes, so a
hierarchy that walks up into a JDK type escapes. Every tool layers the JDK API
of one release under the resolution scope to conclude those references, at the
release jdkRelease derives or
--jdk-release N names.
The layer sits under the old and the new side alike, so a gap in it resolves the same way on both and cancels out. It can turn an unverified reference into a conclusion, never invent a violation.
The stubs come from the ct.sym file of the JDK named by UIKA_JDK, checked
first and authoritative when set, else JAVA_HOME. UIKA_JDK may be a JDK home
or a ct.sym file, and the plugins export it themselves so that the release
they pass and the ct.sym the CLI reads come from one JVM.
ct.sym carries every release below the JDK that ships it, and from JDK 22 on
that JDK's own release too. The plugins read the file to confirm the latter, so
a JDK 22 or later whose ct.sym lacks its own release, as early JDK 22 access
builds did, is clamped one below instead of failing the check. The layered
release has to be one of those, so it
cannot be newer than the JDK uika finds, and on JDK 21 and earlier it has to be
older. A JDK upgrade check reads both of its sides from ct.sym as well. Only
on JDK 21 and earlier does a side that names the JDK's own release come from
its jmods/ instead. Either way, checking an upgrade to the JDK you now run
needs only that one JDK. From JDK 22 on it does not need jmods/, which
Temurin 25 does not ship. Both sources are plain files, so uika reads them
without starting that JDK.
The JDK uika finds has to be at least as new as the release you move to. By
default a plugin hands uika the JDK the build runs on, so a change that moves a
module above that JDK fails the check with exit code 2 until the build runs on
the newer one. The Gradle plugin also knows the JDK each module compiles with.
A module whose release the ct.sym of the JVM running Gradle does not carry is
read from its toolchain's JDK instead, so a daemon pinned to an older JDK still
checks the move. On a JDK 21 daemon that includes a module on 21, since a JDK
21 ct.sym stops at 20. A module whose release the daemon's ct.sym carries
keeps it, whatever toolchain compiles it.
Stubs from JDK 22 on carry PermittedSubclasses, so a JDK upgrade check sees a
class that became sealed when the JDK uika finds is 22 or later. Stubs from JDK
21 and earlier do not carry it, so on those JDKs the check cannot see sealing
changes. It drops the attribute from the jmods/ side too, since reporting
sealing from that side alone would be a false positive.
The layer puts one release under a library pair. A JDK upgrade check makes two releases the compared pair instead, and reports what moving between them breaks.