← Workstreams

Workstream: Kotlin Multiplatform Migration

Status: Planned · Component: Maximize developer productivity

Goal

Make the foundational community.kotlin.* libraries Kotlin Multiplatform — published with a commonMain plus JVM / JS / Wasm / Android targets — so that cross-platform consumers share the same types and contracts, not merely a JVM build. Today the libraries that hold the platform's core value types are compiled JVM-only, which forces any cross-platform (commonMain) contract that wants to reference them to fall back to stringly-typed or duplicated stand-ins. Migrating them unblocks typed, shared contracts across every Kotlin target. This is a multi-library effort — one workstream covering several libraries and steps.

Why it matters (the motivating example)

The trigger is concrete. The Observables read contract throws RemoteObservableDataUnavailable, which must report the freshness constraint the caller required and the watermark the projection reached. Those are a ClockPad / HierarchicalTimestamp — but RemoteObservableDataUnavailable lives in observable-core's commonMain (so the planned Kotlin/JS Proxy and Kotlin/Wasm proxies can throw and catch the one data-access contract), while community.kotlin.clocks.hierarchical is built JVM-only. A commonMain type cannot reference JVM-only types, so the fields are forced to String? (the rendered pad) instead of typed ClockPad?. Nothing about the code is JVM-bound — the clock library's ClockId / HierarchicalTimestamp / ClockPad are plain data classes; only the build is JVM-only. This is one instance of a general pattern: every shared contract that wants a foundational type is blocked until that type is multiplatform.

Current state

  • observable-core and observable-compose are already Gradle Kotlin Multiplatform (jvm / js / wasmJs / android).
  • The foundational libraries they depend on are built JVM-only via kompile (build-kotlin-jvm, a flat src/): community.kotlin.clocks.hierarchical, and — to confirm in discovery — community.kotlin.clocks.simple, community.kotlin.eventlog, and peers. Their source is largely pure Kotlin, so the blocker is the build and publishing, not the code.

Plan / roadmap

  • [ ] Discovery: enumerate the migration set and order. Identify which community.kotlin.* libraries must go multiplatform, and sequence them leaf-first (value-type libraries before the libraries and services that consume them). Candidate leaves: clocks.hierarchical, clocks.simple; then eventlog; then consumers.
  • [ ] Per-library build migration. Move each library from kompile build-kotlin-jvm to multiplatform publishing (Gradle KMP, as observable-core already does — or a kompile multiplatform path if/when one exists). Split sources: pure value types into commonMain; genuinely JVM-bound pieces (e.g. a clock implementation that relies on JVM concurrency) behind expect/actual or kept in jvmMain. Publish jvm / js / wasmJs / android variants.
  • [ ] Portable JSON value facade. Create a small common JsonObject/JsonArray facade (e.g. community.kotlin.json) covering the org.json surface that the toJson(): JSONObject / fromJson(JSONObject) value-serialization convention uses — the JVM actual delegates to org.json so existing value objects keep working, with pure-Kotlin actuals for JS/Wasm — and migrate the convention's signatures to it. This is the "remaining dependency" named in REMOTE_OBSERVABLES.md §14.1: it lets Observables value objects cross the projection wire to Kotlin/JS and Kotlin/Wasm consumers, and that workstream's web-materialization milestone depends on it.
  • [ ] Downstream rewiring. Move each dependency from jvmMain to commonMain in its consumers and replace stringly-typed stand-ins with the now-shared types. First beneficiary: type RemoteObservableDataUnavailable.requiredConstraint / currentWatermark as ClockPad? instead of String? (see the Observables workstream and REMOTE_OBSERVABLES.md §3).
  • [ ] Verify on every target. Each migrated library and its consumers compile and pass on jvm / js / wasmJs / android, per the testing standards.

Prerequisites

This migration is sequenced after a set of foundational fixes that must land first — it will happen, but not before those are addressed.

TODO (needs enumeration): list the specific items that must be fixed before the migration begins. Captured as a placeholder pending that detail.

Graduation

When the foundational community.kotlin.* value-type libraries publish multiplatform artifacts consumed from commonMain across JS and Wasm, and the typed cross-platform contracts they unblock — starting with RemoteObservableDataUnavailable's ClockPad? fields — compile and pass on every target, this graduates: update the affected project pages.