The Rest of the Story: July Edition - JVM Weekly vol. 186
It's end of the month... again!
The new edition has some proper shape. One thread is what the JVM gives up to go fast. Leyden published which of the Java agent API’s thirty-year-old habits are now illegal. Quarkus handed back at build time what the runtime took away. A transpiler reached HotSpot parity by declining to implement parts of the spec.
The second thread took me longer to see, because it arrived as five unrelated releases. Five teams, one month, five answers to the same question: which part of this loop does the AI not get? None of them answered “all of it.”
Plus one very nasty bug, so let’s start there.
1. July: The Rest of the Story
The most alarming thing in July arrived as a mailing list post from someone whose canary nodes were failing.
JDK-8377715, reported on loom-dev, is a regression in the virtual thread implementation where frame thawing can erroneously undo JIT deoptimizations. It surfaced in production on BellSoft Liberica 24 (24.0.2+12) with Spring Boot 3.4 and Kotlin. It presents as a SIGSEGV during G1 heap scanning, in G1ParCopyClosure::do_oop_work, which is about as far from the actual fault as you can get inside one process.
The mechanics: when the JVM processes deoptimizations for newly encountered exception subclasses via HandshakeAllThreads, non-null parameters can appear as null during class_check uncommon traps. One report cites a 75% failure rate on canary nodes. Until a fix is backported, the only reliable mitigation is moving affected services back to platform threads.
The bug’s address matters more than the bug. It doesn’t live in Loom, or C2, or G1, but in the seam between them. Loom’s stack-switching was retrofitted onto deoptimization machinery that predates it by two decades, and the two only disagree under a specific combination of exception-class churn and concurrency. Nobody’s test suite caught it. Somebody’s canary did.
Project Leyden is OpenJDK’s long-running effort to make the JVM start faster by doing work ahead of time instead of at every launch. Its AOT cache is the concrete mechanism, built in three phases: a training run where you exercise the application so the JVM can record which classes it loads and which methods it compiles, an assembly step that turns those observations into a cache file, and production, where the JVM memory-maps that file rather than rediscovering everything. The payoff is substantial, as the Quarkus numbers in the next section show.
Leyden has now published what the cache costs Java agents, tracked as JDK-8387190, targeting JDK 27 with partial applicability to 25 and 26. It comes to four items: two API calls agents may no longer make, no support for native agents, a constraint on how -javaagent is passed, and one restriction that fails silently.
That last one is the early-load problem, and it needs a word about how agents work. The -javaagent:datadog.jar on your command line rewrites bytecode as each class loads, inserting the timing and tracing calls your dashboard later reads. It installs itself in a hook called premain, which is supposed to run before your application’s classes do. Under an AOT cache the JVM may load cached classes before premain, so for those classes the rewriting never happens. Nothing throws and nothing warns. The only sign is that parts of your code quietly stop showing up in traces.
The other three are easier to plan around. Agents may no longer call AppendToBootstrapClassLoaderSearch or AppendToSystemClassLoaderSearch, the APIs they use to add their own classes to the classpath at startup. Native agents (JDK-8387194) aren’t supported at all yet. And -javaagent must be identical across all three phases, even though agents do nothing during assembly, so upgrading your APM agent means running training again.
That’s the bill. Here’s what you’re buying.
The Main Thread put numbers on Leyden with Quarkus: in a Quarkus and PostgreSQL setup, the AOT cache cut median readiness from 1022.4 ms to 380.3 ms, roughly 63%. A training run produces caches for class metadata and pre-compiled code, memory-mapped on subsequent startups. Unlike Native Image, it preserves standard JVM semantics: full JIT, dynamic class loading, and the whole mature debugging and profiling toolchain.
The friction is mundane. The analysis flags a trap around build-tool lifecycles: standard Gradle builds can silently invalidate cache checksums, so the JVM ignores the artifacts you spent a training run producing and you conclude Leyden doesn’t work.
I argued in vol. 185 that Leyden had taken the startup story away from Native Image. This is the number behind that argument, and the Gradle footnote explains why your first attempt will underwhelm you.
Two projects spent July attacking Native Image’s closed-world constraint from opposite ends.
Native Image works by reachability analysis. At build time GraalVM walks your code, finds everything that can be reached, and compiles only that, which is where the small binaries and the fast starts come from. It’s also why reflection, dynamic proxies and runtime bytecode generation have always hurt: if the builder can’t see a class, that class doesn’t exist at runtime.
Crema attacks that from the inside. I covered -H:+RuntimeClassLoading last week: a bytecode interpreter living inside the native binary, able to load classes the builder never saw. GraalVM is handing back some of the dynamism its own analysis took away.
Quarkus Shim comes at it from the other side and doesn’t loosen closed-world at all. It’s a build-time augmentation API for patching third-party libraries you can’t modify: wrap or replace methods, strip code native image can’t handle, all before the image is built and with no -javaagent at runtime. The library was never going to survive reachability analysis, so Shim fixes it in advance.
Line those up next to Leyden and you get three positions on where bytecode patching should happen. Leyden restricts it at runtime to keep its cache valid. Crema permits more of it at runtime, inside a native binary. Shim moves it ahead of the build. Worth keeping straight: Shim patches libraries for compatibility, so it won’t stand in for the observability agent Leyden just constrained.
ParparVM, the Java-to-C transpiler behind Codename One, reports reaching geomean parity with Java 25’s HotSpot. That closes a gap that used to be 4.21x, matching or beating HotSpot in 60% of tested cases at a substantially lower peak memory footprint.
The “cheating” in the title is honest. It’s a deliberate departure from strict JVM spec compliance, bypassing dynamic class loading and complex reflection handling to generate C tuned purely for execution speed. It isn’t a general-purpose HotSpot replacement and nobody claims otherwise.
Three items in a row where the answer to “how do we get faster” was “stop supporting the dynamic thing.”
Soonil N. published the primitive-hashtable benchmark the JVM has been missing on 19 July, and it’s a monster. Budget an hour. Ten libraries (JRE, FastCollect, Fastutil, AndroidX, Trove, Koloboke, Eclipse Collections, HPPC, Agrona, LibGDX), eight operations, three key distributions, two map shapes, interactive graphs you can pan and zoom, and the raw CSV to download. The only comparable prior work is a 2014 DZone post by Roman Leventov, who happens to be Koloboke’s author.
The first third isn’t a benchmark at all. It’s a textbook chapter on hashtable design: separate chaining against open addressing, probe strategies, load-factor mathematics, tombstones against backwards-shift deletion, and what avalanche and bias mean for a hash finalizer. If your model of HashMap stops at “buckets, and long chains become red-black trees,” that section alone is worth the visit.
HashMap has closed most of the gap. Against Leventov’s numbers it went from roughly 9x slower than Koloboke on hits to 2 to 3x, and on misses it now beats several primitive libraries outright, because chaining lets it rule a key out without ever touching the second memory tier. Overall the specialized tables buy 3 to 6.5x memory and 2 to 3x CPU.
One aside worth catching: his note that C++ runs on Swiss tables and Java doesn’t, because the Vector API is still incubating twelve years into Panama. Read that next to GraalVM 25.2 in the Radar, which just switched the Vector API on by default in Native Image.
He closes by deflating his own work. The memory savings are the prize, CPU is secondary, and most JVM code outside high-performance computing has no reason to care. Read it anyway - the design section will change how you read hashtable code afterwards.
Graal Script Agent shipped with GraalVM 25.2. It’s the sequel to the sandboxing story I covered last week: Christian Humer’s team turned the isolation primitives into a prompt-to-plugin workflow. Application developers define extension points that declare what kinds of plugins may be generated and which application APIs they can reach. A model turns a natural-language request into a JavaScript or Python plugin, which runs in a restricted polyglot context with resource limits and schema-driven bindings.
The extension point governs consequences as well as capability: a read-only query can execute immediately, while anything that modifies state should be recorded and previewed for approval rather than committed by the generated script. And a stored plugin runs locally against current application data without another model call, so the model is consulted once, at authoring time.
JetBrains Research arrived at the same conclusion from the opposite side of the same fortnight, and KotlinLLM is the more conservative of the two designs despite sounding like the wilder one. Anastasiia Birillo and Stanislav Sandler have open-sourced it under Apache 2.0 as an IntelliJ IDEA plugin for Kotlin/JVM projects, adding one language feature they call Smart macros: a normal Kotlin call whose body is generated Kotlin source.
KotlinLLM produces a file in your repository that goes through code review like anything else, and the sample projects ship with the generated sources committed so you can read what the model actually wrote. Prior work in this direction (byLLM, nightjar, Healer) targeted interpreted languages, mostly Python. Doing it in a compiled, statically typed language, where the compiler checks the generated code before it runs, is the part worth watching.
The example that makes it click is parsing. Suppose you want beginner-friendly issues from twenty GitHub repositories. One repo labels them good first issue, another beginner, another E-easy, another buries it in the title. You can’t write the complete parser up front because you don’t know all twenty conventions. So you write the intent instead:
val issues: List<Issue> = asLlm(response, hint = "Return all beginner-friendly issues for this repository")The return type is declared normally and the compiler enforces it. The body doesn’t exist yet. Run the application under the plugin’s executor, and when execution reaches that call with real data, the model generates Kotlin source for the conversion, the plugin compiles it and swaps the class into the running JVM by hot reload, and execution continues. Repository seven shows up with a convention nothing covers, the loop fires again and adds a branch. That’s what “evolution” means here: the function body accumulates the cases reality throws at it, in the order reality throws them.
What you’re left with is an ordinary .kt file you read, review and commit. Once generated, the plugin is no longer involved and the code runs as plain Kotlin, so covered scenarios cost no model call, no latency, and behave deterministically. The second macro, mockLlm<T>(), does the same trick for test doubles: declare an interface, get a stateful implementation whose behaviour depends on which methods you call.
JetBrains Air now hosts external agents over the Agent Communication Protocol: GitHub Copilot, OpenCode, Pi, Cline. The part that matters here is IntelliJ-powered code intelligence for Java and Kotlin, which gives agents IDE-native symbol navigation and diagnostics instead of inferring structure from text.
Windows-specific tasks run inside Docker containers for host parity, and each agent gets an isolated workspace.
James Ward gave an IntelliJ IDEA Tech Talk from 👩🏻💻 Marit van Dijk on SkillsJars a couple of weeks back, which is a good excuse to catch up on it. Skills, the SKILL.md format Anthropic standardized in January, have been spreading through this whole edition without anyone naming the distribution problem out loud. IntelliJ 2026.2 added agent skills. JAIPilot ships one in .agents/skills/. BossConsole’s Tool Creator writes the same skill four times over, into .claude/, .codex/, .gemini/ and .opencode/, because that’s what you do when the only distribution mechanism is copy and paste.
We have already talk about this in the past but Ward’s answer is the one the JVM reaches for by reflex. A SkillsJar is an ordinary JAR with SKILL.md files sitting at META-INF/skills/<org>/<repo>/<skill>/, published to Maven Central under the com.skillsjars group and versioned like anything else. You consume it two ways. A Maven or Gradle goal extracts skills into whatever directory your assistant reads (./mvnw skillsjars:extract -Ddir=.claude/skills), or a JVM agent reads them straight off the classpath with no extraction step, which is what Spring AI Agent Utils’ SkillsTool does. Transitive dependencies work. So does security scanning, which matters more than it sounds when the payload is a set of instructions your agent will follow without arguing.
Bruno Borges built a GitHub Copilot extension that automates the JVM profiling lifecycle. It reads JFR data instead of making you squint at an APM dashboard. In the write-up it catches a misconfigured -Xmx, identifies it as a GC-overhead and OOM risk, and proposes flag corrections.
I like this more than most agentic demos for an unglamorous reason. JVM flags are a far better target for a model than business logic: a small, thoroughly documented search space, and a verification step that is a benchmark rather than a code review.
JetBrains is deprecating both the built-in plugin wizard and the GitHub-hosted IntelliJ Platform Plugin Template in favour of a JetBrains-managed IDE Plugin Generator web API, starting with IntelliJ IDEA 2026.1. It fixes template drift: new projects always get current SDK and Gradle configuration without waiting for an IDE update. Whether a web service preserves the community template’s flexibility is the open question.
2. Release Radar
GraalVM 25.2
The headline of GraalVM 25.2, announced by Alina Yurenko 🇺🇦, for anyone paying a cloud bill is compressed references in Native Image, now enabled by default. On 64-bit systems, references between managed objects are stored as 32-bit heap-relative values instead of full 64-bit addresses, which gives more compact reference-heavy object graphs, better cache density, and a smaller image heap. On a Micronaut application backed by Oracle Database, the team measured a 39% reduction in RSS under load on Community Edition 25.2 versus 25.0
G1 GC now works in Native Image on Windows via --gc=G1, completing the platform sweep after 25.1 brought it to Darwin/aarch64. G1-based images also start faster, because more initialization moved to build time, with the biggest wins on applications with large image heaps. They come out smaller when built with PGO or -Os.
The Vector API is now enabled by default when jdk.incubator.vector is in the boot module layer. After the panama-dev threads I covered in vol. 181, where the Vector API struggled to beat C2’s auto-vectorization, it’s notable that Native Image is the one turning it on unasked.
Fourth is Graal Script Agent, covered above.
Embabel 1.0.0
Embabel reached GA. Rod Johnson announced 1.0.0, crediting Igor Dayen, Alexander Heifetz and the community contributors, and has described it as his most important project since founding Spring.
The planner is what separates it from the LangGraph-shaped world. Embabel is written in Kotlin, sits on Spring AI, and is deliberately pleasant from plain Java, but its core is Goal-Oriented Action Planning, an algorithm borrowed from game AI. The framework discovers available actions and goals from your annotated Spring components, derives current world state from the presence or absence of typed domain objects on a shared blackboard, plans a sequence, executes the first action, and replans. It’s an OODA loop that runs until the goal’s preconditions are satisfied.
That planner is deterministic code rather than another LLM call, which is the pitch: explainability and predictability in the one place where a second model call would make the system impossible to audit. Domain objects are Kotlin data classes or Java records, so prompts stay typesafe and survive refactoring, and domain behaviour can be exposed as tools.
Micronaut Framework 5.1.0
Micronaut 5.1.0, announced by Sergio del Amo Caballero, is the first feature release on the 5.x line, and its most consequential line is easy to miss: Micronaut 5.1 is what makes Open DI 1.0.0 possible. ODI is a CDI Lite implementation backed by Micronaut’s compile-time dependency injection. Applications run against the Jakarta CDI API and the ODI runtime, while an annotation processor generates Micronaut bean definitions and proxies at build time. You get CDI semantics without the runtime reflection.
Micronaut Core (5.0.7 to 5.1.10) adds explicit @Introspected.Property, sequenced-collection injection, CDI integration hooks, KSP traversal of Kotlin inner classes, configuration-based logger levels, and per-service client SSL enabled by default.
On the AI side, Micronaut LangChain4j 2.2.0 picks up agentic support, AI-service guardrails resolved from Micronaut beans, evaluation testing, Chroma embedding-store support, and Oracle chat-memory auto-configuration. Micronaut MCP jumps to 2.0.0 on the MCP Java SDK 2.0.0.
In data, Micronaut Data 5.1.1 adds a SQLite dialect, value-based ETags for optimistic locking, and Jakarta JPA metamodel generation, while Micronaut SQL 7.1.0 gains a MyBatis integration. Micronaut Security 5.3.1 is unusually busy: an OWASP HTML Sanitizer module, @RunAs authentication delegation, OIDC locale resolution from profile claims, and authentication mapping. Micronaut Serialization 3.1.0 adds JSON-B/JSON-P compliance plus @JsonKey, @JsonMerge and @JsonTypeId.
One callback I enjoyed: Micronaut Test Resources 4.1.0 adds Floci support. Floci was a GitHub All-Star in vol. 180. Last month Markus Eisele wired it into Quarkus Dev Services. Now it’s a Micronaut test-resources module, which is two framework integrations in two months from a standing start.
Quarkus 3.37
The headline of Quarkus 3.37 is extension-based modularity, and it’s more interesting than the name suggests. The new quarkus-jlink extension produces a jlink-ed modular application: a custom runtime image containing only the JDK modules your application actually uses, everything else stripped. For container deployments where image size is the budget line, that’s a third answer alongside Native Image and Leyden’s AOT cache, giving a smaller artifact on an ordinary JVM with no closed-world analysis.
Second, and squarely on this edition’s other thread: Jackson’s reflection-free serializers are now enabled by default. That’s a structural decision to stop paying reflection on the serialization path, not a CVE patch or a version bump, with quarkus.rest.jackson.optimization.enable-reflection-free-serializers=false as the escape hatch if it breaks you. Given how many reachability-metadata headaches have traced back to reflective serializers, it’s one of the better native-image quality-of-life changes of the year.
Smaller but welcome: the REST Client gains RestMultiResponse, so you can pull status codes and headers out of a streamed response without dropping down to the Vert.x HTTP Client. A new quarkus-rest-data-hibernate-types extension is pulled in automatically when both quarkus-rest-jackson and quarkus-data-hibernate are present.
On the platform side, Quarkus LangChain4j moves to 1.11.2 and Quarkus MCP Server to 1.13.0, which is worth noting given the company this edition keeps. The project now counts 1203 contributors. The rest: Hibernate ORM 7.3 to 7.4, Reactive to 3.4, Search to 8.4. ORM 7.4 carries real behavioural and DDL changes. Pagination limits are now processed in SQL, new NOT NULL constraints appear on timestamp columns, and the PostgreSQL minimum moves to 14, so read the migration guide rather than trusting quarkus update. Search 8.4 is fully backwards-compatible. Elasticsearch Dev Services now default to Elasticsearch 9.4 and OpenSearch 3.6.
WildFly 41
WildFly 41, announced by Brian Stansberry , is a short-cycle release. WildFly 40 was feature-boxed to deliver EE 11 and slipped a month, so 41 exists mostly to get back on the January/April/July/October cadence. It still carries a decent load.
The container story moves forward. JDK 25 images replace the JDK 17 ones across the container, S2I builder and runtime images, and everything rebases from ubi9-minimal to ubi10-minimal. latest now points at JDK 25. Bootable jars gain cloud packaging support via wildfly-cloud-galleon-pack and the Maven plugin.
The operational change I’d flag first is the new transactions-recovery-graceful-shutdown attribute. Set to wait, transaction recovery keeps running during a graceful shutdown, which is how you stop losing in-flight transactions on a rolling restart. Infinispan cache stores now default to non-segmented storage, and JGroups FD_SOCK2 failure-detection ports moved out of the OS ephemeral range with port_range set to 0 for deterministic binding. Several features graduated stability levels, including JGroups TCP TLS and OIDC logout reaching default.
Two deprecations belong in your calendar. The AJP listener in undertow is deprecated, with no encryption, no authentication, no HTTP/2 or WebSocket, and no protocol development since 2001; migrate to HTTP proxying. And the WildFly EE 10 variant will be discontinued with WildFly 42 in the autumn, so the migration runway for EE 10 holdouts is now explicit.
One detail reads as a direct sequel to vol. 181: WildFly stopped deploying tar.gz files to Maven Central for pre-built installations. Stansberry’s reasoning matches the argument Brian Fox made in June. Central imposes upload-bundle size limits, WildFly’s release bundles were far over, and in his words they needed to go on a diet rather than ask for an exception. Sonatype’s hard enforcement starts 11 August.
Java SE 25 is now the recommendation. SE 17 support will likely be withdrawn within a year.
Azul Payara Server & Micro 7.2.0
Payara Server 7.2.0 is Jakarta EE 11 certified across Full Platform, Web Profile and Core Profile; Payara Micro implements Web and Core. Both ship MicroProfile 6.1 complete and pick up Grizzly 5.0.2. Two carried-over compatibility changes: backwards compatibility for defining implicit CDI via glassfish-application.xml, and Payara major-versioned deployment descriptors that let an application pin itself to a Payara major version.
The security story is the interesting part. Brute-force authentication prevention was backported across 7.2.0, 6.40.0, 5.89.0 and 4.1.2.191.57. The fix originated in Eclipse GlassFish and was ported back into Payara, which is a nice reminder of the shared ancestry. Separately, 6.40.0 closes two Jackson CVEs, CVE-2026-54512 and CVE-2026-54513, in PolymorphicTypeValidator. The 7 line isn’t affected.
For the open-source side, Payara Community 7.2026.7 tracks the same development line with the same fixes.
Flyway 13.0.0
Flyway 13.0.0 ships a Flyway MCP server, exposing development-time tools an AI agent can call and running Flyway commands for both state-based and migrations projects. Database migrations are the part of the pipeline where people are least willing to let an agent improvise, so Redgate shipping a first-party MCP surface for exactly that says something about where the enterprise tooling layer thinks this is going.
Beyond that: check drift and check changes JSON output now includes an id per difference, so drift can be resolved against specific objects rather than wholesale. Oracle gains an IncludeOnlineIndexCreation option. The docker provisioner now returns a dedicated error code when a SQL Server or Oracle container is provisioned without accepting the vendor’s EULA.
TornadoVM 5.1.0
TornadoVM 5.1.0 continues the story from vol. 184, where TornadoVM conceded that portability has a price. The headline is FP8 support in E4M3 and E5M2 for the CUDA backend, plus a fix for NaN/Infinity float constants, which means TornadoVM now speaks the numeric format modern quantized inference runs on.
Two performance changes are worth knowing. Large host-to-device transfers are now staged through a pinned host buffer ring, and intra-plan concurrency auto-disables on serial task graphs rather than making you notice. The rest is largely backports into the JDK 25 line, covering CUDA on Windows, NVTX and cuSPARSE, and hybrid CUTLASS, plus a completed hybrid API guide with a custom-provider walkthrough.
GPULlama3.java 1.0 with CUDA support
GPULlama3.java 1.0.0 landed on 28 July, eleven days after the TornadoVM release above and from the same lab. Two things changed at once.
The first is distribution, and it’s the one I’d lead with. Until now this was a repository you cloned and built. It’s now on Maven Central as io.github.beehive-lab:gpu-llama3, with separate artifacts for JDK 21 and JDK 25 (1.0.0-jdk21 and 1.0.0-jdk25) and Maven profiles that auto-activate against whichever JDK you’re on. GPU inference in Java went from a project you evaluate to a dependency you declare.
For anyone who hasn’t met it: GPULlama3.java runs LLM inference on the GPU in pure Java, built on Alfonso² Peterssen Llama3.java, covering Llama3, Mistral, Devstral 2, Qwen2.5, Qwen3, Phi-3 and IBM Granite 3.2+ and 4.0 in GGUF format. There are no hand-written kernels, no JNI layer and no native build step. You write Java, and TornadoVM compiles it down to the device. It already serves as the GPU inference engine inside Quarkus, and since LangChain4j 1.7.1 it has been an officially supported model provider, so GPULlama3ChatModel.builder()…onGPU(true) is the entire integration.
The second is CUDA support, and it’s news rather than a footnote because the NVIDIA path previously went through OpenCL or PTX, TornadoVM’s backends being OpenCL, PTX and SPIR-V. A dedicated CUDA backend is exactly what TornadoVM 5.1.0 was assembling one entry above, where FP8, CUTLASS, cuSPARSE, NVTX and pinned host buffers all landed. Read the two releases together and the framework shipped a backend and its flagship consumer adopted it inside a fortnight, which is easier when both halves report to the same lab.
Read it against libargus.cc in the All-Stars section below for July’s two opposite answers to how a JVM runs a model. GPULlama3.java compiles Java onto the GPU and never leaves the language. libargus.cc goes through Panama to somebody else’s hand-tuned C++. Both now work.
IntelliJ IDEA 2026.2
IntelliJ IDEA 2026.2 shipped on 16 July, and the headline is AI, though not in the direction you’d expect from a company that sells its own assistant. The release adds native GitHub Copilot integration, agent skills, and AI completion support for third-party providers.
In last Rest of the Story I framed JetBrains and Microsoft as opposite bets on the agent harness. Two products in one month say JetBrains isn’t betting against Copilot at all. It’s betting that the harness and the IDE intelligence are the defensible part, and that whichever model you bring is your business. Shipping first-party integration for a competitor’s agent inside your flagship IDE is a strong way to say that out loud. The agent skills piece is the same markdown-instruction-file pattern that keeps surfacing in this edition, including in JAIPilot’s .agents/skills/ directory below.
On the practical side, logpoints are the one I’d try first. A logpoint is a breakpoint that prints a value and keeps going instead of suspending, which retires the write a println, rebuild, run, delete the println loop that everyone still does. It pairs with better navigation from runtime output back to the source line that produced it. Dependency completion does what it sounds like for Gradle and Maven coordinates, so you stop tabbing out to search for the artifact name and current version.
Early Gradle 10 support with migration assistance deserves flagging next to the Gradle roadmap in §1. If Declarative Gradle and the Kotlin DSL default are the destination, the IDE getting migration help in before the release lands is how that transition stops being painful. Also here: a reworked Git conflict resolution flow and Docker Compose improvements.
For platform currency, 2026.2 gives day-one Java 27 support and the stable Kotlin 2.4 language features, plus Spring work aimed at database migration workflows and richer Spring Security insights. Terraform gets a testing framework and TypeScript moves to 7.0.
The stability story lives in a separate post: over 1,300 bug fixes and usability improvements, plus 140 freezes and other performance issues addressed. Worth reading on its own if you work in a large multi-module project, where freezes cost more than features gain.
👩🏻💻 Marit van Dijk 👓 Anton Arhipov Marco Behler Siva Prasad Reddy K prepared video presenting all the changes in proper context.
Kotlin 2.4.10
Kotlin 2.4.10 is a bugfix patch on the 2.4 line I covered in vol. 181, but three entries are worth pulling out. KT-86930 introduces kotlinr in the Kotlin distribution, the only non-fix in the changelog, continuing the “one entry point into all of Kotlin” direction from the Kotlin Toolchain work. KT-87076 fixes @file:CompilerOptions(”-jvm-target“, ...) being ignored in .main.kts scripts under 2.4.0, where it silently fell back to JVM target 1.8. On the Compose side, b/522127447 addresses Compose Compiler 2.4 reporting classes as runtime or Uncertain that were previously inferred stable, a regression that shows up as recomposition cost rather than a compile error.
3. GitHub All-Stars
Three projects about running models properly, and one about running a JVM for a very long time under adversarial conditions.
BossConsole - a governed agent harness that runs on the JVM
BossConsole (BOSS) from Risa Labs is Apache-2.0, built with Kotlin Multiplatform and Compose Multiplatform, and bills itself as the first open-source multi-platform harness in a category otherwise made entirely of closed-source products. You bring the agent (Claude Code, Codex, Gemini CLI, OpenCode) and BOSS gives it a terminal, an embedded browser, an editor, secrets, and roughly 100+ MCP tools across about 20 plugins.
The architecture is the interesting part. BOSS exposes itself over the same MCP layer the agent uses for work. Read-only tools give the agent situational awareness, letting it list tabs, read scrollback, tail the console, snapshot JVM performance and inspect git. Action tools let it act on what it finds.
Two of those tools matter for this edition. Tool Creator scaffolds a plugin with build files, manifest, skeleton UI and CI, and writes its skill in all four CLI formats (.claude/, .codex/, .gemini/, .opencode/). Tool Evolver then measures how that plugin behaves under memory, leaks, logs and output, improves it, hot-reloads it into the running app, and opens a PR. Same bet on persisting and improving generated code as Graal Script Agent and KotlinLLM, aimed at the tooling rather than the application logic.
The governance layer is unusually complete for a young project: server-enforced RBAC via Supabase row-level security, a live per-tool kill switch, secrets whose browser auto-fill reaches the page but never the model, and signed plugins binding pluginId | version | sha256 that fail closed on a swapped JAR.
The README is honest about what that doesn’t cover. Plugins run in-process, crash-isolated by a watchdog but not OS-sandboxed, and the MCP server is loopback-only and single-user. BOSS gives you governance where GraalVM gives you isolation.
Two footnotes. BossTerm, the terminal underneath, is separately useful and streams a session to your phone over an end-to-end encrypted WebSocket with no cloud relay and no account. And building the embedded browser needs a JxBrowser license key, so the full build is less unencumbered than Apache-2.0 suggests.
libargus.cc - Panama doing what Panama was for
libargus.cc (MIT, v1.0.0) is broader than I expected when I opened it. It’s a unified multimodal runtime consolidating LLM text generation, Whisper speech-to-text, Speech-LLM TTS, and vision, audio and video encoding behind a single Project Panama FFM boundary, on GGML and llama.cpp’s libmtmd.
Two design decisions make it worth opening. Pointers-only alignment: flat C functions taking pointers rather than pass-by-value and C++ polymorphic boundaries, with struct padding manually packed so no compiler quietly injects an alignment gap. Zero copy on hot paths: no int[] or float[] anywhere, MemorySegment goes straight through, so token tapes, audio waves and video frames run with no GC footprint. The Java side still reads normally, with Arena.ofConfined(), AutoCloseable and builders throughout.
Then there’s a README section titled Engineering Methodology & Development Velocity, which is the third position on LLMs in this edition. Humans own every memory semantic: Arena lifecycle boundaries, struct alignment packing, off-heap asset recycling. LLMs were used, in the author’s words, strictly as high-speed syntactic compilers for repetitive C-to-Java downcall bindings, builder boilerplate and layout strings, generated from explicit engineering blueprints. He frames it as compilation rather than generation.
Worth reading against GPULlama3.java in the Radar above. libargus.cc says the JVM’s job is to reach native code cleanly and get out of the way. GPULlama3.java says the JVM’s job is to compile your Java onto the accelerator so there’s no native code to reach.
JAIPilot-CLI - the distrust infrastructure around a coding agent
JAIPilot-CLI (MIT, 1.0.11) generates Java unit tests and tracks JaCoCo coverage from the terminal. One correction to how it’s been described elsewhere, including by me: it calls no hosted backend and has no config file of model endpoints. Generation comes from the coding agent you already have authenticated locally, starting with codex. Java 17+, Maven or Gradle, JaCoCo XML reporting configured.
Almost every design decision is about not trusting the agent’s word for anything.
Coverage commands refuse to read whatever report is lying around. status deletes the recognized JaCoCo XML first, runs the clean full suite, and reads only what that run produced. A failed or timed-out refresh fails the command and deletes partial output rather than falling back to stale data. Overlapping refreshes are rejected so concurrent commands can’t consume each other’s reports. Multi-module repos must configure one aggregate report; ambiguous per-module reports are rejected rather than guessed at. Even the “this class probably has a test” indicator is documented as a filename heuristic, with the refreshed coverage counters as the only execution-based truth.
Batch generation copies the project into isolated sandboxes so parallel runs never share build or coverage state, then merges touched test files deterministically, failing rather than overwriting on divergent edits to the same path. Repair is allowlisted: the agent may fix only the generated tests, in a disposable workspace, and those fixes merge transactionally before the clean build runs for real. Nothing prints until JAIPilot has confirmed each generated test actually executed.
Yeah, I hear they are using ElevenLabs 😁 I was using that voice myself for Reachy Mini Pet Project.
And that all-folks!












