Three things happened this month that matter for people who build software. The JDK 27/28 roadmap got concrete, Moon went all-in on WASM plugins, and the Model Context Protocol deleted session state. They are unrelated, but each one makes the same point: the boring part of a system matters more than the clever part.
JDK 27 and 28
JDK 27 hit its release candidate in early June when the main-line repository forked to the stabilization repo at Rampdown Phase One. The formal release is September 15, 2026. Nine JEPs made the cut, split across the core library, HotSpot, security, and the language specification.
The pattern worth noticing is how many of those nine are previews or incubators that have been in flight for years. JEP 531, Lazy Constants, is at its third preview and has already been renamed twice. It started as Stable Values, became Computed Constants in JDK 26, and settled on Lazy Constants because that name better matched the use case. JEP 533, Structured Concurrency, is at its seventh preview under Project Loom. JEP 537, the Vector API, is at its twelfth incubator under Panama. JEP 532, Primitive Types in Patterns, instanceof, and switch, is at its fifth preview.
Twelfth incubator. That is not a typo. The Vector API has been in incubation since JDK 16 and still is not final. I understand why it takes so long: it touches the JIT and the intrinsic pipeline, and the API surface has to be stable before it ships. But at some point the process becomes the problem. Same for Structured Concurrency at seven previews. Letting a feature marinate is good. There is a difference between marinating and pickling.
JDK 28, scheduled for March 2027, has six JEPs targeted so far. JEP 540 defines the Simple JSON API as an incubator, implementing RFC 8259 and superseding JEP 198, which was closed and withdrawn. That one is overdue. Java has needed a standard JSON API for a long time, and the death of JEP 198 in 2016 left the ecosystem relying on Jackson and Gson. A minimal built-in option will not replace those libraries, but it removes a dependency for the common case.
JEP 539, Strict Field Initialization in the JVM, is a preview aimed at compilers that emit class files. It requires fields to be initialized before any read, so you never observe a default 0 or null. For Java itself, final fields already provide most of this guarantee. The JVM-level requirement changes things for other frontends, like Kotlin or Scala, that want stricter semantics without fighting the bytecode verifier.
JEP 535 makes Shenandoah generational by default and marks non-generational mode for removal. JEP 401 brings Value Objects back under a new name, formerly Object Classes and Values. The proposal describes objects with only final fields, no identity, distinguished purely by field values. That is the value-type work that has been on the horizon for a decade.
JEP 542 proposes to finalize PEM encodings of cryptographic objects after three preview rounds. It reclassifies the PEM record class as a regular class and renames the DEREncodable interface to BinaryEncodable. The rename is sensible because the interface describes binary data stored in PEM text, not specifically DER data.
Two drafts could land in JDK 28 as well: a fourth preview of Lazy Constants with no changes, and a ZGC startup and warmup enhancement that creates a small initial heap to reduce OS-level overhead. I would bet on the ZGC one landing first, because lazy constants are still finding their footing and a fourth no-change preview suggests the design is not settled.
Moon v2.0
Moonrepo shipped v2.0, codenamed Phobos. The headline is that the hard-coded platform system is gone, replaced by a WASM plugin-based toolchain architecture. Teams can write toolchain plugins for any language or runtime, and the companion version manager Proto handles tool installation. This is a deliberate bet on WASM as the plugin boundary. The core project no longer needs to maintain first-class support for every language; the community does it.
The tradeoff is that every plugin crosses the WASM boundary, and task execution goes through plugin code instead of native integration. For polyglot repos, the flexibility probably wins. Being able to extend the project graph, modify task commands, and manage tool installation per project outweighs the performance cost of the plugin layer.
Task inheritance changed from file naming conventions to configuration. You now define criteria based on toolchain, stack, language, and tags to decide which projects inherit which tasks. File naming conventions were brittle, especially in repos where the same file name appears in multiple unrelated contexts. A task can also associate with multiple toolchains now, which matters for projects that build with several runtimes.
The .env handling got better too. Moon loads environment-specific files automatically and defers loading until execution time. Docker integration gained per-project overrides and custom Dockerfile templates powered by Tera. Git support now handles worktrees and submodules, which were painful in monorepo setups before.
MCP goes stateless
The Model Context Protocol removed session state from Streamable HTTP. The 2026-07-28 changelog deletes initialize, Mcp-Session-Id, and protocol sessions. The rewrite moves from bidirectional stateful communication to plain request/response. A round-robin load balancer can take any POST now, and a Cloudflare Worker can speak the protocol without a Durable Object.
The interesting part is how continuity survives. Multi round-trip requests use resultType input_required with inputRequests. The server asks for more information, the client sends it back, and the original tool call is retried as a fresh request. The JSON-RPC id must change between the two POSTs. They are independent requests.
State rides in requestState, an opaque string the client must echo byte-for-byte. The official TypeScript SDK builds a fresh server per request via createMcpHandler, and inputResponses hold only the latest round. requestState is the only cross-round memory.
I have mixed feelings about this. The simplicity argument is solid: any load balancer can route anywhere, and the server has no session table to manage. But the docs invite a naive reading that state is gone entirely. It is not gone. It is packed into a blob the client carries. The client becomes the state holder, and the server has to trust that blob. The spec does not describe how to integrity-protect it, which means implementations have to invent their own scheme.
The OAuth angle is where this gets sharp. The elicitation spec says third-party OAuth tokens must not transit the MCP client. A token store makes the server stateful, so you need real storage. Cloudflare's own post says Durable Objects remain the right primitive when the application needs coordinated state. The protocol is stateless, but your application is not.
McpAgent is deprecated and feature-frozen, with isLegacyRequest() as the migration drain. Dynamic Client Registration is deprecated too, with earliest removal at the first revision on or after 2027-07-28. Official clients still default to the 2025 handshake unless you opt into versionNegotiation. Punkpeye noted on the HN thread that the rewrite is wire-incompatible in both directions, and gateways will eat the mess. They will, but the migration path is explicit and the legacy lane is documented.
Three takeaways. Preview fatigue is real in Java, and the JEP process needs to shorten the gap between first preview and final release. Moon's WASM plugin bet is the right call for monorepos that outgrow a single language. And MCP has decided that stateless handshakes beat session state, even when the application underneath still needs a cart or a token store somewhere. Each choice trades something concrete for something abstract, and whether it pays off depends on your deployment, not on the release notes.