.claude/skills/claude-android-skill/SKILL.md
Enforce Clean Architecture and Offline-first patterns in this MovieFinder codebase
npx skillsauth add ChooJeongHo/MovieFinder claude-android-skillInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Analyze or implement $ARGUMENTS with strict enforcement of this project's architectural rules.
Presentation → Domain (UseCase/Repository interface) only
Data → Domain interfaces only
Core → available everywhere
FORBIDDEN: Presentation → Data (direct DAO/API access)
FORBIDDEN: Domain → Android framework (except Paging)
invoke() onlyFlow<T> or suspend result — never raw listsandroidx.paging)ORDER BY for sorting (not sortedBy in ViewModel)StateFlow<UiState> (sealed class: Loading/Success/Error)Channel.CONFLATED + receiveAsFlow() for one-shot events (Snackbar)SavedStateHandle for arguments surviving process deathMutex.tryLock() for duplicate call preventionUse this pattern only for HomeFragment tabs (NowPlaying, Popular):
Network available: API → RemoteMediator → Room → PagingSource → UI
Network absent: Room cache → PagingSource → UI (1h expiry via RemoteKeyEntity.lastUpdated)
Rules:
RemoteKeyEntity.lastUpdated checked before REFRESH (skip if < 1h old)PREPEND always returns MediatorResult.Success(endOfPaginationReached = true)withTransaction (or @Transaction on DAO abstract class)Search uses network-only PagingSource (no Room cache) — this is intentional.
// CORRECT: single repeatOnLifecycle block, parallel launches
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch { viewModel.uiState.collect { ... } }
launch { viewModel.snackbarEvent.collect { ... } }
}
}
// Use launchWithErrorHandler from core/util/CoroutineExt.kt
viewModelScope.launchWithErrorHandler(snackbarChannel) {
_uiState.value = UseCase()
}
// Safe Args always — no manual bundle.putInt
val action = FragmentDirections.actionToDetail(movieId = id)
findNavController().navigate(action)
## Architecture Review: [Target]
### Violations Found
- **[CRITICAL/WARNING]** (file:line): Rule violated → Correct approach
### Compliance Summary
| Layer | Status | Notes |
|-------|--------|-------|
| Presentation | ✅/⚠️/❌ | ... |
| Domain | ✅/⚠️/❌ | ... |
| Data | ✅/⚠️/❌ | ... |
### Offline-First Assessment
[Is the feature properly cache-backed or correctly network-only?]
### Recommended Changes
[Concrete file edits with before/after code snippets]
testing
Write and fix Unit, Hilt integration, and Espresso tests following MovieFinder patterns
development
Systematically diagnose and fix bugs using evidence-driven root cause analysis
tools
Diagnose and fix Kotlin coroutine race conditions, Flow issues, and thread safety bugs
development
Optimize Gradle build speed with Configuration Cache, parallel execution, and AGP 9 tuning