← The Directory
Agent rules

compose kotlin agent skills

Jetpack Compose & Kotlin AI agent skills for Cursor, Claude Code, Codex, Gemini & 27+ agents. Strict MVI · Kotlin 2.x K2 · Compose 2026 · CI-validated.

View source

name: compose-kotlin-agent-skills description: > Enterprise Android/Kotlin skill for 2026 — Kotlin 2.x K2 compiler, AGP 9, Navigation 3, edge-to-edge Compose, strict MVI with atomic _state.update, banned AI antipatterns. Jetpack Compose, Hilt, Room, KMP, CameraX/ML Kit, performance, testing. Use when writing Kotlin for Android, building Compose UI, MVVM/MVI architecture, debugging recomposition, Room DAOs, Navigation 3, coroutines, or asking "structure Android app", "ViewModel UiState", "collectAsStateWithLifecycle", "edge to edge", "AGP 9", "K2 compiler", "Play Store release". license: MIT metadata: author: haidrrrry version: "2.2.0" standard: agentskills.io platforms: target: ["android", "kotlin-multiplatform", "compose-multiplatform"] host: ["macos", "linux", "windows"] agent_compatibility: 27

Android & Kotlin — Enterprise Production Skill (2026)

Authoritative Android engineering kit. Real patterns from AnimatedClockJetpacl, Authenticator, RepLock.

Not an Android library, voice-assistant skill, on-device LLM SDK, or Compose UI library. This is markdown for coding AIs — clone into .cursor/skills/ or .claude/skills/. See README § "What this is NOT".

Meta index: AGENTS.md · CI: python3 scripts/validate_skills.py

Sub-Skills (Modular)

SkillPathScope
Architectureskills/android-kotlin-architecture/SKILL.mdMVI/MVVM, modules, UseCases, UDF
Compose UIskills/android-kotlin-compose/SKILL.mdUI, edge-to-edge, performance, motion
Testingskills/android-kotlin-testing/SKILL.mdTurbine, Compose tests, Hilt fakes

2026 Toolchain — Non-Negotiable

ToolMinimumNotes
Kotlin2.0+ (2.1.x)K2 compiler only — no K1 fallback
AGP9.0+Built-in Kotlin, new DSL defaults
Compose BOM2025.05+Material 3, strong skipping default
Navigation3.x (2.9+ artifact)Type-safe NavKey, @Serializable routes
compileSdk / targetSdk35+Edge-to-edge mandatory
JDK17Required for AGP 9
[versions]
kotlin = "2.1.20"
agp = "9.0.0"
compose-bom = "2025.05.00"
navigation = "2.9.0"
room = "2.7.1"
hilt = "2.56.2"
lifecycle = "2.9.0"
coroutines = "1.10.2"

Kotlin 2.x / K2 Compiler Constraints

  1. Enable K2 — kotlin.compiler.execution.strategy default; use ksp not kapt for Hilt/Room.
  2. Smart casts — prefer when (val x = state) { is Loaded -> ... } over unsafe !!.
  3. Sealed hierarchy — sealed interface UiState + data object Loading : UiState for exhaustive when.
  4. @JvmInline value class for IDs (@JvmInline value class UserId(val raw: String)).
  5. @Immutable / @Stable on UiState and UI models passed to Compose.
  6. Explicit API optional for libraries — kotlin { explicitApi() } in published modules.
  7. No !! except in tests with comment — use requireNotNull / early return.
  8. data class copy for state — never mutate UiState fields in place.
sealed interface AccountsUiState {
    data object Loading : AccountsUiState
    data class Ready(
        val accounts: List<AccountUi>,
        val query: String = ""
    ) : AccountsUiState
    data class Error(@StringRes val messageRes: Int) : AccountsUiState
}

MVI Guardrails — Atomic State Only

Allowed mutation: _state.update { it.copy(...) } or _state.update { prev -> ... }.

Banned in ViewModel:

// BANNED
_state.value = AccountsUiState(...)           // naked assign
_state.value.accounts.add(item)               // mutating list inside state
mutableStateOf / mutableStateListOf in VM      // Compose scope only
viewModelScope.launch { _state.value = ... }  // use update inside launch

Required pattern:

