Koin Annotations dependency injection and JetBrains Navigation 3 in Meshtastic-Android, including the anti-patterns that compile cleanly and then fail at runtime. Use this whenever you add a screen, a route, a ViewModel or a Koin module, or when navigation or injection behaves unexpectedly.
90%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
This skill covers dependency injection (Koin Annotations 4.2.x) and Navigation 3 1.2 (the JetBrains navigation3-ui mirror over AndroidX navigation3-runtime) architecture, constraints, and anti-patterns within the Meshtastic-Android KMP codebase.
@Module, @ComponentScan, and @KoinViewModel annotations directly in commonMain shared modules to encapsulate dependency graphs per feature.@Module classes are active automatically. Ensure they are included by the app root module (@Module(includes = [...])) in androidApp/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt and desktopApp/.../DesktopKoinModule.kt.Context, Activity, Application) into shared commonMain business logic. Inject interfaces instead.koinViewModel() or injected bindings within Compose navigation graphs.compileSafety on a library module. Validation is whole-graph and runs at the @KoinApplication entry point, so a library validates against a graph it cannot see and reports KOIN-D003 for definitions its consumers supply. KoinConventionPlugin enables it only for the modules in KOIN_ENTRY_POINTS.skipDefaultValues = true behavior skips parameters with default Kotlin values.The project uses the K2 Compiler Plugin (koin-compiler-plugin, not KSP). The canonical startup uses the plugin's typed startKoin<T>() stub, which the plugin transforms at compile time via IR:
// Bootstrap class — separate from @Module, references the root module graph
@KoinApplication(modules = [AppKoinModule::class])
object AndroidKoinApp
// In Application.onCreate()
startKoin<AndroidKoinApp> {
androidContext(this@MeshUtilApplication)
workManagerFactory()
}@KoinApplication goes on a dedicated bootstrap object, not on a @Module class.startKoin<T>() (from org.koin.plugin.module.dsl) is a compiler plugin stub — if the plugin isn't applied, it throws NotImplementedError.stopKoin() uses the standard runtime API (org.koin.core.context.stopKoin).compileSafety is on at the entry points only (:androidApp, :desktopApp). Validation is whole-graph, so the flag is only meaningful where the graph is assembled. A new app target must be added to KOIN_ENTRY_POINTS or it is never validated.@Module(includes = ...) levels below the entry point is invisible to the index. The flavor modules carry @Configuration as well as their includes for this reason; dropping the includes removes them from the runtime graph, which KoinVerificationTest catches.module { } definitions are not reachable by the assembled graph, which is why :desktopApp uses @Module classes.NavKey, NavBackStack, EntryProviderScope).core:navigation/src/commonMain/.../Routes.kt as @Serializable sealed interface hierarchies. Don't use ad-hoc strings.EntryProviderScope<NavKey> in commonMain (e.g., fun EntryProviderScope<NavKey>.settingsGraph(backStack)).MeshtasticNavDisplay (from core:ui/commonMain) as the Navigation 3 host. It owns the entry decorators; do not create them in app hosts or feature modules.MeshtasticNavDisplay renders ListDetailSceneStrategy scenes (listPane(), detailPane(), extraPane() entry metadata) and falls back to a single pane. It registers no dialog or supporting-pane strategy, so that metadata has no effect.NavigationBackHandler (androidx.navigationevent.compose, catalog alias jetbrains-navigationevent-compose) for back gestures in multiplatform code. Do not use Android's BackHandler.DeepLinkRouter.route() in core:navigation to synthesize typed backstacks from RESTful paths.NavBackStack list for multiple tabs. Use MultiBackstack (from core:navigation).NavEntryDecorator set. Navigation 3 pops every entry missing from the stack it is given, so a shared saveable-state or ViewModel-store decorator clears the tab you just left. The MultiBackstack overload of MeshtasticNavDisplay gives every tab's stack its own saveable-state and ViewModel-store decorators through rememberDecoratedNavEntries, following the per-stack decorators of the Navigation 3 multiple back stacks recipe, and passes only the active tab's entries to NavDisplay. Its entryProvider must therefore resolve every tab's keys, not only the active tab's.NavBackStack<NavKey> directly with add(...) and removeLastOrNull().entryProvider and entry<K> are inline, so inline entries become remembered lambdas the compiler updates in place and a stale back-stack capture passes unseen. Declare them in a plain EntryProviderScope<NavKey> extension, as feature graphs do.androidApp/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.ktandroidApp/src/main/kotlin/org/meshtastic/app/di/AndroidKoinApp.ktandroidApp/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.ktcore/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/Routes.ktdesktopApp/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.ktmeshtastic/Meshtastic-Android
1bedc5d
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.