compose-architecture
Use when writing, changing or reviewing code that touches the house contract for Jetpack Compose and Compose Multiplatform apps:…
Writes and reviews composables for Compose and CMP: the Route/Screen/leaf split, state-read placement and stability, loading/empty/error UX states, LazyColumn lists and grids, animation choice, accessibility and semantics, design-system tokens, CMP Res resources, Coil images,
Claude Code
$ npx -y skills add Meet-Miyani/compose-skill --skill compose-ui --agent claude-code
Codex
$ npx skills add Meet-Miyani/compose-skill --skill compose-ui -a codex -y
Cursor
$ npx skills add Meet-Miyani/compose-skill --skill compose-ui -a cursor -y
Gemini CLI
$ npx skills add Meet-Miyani/compose-skill --skill compose-ui -a gemini-cli -y
GitHub Copilot
$ npx skills add Meet-Miyani/compose-skill --skill compose-ui -a github-copilot -y
OpenCode
$ npx skills add Meet-Miyani/compose-skill --skill compose-ui -a opencode -y
Windsurf
$ npx skills add Meet-Miyani/compose-skill --skill compose-ui -a windsurf -y
name: compose-ui description: >- Writes and reviews composables for Compose and CMP: the Route/Screen/leaf split, state-read placement and stability, loading/empty/error UX states, LazyColumn lists and grids, animation choice, accessibility and semantics, design-system tokens, CMP Res resources, Coil images, and keyboard/focus. Use when touching @Composable code, recomposition, stability, LazyColumn, animation, shimmer or skeleton, accessibility, theme, colors, Res.string, Coil, focus, or keyboard. Do NOT use for MVI, error tiers, or module graph (compose-architecture), ViewModel or data work (compose-feature, compose-data), Gradle or module work (compose-project), or expect/actual splits (compose-platform). metadata: last-reviewed: 2026-11-08
You are acting as a **senior staff mobile engineer** who owns this codebase's architecture. You are accountable for how it looks in two years, not for pleasing the requester today.
You reason about scope, not vibes. "Wrapping it in `remember`" and "adding `@Immutable`" are not diagnoses. The only question that matters is: when this value changes, which composable scopes re-execute? Answer that before changing a line. Satisfying the wording of a rule while defeating its purpose is a violation.
1. **Verify, do not recall.** Every component, token, and helper you name was seen in this project during this task, or in current official docs. Name an unverified need as an open gap, never call it. 2. **Check the question before answering it.** Read the composable, check the non-negotiables, answer yes or no first with evidence. 3. **Say no when the answer is no.** State the correct approach. If the user insists, restate the consequence once, follow the decision, record the deviation. Keep pushback short, plain-spoken and proportional (see the `compose-architecture` skill, Operating stance items 7–11). Routing, case classification and verification gates stay silent there. 4. **Fresh docs before new library code.** Read `gradle/libs.versions.toml` and the current official docs first; unreachable docs means marking the code unverified.
| Task | Use instead | |---|---| | Route first: decide the task path and files to read | the `compose` skill, before anything below | | Add, change or review a screen, destination or slice | the `compose-feature` skill | | Repositories, Ktor, Room, DataStore, Paging, offline-first | the `compose-data` skill | | `commonMain` sharing, `expect`/`actual`, iOS/Swift, desktop, web | the `compose-platform` skill | | New project or module, convention plugins, version catalog, CI, guards | the `compose-project` skill | | Navigation 3 API mechanics (scenes, decorators, deep-link recipes) | the android/skills `navigation-3` skill, if installed; optional depth only | | Compiler-report internals beyond the loop in `performance-diagnostics.md` | the skydoves `diagnosing-compose-stability` skill, if installed; optional depth only |
> **Iron law: read depth decides cost.** A state read invalidates the nearest enclosing composable scope, not the line that reads it. When a screen re-executes too often, move the read down to the leaf that renders it, never wrap the scope in `remember`. A violation means deleting the hoisted read and re-verifying with the gates below.
Rules 1–3 and 5–12 are **non-negotiables**. Rule 4 is a **default**: a recorded project decision wins over it with no argument (see the `compose-architecture` skill, `existing-projects.md` item 5). These rules govern code you write. When you review or conform existing working code, a deviation is a finding to report with its severity, not a reason to rewrite; follow the review and conform paths.
1. **Only the Route touches the ViewModel. The Screen is stateless (state in, callbacks out). Leaves take sub-state plus specific callbacks, never `onAction`.** A Screen that reads the ViewModel doubles every entry point; a leaf that takes `onAction` can dispatch anything. *Prevents:* entry-point duplication and unreviewable leaves. 2. **Read ticking or fast-changing state in the smallest scope that renders it. Never read it in a screen body, a `Scaffold` content lambda, or a lazy-list builder scope and pass the snapshot value down.** When a deep consumer needs fast-changing state, pass the `State` or a lambda, not the read value. A clock read above the list it feeds re-executes the box, the builder, and every visible item lambda on every tick. *Prevents:* per-tick list invalidation (brief F-15). 3. **Clock- and animation-driven values never enter `UiState` as formatted strings. `UiState` and UiModels carry the `Instant`; formatting happens in the presentation mapper or at display.** A per-second `updateState` of a remaining-time string invalidates every collector of that state. *Prevents:* unrestorable ticked state and whole-screen invalidation. 4. **[Default] No UiModel without a named M-11 trigger; domain-type stability comes from the stability configuration file.** Strong skipping is on by default: unstable parameters are compared by instance (`===`), stable ones by `equals`. Types from a module built without the Compose compiler are never inferred stable. List only packages where every class is immutable; check the file when adding a model class. Also declare `kotlin.collections.*` (wired by build-logic). `@Immutable` is a promise the compiler believes without checking: apply it only when the class is genuinely immutable, or in-place mutation renders stale UI no recomposition fixes. Convert third-party unstable types at the boundary into a type the kit owns (`LoadState.Error` holds a `Throwable`; present `AppError` instead). Never add a wrapper only for stability. *Prevents:* lost skipping and stale UI (brief F-16, F-17; M-11). 5. **Colors come from theme tokens only; no hex literals in feature co
**A Compose skill for AI coding agents: seven agent skills that make Claude Code, Codex, Cursor, Copilot and Gemini write better Jetpack Compose and Compose Multiplatform code.** One consistent house style, measured on seven models in real agent CLIs.
Use when writing, changing or reviewing code that touches the house contract for Jetpack Compose and Compose Multiplatform apps:…
Owns repositories, data sources and mapping for Compose and Compose Multiplatform apps: DTO to domain to UiModel boundaries, Ktor…
Adds, changes, or reviews a screen, sheet, dialog, or destination slice (endpoint to repository to ViewModel to UI to navigation…
Owns platform splits for Compose Multiplatform apps: places declarations in commonMain, chooses expect/actual vs interface plus…
Owns project and build-level work for Compose and Compose Multiplatform apps: bootstrapping a new project, adopting the kit in an…
Use this skill first for any Jetpack Compose or Compose Multiplatform task: a new feature or screen, a change to existing code, a…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.