$npx -y skills add skydoves/compose-performance-skills --skill enforcing-stability-in-ciUse this skill to set up a CI gate that fails the build when Compose stability silently regresses, using the skydoves/compose-stability-analyzer Gradle plugin (primary) or the j-roskopf/ComposeGuard plugin (alternative for non-Android multiplatform). Covers applying `com.gith
| 1 | # Enforcing Stability in CI — make stability regressions fail the build |
| 2 | |
| 3 | Stability is a property that silently regresses. A new `var` in a shared data class can disable skipping across dozens of screens, and nothing in the build output complains. Treat stability like binary compatibility: commit a baseline, diff it on every PR, fail the build when the diff goes the wrong way. This skill sets that gate up using the `skydoves/compose-stability-analyzer` Gradle plugin and points to `j-roskopf/ComposeGuard` for non-Android multiplatform projects. |
| 4 | |
| 5 | ## When to use this skill |
| 6 | |
| 7 | - The team wants a stability service-level objective enforced in CI, not a manual check. |
| 8 | - A PR introduced an unstable parameter and nobody noticed until field reports of dropped frames came in. |
| 9 | - Migrating a large app to strong skipping and need a regression net while refactoring. |
| 10 | - Standardizing stability practice across a monorepo with many feature modules. |
| 11 | - The user mentions `stabilityCheck`, `stabilityDump`, baseline drift, ComposeGuard, or "fail the build on stability regression". |
| 12 | |
| 13 | ## When NOT to use this skill |
| 14 | |
| 15 | - A single-developer throwaway prototype. The baseline maintenance cost is not worth it. |
| 16 | - Stability has not been diagnosed at all yet. Run `../diagnosing-compose-stability/SKILL.md` first to make sure the current state is acceptable; baselining a broken state just locks the brokenness in. |
| 17 | - The conceptual question is "why did the compiler classify X as Y?". Use `../understanding-stability-inference/SKILL.md`. |
| 18 | |
| 19 | ## Prerequisites |
| 20 | |
| 21 | - Kotlin 2.0+ with the Compose compiler Gradle plugin (`org.jetbrains.kotlin.plugin.compose`). Strong skipping is default on. |
| 22 | - Gradle 8+. |
| 23 | - A working CI pipeline that already runs `./gradlew assemble` or equivalent. |
| 24 | - Compose Compiler reports already understood (read `../diagnosing-compose-stability/SKILL.md` first). |
| 25 | - Ability to commit generated files to the repository (the `.stability` baseline must be checked in). |
| 26 | |
| 27 | ## Workflow |
| 28 | |
| 29 | ### 1. Apply the plugin |
| 30 | |
| 31 | Add the plugin to the version catalog and apply it to every module that ships UI composables. |
| 32 | |
| 33 | ```toml |
| 34 | # gradle/libs.versions.toml |
| 35 | [versions] |
| 36 | compose-stability-analyzer = "0.7.3" |
| 37 | |
| 38 | [plugins] |
| 39 | compose-stability-analyzer = { id = "com.github.skydoves.compose.stability.analyzer", version.ref = "compose-stability-analyzer" } |
| 40 | ``` |
| 41 | |
| 42 | ```kotlin |
| 43 | // app/build.gradle.kts (and every UI feature module) |
| 44 | plugins { |
| 45 | alias(libs.plugins.android.application) |
| 46 | alias(libs.plugins.kotlin.android) |
| 47 | alias(libs.plugins.kotlin.compose) |
| 48 | alias(libs.plugins.compose.stability.analyzer) |
| 49 | } |
| 50 | ``` |
| 51 | |
| 52 | The plugin must be applied **after** the Compose compiler plugin so it can read the same compiler output. |
| 53 | |
| 54 | ### 2. Configure the DSL block |
| 55 | |
| 56 | ```kotlin |
| 57 | // app/build.gradle.kts |
| 58 | composeStabilityAnalyzer { |
| 59 | enabled.set(true) |
| 60 | |
| 61 | stabilityValidation { |
| 62 | enabled.set(true) |
| 63 | outputDir.set(layout.projectDirectory.dir("stability")) |
| 64 | failOnStabilityChange.set(true) |
| 65 | ignoredPackages.set( |
| 66 | listOf( |
| 67 | "com.example.preview", // @Preview-only composables |
| 68 | "com.example.benchmark", // microbenchmark fixtures |
| 69 | ) |
| 70 | ) |
| 71 | ignoredClasses.set( |
| 72 | listOf( |
| 73 | "com.example.testing.FakeRepository", |
| 74 | ) |
| 75 | ) |
| 76 | stabilityConfigurationFiles.add( |
| 77 | rootProject.layout.projectDirectory.file("stability_config.conf") |
| 78 | ) |
| 79 | } |
| 80 | } |
| 81 | ``` |
| 82 | |
| 83 | Key settings: |
| 84 | |
| 85 | - `outputDir` — where `.stability` baseline files are written. Pick a path **inside** the module so it is naturally co-located with the source. Do not write to `build/`; that directory is gitignored and the baseline must be committed. |
| 86 | - `failOnStabilityChange` — set to `true` for CI. Locally, developers may |