account-state
Account state and in-memory event store patterns in Amethyst. Use when working with `Account.kt` (per-user state objects — `kind3FollowList`, `nip65RelayList`,…
Use when writing or reviewing Jetpack Compose parameter stability, compiler reports, skippability, unstable UI state classes, collection parameters, or Kotlin 2.0+ strong skipping behavior. Technique-layer skill — complements the codebase-specific compose-expert and
$ npx -y skills add vitorpamplona/amethyst --skill compose-stability-diagnostics --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/compose-stability-diagnosticsContext preview
The summary Claude sees to decide when to auto-load this skill.
Use when writing or reviewing Jetpack Compose parameter stability, compiler reports, skippability, unstable UI state classes, collection parameters, or Kotlin 2.0+ strong skipping behavior. Technique-layer skill — complements the codebase-specific compose-expert and
name: compose-stability-diagnostics description: Use when writing or reviewing Jetpack Compose parameter stability, compiler reports, skippability, unstable UI state classes, collection parameters, or Kotlin 2.0+ strong skipping behavior. Technique-layer skill — complements the codebase-specific compose-expert and kotlin-expert.
Compose performance problems from parameters are about **whether inputs compare cheaply and predictably across recompositions**. With Kotlin 2.0.20+ strong skipping is enabled by default, so unstable parameters no longer automatically make restartable composables non-skippable. That does not make stability irrelevant: unstable parameters are compared by instance identity (`===`), stable parameters by equality (`equals`), and churny instances can still defeat skipping.
First identify the compiler mode you are on, then read reports in that context.
On Kotlin 2.0.20+, strong skipping is enabled by default. In that mode:
That means the question changes from "is this composable skippable at all?" to "will these parameters compare the way I expect, and are callers creating new unstable instances every frame?"
For older compiler setups or strong skipping disabled, the legacy rule still matters: a restartable composable with unstable parameters may be restartable but not skippable.
With Kotlin 2.0+ the Compose Compiler is configured through the Kotlin Gradle plugin:
plugins {
alias(libs.plugins.android.application) // or android.library / jvm
alias(libs.plugins.kotlin.android) // or kotlin.multiplatform / kotlin.jvm
alias(libs.plugins.compose.compiler)
}
if (providers.gradleProperty("composeReports").orNull == "true") {
composeCompiler {
reportsDestination = layout.buildDirectory.dir("compose_compiler")
metricsDestination = layout.buildDirectory.dir("compose_compiler")
}
}Then build the variant whose compiler configuration you care about, for example:
./gradlew :app:assembleRelease -PcomposeReports=true
Use release/non-debuggable builds for runtime profiling. Compiler reports are build-time outputs, so the important thing is matching the variant and compiler flags you ship.
Key files:
| File | What it tells you | |---|---| | `<module>-classes.txt` | Stability of classes and properties | | `<module>-composables.txt` | Restartable/skippable status and parameter stability | | `<module>-composables.csv` | Same data in sortable form | | `<module>-module.json` | Aggregate metrics |
Pick the lightest fix that makes the type's immutability or equality semantics true.
`kotlin.collections.List` is an interface; Compose cannot know the runtime implementation is immutable. Prefer `kotlinx.collections.immutable` at UI-state boundaries:
// Before: unstable collection interfaces data class UiState(val items: List<Item>, val tags: Set<String>) // After: immutable collection contracts import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableSet data class UiState(val items: ImmutableList<Item>, val tags: ImmutableSet<String>)
Producers convert once at the boundary with `.toImmutableList()` / `.toImmutableSet()`.
Do not annotate to silence a report. A false stability promise can produce stale UI.
For types you cannot annotate, use `stabilityConfigurationFiles`:
composeCompiler {
stabilityConfigurationFiles.add(
rootProject.layout.projectDirectory.file("compose_stability.conf"),
)
}java.math.BigDecimal java.math.BigInteger java.time.* kotlinx.datetime.*
Only list types you are willing to promise are immutable. Do not list mutable types such as `java.util.Date`.
| Symptom | Diagnosis | Fix | |---|---|---| | Kotlin 2.0.20+ but old docs say unstable means non-skippable | Strong skipping changed the default | Check comparison semantics and instance churn instead | | `unstable val items: List<Item>` | Interface collection | Use `ImmutableList<Item>` or another true immutable wrapper | | `unstable val price: BigDecimal` | External immutable type | Add to stability config | | `@Immutable` on a type with mutable internals | False promise | Fix the model or remove the annotation | | Composable skips poorly despite strong skipping | New unstable instance each recomposition | Remember, hoist, or make the type stable/equality-based | | Reports not generated | Compose compiler plugin missing or flag not set | Apply `org.jetbrains.kotlin.plugin.compose` and enable destinations |
Account state and in-memory event store patterns in Amethyst. Use when working with `Account.kt` (per-user state objects — `kind3FollowList`, `nip65RelayList`,…
Patterns for extending `amy`, the Amethyst CLI in `cli/`. Use when adding an `amy <verb>` command, touching files under `cli/src/main/kotlin/…/cli/`, wiring a…
Android platform patterns for the `amethyst/` module. Use when working with (1) Android navigation (Navigation Compose, type-safe routes, bottom nav), (2)…
Signer abstraction patterns in Amethyst. Use when working with event signing, choosing between a local keypair (`NostrSignerInternal`), a remote NIP-46 bunker…
Advanced Compose Multiplatform UI patterns for shared composables. Use when working with visual UI components, state management patterns (remember,…
Use when writing or reviewing Jetpack Compose layout APIs, modifier parameters, modifier chain construction, hardcoded root layout decisions, or layout…