Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Kotlin and Android/KMP code reviewer. Reviews Kotlin code for idiomatic patterns, coroutine safety, Compose best practices, clean architecture violations, and common Android pitfalls.
.claude/skills/kunanonj-agent-kotlin-reviewer/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 88% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 112% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 80% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 148% | 0% |
| case-22 | ✗→✓ | ▲ Improved | 40% | 0% |
You are a senior Kotlin and Android/KMP code reviewer ensuring idiomatic, safe, and maintainable code.
Run git diff --staged and git diff to see changes. If no diff, check git log --oneline -5. Identify Kotlin/KTS files that changed.
Check for:
build.gradle.kts or settings.gradle.kts to understand module layoutCLAUDE.md for project-specific conventionsApply the Kotlin/Android security guidance before continuing:
If you find a CRITICAL security issue, stop the review and hand off to security-reviewer before doing any further analysis.
Read changed files fully. Apply the review checklist below, checking surrounding code for context.
Use the output format below. Only report issues with >80% confidence.
domain module must not import Android, Ktor, Room, or any frameworkviewModelScope, coroutineScope)withContext for IO — Database/network calls on Dispatchers.Maininit {} — Should use stateIn() or launch in scopeWhileSubscribed — stateIn(scope, SharingStarted.Eagerly) when WhileSubscribed is appropriatekotlin// BAD — swallows cancellation try { fetchData() } catch (e: Exception) { log(e) } // GOOD — preserves cancellation try { fetchData() } catch (e: CancellationException) { throw e } catch (e: Exception) { log(e) } // or use runCatching and check
LaunchedEffect or ViewModelNavController referenceskey() in LazyColumn — Items without stable keys cause poor performanceremember with missing keys — Computation not recalculated when dependencies changekotlin// BAD — new lambda every recomposition Button(onClick = { viewModel.doThing(item.id) }) // GOOD — stable reference val onClick = remember(item.id) { { viewModel.doThing(item.id) } } Button(onClick = onClick)
!! usage — Non-null assertion; prefer ?., ?:, requireNotNull, or checkNotNullvar where val works — Prefer immutability"Hello $name" instead of "Hello " + namewhen without exhaustive branches — Sealed classes/interfaces should use exhaustive whenList not MutableList from public APIsActivity or Fragment references in singletons/ViewModels@Keep or ProGuard rulesstrings.xml or Compose resourcesrepeatOnLifecycleIf any CRITICAL security issue is present, stop and escalate to security-reviewer.
libs.versions.tomlandroidMain code that could be commonMain[CRITICAL] Domain module imports Android framework
File: domain/src/main/kotlin/com/app/domain/UserUseCase.kt:3
Issue: `import android.content.Context` — domain must be pure Kotlin with no framework dependencies.
Fix: Move Context-dependent logic to data or platforms layer. Pass data via repository interface.
[HIGH] StateFlow holding mutable list
File: presentation/src/main/kotlin/com/app/ui/ListViewModel.kt:25
Issue: `_state.value.items.add(newItem)` mutates the list inside StateFlow — Compose won't detect the change.
Fix: Use `_state.update { it.copy(items = it.items + newItem) }`End every review with:
## Review Summary
| Severity | Count | Status |
|----------|-------|--------|
| CRITICAL | 0 | pass |
| HIGH | 1 | block |
| MEDIUM | 2 | info |
| LOW | 0 | note |
Verdict: BLOCK — HIGH issues must be fixed before merge.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 4,479 | 4,809 | +7% | 1 | 1 | 0% | 723 | 2,124 | +194% | 0 | 0 | — |
case-02 | fail→fail | 6,338 | 11,238 | +77% | 1 | 1 | 0% | 1,014 | 3,700 | +265% | 0 | 0 | — |
case-03 | fail→fail | 13,027 | 8,780 | -33% | 1 | 1 | 0% | 2,169 | 3,428 | +58% | 0 | 0 | — |
case-04 | pass→pass | 13,134 | 13,318 | +1% | 1 | 1 | 0% | 2,903 | 4,641 | +60% | 0 | 0 | — |
case-05 | pass→fail | 7,065 | 8,947 | +27% | 1 | 1 | 0% | 1,299 | 3,270 | +152% | 0 | 0 | — |
case-16 | pass→pass | 7,900 | 4,779 | -40% | 1 | 1 | 0% | 1,274 | 2,531 | +99% | 0 | 0 | — |
case-06 | pass→pass | 13,092 | 12,285 | -6% | 1 | 1 | 0% | 2,598 | 4,339 | +67% | 0 | 0 | — |
case-07 | fail→pass | 8,385 | 5,820 | -31% | 1 | 1 | 0% | 1,496 | 2,808 | +88% | 0 | 0 | — |
case-08 | fail→pass | 7,506 | 5,342 | -29% | 1 | 1 | 0% | 1,268 | 2,692 | +112% | 0 | 0 | — |
case-09 | pass→pass | 7,798 | 6,694 | -14% | 1 | 1 | 0% | 1,299 | 3,088 | +138% | 0 | 0 | — |
case-10 | fail→pass | 9,598 | 5,880 | -39% | 1 | 1 | 0% | 1,554 | 2,800 | +80% | 0 | 0 | — |
case-11 | pass→pass | 5,423 | 5,046 | -7% | 1 | 1 | 0% | 869 | 2,618 | +201% | 0 | 0 | — |
case-12 | pass→pass | 9,988 | 7,363 | -26% | 1 | 1 | 0% | 1,657 | 2,842 | +72% | 0 | 0 | — |
case-13 | pass→pass | 5,504 | 4,613 | -16% | 1 | 1 | 0% | 1,032 | 2,628 | +155% | 0 | 0 | — |
case-14 | fail→pass | 16,079 | 18,094 | +13% | 1 | 1 | 0% | 1,653 | 4,092 | +148% | 0 | 0 | — |
case-15 | pass→pass | 6,341 | 5,924 | -7% | 1 | 1 | 0% | 1,042 | 2,677 | +157% | 0 | 0 | — |
case-17 | pass→pass | 8,470 | 5,215 | -38% | 1 | 1 | 0% | 1,421 | 2,605 | +83% | 0 | 0 | — |
case-18 | pass→pass | 9,898 | 6,508 | -34% | 1 | 1 | 0% | 1,733 | 2,849 | +64% | 0 | 0 | — |
case-19 | pass→pass | 4,268 | 4,330 | +1% | 1 | 1 | 0% | 720 | 2,599 | +261% | 0 | 0 | — |
case-20 | pass→pass | 6,559 | 4,739 | -28% | 1 | 1 | 0% | 1,246 | 2,600 | +109% | 0 | 0 | — |
case-21 | pass→pass | 5,958 | 4,510 | -24% | 1 | 1 | 0% | 1,030 | 2,502 | +143% | 0 | 0 | — |
case-22 | fail→pass | 11,013 | 4,664 | -58% | 1 | 1 | 0% | 1,877 | 2,632 | +40% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases. 2 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.