Use this skill to run an end-to-end Jetpack Compose performance audit when the symptom is broad ("the app feels sluggish", "scroll is rough everywhere", "we're starting a perf sprint", "what should we fix first?"). Orchestrates the four-phase Measure → Diagnose → Fix → Verify loop by sequencing the 25 focused skills (release-mode setup, R8, Baseline Profiles, Compose Compiler reports, stability inference, Layout Inspector, `@TraceRecomposition`, stabilization, strong skipping, phase-deferral, derivedStateOf, lazy layouts, lazy prefetch, Modifier.Node, modifier ordering, flow collection, effects, CI gates, hot-reload) and produces a written audit report with Before/After Macrobenchmark numbers. Use when the developer wants a perf sprint kickoff, a pre-release perf gate, onboarding to a perf-troubled codebase, or a written deliverable. Use when the user mentions "audit", "perf review", "perf sprint", "where do I start", or has no specific symptom yet.
npx skills add https://github.com/skydoves/compose-performance-skills --skill auditing-compose-performance
This is the highest-level entry point in the compose-performance-skills library. When the developer's symptom is broad — "the app feels sluggish", "scroll is rough everywhere", "we're starting a perf sprint", "where do we even start?" — Claude enters here. The orchestrator does NOT replace the 25 focused skills; it sequences them through a four-phase loop and produces a written audit report at the end.
The phases are Measure → Diagnose → Fix → Verify, run in order, never skipped. Phase 1 establishes baseline numbers from a release + R8 build on a real device, because anything else is fiction. Phase 2 turns symptoms into named causes from the Compose Compiler reports, Layout Inspector, and runtime tracing. Phase 3 applies one targeted fix at a time, re-measuring between fixes so the delta of each change is provable. Phase 4 regenerates the Baseline Profile, locks in a CI stability gate, and commits the baseline files so regressions cannot slip back.
Perf work without measurement is guessing. Skydoves hot take #1 applies throughout: DO NOT chase 100% skippability — that is a diagnostic on a compiler report, not the goal. The goal is FrameTimingMetric and StartupTimingMetric improvement on a real device.
LazyColumn, derivedStateOf not firing, custom modifier recomposing). Go straight to the focused skill. See ../../INDEX.md for the symptom→skill map.../../INDEX.md../gradlew assembleRelease).../../measurement/generating-baseline-profiles/SKILL.md).stabilityConfigurationFile. Kotlin 2.0.20+ for Strong Skipping default. AGP 8.2+ for the Baseline Profile Generator template.Run all four phases in order. DO NOT skip ahead. After each Phase 3 fix, return to Phase 1 to re-measure and Phase 2 to re-diagnose before applying the next fix.
../../measurement/testing-compose-in-release-mode/SKILL.md.proguard-android-optimize.txt, resource shrinking on). Cross-link ../../build/configuring-r8-for-compose/SKILL.md.../../measurement/generating-baseline-profiles/SKILL.md.MacrobenchmarkRule + StartupTimingMetric under CompilationMode.Partial(BaselineProfileMode.Require). Run ≥10 iterations.FrameTimingMetric (P50, P90, P99). Run ≥5 iterations on the same device.// Phase 1 baseline scroll measurement (record P50/P90/P99 of frameDurationCpuMs)
@Test fun feedScroll() = rule.measureRepeated(
packageName = "com.example",
metrics = listOf(FrameTimingMetric()),
iterations = 5,
startupMode = StartupMode.WARM,
compilationMode = CompilationMode.Partial(BaselineProfileMode.Require),
) {
startActivityAndWait()
device.findObject(By.res("feed")).fling(Direction.DOWN)
}
../../stability/diagnosing-compose-stability/SKILL.md.<module>-composables.txt. List every restartable-but-not-skippable composable and the unstable parameter that blocks skipping.<module>-classes.txt. List every unstable class with the offending field (a var, an unstable field type, an interface, etc.).runtime, unknown, "this looks stable but the compiler disagrees"), run ../../stability/understanding-stability-inference/SKILL.md to walk the 12-phase algorithm.../../recomposition/debugging-recompositions/SKILL.md.@TraceRecomposition. Cross-link ../../measurement/tracing-recompositions-at-runtime/SKILL.md.LazyColumn row beats a 10× recomposition on a one-off settings screen. DO NOT rank by compiler-report severity alone.For each ranked issue, pick the matching focused skill and apply the fix. PREFERRED: one PR per skill, so the diff is reviewable and bisectable. After each fix, re-run Phase 1 (measure) and Phase 2 (diagnose) to confirm the change moved the right needle and did not regress another.
data class, List/Set/Map parameter, java.time.LocalDateTime) → ../../stability/stabilizing-compose-types/SKILL.md.@DontMemoize or @NonSkippableComposable) → ../../recomposition/using-strong-skipping-correctly/SKILL.md.Modifier.offset { }, Modifier.graphicsLayer { }, Modifier.drawBehind { }) → ../../recomposition/deferring-state-reads/SKILL.md.derivedStateOf misuse (missing remember, captured non-state vars, used where input frequency does not exceed output frequency) → ../../recomposition/choosing-derivedstateof/SKILL.md.key and contentType for LazyColumn/LazyRow/LazyVerticalGrid, hoisting modifier chains out of the items lambda → ../../lists/optimizing-lazy-layouts/SKILL.md.../../lists/configuring-lazy-prefetch/SKILL.md.Modifier.composed { } to Modifier.Node + ModifierNodeElement → ../../modifiers/migrating-to-modifier-node/SKILL.md.clip after background) → ../../modifiers/ordering-modifier-chains/SKILL.md.collectAsState → collectAsStateWithLifecycle, hoist Flow<T> parameters out of composables, add .conflate() / .distinctUntilChanged()) → ../../side-effects/collecting-flows-safely/SKILL.md.LaunchedEffect vs RememberedEffect vs DisposableEffect vs SideEffect, stale callbacks via rememberUpdatedState) → ../../side-effects/using-efficient-effects/SKILL.md.../../measurement/generating-baseline-profiles/SKILL.md.stabilityDump once, then stabilityCheck on every PR). Cross-link ../../stability/enforcing-stability-in-ci/SKILL.md.app/src/main/generated/baselineProfiles/baseline-prof.txt (or wherever the consumer module placed it).app/stability/*.stability baseline files generated by :stabilityDump.Claude MUST produce this file at the end of the audit. Save it under docs/perf-audit-<date>-<module>.md (or wherever the project stores reports).
# Compose Performance Audit — <date> — <module>
## Environment
- Compose UI: <version>
- Compose Compiler: <version>
- Kotlin: <version>
- AGP: <version>
- Device: <model> / <API>
## Baseline (Phase 1)
- Cold startup median: <ms>
- Scroll FrameTimingMetric (P50/P90/P99): <ms> / <ms> / <ms>
- Baseline Profile present: yes/no
## Diagnosis (Phase 2)
- Restartable-not-skippable composables: <count>
- Unstable classes: <count>
- Top 5 recomposition hotspots: <list>
- Phase-misplaced reads: <count>
## Fixes applied (Phase 3)
| Skill | Change | Files | Macrobench delta |
| ----- | ------ | ----- | ---------------- |
| stability/stabilizing-compose-types | wrap List<Snack> with ImmutableList | feed/SnackList.kt | scroll P90 18ms → 12ms |
| recomposition/deferring-state-reads | offset(x.dp) → offset { } | hero/Hero.kt | scroll P99 33ms → 19ms |
| ... | ... | ... | ... |
## Verification (Phase 4)
- Cold startup median: <ms> (Δ <ms>)
- Scroll FrameTimingMetric (P50/P90/P99): <ms> / <ms> / <ms> (Δ ...)
- Baseline Profile regenerated: yes
- CI stability gate active: yes / no
## Open items / follow-ups
- <list>
// WRONG
// "Scroll feels rough — let me wrap this List in ImmutableList and add @Immutable everywhere."
// WRONG because: no baseline number exists, so any later claim of improvement is unfalsifiable.
// RIGHT
// 1. Run MacrobenchmarkRule on the suspect surface. Record P50/P90/P99 in the report.
// 2. Read the Compose Compiler report for the same surface. Identify the named cause.
// 3. Apply ONE targeted fix from a sibling skill.
// 4. Re-run the same Macrobenchmark. Compute the delta. Record it.
WRONG: a single PR titled "perf improvements" that
- converts 4 data classes to ImmutableList parameters
- migrates 2 custom modifiers to Modifier.Node
- moves 3 graphicsLayer reads down a phase
- adds a Baseline Profile module
WRONG because: if the Macrobench delta is mixed (startup faster, scroll slower), the
audit cannot attribute the regression to a specific change. Bisect impossible.
RIGHT: four PRs, each scoped to a single skill, each with its own Before/After
Macrobench number in the description. The audit report links to each PR in the
Phase 3 table.
WRONG: "We got composables.txt skip rate from 71% to 100%. Audit complete."
WRONG because: skippability is a means. The end is FrameTimingMetric and
StartupTimingMetric improvement on a real device. A 100% skippable app can still
drop frames if the work happens in Layout or Draw.
RIGHT: "Scroll P90 dropped from 18ms to 11ms. Cold startup median dropped from
820ms to 610ms. Skip rate improved as a side effect; we did not target it directly."
| Capability | Min version |
| --- | --- |
| Strong Skipping default ON | Kotlin 2.0.20+ |
| LazyLayoutCacheWindow | Compose Foundation 1.9+ |
| Pausable composition in lazy prefetch (default) | Compose Foundation 1.10+ |
| stabilityConfigurationFile DSL | Compose Compiler 1.5.5+ |
| Baseline Profile Generator template | AGP 8.2+ |
| R8 full mode default | AGP 8.0+ |
| Modifier.animateItem() GA | Compose UI 1.7+ |
| rememberGraphicsLayer() | Compose UI 1.7+ |
FrameTimingMetric and StartupTimingMetric improvement, not a metric on the compiler report.app/stability/*.stability files committed; CI stabilityCheck gate is active.docs/perf-audit-<date>-<module>.md) and circulated.Phase 1 — Measure:
../../measurement/testing-compose-in-release-mode/SKILL.md — debug builds lie; measure release + R8.../../measurement/generating-baseline-profiles/SKILL.md — Baseline Profile Generator + Macrobenchmark end-to-end.../../build/configuring-r8-for-compose/SKILL.md — full mode, optimize ProGuard file, trust consumer rules.Phase 2 — Diagnose:
../../stability/diagnosing-compose-stability/SKILL.md — enable and read the Compose Compiler reports.../../stability/understanding-stability-inference/SKILL.md — 12-phase algorithm, $stable field, generic bitmasks.../../stability/using-stability-analyzer-ide-plugin/SKILL.md — IDE plugin for inline stability inspection.../../stability/visualizing-recomposition-cascades/SKILL.md — visualize how unstable parameters propagate through the composable tree.../../recomposition/debugging-recompositions/SKILL.md — Layout Inspector counts and Argument Change Reasons.../../measurement/tracing-recompositions-at-runtime/SKILL.md — @TraceRecomposition for release-grade tracing.Phase 3 — Fix:
../../stability/stabilizing-compose-types/SKILL.md — three-tier waterfall: rewrite, annotate, configure.../../recomposition/using-strong-skipping-correctly/SKILL.md — verify mode, audit lambda capture sites, escape hatches.../../recomposition/deferring-state-reads/SKILL.md — push reads down to Layout/Draw via lambda modifiers.../../recomposition/choosing-derivedstateof/SKILL.md — only when input frequency exceeds output frequency.../../recomposition/avoiding-subcomposition-pitfalls/SKILL.md — recognize when SubcomposeLayout / BoxWithConstraints cost outweighs benefit.../../lists/optimizing-lazy-layouts/SKILL.md — key, contentType, Modifier.animateItem(), hoisting.../../lists/configuring-lazy-prefetch/SKILL.md — LazyLayoutCacheWindow, pausable prefetch, NestedPrefetchScope.../../modifiers/migrating-to-modifier-node/SKILL.md — Modifier.Node + ModifierNodeElement over composed { }.../../modifiers/ordering-modifier-chains/SKILL.md — wrap-the-next-modifier mental model.../../side-effects/collecting-flows-safely/SKILL.md — collectAsStateWithLifecycle, hoist Flow<T> parameters.../../side-effects/using-efficient-effects/SKILL.md — pick the cheapest correct effect API.Phase 4 — Verify:
../../stability/enforcing-stability-in-ci/SKILL.md — stabilityDump baseline + stabilityCheck CI gate.../../measurement/generating-baseline-profiles/SKILL.md — regenerate after fixes; commit baseline-prof.txt.Hot-reload (developer-loop velocity, optional but recommended):
../../hot-reload/setting-up-compose-hotswan/SKILL.md — install and configure HotSwan for sub-second iteration.../../hot-reload/preserving-state-across-reloads/SKILL.md — keep remember/rememberSaveable state through a reload.../../hot-reload/understanding-hot-reload-limits/SKILL.md — what reloads cleanly vs what forces a full rebuild.../../hot-reload/iterating-with-ai-and-mcp/SKILL.md — pair HotSwan with an MCP-aware AI tool for tight loops.Symptom and API lookup: ../../INDEX.md.
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
Automatically creates user-facing changelogs from git commits by analyzing commit history, categorizing changes, and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling
Use when starting feature work that needs isolation from current workspace or before executing implementation plans - creates isolated git worktrees with smart directory selection and safety verification
Take skydoves/auditing-compose-performance from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.