$npx -y skills add skydoves/compose-performance-skills --skill understanding-stability-inferenceUse this skill to explain why the Compose compiler classified a class or composable parameter as stable, runtime, unknown, or unstable. Covers the 12-phase inference algorithm, the five compiler-level stability types (Certain / Runtime / Unknown / Parameter / Combined), the gener
| 1 | # Understanding Stability Inference — read the compiler's mind |
| 2 | |
| 3 | Stability is decided by a 12-phase algorithm baked into the Compose compiler. This skill teaches Claude how the algorithm walks a type so it can explain *why* a report says what it says, and predict classifications before the report is even generated. Pair this with `../diagnosing-compose-stability/SKILL.md` (which generates the report) and `../stabilizing-compose-types/SKILL.md` (which fixes obvious unstable types). Reach for this skill when the simpler skills produce a verdict that surprises the developer. |
| 4 | |
| 5 | ## When to use this skill |
| 6 | |
| 7 | - The developer asks "why is `Foo` classified as `runtime stable` and not `stable`?" |
| 8 | - A report shows `runtime` or `unknown` for a class that "looks fine". |
| 9 | - Generics involved: `Box<T>`, `Wrapper<A, B>`, `Pair<String, Int>`, `ImmutableList<User>`. |
| 10 | - The class lives in another module and ships as a `.class`/`.kotlin_metadata` artifact. |
| 11 | - The developer asks about the `$stable: Int` field, `@StabilityInferred`, or cross-module classification. |
| 12 | - A self-referential type (`class Node(val children: List<Node>)`) is unstable for non-obvious reasons. |
| 13 | - A Java type, an interface, or an abstract base appears in a parameter list and surprises the developer. |
| 14 | |
| 15 | ## When NOT to use this skill |
| 16 | |
| 17 | - The fix is mechanical (`var` → `val`, `List` → `ImmutableList`, `Flow` parameter removal). Use `../stabilizing-compose-types/SKILL.md`. |
| 18 | - No report exists yet. Run `../diagnosing-compose-stability/SKILL.md` first. |
| 19 | - The developer wants CI enforcement of stability. Use `../enforcing-stability-in-ci/SKILL.md`. |
| 20 | |
| 21 | ## Prerequisites |
| 22 | |
| 23 | - Compose Compiler reports already generated, or at least one `<module>-classes.txt` and `<module>-composables.txt` available in `build/compose_compiler/`. |
| 24 | - The developer understands the basic stability vocabulary: `stable`, `unstable`, `skippable`, `restartable`, `@Stable`, `@Immutable`. |
| 25 | - Kotlin 2.0+ with the Compose compiler plugin (Strong Skipping default ON). |
| 26 | |
| 27 | ## Workflow — diagnostic question and answer tree |
| 28 | |
| 29 | Walk the type through the same 12 phases the compiler does. For each call site, ask the questions in order; the first matching phase wins. |
| 30 | |
| 31 | The canonical phase order (used everywhere in this skill and matching `references/twelve-phase-algorithm.md`): |
| 32 | |
| 33 | 1. **Phase 1 — primitive / `String` / function / `Unit` fast path → `Stable`.** |
| 34 | The compiler returns immediately. No field analysis runs. Mention the fast path so the developer knows nothing else was inspected. |
| 35 | 2. **Phase 2 — type parameter substitution.** |
| 36 | A bare type variable `T` becomes `Stability.Parameter(T)`; resolution is deferred to the call site that substitutes it. |
| 37 | 3. **Phase 3 — nullable unwrap (`Int?` → analyze `Int`).** |
| 38 | Nullability does not change stability; the algorithm strips the `?` and recurses. |
| 39 | 4. **Phase 4 — inline class — check underlying type.** |
| 40 | `value class Wrapper(val raw: T)` is exactly as stable as `T`. |
| 41 | 5. **Phase 5 — cycle detection (recursive trees → conservative UNSTABLE).** |
| 42 | The algorithm bails on cycles to guarantee termination. The escape hatch is `@Stable`/`@Immutable` on the recursive class, which fires in phase 6 before phase 5 is reached. |
| 43 | 6. **Phase 6 — annotations check (`@Stable`, `@Immutable`, `@StableMarker`).** |
| 44 | Yes → `Stability.Certain` (stable). `@Immutable` enables additional optimizations beyond `@Stable` because the compiler can promote reads of properties to static expressions and elide equality checks; `@Stable` only promises change notification. |
| 45 | 7. **Phase 7 — Known Stable Constructs registry hit** (`Pair` / `Triple` / `Result` / `ImmutableList` / `dagger.Lazy` / `ClosedRange` / etc.). |
| 46 | Returns `Stability.Parameter` with the registry's bitmask. See `references/bitmask-encoding.md` for the full registr |