Skip to content

M25 · Build the core everywhere Kotlin does - #64

Merged
tonytonycoder11 merged 6 commits into
mainfrom
feat/m25-multiplatform-core
Jul 31, 2026
Merged

M25 · Build the core everywhere Kotlin does#64
tonytonycoder11 merged 6 commits into
mainfrom
feat/m25-multiplatform-core

Conversation

@tonytonycoder11

@tonytonycoder11 tonytonycoder11 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Closes #23. #63 is merged and this branch carries it, so the diff below is the multiplatform work alone.

kdrant-core now compiles for the JVM and for eight Kotlin/Native targets: iosArm64,
iosSimulatorArm64, iosX64, macosArm64, macosX64, linuxArm64, linuxX64, mingwX64. What made
that a two-file change rather than a rewrite is the transport seam: the models, DSLs, error hierarchy
and client logic never knew there was a wire, so moving them into commonMain was a git mv and the
compiler had nothing to say.

Kotlin/JS was added and then dropped. Dependency review failed the branch on a high-severity
advisory in brace-expansion, reached through the yarn.lock that the JS target's Karma and webpack
test tooling needs. Nothing published contains that package, so the finding could have been suppressed.
It is a better question than that: kdrant-core on JS is models, a DSL and a transport interface with
no implementation behind it, because Ktor CIO and grpc-java are both JVM-only. The target was carrying
the repository's only npm dependency graph in exchange for a library nobody can currently use. One line
brings it back the day someone writes a JS engine.

Against the exit criterion"kdrant-core builds for commonMain with no java.* on the common
path, and the JVM artifact stays source-compatible"
:

  • commonMain compiles for all nine targets. That is the compiler's own proof about java.*; there is
    nothing to grep for.
  • The JVM api dump is unchanged, byte for byte. apiCheck reports no diff.

Two of the four items in the issue were already done. java.util.UUID and java.time.Instant do
not appear in the core at all — a point id is a ULong or a string, and a datetime bound is the RFC
3339 text Qdrant parses — so neither ever needed a JVM type.

The third was real and is the one declaration this migration had to split. Only the JVM publishes an
IO dispatcher, which is the right default for a client that blocks on a socket rather than a CPU.
Kotlin/Native has one that the coroutines library still keeps internal, so it gets Dispatchers.Default
and the KDoc says why. Passing your own dispatcher works as before.

The fourth is deliberately not here. "The Ktor engine parameterised per target" would mean picking
and testing a Ktor engine per platform. The engines stay JVM-only; a multiplatform consumer shares its
models and query building and supplies its own transport. That is its own piece of work, and the exit
criterion is about the core.

The artifact layout changes, and it is why this is 2.0 and not a minor. kdrant-core now carries
Gradle module metadata and the JVM classes live in kdrant-core-jvm. A Gradle build resolves the
variant from the same coordinate and sees nothing; a Maven build naming kdrant-core gets no classes.
The BOM constrains both and STABILITY.md says so.

Three build surfaces move with it.

  • Releases build on macos-latest. No Linux host compiles the Apple targets, and one that skipped them
    would publish a release quietly missing its iOS klibs. The linked-artifacts step also learns that the
    JVM jar is now kdrant-core-jvm-<version>.jar, which it would otherwise fail to find.
  • CI gains an Apple targets job on macOS, for the same reason: without it an iOS break is first seen
    at release.
  • kdrant-core's -javadoc.jar holds Dokka HTML. The Dokka Javadoc generator refuses a multiplatform
    project outright, and Maven Central asks for a jar rather than for Javadoc.

Tests. The existing eighty stay on the JVM, where JUnit 5 and kotest-property live; rewriting them
to kotlin.test would run the same assertions in more places against platform-independent code. What
runs everywhere instead is a commonTest suite aimed at what could actually differ: the hand-written
serializers, a point id above Long.MAX_VALUE, an integer payload value above 2^53, and the config's
validation. Verified locally on the JVM, linuxX64, macosArm64 and iosSimulatorArm64.

