Two threads this month.
The first is an old trade-off with new pricing. In 2020 Shopify picked React Native so it would stop building every feature twice. In September it went back to Swift and Kotlin, because coding agents made building twice affordable. The same month Netflix shipped a Java build tool that keeps its output short for coding agents, and Markus Eisele measured an agent that got faster by giving worse answers.
The second is the platform saying “wait” while projects around it keep shipping. OpenJDK removed JVMCI in JDK 27. TornadoVM answered with two major versions in three weeks. On the mailing lists, the Vector API heard “after Valhalla” once again, and a proposal to prepare libraries for Valhalla heard “bump your minimum Java version”.
Plus Groovy 6 went GA, so the August story has an ending.
1. September: The Rest of the Story
Shopify is leaving React Native, and the reason is not performance. Native is now the future of mobile at Shopify is by Mustafa Ali, the same person who wrote in January 2025 that the future of React Native was bright. So this is not a team that got burned and left angry.
The 2020 decision had three reasons: stop building the same features twice, let developers work across the stack, stop chasing feature parity. React Native delivered all three, by Shopify’s own account. What changed is the first line of the budget. The post says it plainly: “LLMs changed one of the core assumptions behind our 2020 decision”. Building the same feature in Swift and Kotlin “no longer carries the cost it used to”.
The rest is the migration plan. Rewrites from scratch rather than a gradual port. The Shop app went from proof of concept to a native app in the stores in 12 weeks. The main Shopify app, with 300+ screens, widgets and an Apple Watch app, ships later this year. Shopify built a tool called Helix for it. Helix splits a screen into checkpoints. Each checkpoint needs tests, a visual comparison with the running app, two adversarial code reviewers and a human sign-off before the next one starts. The authors also describe what happens without it: one-shotting a React Native codebase into native gives “a huge amount of unmaintainable code that can’t be shipped”.
For JVM readers, the Android half is plain Kotlin. The post does not mention Kotlin Multiplatform at all. Two separate native codebases, with agents doing the translation, is the setup KMP was built to avoid. I would like to read JetBrains’ answer to that.
Netflix open-sourced a build tool that treats module-info.java as the project file. ja, by Danny Thomas from Netflix’s JVM ecosystem team, is in preview and needs JDK 25 or newer. The module descriptor you already write for JPMS becomes the only project descriptor. Dependency versions go in a comment next to requires, and the main class goes in a Javadoc tag:
/**
* @mainClass com.example.hello.Main
*/
module com.example.hello {
requires org.apache.commons.text; // @1.13.1
}ja init, ja require, ja run and ja tool jdeps do what the names say. ja install installs a Maven artifact as a command with its own runtime (the README demo is cowsay, naturally). Underneath are separate tools: jig resolves versions, compiles and bridges to Maven repositories, jfmt formats, jist searches symbols, jdocserver serves API docs. The installer creates a ja-enabled copy of the JDK and leaves the source JDK alone.
Three lines in the README connect it to Shopify. The output is kept “concise so coding agents can work effectively”. jist gives coding agents “access to symbols and sources without indexing, LSPs or MCPs”. And jfmt exists partly to fix “the very common whitespace, indentation, import ordering and qualified class references introduced in agent written code”. In vol. 189, Databricks and JetBrains were betting on a semantic model behind an LSP. Netflix bets on the opposite end: make the build small enough that an agent does not need a model of it.
My caveats: 86 commits over three weeks, all by one person, version 0.22.1. And the JPMS adoption problem is real. Many libraries you depend on still ship without a module descriptor, and a tool built on module-info inherits that problem. It is still worth a look, because JPMS is rarely offered as the simple option.
JetBrains renamed its AI products and gave away a plugin. JetBrains Air, announced by CEO Kirill Skrygan, is an umbrella for Air in the IDEs, Air Teams for coordinating developers with autonomous agents, and Air Governance, which is JetBrains Central under a new name. Junie runs across all of them, and third-party agents connect through ACP, the Agent Client Protocol from vol. 189. The post has no prices and no dates, so treat it as direction rather than product.
The post itself has better sentences than the product names. “AI can produce code. Organizations still have to produce software.” And, closer to Shopify’s Helix than JetBrains probably intended: “Code becomes cheaper to generate but more expensive to verify.”
In the same month, the Grails plugin moved to the Apache Software Foundation, written up by Andrey Belyaev. From IntelliJ IDEA 2026.2 it is no longer bundled with Ultimate. It lives on the Marketplace as “Apache Grails” with the same plugin ID, and the Grails PMC owns releases and IDE compatibility. If your onboarding script assumes GSP support comes with the IDE, update the script.
Markus Eisele measured the cost of a faster agent. His benchmark ran IBM Bob against Open Liberty twice, alone and with a code-review-graph MCP server that pre-computes a code graph to shrink the context. The task was strict on purpose: find five specific call sites in FeatureManager.updateFeatures, with file and line citations, in four tool calls.
With the graph, Bob was 18.8% faster on average (20.6 s against 25.3 s). Without the graph it found all five call sites in both runs. With the graph it found none in the first run and one in the second. Eisele’s conclusion: “smaller context is not automatically better context”. He also says the speed-up figures “quickly became marketing numbers” once somebody checked the answers.
Two paired runs is a small sample, and the author lists that caveat himself. But it is the rare benchmark that reports the answers along with the latency.
The same week he published a jqwik tutorial on Quarkus. Property-based testing generates thousands of inputs and checks an invariant. It then shrinks a failure to the smallest input that still breaks it. jqwik’s first failure was an order of 875 units with a subtotal of 161,673.40. It shrank that to a one-cent item, quantity 6, discount 9%: the buggy code rounds per unit and charges 0.06 where the correct answer is 0.05.
One piece of context the tutorial skips. The version it uses, jqwik 1.10.1, is the release after the anti-AI affair from vol. 183. The README now opens with an Anti-AI Usage Clause and declares the project in “pure maintenance mode” without funding. That does not make the tutorial wrong. It does mean you should read the README before you add the dependency.
And a third one: Quarkus Goblin, a young Quarkiverse extension by ErwanLT that injects latency, forced HTTP statuses and exceptions at the REST boundary in dev mode. Eisele uses it to check whether @Timeout, @Retry and @Fallback engage when an upstream service slows down.
TornadoVM shipped two major versions in three weeks, and the first one is an answer to JDK 27. TornadoVM compiles Java methods to GPU kernels at runtime. Its compiler pipeline was built on JVMCI, the interface that lets a compiler written in Java plug into HotSpot. Vol. 192 covered how JDK 27 removed JVMCI from OpenJDK.
TornadoVM 6.0, on September 2, replaced JVMCI with a layer built on reflection, ASM and Unsafe. One build now runs on JDK 21 through 27. It also replaced the JNI shims with the FFM API, which removed about seven thousand lines of C++. The PTX, SPIR-V and FPGA backends are gone. OpenCL, CUDA-C and Metal remain.
(Yes, Unsafe. The JDK is removing that too, on a slower schedule than JVMCI.)
TornadoVM 7.0 followed on September 22, and it has its own entry in the Release Radar below.
Then a Reddit post went around with the title “Kotlin can now access NVIDIA’s CUDA and cuTile”, and it needs a footnote. What exists is PR #1123 by Christos Kotselidis, still open. Kotlin compiles to JVM bytecode, and TornadoVM compiles bytecode, so Kotlin kernels run on the existing backends. The PR measures Kotlin against Java on TornadoVM, with median time ratios between 0.99 and 1.12, not against handwritten CUDA. Its best detail is in the description: before the fix, “@Parallel loops written in Kotlin compile but silently run on one thread”. The tile DSL uses Kotlin context parameters, so a + b inside a tile scope compiles to tc.add(a, b).
Babylon, the OpenJDK project that wants to do some of this inside the JDK, also moved a little. The Code Reflection JEP draft by Paul Sandoz was updated on September 21 and is still Submitted, without a number or a release. The module is jdk.incubator.code and the annotation is @Reflect. If you see @CodeReflection in a blog post, that post is older than the draft.
Two old JDK corners got better defaults, and this one is long overdue. Deniss Larka, who maintains JConsoleBooster, a modernized JConsole, sent me both of these in August, and they sat in my inbox far longer than they deserved. Greetings, Deniss, and sorry for the wait.
The first revives JMXMP. If you ever connected JConsole to a server in another network, you know the ritual: open the JMX port, discover that RMI opened a second, random one, and file a firewall ticket for both. The JMX specification had a second transport with one socket and one port, service:jmx:jmxmp://host:9875. It lost because RMI shipped by default and JMXMP was a separate download (jmxremote_optional, from Sun’s OpenDMK), and its code has been effectively abandoned since around 2008.
druvu-lib-jmxmp 2.0.0 splits it into three JPMS modules that jlink accepts. It keeps the public API frozen and checks it with a snapshot test. It also changes the defaults. Authentication is mandatory. The connection is always encrypted, with your own TLS context or with an ephemeral self-signed certificate that the server generates. Deserialization goes through a deny-by-default filter. The article also describes an authorization check from the SecurityManager era that survived in the old code. On a modern JDK it asks for the caller’s identity, always gets “nobody”, and lets the request through. The author calls it “an allow-all with extra steps”.
The “When you should not use this” section is worth copying into more READMEs. If an SSH tunnel to the RMI connector works for you, keep it. JDK Mission Control does not speak JMXMP. The license is the original one, CDDL or GPLv2 with the Classpath exception.
The second library answers a smaller question that you have probably hit. ServiceLoader calls only a no-arg constructor or a static provider() method, so there is nowhere to pass an argument. The ServiceLoader article walks through the usual workarounds: construct an empty object and call init, or put a factory interface behind ServiceLoader. druvu-lib-loader packages the second one. A ComponentFactory receives typed Dependencies, and ComponentLoader.load throws if two implementations match, instead of silently picking one. The caller writes AccBook.load(path). The author names the trade-off himself: unlike Spring, a missing dependency fails when load() runs, not when the application starts.
Put it next to Netflix’s ja above, and this month had two projects that build on JPMS provides and uses instead of working around them.
A lap around the mailing lists.
On panama-dev, Zoran Sevarac, PhD of Deep Netts asked AI/ML users for Vector API feedback on September 15. Sevarac is a community member rather than an OpenJDK team member, and the numbers come from the replies. Joshua Zhu from Alibaba reported roughly 8 to 16 times faster dot products for vector similarity, about 2 times faster index construction, and 25% to 35% on some database operators. Alibaba also added a prefetch API to its internal JDK. Paul Sandoz named the main limit: C2 has no calling convention that passes vectors in registers. So when a vector crosses a method call that did not get inlined, it gets boxed into an object. Rémi Forax put a date on it, sort of: the problem stays “at least until JEP 401 lands and vectors are retrofitted to be value objects”. The API is in incubator number twelve, in case you lost count.
On valhalla-dev, Anderson Vasconcelos Pires proposed a standard way for libraries to mark classes as “value-ready” today, with a build plugin that emits a Multi-Release JAR in which those classes become value classes on a Valhalla JVM. Rémi Forax built roughly this in 2024 as Einherjar. Chen Liang answered with the tip-and-tail model from JEP 14: “Don’t try to make your library work for all Java versions. If you want a new Java feature, bump your minimum Java version”. Brian Goetz replied with one line, a link to JEP 390. Nobody made a formal decision, but library maintainers who hoped for a migration path got a clear signal.
On leyden-dev, Ioi Lam‘s iterative AOT training prototype got numbers in August. It trains an AOT cache in steps, feeding one run’s cache (-XX:AOTCache) into the next run (-XX:AOTCacheOutput). A framework could ship a trained cache, and your application could add its own training on top. The earlier classpath must be a prefix of the later one. On his javac benchmark, merged with the JEP 544 code-compilation work, startup went from about 0.25 s without AOT to under 0.1 s. The PR is still a draft with a merge conflict, so do not plan a release around it.
Quarkus and MCP went stateless. The MCP spec revision 2026-07-28 dropped sessions. There is no initialize handshake and no session ID tied to one server instance. Every request is a self-contained JSON-RPC message. New headers let a load balancer route a request without parsing the body. Kevin Dubois explains what that means for Quarkus: the quarkus-mcp-server 2.0.x line already supports it. Old clients fall back to sessions on the same endpoint, so you can migrate clients one by one. Client support is planned for 2.1.0. In practice, you no longer need sticky sessions or a shared session store to run more than one replica.
TachyonMCP, by Konstantin Pavlov, supports both spec revisions on one server too. It is an MCP runtime on Netty, currently at 1.0 beta. Java 21+ code stays blocking on virtual threads, Kotlin code uses coroutines, and there is no Spring or servlet container.
And if you watched Quarkus Insights #261 with Martin Kouba and thought Quarkus Signals was new: it shipped as an experimental core extension in Quarkus 3.36 in May. It combines CDI’s type-safe resolution with the three modes of the Vert.x event bus (publish, send, request), in process only. Receivers are always asynchronous.
Jakarta EE 12 has its first spec. CDI 5.0 passed its ballot on September 11. It adds @Eager for eagerly started application-scoped beans and @AutoClose for beans that implement AutoCloseable, plus asynchronous invokers. It also changes the Maven coordinates to jakarta.cdi:jakarta.cdi-api, so read the relocation notes before a version bump. Ivar Grimstad reports that the plan is to release the Core Profile before JakartaOne Livestream on December 1, with Web Profile in Q1 2027 and the full Platform in Q2. Also in community review: a proposal from Gerrit Grunwald for a Jakarta CRaC specification. Checkpoint and restore across runtimes, with a standard API instead of one per vendor.
Two to close.
Charles Nutter presented JRuby: Past, Present, and Future at Euruko 2026 in Brno. JRuby 10.1 is compatible with Ruby 4.0, “released only 3 months after CRuby 4.0”, and it shrinks integers from 40 bytes to 32 or 24. JRuby on Rails turned 20. The last slide says “One self-funded developer, many community members”. If your company runs JRuby, that slide is addressed to you.
And 👾 Sam Gammon 🇺🇦 of Elide opened a Compiler Explorer PR that shows the machine code GraalVM Native Image produces for a Java or Kotlin snippet, next to the bytecode. It is experimental and not merged upstream. A small example takes about 20 seconds on a cloud VM and 40 to 50 on the author’s machine, so this is not a tool for fast iteration. But until now, reading native-image assembly required a local build and a lot of patience.
Under Elide umbrella there is a sea of fascinating projects, and you can be sure we’ll have more coverage of it in the future.
2. Release Radar
JobRunr 9
JobRunr 9 came out on September 30, and its theme is seeing inside a job. JobRunr is a background-job library for Java that stores jobs in your existing database. Version 8 added runStepOnce, which makes a job durable: every step is recorded, and a retry skips the steps that already finished. Version 9 draws that.
The team demonstrates it on a broken invoice run. 600 customers, each invoice a durable job with four steps, and a payment provider switched to time out after two seconds:
long usage = jobContext.runStepOnce("calculate-usage", () -> calculateUsage(customerId, period));
String pdf = jobContext.runStepOnce("generate-pdf", () -> generatePdf(invoiceId, usage));
String chargeId = jobContext.runStepOnce("charge-card", () -> paymentProvider.charge(customerId, invoiceId));
jobContext.runStepOnce("send-email", () -> sendInvoiceEmail(customerId, pdf, chargeId));The new job history Chart, in the open-source version, draws every attempt and every step on one timeline. You can see the first attempt fail on charge-card, and on the retry the two finished steps show up as skipped. It also works for plain jobs, where it tells you whether a job was slow or just waited in the queue for a free worker.
The article states the guarantee precisely. A completed step never runs again. A step that was running when the JVM died can run again, so the charge call still needs an idempotency key (the demo passes the invoice ID). No retry mechanism can remove that requirement.
The rest is in JobRunr Pro. Job Analytics shows volume, retries and processing time per job type, server and exception. It reports averages and extremes but no percentiles, so keep Micrometer for p95 and p99. You can now pause a batch halfway: running jobs finish their attempt, and the rest wait in Pending. In the demo, the team paused 15 seconds after the provider went down and resumed after the fix, and all 600 customers were charged exactly once. And on Postgres, jobs start through LISTEN/NOTIFY instead of UDP multicast, which many cloud networks block. The article measures a median of 7 ms from enqueue to processing, against 7.7 s when multicast is blocked and the server falls back to polling. The authors add the caveat themselves: where multicast worked, v8 was just as fast. Behind PgBouncer in transaction mode, the listener needs a direct connection.
Ronald Dehuysser live-codes durable jobs, the Chart and batch pausing on Thursday, October 1, at 12:30 CEST, and the recording stays on YouTube afterward.
Apache Groovy 6.0
In vol. 189 I wrote that Groovy 6 shipped structured concurrency as a Java library while the release was still in beta. Paul King announced the GA on September 24, after three release candidates, so that caveat is gone. groovy-concurrent-java, async/await on virtual threads, the contract annotations checked at compile time: all of it is now in a final release (the async and concurrency features are still marked incubating).
New since August: a val keyword, module imports on JDK 17, and invokedynamic invalidation scoped so that a metaclass change no longer deoptimizes every call site in the program.
The breaking changes matter more than usual this time. JDK 17 is the minimum, and the release is tested up to JDK 27. Security Manager support is removed. The launchers no longer add . to an explicit classpath. And ‘X’ as Class no longer runs static initializers, which breaks the old idiom of registering a JDBC driver by loading its class. If you have a Groovy script from 2012 that connects to a database, test it first.
Some changes will not show up as compile errors. key in map now checks the keys, as map.containsKey(key). Before, it checked whether the value was truthy. That can change an if in a Jenkins pipeline or a Spock spec without a warning. And groovy-sql now throws SQLException for a GString query that interpolates a value inside quotes, where earlier versions only warned. -Dgroovy.sql.injection.lenient=true restores the old behavior while you port.
Gradle 9.8.0
Gradle 9.8.0 arrived on September 24 with Java 27 support for the daemon and toolchains (the RC was in vol. 192). The biggest win is on Windows. On some machines, especially virtualized ones, reading the system clock is slow, and a build reads it many times. Gradle now detects this and switches to a faster time source, and the release notes say builds on affected machines are “up to 45% faster”. The useful addition for corporate builds: with org.gradle.mirror.maven.settings=true, Gradle reuses the mirrors from your Maven settings.xml, so the company Nexus is configured in one place. Problem locations in the terminal are now clickable. GenerateMavenPom is finally up-to-date checked, where before it ran on every build. The exception is a POM customized with withXml: that task still runs every time.
The line to note for later is a deprecation: starting processes at configuration time, because “Configuration Cache will be enabled by default in Gradle 10”. Embedded Kotlin moves to 2.4.10.
Kotlin 2.4.20
Kotlin 2.4.20 arrived on September 7, written up by Dániel Csorba. The runner command is renamed from kotlin to kotlinr, “to avoid a naming conflict with the kotlin command in the Kotlin Toolchain”, so the name kotlin now belongs to the toolchain. There is an experimental native image of the compiler, a drop-in replacement for kotlinc. Kotlin/Native incremental compilation (kotlin.incremental.native=true) is Beta. It has existed since 1.9.20, and JetBrains plans to turn it on by default soon. when compiled with invokedynamic is now stable and on by default on JVM 21+.
The updated roadmap moved “Stabilize Kotlin Notebooks” and “Improve Kotlin scripting and experience with .gradle.kts” to the removed section. On the new side: Kotlin/Wasm to Stable, and KAPT performance comparable to Java annotation processing. JetBrains also wants your answers in the 2026 Kotlin Developer Survey, about fifteen minutes long.
Context parameters went stable in 2.4.0, and September showed them in real APIs. Ktor’s typed authentication uses them (Release Radar below), and so does TornadoVM’s tile DSL above.
On the Google side, Guillaume Laforge announced ADK for Kotlin 1.0, Google’s Agent Development Kit, with a Kotlin Multiplatform core. It runs on the server and on Android, with on-device models through Gemini Nano and LiteRT-LM. KSP generates tool schemas at compile time, without runtime reflection. The version in the announcement is already three releases old: 1.2.0 came out on September 28. Separately, Laforge brought his unofficial Antigravity SDK for Java to parity with the Python version and runs agents on a local Gemma 4 model. It is his personal project and labeled unofficial, so do not read it as a Google release.
GraalVM 25.4
GraalVM 25.4 (strictly 25.4.4.1.1, on JDK 25.0.4.1) came out on September 22, the fourth release of the monthly cadence. Community Edition and Oracle GraalVM ship the same version on the same day. In Native Image, identity-hash fields are now optional by default with Serial GC and get added only to the objects that need one. Serial GC is the Native Image default, so most images save memory. String concatenation is outlined, which makes binaries smaller. And registerBuildTimeBootstrapIndy lets frameworks run invokedynamic bootstrap methods at build time. Spring, Quarkus and Micronaut can use this hook in their AOT processing.
One detail for Native Image users: with -H:+StrictRuntimeJavaOptions, runtime -ea now sets the assertion status of classes loaded or initialized at runtime. It does not affect classes initialized at build time.
Breaking: GraalPy dropped Bouncy Castle and Truffle removed deprecated APIs. If your Python code needs Bouncy Castle, add org.graalvm.python:python-bouncycastle-support and the Bouncy Castle jars yourself.
TornadoVM 7.0
TornadoVM 7.0 came out on September 22, followed by 7.0.1 two days later. It adds TileContext, a third way to write a kernel next to @Parallel and KernelContext. It compiles through NVIDIA’s CUDA Tile. You describe work on tiles of a matrix, and NVIDIA’s toolchain decides how to map them onto the GPU. It is CUDA-only and needs CUDA 13.3 or newer.
The change more likely to affect existing code is a default. When a kernel failed to compile or launch, TornadoVM used to fall back to plain Java on the CPU without telling you. From 7.0 that fallback is off (tornado.recover.bailout=False), and the failure is an error. The PR explains why: by the program’s output, a broken device path is “indistinguishable from a working one”. If you prefer the old behavior, -Dtornado.recover.bailout=True brings it back. And 7.0.1 publishes the CUDA backend and the CUDA library bindings, such as cuBLAS and cuDNN, to Maven Central, so you can depend on them without the SDK archive.
In the same week, the same lab renamed its LLM engine. jitLLM is GPULlama3.java under a new name, the project from the July edition. The transformer kernels are written in Java, and TornadoVM compiles them to CUDA, OpenCL or Metal at runtime. The version number starts again at 1.0.0, released on September 25, and releases depend on TornadoVM 7.0.1. If you already use it, the Maven coordinates changed from gpu-llama3 to io.github.beehive-lab:jitllm, and the JDK variants are now jdk21 and jdk22plus. The new features are about serving: an OpenAI-compatible server (jitllm serve, marked preview), tensor-core batch prefill on the CUDA backend, and Gemma 4 support. Most of the commits come from Michalis Papadimitriou and Orion Papadakis.
Ktor 3.6.0
Ktor 3.6.0, announced by Simon Vergauwen , is mostly experimental features, and they are good ones. Typed authentication (jwt<User>(...), authenticateWith(...)) uses context parameters, so it needs Kotlin 2.4.0. A new OpenID Connect plugin takes the issuer URL and handles discovery, JWKS and the callbacks. The Netty engine gets HTTP/3 over QUIC. For Multiplatform clients, ktor-client-engine-defaults picks an engine per target, so HttpClient() in common code works without configuration.
The OpenID Connect plugin runs on the JVM only. It covers PKCE, token introspection (RFC 7662), resource indicators (RFC 8707) and protected resource metadata (RFC 9728), the same RFC that Open Liberty serves below. On the JVM, the CIO client used system DNS resolution, which can block threads, and a new dnsResolver property lets you replace it. HTTP/3 is experimental for a reason: this release fixes a bug where the listener “can only ever serve ONE QUIC connection”.
Open Liberty 26.0.0.9
Open Liberty 26.0.0.9, written up by Ismath Badsha, makes the MCP Server feature GA. You annotate a CDI method with @Tool, and Liberty handles the protocol, discovery, security and observability. It also serves OAuth 2.0 Protected Resource Metadata (RFC 9728), which MCP clients use to discover how to authenticate. GA covers tools only: if the application uses @Prompt, @Resource or @ResourceTemplate, Liberty logs a warning, and the @Tool methods still work. If you tried the beta, the feature is renamed from mcpServer-1.0 to mcp-1.0, and the server.xml element from <mcpServer> to <mcp>. If you come from 26.0.0.8-beta or earlier, the API types also moved to org.mcpjava.server.*.
Even without MCP, this release is worth installing. It fixes a set of CVEs, and three of them are request smuggling issues in the servlet features from 3.1 to 6.1, in versions back to 17.0.0.3.
LangChain4j 1.20.0
LangChain4j 1.20.0, from Dmytro Liubarskyi, lets AI service methods return CompletableFuture or Flow.Publisher and run without blocking a thread. It is experimental. Before you switch a method to CompletableFuture, read the defaults, because the async path behaves differently. Multiple tool calls run concurrently. A tool execution error fails the invocation instead of going back to the LLM, and an argument-parse error goes to the LLM instead of failing. Jackson 3 support is opt-in with one extra dependency, and with it a JSON failure is thrown as JsonReadException or JsonWriteException. The langchain4j-github-models module is removed.
JHipster 9.4.0
JHipster 9.4.0, from Matt Raible, adds Playwright as an end-to-end option next to Cypress, with specs ported line by line so you can compare the two. Webpack is gone: Angular microfrontends use esbuild with Native Federation, and Vue uses Vite or Rsbuild. The yeoman generator now throws if a template path resolves outside the generator root, so check your blueprints.
The Playwright port also found bugs: it “surfaced two application bugs that Cypress had hidden”, and both are fixed. Cassandra projects get Liquibase, on by default, so the schema no longer needs a separate Docker image. A new jhipster describe command lists commands, options and prompts in a machine-readable form, which helps in CI scripts. The release closes 272 issues and pull requests since 9.3.0.
3. GitHub All-Stars
Three this month, plus one warning.
claude-s40, Claude on a phone from 2007
claude-s40 by Emir Karşıyakalı starts from the best README line of the month: “A 2007 Nokia can’t search Google anymore, so I gave it Claude.”
The phone side is a MIDlet for Series 40: CLDC 1.1 and MIDP 2.0, about 100 KB after ProGuard. It compiles to class version 46.0, like the MIDlets that shipped on those phones. If you wrote J2ME in 2007, most of it will look familiar.
The hard part is the network. The Nokia 6300 speaks TLS 1.0 without SNI, and its root certificates stop around 2008. The README explains why a CDN is no option: Cloudflare serves such a client only ECDSA certificates and requires SNI, so the handshake fails before a certificate arrives. The solution is a small Go server with its own private CA. The user saves the CA certificate through the phone’s own browser. The server terminates TLS 1.0 and calls Claude with the official SDK. The phone gets replies of at most 8 KiB. Long answers come in 2,000-character parts, with Markdown and emoji stripped for the 240x320 screen.
The engineering detail I liked most is about money. The Messages API has no idempotency key. So the server records a request as pending before it calls Claude. It never retries on its own, and it marks a timeout as uncertain. That is the JobRunr idempotency argument from above, solved on a phone from 2007.
pqc-migration-readiness, what a post-quantum migration would cost
JDK 27 turned on post-quantum hybrid key exchange in TLS by default (vol. 192). That covers the transport. Your own code that loads an RSAPublicKey still has to change. pqc-migration-readiness by Arpan Sharma estimates how much. It is a static auditor under Apache 2.0 that reads source without a build or a classpath. It finds quantum-vulnerable JCA usage and scores how hard the surrounding code is to change. The output is a ranked migration plan plus SARIF.
Then the author ran it over 27 well-known Java projects. Apache MINA SSHD leads with an estimate of 5 to 12 months of one engineer’s work. Micronaut, Dropwizard, Shiro and PDFBox come out at zero. Most of the cost is in code typed to RSAPublicKey or ECPrivateKey instead of PublicKey and PrivateKey, not in the algorithm calls. So a library like jjwt looks expensive because its API exposes many key types, not because the code is bad.
The report lists its own limits, and the maintainer threads add more. Clément Escoffier confirmed for Quarkus that there are more call sites in Vert.x, Netty and Elytron, where the algorithm arrives through an API that a syntactic scan cannot see. jjwt itself reports zero vulnerable call sites because its algorithms go through a registry. And Achim Kraus of Eclipse Californium did the calculation for a volunteer project: the scan’s 9 weeks of work, at “2h free time per calendar week”, comes to “more than 10 years ;-)”, before the DTLS 1.3 work that post-quantum support would also need.
The effort figures are a heuristic, and the report says so at the top. Use them to rank your work, not to plan a budget.
t3craft, coding agents as Minecraft villagers
t3craft by Maxwell Young is a Fabric mod for Minecraft Java 26.3, written in Java, that turns the game into a client for T3 Code, a GUI for coding agents. Your agent threads appear as villagers with name tags like Needs you: Bash: npm test. Right-click one to approve the command, or press Y and N in the in-game panel. When an agent finishes, a note block plays.
The JVM part is under the jokes. The mod embeds an MCP server built on the JDK’s own com.sun.net.httpserver.HttpServer and Gson, without an MCP SDK. It binds to loopback and refuses requests with a browser Origin header. Open Liberty lists the same DNS-rebinding protection in its MCP GA above. It has four tools, and minecraft_run_command stays off until you enable it. When it is on, the agent can /fill a wall and read back “Successfully filled 49 block(s)”. The author notes that the T3 client protocol is not a public API and can change, and the commit history spans about seventeen hours.
PS: Maven 4.0.0-rc-7 came out on September 24, announced by Guillaume Nodet as “expected to be the last before the Maven 4.0.0 GA release, which we aim to publish in the coming weeks”. I will write about it when GA ships. If you maintain a plugin that has never run on Maven 4, this month is a good time to try.
Thanks for reading JVM Weekly!














