Validating Compose Stability logo

Validating Compose Stability

Community
skydoves
validating-compose-stability

Use this skill to gate CI on Jetpack Compose stability — catch when a composable becomes unskippable/unrestartable or a parameter goes stable → unstable, before it ships and tanks recomposition. Covers the Compose Stability Analyzer Gradle plugin's `stabilityDump` (write a `.stability` baseline) and `stabilityCheck` (compare current compilation against it, fail on regression) tasks, the `composeStabilityAnalyzer { stabilityValidation { … } }` DSL (`failOnStabilityChange`, `ignoreNonRegressiveChanges`, `ignored*` lists, `allowMissingBaseline`, `stabilityConfigurationFiles`), the `@IgnoreStabilityReport` annotation, committing `app/stability/app.stability` as a team baseline, the deliberate baseline-update workflow, and GitHub Actions wiring (`needs: build`, since the analysis reads compiled output). Use when the user mentions "stabilityCheck", "stabilityDump", "compose stability analyzer", "stability baseline", "@IgnoreStabilityReport", "composeStabilityAnalyzer", or "fail the build on stability changes".

Overview

Publisherskydoves
Repositoryandroid-testing-skills
Skill namevalidating-compose-stability
Stars
338
Forks
14
Bundled files
Instructions only
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by skydoves on GitHub. Read the source before you install it.

Installation

Install the Validating Compose Stability AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/skydoves/android-testing-skills/tree/main/compose/stability/validating-compose-stability
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/skydoves/android-testing-skills.git /tmp/android-testing-skills
mkdir -p .claude/skills
cp -r /tmp/android-testing-skills/compose/stability/validating-compose-stability .claude/skills/validating-compose-stability
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Validating Compose Stability in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Validating Compose Stability on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Validating Compose Stability is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Validating Compose Stability — A CI Gate On Skippability And Parameter Stability

Compose stability — whether a composable is skippable/restartable and whether its parameters are stable — is invisible until a recomposition profiler catches it in production. This skill makes it a build gate: snapshot the current stability of every composable into a committed .stability baseline, and fail CI when a change makes anything less stable. The tool is the Compose Stability Analyzer Gradle plugin. (Authoring composables for stability — @Immutable/@Stable, the compiler config file, deferred reads — is covered in the compose-performance-skills repo; this skill is purely the validation/CI layer.)

When to use this skill

  • The user wants ./gradlew stabilityCheck to fail CI when a composable becomes unskippable or a parameter flips stable → unstable.
  • The user wants a committed, reviewable baseline of every composable's stability (.stability files) so changes show up in PR diffs.
  • A recomposition regression shipped because nothing flagged a List parameter or a non-@Immutable data class added to a hot composable.
  • The user mentions stabilityDump, stabilityCheck, composeStabilityAnalyzer, @IgnoreStabilityReport, or "stability baseline".
  • The user wants warning-only locally and hard-fail in CI.

When NOT to use this skill

  • The user wants to fix an unstable parameter (@Immutable/@Stable, kotlinx.collections.immutable, stability_config.conf for external types, deferred state reads, derivedStateOf) — that is Compose performance authoring; see the compose-performance-skills repo's stability skills. This skill only validates and gates.
  • The user wants to find which recompositions are firing at runtime (Layout Inspector recomposition counts, Recomposer metrics, composition tracing) — different tool, runtime not build time.
  • The user is doing UI behavioral or screenshot testing — see ../../setup/choosing-test-rule-vs-runtest/SKILL.md and ../../preview/capturing-preview-screenshots-in-ci/SKILL.md.