kdrant-core now compiles for the JVM, for JS, and for nine Kotlin/Native
targets. What made that a two-file change rather than a rewrite is the
transport seam: the models, the DSLs, the error hierarchy and the client
logic never knew there was a wire, so moving them into commonMain was a
git mv and the compiler had nothing to complain about.

The issue named four things to fix. Two of them were already gone.
java.util.UUID and java.time.Instant do not appear in the core at all: a
point id is a ULong or a string, and a datetime bound is the RFC 3339 text
Qdrant parses, so neither ever needed a JVM type. Saying so is more useful
than quietly not doing it.

The third was real and is the one declaration this migration had to split.
Only the JVM publishes an IO dispatcher, which is the right default for a
client that blocks on a socket rather than on a CPU. Kotlin/Native has one
and the coroutines library still keeps it internal; Kotlin/JS has a
single-threaded runtime and nothing to move off it. Both get Default, and
the KDoc says which reason applies where. Passing your own dispatcher works
as it always did.

The fourth, parameterising the Ktor engine per target, is not here and
should not be: Ktor CIO and grpc-java are both JVM-only, so the engines stay
JVM-only and a multiplatform consumer shares its models and its query
building and supplies its own transport. Publishing a common engine would
mean picking a Ktor engine per target and testing it there, which is its own
piece of work and not what this milestone's exit criterion asks for.

The exit criterion is met on both halves. commonMain builds for every
target, which is the compiler's own proof that no java.* survives on the
common path. And the JVM api dump is unchanged byte for byte, so the
artifact stays source-compatible.

The artifact layout does change, and that is the reason this is 2.0 rather
than a minor. kdrant-core carries Gradle module metadata now and the JVM
classes live in kdrant-core-jvm. Gradle reads the metadata and resolves the
variant, so a Gradle build sees nothing; Maven does not, and a Maven build
naming kdrant-core would get no classes. The BOM constrains both, and
STABILITY.md says so where a reader will look.

Three build surfaces move with it. Releases are built on macOS, because no
Linux host compiles the Apple targets and one that skipped them would
publish a release quietly missing its iOS klibs. CI gains a macOS job for
the same reason: without it that break would first be seen at release. And
the javadoc jar holds Dokka's HTML, because the Dokka Javadoc generator
refuses a multiplatform project outright and Maven Central asks for a jar
rather than for Javadoc.

The existing eighty tests stay on the JVM, where JUnit 5 and kotest-property
live. Rewriting them to kotlin.test would run the same assertions in more
places against platform-independent code. What runs everywhere instead is a
suite aimed at what could actually differ: the hand-written serializers, a
point id above Long.MAX, an integer payload value above 2^53 where a JS
number would lose it, and the config's own validation.
@tonytonycoder11
tonytonycoder11 requested a review from a team as a code owner July 31, 2026 13:45
Base automatically changed from feat/m31-grpc-transport to main July 31, 2026 14:03
Dependency review failed the multiplatform branch on a high-severity
advisory in brace-expansion, reached through kotlin-js-store/yarn.lock —
the lockfile the Kotlin/JS target's Karma and webpack test tooling needs.
Nothing published contains that package, so the finding could have been
suppressed. It is a better question than that.

kdrant-core on JS is models, a DSL and a transport interface with no
implementation behind it: Ktor CIO and grpc-java are both JVM-only, so
there is nothing on that target to send a request with. The target was
carrying the repository's only npm dependency graph in exchange for a
library nobody can currently use.

The eight native targets stay, and they are the ones the migration was
for: an Android application and an iOS application sharing their models
and their query building. One line brings JS back the day someone writes
an engine for it.
@tonytonycoder11
tonytonycoder11 merged commit 6ec5315 into main Jul 31, 2026
7 checks passed
@tonytonycoder11
tonytonycoder11 deleted the feat/m25-multiplatform-core branch July 31, 2026 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

M25 · Kotlin Multiplatform core

1 participant