Skill: KMP Architecture & Source-Set Bridging
SkillDev toolsLets your agent look up and work with the Android app for Meshtastic mesh radios.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Skill: KMP Architecture & Source-Set Bridging skill
About this capability
Android application for Meshtastic
What this skill tells your AI
The instructions your AI receives, as published by meshtastic/meshtastic-android in .skills/kmp-architecture/SKILL.md and read by ahel’s review.
Description
Guidelines on managing Kotlin Multiplatform (KMP) source-sets, expected abstractions, networking, database, and platform integration rules.
1. Source-Set Boundaries
commonMain: All business logic, DB entities, API network logic, ViewModels, and UI rendering. NOjava.*orandroid.*imports.androidMain: Android framework integration (Context, system services, NFC hardware, BLE Android bindings).jvmMain/jvmAndroidMain: Shared JVM code between Android and Desktop. Uses themeshtastic.kmp.jvm.androidconvention plugin to bridgejvmandandroidsource sets without manualdependsOnhacks.androidApp/desktopApp: Host shells. Responsible for Koin DI root wiring (MainKoinModule/AppKoinModule,DesktopKoinModule), host-level UI themes, and running theMeshtasticNavDisplay.
2. Bridging Strategies
- Interface + DI (Preferred): Expose an interface in
core:repositoryorcore:ui(e.g.LocationRepository,MapViewProvider), implement it inandroidMainor the hostandroidApp, and bind it via Koin orCompositionLocal. expect/actual(Restricted): Use only when a platform API cannot be abstracted cleanly (e.g. low-level File I/O mappings,uppercase()Locale helpers). Avoid deep class hierarchies usingexpect/actual.- Naming: An
expectdeclaration and its shared helpers must live in differently named files within the same package, or the JVM target fails with duplicate class errors. Live example:LogExporter.kt(expect) alongsideLogFormatter.kt(shared helpers).
- Naming: An
- Shared Helpers: Do not duplicate pure Kotlin logic between
androidMainandjvmMain. Extract to acommonMainhelper.
3. Core Libraries & Constraints
- Concurrency:
kotlinx.coroutines. Useorg.meshtastic.core.common.util.ioDispatcheroverDispatchers.IOdirectly. InjectCoroutineDispatchersfromcore:diinto classes that need dispatchers — never referenceDispatchers.IO/Main/Defaultdirectly in business logic. - Error Handling: Use
safeCatching {}fromcore:commoninstead ofrunCatching {}in coroutine/suspend contexts.runCatchingswallowsCancellationException, breaking structured concurrency. KeeprunCatchingonly in cleanup/teardown code (abort, close, eviction loops). - Standard Library Replacements:
ConcurrentHashMap->atomicfuor Mutex-guardedmutableMapOf().java.util.concurrent.locks.*->kotlinx.coroutines.sync.Mutex.java.io.*->Okio(BufferedSource/BufferedSink).
- Networking: Pure Ktor. No OkHttp. Ktor
Loggingplugin for debugging. - HTTP Configuration: Use
HttpClientDefaultsfromcore:networkfor shared base URL (API_BASE_URL), timeouts, and retry constants. Both Android (NetworkModule) and Desktop (DesktopKoinModule) HttpClient instances must use these. Feature API services use relative paths;DefaultRequestsets the base URL. - BLE: Route through
core:bleusing Kable. - Room KMP: Use
factory = { MeshtasticDatabaseConstructor.initialize() }inRoom.databaseBuilder.
4. Hierarchy & Source-Set Conventions
- Hierarchy template first: Prefer Kotlin's default hierarchy template and convention plugins over manual
dependsOn(...)graphs. Manual source-set wiring should be reserved for cases the template cannot model. expect/actualrestraint: Prefer interfaces + DI for platform capabilities; useexpect/actualfor small unavoidable platform primitives. Avoid broad expect/actual class hierarchies when an interface-based boundary is sufficient.- Shared helpers over duplicated lambdas: When
androidMainandjvmMaincontain identical pure-Kotlin logic (formatting, action dispatch, validation), extract tocommonMain. Examples:formatLogsTo(),handleNodeAction(),findNodeByNameSuffix(),MeshtasticAppShell,BaseRadioTransportFactory.
5. Dependency Catalog Aliases
- JetBrains fork aliases: Version catalog aliases for JetBrains-forked AndroidX artifacts use the
jetbrains-*prefix (e.g.,jetbrains-lifecycle-runtime-compose,jetbrains-navigation3-ui). Plainandroidx-*aliases are true Google AndroidX artifacts. Never mix them up incommonMain. - Compose Multiplatform: Version catalog aliases for Compose Multiplatform artifacts use the
compose-multiplatform-*prefix (e.g.,compose-multiplatform-material3,compose-multiplatform-foundation). Never use plainandroidx.composedependencies incommonMain. - Dependencies: Always check
gradle/libs.versions.tomlbefore assuming a library is available.
6. I/O & Serialization
- Okio standard: This project standardizes on Okio (
BufferedSource/BufferedSink). JetBrains recommendskotlinx-io(built on Okio), but this project has not migrated. Do not introducekotlinx-iowithout an explicit decision. - Room KMP: Always use
factory = { MeshtasticDatabaseConstructor.initialize() }inRoom.databaseBuilderandinMemoryDatabaseBuilder. DAOs and Entities reside incommonMain. - Room Patterns:
- Use
@Upsertfor insert-or-update operations instead of manualINSERT OR IGNORE+UPDATElogic. - Use
LIMIT 1on@Querymethods that expect a single row. - Prevent N+1 queries: batch operations with
@Upsert fun putAll(items: List<T>)or chunkedWHERE INqueries (chunk size ≤ 999 to respect SQLite bind parameter limit).
- Use
7. Build-Logic Conventions
- In
build-logic/convention, prefer lazy Gradle configuration (configureEach,withPlugin, provider APIs). AvoidafterEvaluatein convention plugins unless there is no viable lazy alternative.
8. Onboarding a New Target (Desktop/iOS)
- Ensure all new logic compiles against the KMP core (
jvm(),iosArm64(), etc.). - Do not use platform-specific constructs in
commonMainor you break the iOS/Desktop builds. - Test using
kmpSmokeCompileto verify cross-platform compilation. - For desktop wiring, copy the pattern in
desktopApp/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.ktand useNoopStubs.ktto temporarily mock missing platform implementations.
Reference Anchors
- Shared Okio I/O:
core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt - Desktop DI Stubs:
desktopApp/src/main/kotlin/org/meshtastic/desktop/stub/NoopStubs.kt - Version Catalog:
gradle/libs.versions.toml - Convention Plugins:
build-logic/convention/
Signals
- GitHub stars
- 2k
- Forks
- 512
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
kmp-architecture- Source
- github.com/meshtastic/meshtastic-android