name: state-and-background-footguns
description: "Reactive state and background-work traps: StateFlow conflation, flatMapLatest, job lifecycles, ViewModel bases, WorkManager, backups, crash reporting, logging, Glance widgets. Use when state lags or flips, or work runs twice or never."
23 traps mined from a production Kotlin and Compose Multiplatform app, one short file each under `references/`. Every file gives the working pattern, the **Traps** (the specific ways it fails in practice, and why), and a **Verifying it** section with commands to run against the user's own tree.
**How to use this index.** Match the code about to be written, or the symptom being chased, against the entries below: each names its topic and the symptom that should send you to it. Read every file that plausibly applies with the Read tool before proposing code or a fix; each is under 150 lines. Where a Verifying command names the source project's paths, substitute the equivalent paths in the user's tree.
**Cross-references.** A backticked trap name inside a file that is not listed here lives in a sibling area skill: glob `../*/references/<name>.md` to open it.
- [`stateflow-conflation-inverts-state`](references/stateflow-conflation-inverts-state.md) — A conflating state holder keeps one slot, so a callback that writes it several times per event makes write ORDER the correctness question — collectors see only the last write. Use when a spinner sits over content that is already loaded, when a loading flag clears at the wrong moment, or when a screen shows the state that was true one step ago.
- [`named-job-lifecycle-discipline`](references/named-job-lifecycle-discipline.md) — One `var xJob: Job?` field per concern, cancel-before-relaunch as an invariant at every launch site, and teardown writes wrapped so a cancellation cannot stop them halfway. Use when periodic updates arrive several times per tick, when stale results from a previous item overwrite the current one, or when a long-lived object keeps working after it was released.
- [`readonly-stateflow-cold-to-hot`](references/readonly-stateflow-cold-to-hot.md) — Expose state through `asStateFlow()` rather than an upcast of the mutable holder, and turn a cold source flow hot with `stateIn(scope, WhileSubscribed(timeout), initial)` so it survives collector churn without running forever. Use when a screen re-queries its source on every rotation or navigation, when work continues after the last collector leaves, or when something outside the owner is writing state it should only be reading.
- [`flatmaplatest-resubscribe-composite-key`](references/flatmaplatest-resubscribe-composite-key.md) — Re-subscribe a downstream flow whenever a key flow changes using flatMapLatest, then collapse the resulting high-frequency stream to a composite key with distinctUntilChanged before firing expensive one-shot work. Use when a fetch re-runs on every progress tick, when it fires for the wrong item after a fast switch, or when a side effect never runs because one of its inputs arrives last.
- [`distinct-by-key-reset-cancel-per-item`](references/distinct-by-key-reset-cancel-per-item.md) — Per-item pipelines that key on the item id with distinctUntilChangedBy, cancel the previous item's in-flight work before starting the next, and reset the visible state before filling it — so nothing from the previous item can appear under the new one. Use when a detail screen briefly shows the last item's artwork or text, when a slow response overwrites a newer one, or when a field stays populated after moving to an item that has no value for it.
- [`combine-two-flags-to-gate`](references/combine-two-flags-to-gate.md) — Turn several independent condition flows into one on/off gate with `combine` + `distinctUntilChanged` + `collectLatest`, make both the start and the teardown branch idempotent, and run teardown uncancellably. Use when a subsystem starts before it is fully configured, keeps running after one of its preconditions is withdrawn, or ends up half-started after a fast toggle.
- [`compose-multiplatform-viewmodel-base`](references/compose-multiplatform-viewmodel-base.md) — A shared ViewModel base class for Compose Multiplatform — container-aware so subclasses can pull extra dependencies without constructor threading, with one loading/error surface for every screen — and the blocking resource-lookup hazard that such a base almost always grows. Use when every screen is re-implementing its own loading dialog, when a base class needs a dependency only some subclasses use, or when app start stutters on the main thread before anything is drawn.
- [`dual-mode-persist-blocking-or-async`](references/dual-mode-persist-blocking-or-async.md) — One suspend persistence body driven two ways — blocking on the shutdown path, where the write must complete before the scope dies, and fire-and-forget on periodic ticks — instead of two copies that drift apart. Use when saved state is correct while the app runs but wrong after a hard quit, when a teardown save silently does nothing, or when two save functions have grown different guards.
- [`pending-state-makes-waiting-legible`](references/pending-state-makes-waiting-legible.md) — Model "asked, and not yet answered" as its own field, because without it a rejected request and a peer who simply has not looked at their screen are indistinguishable — both look like nothing happened. Covers the one set against many clears, clearing locally when no answer will ever come, and giving the user a way out. Use when an action appears to do nothing, when a screen can get stuck waiting forever, or when an error message has no place to appear.
- [`preset-identity-read-back-from-value`](references/preset-identity-read-back-from-value.md) — Derive "which preset is this" by comparing the presets against the value in force instead of storing a label beside it, so editing drops t