Prerequisites

  • The Compose Stability Analyzer Gradle plugin applied to each Compose module (apply via the plugins block; coordinates and version from the project's catalog / the plugin docs).
  • Compose compiler metrics available — the plugin analyzes compiled output, so the module's Kotlin compilation must run before stabilityCheck/stabilityDump. In CI this means a needs: build (or running :module:compileDebugKotlin first) — running the check before compilation produces wrong results or fails.
  • A baseline committed to version control: <module>/stability/<module>.stability (generated by stabilityDump).

Workflow

  • 1. Configure the validation block. In each Compose module's build.gradle.kts:
kotlin
composeStabilityAnalyzer {
    stabilityValidation {
        enabled.set(true)
        outputDir.set(layout.projectDirectory.dir("stability"))   // where the .stability baseline lives
        includeTests.set(false)
        ignoredPackages.set(listOf("com.example.internal"))
        ignoredClasses.set(listOf("PreviewComposables"))
        ignoredProjects.set(listOf("benchmarks", "examples"))
        failOnStabilityChange.set(true)            // build fails on stability changes; false => warning-only
        ignoreNonRegressiveChanges.set(false)      // true => only regressions count; new-stable / improvements ignored
        allowMissingBaseline.set(false)            // true => no baseline yet is not a failure (bootstrap only)
        stabilityConfigurationFiles.add(           // same format as the Compose compiler's stability config
            rootProject.layout.projectDirectory.file("stability_config.conf")
        )
    }
}

Key knobs: failOnStabilityChange (the gate), ignoreNonRegressiveChanges (report only regressions, ignore improvements and newly-added stable composables), stabilityConfigurationFiles (the Compose-compiler-format file declaring external types stable), and the ignored* lists for packages/classes/modules outside the contract.

  • 2. Generate the baseline once and commit it. With the module compiled:
bash
./gradlew :app:compileDebugKotlin
./gradlew :app:stabilityDump          # writes app/stability/app.stability
git add app/stability/app.stability
git commit -m "Add Compose stability baseline"

The .stability file is human-readable: per composable it lists the fully-qualified signature, skippable/restartable status, and each parameter's stability classification with the reason. Android projects get variant-specific tasks (debugStabilityDump, releaseStabilityCheck, …); multi-module projects get one .stability file per module — ./gradlew stabilityDump runs them all, or target a module.

  • 3. Run the check in CI, after compilation. stabilityCheck compares the current compilation against the committed baseline and reports three change kinds: ~ stability regression (a composable/parameter became less stable), + a composable added, - a composable removed. On a regression the build fails with a message naming the affected composables and how their stability changed, and tells you to run stabilityDump if the change was intentional.
yaml
# .github/workflows/ci.yml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { java-version: '21', distribution: 'zulu' }
      - run: ./gradlew :app:compileDebugKotlin

  stability_check:
    name: Compose Stability Check
    runs-on: ubuntu-latest
    needs: build                       # MANDATORY — the analysis reads compiled output
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { java-version: '21', distribution: 'zulu' }
      - run: ./gradlew stabilityCheck
  • 4. Accept an intentional stability change deliberately. When a regression is real and accepted (a justified List parameter, an external type that genuinely can't be stable), update the baseline as a documented commit — never silently:
bash
./gradlew :app:compileDebugKotlin
./gradlew :app:stabilityDump
git add app/stability/app.stability
git commit -m "Update stability baseline: PokemonList now takes List<Pokemon> — justified by [reason]"

The diff in the .stability file is the review artifact; the commit message is the audit trail.

  • 5. Exclude things that are not part of the contract. Annotate composables that should never be tracked — @Preview functions, internal scaffolding — with @IgnoreStabilityReport; they drop out of both the dump and the check. For whole packages/classes/modules, use ignoredPackages/ignoredClasses/ignoredProjects.
kotlin
@IgnoreStabilityReport
@Preview
@Composable
fun UserCardPreview() { UserCard(user = User("John", 30)) }
  • 6. (Optional) strict in CI, warning-only locally. Gate hard on the CI server, stay non-blocking on a developer machine:
kotlin
composeStabilityAnalyzer {
    stabilityValidation { failOnStabilityChange.set(System.getenv("CI") == "true") }
}

Most CI platforms set CI=true.

Patterns

Pattern: running stabilityCheck before compilation

yaml
# WRONG
stability_check:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - run: ./gradlew stabilityCheck            # no compiled output yet
# WRONG because: the analyzer reads the Compose compiler's output. With nothing compiled it
# either fails or reports incorrect results. The check must depend on a build step.
yaml
# RIGHT
stability_check:
  runs-on: ubuntu-latest
  needs: build                                  # or: - run: ./gradlew :app:compileDebugKotlin stabilityCheck
  steps:
    - uses: actions/checkout@v4
    - run: ./gradlew stabilityCheck

Pattern: regenerating the baseline to "make CI green"

bash
# WRONG — CI failed on a stability regression, so:
./gradlew stabilityDump && git commit -am "fix stability check"
# WRONG because: stabilityDump rewrites the baseline to whatever the code currently produces, so
# the check trivially passes — the regression ships, undocumented. The dump is for *accepting* a
# change you understand, with a commit message that says why; it is not a way to silence the gate.
bash
# RIGHT — investigate the ~ entries first; only then, if the change is justified:
./gradlew :app:compileDebugKotlin :app:stabilityDump
git add app/stability/app.stability
git commit -m "Update stability baseline: <composable> <what changed> — justified by <reason>"

Pattern: @Preview functions polluting the baseline

kotlin
// WRONG — preview functions in the .stability baseline, churning it on every preview edit
@Preview @Composable fun ProfilePreview() { Profile(sampleUser) }
// WRONG because: previews are tooling entry points, not API. Tracking them means the baseline
// diffs every time someone tweaks a preview, drowning real regressions.
kotlin
// RIGHT — annotate them out (or ignoredClasses = listOf("...Preview...") for a naming convention)
@IgnoreStabilityReport @Preview @Composable fun ProfilePreview() { Profile(sampleUser) }

Mandatory rules

  • MUST commit the generated .stability baseline files to version control — they are the shared contract; an uncommitted baseline gates nothing.
  • MUST run stabilityCheck only after Kotlin compilation (CI: needs: a build job, or compile in the same Gradle invocation). The analyzer consumes compiled output.
  • MUST treat a stabilityDump baseline update as a deliberate, reviewed commit with a justification in the message — never as a way to clear a failing check.
  • MUST exclude @Preview and other non-API composables from the contract via @IgnoreStabilityReport (or ignoredClasses/ignoredPackages) so the baseline diffs only on real changes.
  • MUST NOT set failOnStabilityChange = false (or allowMissingBaseline = true) permanently — those are for bootstrapping; once a baseline exists, the gate must fail on regressions.
  • MUST NOT use this skill as the place to fix instability — that is Compose authoring (@Immutable/@Stable, immutable collections, the compiler stability config); see the compose-performance-skills repo.
  • PREFERRED: ignoreNonRegressiveChanges = true if the team only cares about regressions and finds +/new-stable noise distracting.
  • PREFERRED: strict in CI, warning-only locally via failOnStabilityChange.set(System.getenv("CI") == "true").

Verification

  • ./gradlew stabilityDump produces <module>/stability/<module>.stability and it is committed (git status clean after a no-op dump).
  • ./gradlew stabilityCheck passes on the committed baseline and fails when a composable is deliberately made unstable (e.g. add a List<T> parameter to a tracked composable) with a message naming it and a ~ entry.
  • The CI workflow's stability job has needs: build (or compiles before stabilityCheck).
  • @Preview / scaffolding composables carry @IgnoreStabilityReport (or match an ignoredClasses pattern) and do not appear in the .stability file.
  • failOnStabilityChange is true (or CI-gated to true) and allowMissingBaseline is false once the baseline exists.

References

  • skydoves.github.io/compose-stability-analyzer/gradle-plugin/stability-validation/ — stabilityDump / stabilityCheck tasks, the composeStabilityAnalyzer { stabilityValidation { … } } DSL and every option, .stability baseline format, ~/+/- change types, @IgnoreStabilityReport, multi-module behavior.
  • skydoves.github.io/compose-stability-analyzer/gradle-plugin/ci-cd/ — GitHub Actions wiring, the mandatory needs: build dependency, build-failure behavior, the baseline-update workflow, failOnStabilityChange.set(System.getenv("CI") == "true").
  • github.com/skydoves/compose-stability-analyzer — the plugin source, coordinates, and full configuration reference.
  • developer.android.com/develop/ui/compose/performance/stability — Compose stability concepts (skippable/restartable, stable parameters) the baseline is built on.
  • developer.android.com/develop/ui/compose/performance/stability/fix — fixing instability (@Immutable/@Stable, immutable collections, the compiler stability_configuration_path file) — the authoring side this gate protects.
  • Sibling skill: ../../preview/capturing-preview-screenshots-in-ci/SKILL.md — another Compose CI gate (device-rendered preview catalog); same needs: build-style ordering concerns.

Frequently asked questions

What does the Validating Compose Stability AI skill do?

Use this skill to gate CI on Jetpack Compose stability — catch when a composable becomes unskippable/unrestartable or a parameter goes stable → unstable, before it ships and tanks recomposition. Covers the Compose Stability Analyzer Gradle plugin's `stabilityDump` (write a `.stability` baseline) and `stabilityCheck` (compare current compilation against it, fail on regression) tasks, the `composeStabilityAnalyzer { stabilityValidation { … } }` DSL (`failOnStabilityChange`, `ignoreNonRegressiveChanges`, `ignored*` lists, `allowMissingBaseline`, `stabilityConfigurationFiles`), the `@IgnoreStab...

Why use Validating Compose Stability on TypingMind?

Because you install it once and use it with any model. Validating Compose Stability is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Validating Compose Stability in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/skydoves/android-testing-skills/tree/main/compose/stability/validating-compose-stability. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Validating Compose Stability?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Validating Compose Stability?

As many as you like. As long as a model supports skills, you can use Validating Compose Stability with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Validating Compose Stability AI skill free?

Yes. It is published on GitHub by skydoves under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