class AccountsViewModel @Inject constructor(
    private val repository: AccountsRepository
) : ViewModel() {

    private val _state = MutableStateFlow<AccountsUiState>(AccountsUiState.Loading)
    val state: StateFlow<AccountsUiState> = _state.asStateFlow()

    private val _effects = Channel<AccountsEffect>(Channel.BUFFERED)
    val effects: Flow<AccountsEffect> = _effects.receiveAsFlow()

    fun onEvent(event: AccountsEvent) {
        when (event) {
            AccountsEvent.Refresh -> refresh()
            is AccountsEvent.QueryChanged -> _state.update { current ->
                when (current) {
                    is AccountsUiState.Ready -> current.copy(query = event.query)
                    else -> current
                }
            }
        }
    }

    private fun refresh() {
        viewModelScope.launch {
            _state.update { AccountsUiState.Loading }
            repository.load()
                .onSuccess { list ->
                    _state.update { AccountsUiState.Ready(accounts = list.map { it.toUi() }) }
                }
                .onFailure {
                    _state.update { AccountsUiState.Error(R.string.error_load_failed) }
                }
        }
    }
}

UI collects:

@Composable
fun AccountsRoute(vm: AccountsViewModel = hiltViewModel()) {
    val state by vm.state.collectAsStateWithLifecycle()
    LaunchedEffect(Unit) {
        vm.effects.collect { effect -> /* one-shot: nav, snackbar */ }
    }
    AccountsScreen(state = state, onEvent = vm::onEvent)
}

Navigation 3 + Edge-to-Edge

@Serializable data object Home
@Serializable data class Detail(val id: String)

@Composable
fun AppNavHost(navController: NavHostController = rememberNavController()) {
    NavHost(navController, startDestination = Home) {
        composable<Home> { HomeScreen(onOpen = { navController.navigate(Detail(it)) }) }
        composable<Detail> { entry ->
            val route = entry.toRoute<Detail>()
            DetailScreen(id = route.id)
        }
    }
}
  • Routes = @Serializable types, not string paths
  • Pass IDs in nav args — fetch models in destination ViewModel
  • enableEdgeToEdge() in Activity — see compose sub-skill

Deep dive: references/07-navigation.md

Banned Antipatterns — AI Hallucination Lookup

Full table with WRONG/RIGHT pairs and row numbers: references/00-banned-antipatterns.md

Load before reviewing any agent-generated Kotlin. Compose audit checklist: skills/android-kotlin-compose/SKILL.md § REVIEW MODE.

Reference Routing

FileTopic
references/00-banned-antipatterns.mdMaster banned table — load first for reviews
references/01-architecture.mdClean Arch, MVVM, modules
references/02-compose-ui.mdComposition, M3, theming
references/03-animations.mdMotion, Canvas
references/04-coroutines-flow.mdFlow, errors, cancellation
references/05-hilt-di.mdDI, scopes
references/06-room-db.mdRoom, offline-first
references/07-navigation.mdNav 3, deep links
references/08-kmp-cmp.mdKMP, expect/actual
references/09-networking.mdKtor, JWT
references/10-performance.mdRecomposition, Coil, profiles
references/11-testing.mdTurbine, UI tests
references/12-camera-mlkit.mdCameraX, pose
references/13-release-checklist.mdR8, Play Store
references/14-datastore.mdDataStore, SP migration, encryption
references/15-paging3.mdPaging 3, RemoteMediator, asState()
references/16-coil-image.mdCoil 3.4, AsyncImage, cache
references/17-accessibility.mdTalkBack, semantics, WCAG
references/18-gradle-build-logic.mdConvention plugins, KSP, R8, baseline profiles
references/19-xml-to-compose-migration.mdXML → Compose, RxJava → Flow

Platforms

Target (what the docs teach): Android · Kotlin Multiplatform · Compose Multiplatform Host (where the docs are read): macOS · Linux · Windows — anywhere your AI agent runs Agent compatibility: 27 install guides → agents/README.md

Repo: https://github.com/haidrrrry/compose-kotlin-agent-skills

Mandatory Defaults (Summary)

  • Strings: stringResource / @StringRes — zero hardcoded UI text
  • State: ViewModel StateFlow + _state.update { }
  • UI: Stateless composables, modifier first optional param
  • Lists: LazyColumn + keys + contentType
  • DI: @HiltViewModel + constructor inject
  • Tests: Fakes + Turbine — see testing sub-skill

Validation Before PR

python3 scripts/validate_skills.py --strict

Anti-Rationalizations

ExcuseReality
"UiState overkill"data object Loading costs one line
"Keys later"Broken scroll/focus NOW
"Prototype skip repo"Prototypes ship
"collectAsState fine"Background collector drains battery

Examples

PathPatterns
examples/animated-clock/Canvas, rotate(), particles
examples/authenticator/Room Flow, combine, CompositionLocal theme