account-state
Account, LocalCache, User/Note and feeds in commons/model + commons/feeds: per-user XState objects (.flow),…
Coroutines/Flow in Amethyst: picking IO vs Default, scopes and cancellation (CancellationException, withTimeout), StateFlow/SharedFlow/Channel and stateIn choices, runTest. Use when writing or reviewing async Kotlin code.
Claude Code
$ npx -y skills add vitorpamplona/amethyst --skill kotlin-coroutines --agent claude-code
Codex
$ npx skills add vitorpamplona/amethyst --skill kotlin-coroutines -a codex -y
Cursor
$ npx skills add vitorpamplona/amethyst --skill kotlin-coroutines -a cursor -y
Gemini CLI
$ npx skills add vitorpamplona/amethyst --skill kotlin-coroutines -a gemini-cli -y
GitHub Copilot
$ npx skills add vitorpamplona/amethyst --skill kotlin-coroutines -a github-copilot -y
OpenCode
$ npx skills add vitorpamplona/amethyst --skill kotlin-coroutines -a opencode -y
Windsurf
$ npx skills add vitorpamplona/amethyst --skill kotlin-coroutines -a windsurf -y
name: kotlin-coroutines description: Coroutines/Flow in Amethyst: picking IO vs Default, scopes and cancellation (CancellationException, withTimeout), StateFlow/SharedFlow/Channel and stateIn choices, runTest. Use when writing or reviewing async Kotlin code.
Repo-specific rules only. Textbook coroutines/Flow knowledge is assumed.
**`Dispatchers.IO` is the default; `Default` only for provably lock-free CPU work.** The full rule, with the reasons and examples (`ParallelEventVerifier`, `PoWMiner.MiningDispatcher`, `BagSweep`, `NostrSignerExternal`), is `commons/ARCHITECTURE.md` › "Choosing a dispatcher". Read it before writing `flowOn`/`withContext`. In `commonMain`, `Dispatchers.IO` needs `import kotlinx.coroutines.IO`.
Never write your own `callbackFlow { openReqSubscription(...); awaitClose { ... } }` REQ loop. One-shot and paged fetches, publish-and-confirm, COUNT and negentropy sync already exist as `INostrClient` extensions in `quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/accessories/` (catalog: its `README.md`; flow helpers in `../reqs/`). Their timeouts are idle windows (`idleTimeoutMs`), not deadlines. For loads tied to a composable's lifetime, see the `relay-client` skill.
`callbackFlow` is still the right bridge for platform callbacks; copy `amethyst/.../service/connectivity/ConnectivityFlow.kt` (initial emit, `awaitClose` unregister, `distinctUntilChanged().debounce(200).flowOn(Dispatchers.IO)`).
`scope: CoroutineScope`; Android services build `CoroutineScope(Dispatchers.IO + SupervisorJob() + exceptionHandler)` and `cancel()` it in `onDestroy` (`PushNotificationReceiverService`). Hundreds of such fields are legitimate; don't flag them on sight.
and do not `launch` from `init`. Let the owner pick the scope.
`GlobalScope`.
`CancellationException`.
rethrow turns one slow RPC into a dead loop: rethrowing it ended the cordn sync loop for the whole session. Either catch the timeout narrowly, right at the `withTimeout`, or rethrow only when the coroutine itself is cancelled (`if (!isActive) throw e`). See cordn-kmp's `cordn/client/CordnSyncLoop.kt` for both.
block pure and cheap (it can retry); build expensive values before it.
new sharing coroutine).
stays at the initial value. If code reads `.value` synchronously, use `Eagerly`.
need `.value`.
`Channel(Channel.BUFFERED).receiveAsFlow()`, not a no-replay `MutableSharedFlow` that drops emissions with no collector. A `Channel` delivers each item to ONE collector; if every observer must see it, model it as state.
loading state.
dependency; collect into a list in `backgroundScope.launch { flow.collect { } }` and drive time with `advanceUntilIdle()`, `runCurrent()`, `advanceTimeBy()`.
or `UnconfinedTestDispatcher`) so delays run on virtual time.
loaded CI). Wait on the test scheduler or a condition.
Account, LocalCache, User/Note and feeds in commons/model + commons/feeds: per-user XState objects (.flow),…
amy CLI in cli/: adding an `amy <verb>` command, Main.kt dispatch, Output.emit/--json contract, exit codes 0/1/2/124, ~/.amy…
Android shim in `amethyst/`: MainActivity, AndroidManifest/intent filters, :napplet process, play/fdroid flavors, R8 +…
NostrSigner in Amethyst: NostrSignerInternal (local key), NostrSignerRemote (NIP-46 bunker), NostrSignerExternal (NIP-55 Android…
Shared Compose UI in `commonsUI/`: screens, components, the nav shell (`ScreenLayoutSpec`, `AppShellLayout`, Navigation 3 back…
Desktop JVM shim in desktopApp/: AmethystDesktop window, DesktopMenuBar, platform ports, OS keyring; :desktopApp:run, packaging.…
Decompile Android APK, XAPK, JAR, and AAR files using jadx or Fernflower/Vineflower. Reverse engineer Android apps, extract HTTP…