account-state
Account state and in-memory event store patterns in Amethyst. Use when working with `Account.kt` (per-user state objects — `kind3FollowList`, `nip65RelayList`,…
Advanced Kotlin patterns for AmethystMultiplatform. Flow state management (StateFlow/SharedFlow), sealed hierarchies (classes vs interfaces), immutability (@Immutable, data classes), DSL builders (type-safe fluent APIs), inline functions (reified generics, performance). Use when
$ npx -y skills add vitorpamplona/amethyst --skill kotlin-expert --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/kotlin-expertContext preview
The summary Claude sees to decide when to auto-load this skill.
Advanced Kotlin patterns for AmethystMultiplatform. Flow state management (StateFlow/SharedFlow), sealed hierarchies (classes vs interfaces), immutability (@Immutable, data classes), DSL builders (type-safe fluent APIs), inline functions (reified generics, performance). Use when
name: kotlin-expert description: Advanced Kotlin patterns for AmethystMultiplatform. Flow state management (StateFlow/SharedFlow), sealed hierarchies (classes vs interfaces), immutability (@Immutable, data classes), DSL builders (type-safe fluent APIs), inline functions (reified generics, performance). Use when working with: (1) State management patterns (StateFlow/SharedFlow/MutableStateFlow), (2) Sealed classes or sealed interfaces, (3) @Immutable annotations for Compose, (4) DSL builders with lambda receivers, (5) inline/reified functions, (6) Kotlin performance optimization. Complements kotlin-coroutines agent (async patterns) - this skill focuses on Amethyst-specific Kotlin idioms.
Advanced Kotlin patterns for AmethystMultiplatform. Covers Flow state management, sealed hierarchies, immutability, DSL builders, and inline functions with real codebase examples.
**Kotlin in Amethyst:**
State Management (Hot Flows)
├── StateFlow<T> # Single value, always has value, replays to new subscribers
├── SharedFlow<T> # Event stream, configurable replay, multiple subscribers
└── MutableStateFlow<T> # Private mutable, public via .asStateFlow()
Type Safety (Sealed Hierarchies)
├── sealed class # State variants with data (AccountState.LoggedIn/LoggedOut)
└── sealed interface # Generic result types (SignerResult<T>)
Compose Performance (@Immutable)
├── @Immutable # 173+ event classes - prevents recomposition
└── data class # Structural equality, copy(), immutable by convention
DSL Patterns
├── Builder classes # Fluent APIs (TagArrayBuilder)
├── Lambda receivers # inline fun tagArray { ... }
└── Method chaining # return this
Performance
├── inline fun # Eliminate lambda overhead
├── reified type params # Runtime type info (OptimizedJsonMapper)
└── value class # Zero-cost wrappers (NOT USED yet in Amethyst)**Delegation:**
---
**Mental model:** StateFlow is a "hot" observable state holder. Always has a value, new collectors immediately get current state.
**Amethyst pattern:**
// AccountManager.kt:48-50
class AccountManager {
private val _accountState = MutableStateFlow<AccountState>(AccountState.LoggedOut)
val accountState: StateFlow<AccountState> = _accountState.asStateFlow()
fun login(key: String) {
_accountState.value = AccountState.LoggedIn(...)
}
}**Key principles:** 1. **Private mutable, public immutable**: `_accountState` (MutableStateFlow) private, `accountState` (StateFlow) public 2. **Always has value**: Initial value required (`LoggedOut`) 3. **Single value**: Replays ONE most recent value to new subscribers 4. **Hot**: Stays in memory, all collectors share same instance
**See:** AccountManager.kt:48-50, RelayConnectionManager.kt:49-52
**Mental model:** SharedFlow is a "hot" broadcast stream for events. Configurable replay buffer, doesn't require initial value.
**Amethyst pattern:**
// RelayConnectionManager.kt:52-53 val connectedRelays: StateFlow<Set<NormalizedRelayUrl>> = client.connectedRelaysFlow() val availableRelays: StateFlow<Set<NormalizedRelayUrl>> = client.availableRelaysFlow()
**When to use StateFlow vs SharedFlow:**
| Scenario | Use StateFlow | Use SharedFlow | |----------|---------------|----------------| | **UI state** | ✅ Current screen data, login status | ❌ | | **One-time events** | ❌ | ✅ Navigation, snackbars, toasts | | **Always has value** | ✅ | ❌ Optional | | **Replay count** | 1 (latest only) | Configurable (0, 1, n) | | **Backpressure** | Conflates (drops old) | Configurable buffer |
**Best practice:**
// State: Use StateFlow private val _uiState = MutableStateFlow(UiState.Loading) val uiState: StateFlow<UiState> = _uiState.asStateFlow() // Events: Use SharedFlow private val _navigationEvents = MutableSharedFlow<NavEvent>(replay = 0) val navigationEvents: SharedFlow<NavEvent> = _navigationEvents.asSharedFlow()
❌ **Exposing mutable state:**
val accountState: MutableStateFlow<AccountState> // BAD: Can be mutated externally
✅ **Expose immutable:**
val accountState: StateFlow<AccountState> = _accountState.asStateFlow() // GOOD
---
❌ **SharedFlow for state:**
val loginState = MutableSharedFlow<LoginState>() // BAD: State might get lost
✅ **StateFlow for state:**
val loginState = MutableStateFlow(LoginState.LoggedOut) // GOOD: Always has value
**See:** `references/flow-patterns.md` for comprehensive examples.
---
**Mental model:** Sealed classes represent a closed set of variants that share common data/behavior.
**Amethyst pattern:**
// AccountManager.kt:36-46
sealed class AccountState {
data object LoggedOut : AccountState()
data class LoggedIn(
val signer: NostrSigner,
val pubKeyHex: String,
val npub: String,
val nsec: String?,
val isReadOnly: Boolean
) : AccountState()
}
// Usage
when (state) {
is AccountState.LoggedOut -> showLogin()
is AccountState.LoggedIn -> showFeed(state.pubKeyHex)
} // Exhaustive - compiler enforces all cases**Key principles:** 1. **Closed hierarchy**: All subclasses known at compile-time 2. **Exhaustive when**: Compiler ensures all cases handled 3. **Shared data**: Sealed class can hold common properties 4. **Single inheritance**: Subclass can't extend another class
**When to use:**
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…