kotlin-sdk
Skill4 starsChanged 9 days agoMITView on GitHub
Use ONLY for the Ditto Kotlin SDK (not Swift, Java, JS, C#, C++, Rust, Flutter or Go — each has its own skill). Triggers on .kt/.kts files, build.gradle.kts, com.ditto:ditto-kotlin or live.ditto:ditto coordinates, DittoConfig, DittoFactory.create, registerObserver, ditto.store.execute, and errors like "Query result item has already been closed.", "Unable to find binary 'libdittoall'", "Identifier conflict … FAIL conflict policy". Android and JVM targets. Ditto SDK 4.14.x, 5.0.x, 5.1.x, 5.2.x.
In this repository8 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- data-modelingSkill
- data-streamsSkill
- ditto-mesh-networkingSkill
- swift-sdkSkill
- sync-subscriptionsSkill
- write-dqlSkill
The file
We split this skill into the rules it gives an agent
7 rules. About 0 are common, 6 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 16
Use the shipping API: on v5 everything is DittoFactory.create(DittoConfig), ditto.sync, ditto.store with DQL, and suspend/Flow observers.
- Distinctive: line 23
Read only the reference sections the task needs.
- Distinctive: line 24
Do not preload all references or the README (the capability/evidence map).
- Distinctive: line 35
Use this skill for Kotlin Ditto code: API shape, initialization, object lifetimes, threading, error types, and build configuration.
- Distinctive: line 68
Read sdk-5.2.md when adopting 5.2.0 features or reviewing an upgrade: encryption, store deletion, startup configuration and behavior fixes.
- Distinctive: line 116
Read concurrency.md for the per-API table and reasoning.
- Distinctive: line 150
Read anti-patterns.md for wrong/right forms.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Ditto Kotlin SDK
Use the shipping API: on v5 everything is DittoFactory.create(DittoConfig),
ditto.sync, ditto.store with DQL, and suspend/Flow observers; the v4
Ditto(dependencies, identity) constructor and legacy collection API are gone.
Establish the version line first (com.ditto:ditto-kotlin = v5, live.ditto:ditto =
v4, Android-only); emit v5 code by default and say which version you assumed. Do not
invent APIs from memory — the v4→v5 surface changed completely.
Read only the reference sections the task needs; inspect a long file's contents first. Do not preload all references or the README (the capability/evidence map).
What this skill does
Writes and reviews Ditto Kotlin code: Gradle setup, initialization, authentication, DQL store access, sync, observers and Flows, attachments, presence, transports, object lifetimes, threading, errors and testing. Supports 4.14.x (LTS), 5.0.x, 5.1.x and 5.2.x on Android (both lines) and plain JVM (5.x only).
When to use this skill
Use this skill for Kotlin Ditto code: API shape, initialization, object lifetimes, threading, error types, and build configuration.
Hand off rather than restating; each row names a public URL in case the concept skill is not installed:
| Question | Skill, if installed | Otherwise |
| DQL syntax, operators, statement semantics | write-dql | https://docs.ditto.live/dql/dql |
Which CRDT type, embed vs split, document size, dql_strict_mode | data-modeling | https://docs.ditto.live/key-concepts |
| What a subscription is, mesh propagation | sync-subscriptions | https://docs.ditto.live/sdk/latest/sync/syncing-data |
| Transport selection strategy, platform radios | ditto-mesh-networking | https://docs.ditto.live/sdk/latest/sync/customizing-transport-configurations |
| v4 → v5 migration procedure | — | https://docs.ditto.live/sdk/latest/migration-guides/kotlin-v4 and https://docs.ditto.live/sdk/latest/migration-guides/kotlin-dql |
Supported versions and packages
Packages: com.ditto:ditto-kotlin (v5; KMP root — Gradle resolves -android / -jvm)
and live.ditto:ditto (4.14.x, Android-only). Published API reference:
https://software.ditto.live/kotlin/ditto-kotlin/5.2.0/api-reference/.
references/versions.md owns every fact that differs
across version lines, including package coordinates per version and the minimum
toolchain. v4 and v5 are different APIs, not two versions of one.
For new apps and POCs, check the package manager for the latest stable release and use that resolved version. 5.2.0 is the latest version this skill knows about, not a permanent recommendation or claim about today’s latest release. Preserve an existing project’s selected version unless an upgrade is requested. If a newer release is found, read its release notes/API changes and validate the generated code. If lookup is unavailable, state that limitation; do not silently call 5.2.0 “latest”. Resolve Maven Central metadata and share the selected version across Kotlin and native binaries; see setup.md.
Read sdk-5.2.md when adopting 5.2.0 features or reviewing an upgrade: encryption, store deletion, startup configuration and behavior fixes.
Capability exceptions
Full twenty-row evidence map: README.md, for capability reviews only.
| ID | Capability | Support | Required caveat |
| CAP-13 | Presence graph and connection requests | partial | Graph, metadata and observe() are docs-verified; connectionRequestHandler only fires with a second peer and was not executed. presence.md |
| CAP-20 | Preview and platform-specific APIs | preview | Data Streams: 5.1.0+, @file:OptIn(com.ditto.kotlin.PreviewDataStreams::class), "breaking changes without notice", NGN required; two-peer fixture executed on the JVM. data-streams.md. Multicast: beta from 5.1.0, off by default. preview.md |
Minimum viable app
Open → subscribe → observe → write (JVM shown; on Android hold the instance in
Application; no ditto-binaries classifier is needed there):
fun main() = runBlocking {
val ditto = DittoFactory.create(
DittoConfig(
databaseId = "my-app",
connect = DittoConfig.Connect.SmallPeersOnly(),
persistenceDirectory = "/tmp/ditto-data",
)
)
ditto.setOfflineOnlyLicenseToken(System.getenv("DITTO_OFFLINE_LICENSE"))
ditto.sync.start()
val subscription = ditto.sync.registerSubscription("SELECT * FROM cars")
val observer = ditto.store.registerObserver("SELECT * FROM cars") { result ->
println("cars: ${result.items.size}") // extract inside the lambda!
}
ditto.store.execute(
"INSERT INTO cars DOCUMENTS (:car) ON ID CONFLICT DO UPDATE",
mapOf("car" to mapOf("_id" to "car1", "make" to "Toyota"))
)
observer.close(); subscription.close(); ditto.sync.stop(); ditto.close()
}Complete program, dependencies and run transcripts (5.2.0/5.1.0/5.0.3 JVM and Android,
4.14.7 Android):
examples/minimal-app.md.
Concurrency and threading model
Read concurrency.md for the per-API table and reasoning. Core constraints:
- Observer handlers start on the SDK's scope: the main thread on Android
(
MainScope()),Dispatchers.Defaulton JVM — source-verified and executed on a Pixel. Treat every handler as UI-thread code. - Hop inside the handler:
withContext(Dispatchers.IO)for extraction and decode, then plain data back to the UI. The result stays open while the handler is suspended..flowOn()does not reach theobserve()transform — it runs on the SDK scope. DittoFactory.create,sync.start()/stop(),registerSubscription,registerObserverare not suspend.store.execute,store.collect,auth.login, attachment fetches are suspend.- Handlers are serialized with back-pressure: the next event is produced only after
the handler returns, coalescing intermediate changes. Never
launchper event, never.buffer(); use.conflate()/stateInfor latest-only UI,collect()for ordered pipelines. - Cancelling the collecting scope (
viewModelScope,lifecycleScope) closes anobserve()/collect()observer; aregisterObserverhandle needsclose(). expirationHandleris a property holding a suspend lambda — calllogindirectly in its body, never insidelaunch { }.
Object lifetimes and retention
One Ditto per process, owned by Application/a DI singleton (Android) or a top-level
holder (JVM); closing it closes every child resource. Retain DittoSyncSubscription
and registerObserver handles so you can close() them; never retain
DittoQueryResult or its items — they die when the handler returns
(IllegalStateException: Query result item has already been closed.). Flows from
observe()/collect() need no handle: the collecting scope is the lifetime. Do not
cache ditto.sync.subscriptions or ditto.store.observers; they are live views. Handle
table and per-component cleanup: lifetimes.md.
Anti-patterns
Read anti-patterns.md for wrong/right forms.
- Returning
result.items(or the result) from a handler orobserve()transform:Query result item has already been closed./ "You must not return DittoQueryResult". - An exception escaping a
registerObserverhandler: the SDK logsUnhandled exception in observer event handlerand closes the observer. .buffer()on anobserve()chain: unbounded queue, eventual OOM.scope.launch { … }per event inside a handler: lost ordering, closed-item crashes..flowOn(Dispatchers.IO)to move decoding off main: no effect on the transform.- Casting
item.value["x"] as String:ClassCastException— use.stringOrNullorjsonString(). setExpirationHandler(…)does not compile;launcharoundauth.loginabandons it.- Plain
INSERTof an existing_id:Identifier conflict … FAIL conflict policy— addON ID CONFLICT DO UPDATE. - JVM without
ditto-binaries:Unable to find binary 'libdittoall…'on create.
Version differences
| Concern | 4.14.x | 5.0.x / 5.1.x / 5.2.x |
| Package / namespace | live.ditto:ditto, live.ditto.* (Android-only) | com.ditto:ditto-kotlin, com.ditto.kotlin.* (Android + JVM) |
| Initialization | Ditto(deps, DittoIdentity.…) | DittoFactory.create(DittoConfig(…)), not suspend |
| Start sync | ditto.startSync() | ditto.sync.start() |
| Mutating DQL | requires ditto.disableSyncWithV3() first (else StoreError: … disable sync with V3 …, executed) | works by default |
| Query model | legacy collection API (deprecated) + DQL | DQL only, via ditto.store.execute |
| Observers | non-suspend callback, DittoSignalNext | suspend handler / observe() Flow / collect() |
| Errors | DittoError | DittoException sealed hierarchy |
| On-disk store | default dir ditto | default dir ditto-{databaseId} for new stores; a default-path v4 store is picked up |
Full delta, on-disk migration and removed-API table: references/versions.md.
References
Choose the matching row and section; alternatives, not a reading checklist.
| If you need to know… | Read |
| How do I install it and what are the platform minimums? | setup.md |
| How do I open Ditto, authenticate, and start syncing? | connect.md |
| Which thread does a handler run on; how do I hop safely? | concurrency.md |
| What must I retain or close, and where? | lifetimes.md |
| Diagnose a symptom | anti-patterns.md, then errors.md |
| How do I change which transports are used? | transports.md |
| How do I subscribe and check sync status? | sync.md |
| How do I run DQL, transact, index, or evict? | store.md |
| How do observers, Flows, diffing and back-pressure work? | observers.md |
| How do I store and fetch a file? | attachments.md |
| Who else is on the mesh right now? | presence.md |
| How do I turn on logs and see disk usage? | observability.md |
| How do I test code that uses Ditto? | testing.md |
| What changed between 4.14.x and 5.x, on disk and in the API? | versions.md |
| What is preview or platform-specific here? | preview.md |
| Stream raw bytes to one peer (Data Streams, preview) | data-streams.md; concepts in the data-streams skill |
| How do I generate a whole runnable Gradle project? | gradle-project-generation.md |
| Where should the instance live; which observer shape? | best-practices.md |
| Build a complete example | minimal-app.md (compiled and run) |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

