Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Use when building native Android with Jetpack Compose - stateless composables, state hoisting, ViewModel-owned state, edge-to-edge, predictive back, adaptive layout, atomic components, and Material theming over hardcoded values
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
설치 방법 보기설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.
Native Android (Jetpack Compose) is chosen when a task needs deep Android integration or the app is already native (see native-vs-flutter-decision). Same discipline as Flutter/SwiftUI: small stateless composables, state hoisted out, reusable atomic components.
Core principle: Composables are stateless functions of state. State is hoisted to a ViewModel; composables emit events upward.
onXxx) — it does not own app state. The screen-level composable connects a ViewModel to the stateless UI.ViewModel owns state and logic, exposed as StateFlow/UiState; the composable collects it with collectAsStateWithLifecycle(). Networking/business rules live in the ViewModel + repository, never in the composable.Loading | Data | Empty | Error — when over it renders every branch. No happy-path-only screens.MaterialTheme.colorScheme / typography / a spacing tokens object (MaterialTheme.spacing, see mobile-design-system-foundation) — never scattered Color(0xFF...) and magic dp. Support dark theme + dynamic color.remember/derivedStateOf to avoid recomputation; hoist remembered state only when it's genuinely local (scroll, text field).@Preview reusable composables in their key states, and across the matrix (see below).enableEdgeToEdge() before setContent in the activity — the opt-out is gone at targetSdk 35+.Scaffold: use its innerPadding and Modifier.consumeWindowInsets(innerPadding) on the content so a nested scrollable doesn't double-apply it.Modifier.imePadding() once, at the container that should move with the keyboard — applying it a second time further down the tree produces extra empty space.android:windowSoftInputMode="adjustResize" in the manifest for screens with text input.NavigationSuiteScaffold does not automatically propagate its padding to the content slot — apply insets explicitly inside it, don't assume it's handled.onBackPressed() / OnBackPressedCallback without isEnabled toggling is not called the same way once predictive back is the default — use BackHandler (simple) or PredictiveBackHandler (custom transition) in Compose, or Navigation 3's built-in handling.currentWindowAdaptiveInfo().windowSizeClass to branch layout by available width/height — never by Configuration.orientation or a hardcoded device check.NavigationSuiteScaffold switches a bottom nav bar to a nav rail automatically at the window-size-class breakpoint — prefer it over hand-rolling the switch.GridCells.Adaptive(minWidth) for grids that should reflow by available width.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY) and Android 17/API 37 (no opt-out) ignore screenOrientation/resizeableActivity restrictions on screens ≥600dp — design every screen to work in both orientations and at tablet width; don't rely on an orientation lock to avoid building the wider layout.@PreviewScreenSizes, @PreviewFontScale, @PreviewLightDark, or a custom multi-preview with Devices.PHONE/Devices.TABLET — see mobile-visual-self-review for the full size/text-scale/dark matrix.DeviceConfigurationOverride(ForcedSize(...) then FontScale(...) then DarkMode(...)) for instrumented matrix tests.rule.enableAccessibilityChecks() on an API 34+ device/emulator before UI-mutating test actions (inconclusive on Robolectric — say so if that's all you ran); see mobile-accessibility.contentDescription on every Image/Icon that carries meaning; null explicitly for a purely decorative one.Modifier.semantics(mergeDescendants = true) on a composite row so a screen reader announces it once.Modifier.size/minimumInteractiveComponentSize().// ViewModel owns state + logic (unit-testable, no Compose)
class TaskListViewModel(private val repo: TaskRepository) : ViewModel() {
private val _state = MutableStateFlow<UiState<List<Task>>>(UiState.Loading)
val state: StateFlow<UiState<List<Task>>> = _state
fun load() = viewModelScope.launch {
_state.value = UiState.Loading
_state.value = try {
val items = repo.list()
if (items.isEmpty()) UiState.Empty else UiState.Data(items)
} catch (e: CancellationException) {
throw e // never swallow cancellation
} catch (e: Exception) {
UiState.Error(e)
}
}
}
// stateless composable renders state, emits events
@Composable
fun TaskListScreen(state: UiState<List<Task>>, onRetry: () -> Unit) = when (state) {
is UiState.Loading -> AppSpinner()
is UiState.Error -> AppErrorView(message = state.cause.message, onRetry = onRetry)
is UiState.Data -> TaskList(tasks = state.value)
is UiState.Empty -> EmptyView()
}
The fix from the previous version: runCatching { } wrapped around a suspend call inside viewModelScope.launch catches CancellationException along with real errors, which breaks structured concurrency (a cancelled screen keeps "loading" instead of stopping) — use try/catch and rethrow CancellationException explicitly. The Empty branch is now actually emitted when the list comes back empty, not just declared and never reached.
ViewModel with a fake/mockk repository and a test dispatcher — no Compose.createComposeRule) assert visible behavior and clicks. Test-first.MaterialTheme + tokens.collectAsStateWithLifecycle (leaks/waste).Empty declared but never emitted.runCatching around a suspend call, swallowing CancellationException.@Composable that imports a repository or Retrofit service.Color(0xFF...)/magic dp outside the theme.when.enableEdgeToEdge() missing from a new or recently targetSdk-bumped activity.아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.
로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.
Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.