About compose-kotlin-agent-skills
A set of Android and Kotlin engineering skills for coding AIs, organized into architecture, Compose UI, and testing modules. It includes current toolchain constraints, antipattern references, and examples for Android, Kotlin Multiplatform, and Compose Multiplatform projects.
Key features
- 01Modular architecture, Compose UI, and testing guides
- 02Kotlin 2.x and K2 compiler conventions
- 03MVI state management rules
- 04Navigation, accessibility, and performance references
- 05Antipattern index and example projects
- 06Skill validation script
Use cases
- 01Review AI-generated Kotlin code
- 02Set up Android architecture and state management
- 03Build Compose interfaces and write tests
- 04Plan an XML-to-Compose migration
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ haidrrrry/ compose-kotlin-agent-skills --skill compose-kotlin-agent-skillsAfter installing, type/compose-kotlin-agent-skillsin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
.gitattributesCITATION.cffAGENTS.mdCODE_OF_CONDUCT.mdCHANGELOG.mdLICENSECONTRIBUTING.mdROADMAP.mdREADME.mdSECURITY.md.github/pull_request_template.md.github/ISSUE_TEMPLATE/bug_report.mdfeature_request.mdnew_agent.md.github/workflows/skill-lint.ymlagents/_shared-snippet.mdREADME.mdaider.mdamazon-q.mdbolt.mdclaude.mdaugment.mdcline.mdcodex.mdcursor.mdcontinue.mdcopilot.mddeepseek.mddevin.mdfactory.mdDetails
From the project READMEAndroid & 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)
| Skill | Path | Scope |
|---|---|---|
| Architecture | skills/android-kotlin-architecture/SKILL.md | MVI/MVVM, modules, UseCases, UDF |
| Compose UI | skills/android-kotlin-compose/SKILL.md | UI, edge-to-edge, performance, motion |
| Testing | skills/android-kotlin-testing/SKILL.md | Turbine, Compose tests, Hilt fakes |
2026 Toolchain — Non-Negotiable
| Tool | Minimum | Notes |
|---|---|---|
| Kotlin | 2.0+ (2.1.x) | K2 compiler only — no K1 fallback |
| AGP | 9.0+ | Built-in Kotlin, new DSL defaults |
| Compose BOM | 2025.05+ | Material 3, strong skipping default |
| Navigation | 3.x (2.9+ artifact) | Type-safe NavKey, @Serializable routes |
| compileSdk / targetSdk | 35+ | Edge-to-edge mandatory |
| JDK | 17 | Required 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
- Enable K2 —
kotlin.compiler.execution.strategydefault; usekspnotkaptfor Hilt/Room. - Smart casts — prefer
when (val x = state) { is Loaded -> ... }over unsafe!!. - Sealed hierarchy —
sealed interface UiState+data object Loading : UiStatefor exhaustivewhen. @JvmInline value classfor IDs (@JvmInline value class UserId(val raw: String)).@Immutable/@Stableon UiState and UI models passed to Compose.- Explicit API optional for libraries —
kotlin { explicitApi() }in published modules. - No
!!except in tests with comment — userequireNotNull/ early return. data classcopy 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 =
@Serializabletypes, 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
| File | Topic |
|---|---|
references/00-banned-antipatterns.md | Master banned table — load first for reviews |
references/01-architecture.md | Clean Arch, MVVM, modules |
references/02-compose-ui.md | Composition, M3, theming |
references/03-animations.md | Motion, Canvas |
references/04-coroutines-flow.md | Flow, errors, cancellation |
references/05-hilt-di.md | DI, scopes |
references/06-room-db.md | Room, offline-first |
references/07-navigation.md | Nav 3, deep links |
references/08-kmp-cmp.md | KMP, expect/actual |
references/09-networking.md | Ktor, JWT |
references/10-performance.md | Recomposition, Coil, profiles |
references/11-testing.md | Turbine, UI tests |
references/12-camera-mlkit.md | CameraX, pose |
references/13-release-checklist.md | R8, Play Store |
references/14-datastore.md | DataStore, SP migration, encryption |
references/15-paging3.md | Paging 3, RemoteMediator, asState() |
references/16-coil-image.md | Coil 3.4, AsyncImage, cache |
references/17-accessibility.md | TalkBack, semantics, WCAG |
references/18-gradle-build-logic.md | Convention plugins, KSP, R8, baseline profiles |
references/19-xml-to-compose-migration.md | XML → 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,
modifierfirst 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
| Excuse | Reality |
|---|---|
| "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
| Path | Patterns |
|---|---|
examples/animated-clock/ | Canvas, rotate(), particles |
examples/authenticator/ | Room Flow, combine, CompositionLocal theme |