This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kmp-stateflow-threadsafe-mutation-guardrail

Kotlin Multiplatform StateFlow Atomic Confinement and Thread-Safe Mutation Guardrail

Guarantees thread-safe shared mutable state between JVM and Kotlin/Native runtimes by forbidding direct property assignment and unconfined coroutine execution on MutableStateFlow. Enforces atomic state transitions via .update{} and pins mutations to deterministic coroutine contexts to eliminate race conditions and memory model desynchronization across platforms.

Prohibited Anti-Pattern: Direct MutableStateFlow.value reassignments from arbitrary or unconfined coroutine scopes across JVM and Darwin runtimes

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Kotlin Multiplatform StateFlow Atomic Confinement and Thread-Safe Mutation Guardrail - Guarantees thread-safe shared mutable state between JVM and Kotlin/Native runtimes by forbidding direct property assignment and unconfined coroutine execution on MutableStateFlow. Enforces atomic state transitions via .update{} and pins mutations to deterministic coroutine contexts to eliminate race conditions and memory model desynchronization across platforms.
globs: **/commonMain/**/*.kt, **/jvmMain/**/*.kt, **/iosMain/**/*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-stateflow-threadsafe-mutation-guardrail"
---

# Kotlin Multiplatform StateFlow Atomic Confinement and Thread-Safe Mutation Guardrail

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Direct MutableStateFlow.value reassignments from arbitrary or unconfined coroutine scopes across JVM and Darwin runtimes

## System Constraints
You are a Principal Mobile Architect enforcing Kotlin Multiplatform (KMP) StateFlow thread-safe mutations. Mandate strict state isolation, atomic compare-and-set semantics via update{}, read-only boundary exposure via asStateFlow(), and confined dispatcher execution.

## Strict Guidelines
- Engineers MUST ALWAYS expose mutable state externally as an immutable StateFlow<T> using the .asStateFlow() operator.
- Engineers MUST NEVER mutate state via direct property setter (_state.value = transform(_state.value)) when the new state derives from previous state; engineers MUST use .update { ... } or .getAndUpdate { ... }.
- Code MUST isolate state mutation coroutines to a known, confined CoroutineDispatcher (such as Dispatchers.Main.immediate or an explicit single-threaded executor dispatcher) to prevent concurrent preemption in native Apple runtimes.
- Engineers MUST NOT execute suspending, blocking, or I/O operations inside the StateFlow.update { } lambda.
- Services MUST instantiate StateFlow with immutable data classes where all properties are declared as val and contain deep-immutable collections (e.g., kotlinx.collections.immutable or persistent primitives).

## Bad vs Good Implementations
### Unconfined MutableStateFlow Mutation vs. Atomic Confined State Reducer

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Publicly exposing MutableStateFlow permits external modules to mutate state unpredictably. Reading state.value and assigning it back on Dispatchers.Default creates a classic race condition in multi-threaded runtimes (JVM and Kotlin/Native), causing lost updates and out-of-order state emissions.
```kotlin
package com.example.kmp.feature.user

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch

data class ProfileState(val name: String = "", val followCount: Int = 0)

class ProfileViewModel(private val scope: CoroutineScope) {
    // ANTI-PATTERN: Exposing mutable state flow publicly
    val state = MutableStateFlow(ProfileState())

    fun incrementFollowers() {
        // ANTI-PATTERN: Unconfined thread mutation and non-atomic read-then-write
        scope.launch(Dispatchers.Default) {
            val current = state.value
            // Race condition: concurrent invocations will overwrite and lose increments
            state.value = current.copy(followCount = current.followCount + 1)
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: The mutable state is encapsulated as private and exposed as a read-only StateFlow. The mutation occurs atomically using .update {}, which internally executes an atomic Compare-And-Set loop without locks, pinned to Dispatchers.Main.immediate to ensure deterministic order across iOS and Android.
```kotlin
package com.example.kmp.feature.user

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

data class ProfileState(val name: String = "", val followCount: Int = 0)

class ProfileViewModel(
    private val scope: CoroutineScope,
    private val mainDispatcher: kotlinx.coroutines.CoroutineDispatcher = Dispatchers.Main.immediate
) {
    private val _state = MutableStateFlow(ProfileState())
    val state: StateFlow<ProfileState> = _state.asStateFlow()

    fun incrementFollowers() {
        // PRODUCTION STANDARD: Confined dispatcher context with atomic CAS via update {}
        scope.launch(mainDispatcher) {
            _state.update { previous ->
                previous.copy(followCount = previous.followCount + 1)
            }
        }
    }
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Kotlin Multiplatform StateFlow Atomic Confinement and Thread-Safe Mutation Guardrail

Kotlin Multiplatform StateFlow Atomic Confinement and Thread-Safe Mutation Guardrail defines an industrial-grade behavioral standard for mobile engineering. Guarantees thread-safe shared mutable state between JVM and Kotlin/Native runtimes by forbidding direct property assignment and unconfined coroutine execution on MutableStateFlow. Enforces atomic state transitions via .update{} and pins mutations to deterministic coroutine contexts to eliminate race conditions and memory model desynchronization across platforms. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Direct MutableStateFlow.value reassignments from arbitrary or unconfined coroutine scopes across JVM and Darwin runtimes Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftDirect MutableStateFlow.value reassignments from arbitrary or unconfined coroutine scopes across JVM and Darwin runtimesCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS expose mutable state externally as an immutable StateFlow<T> using the .asStateFlow() operator.

Engineers MUST ALWAYS expose mutable state externally as an immutable StateFlow<T> using the .asStateFlow() operator.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST NEVER mutate state via direct property setter (_state.value = transform(_state.value)) when the new state derives from previous state

Engineers MUST NEVER mutate state via direct property setter (_state.value = transform(_state.value)) when the new state derives from previous state; engineers MUST use .update { ... } or .getAndUpdate { ... }.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Code MUST isolate state mutation coroutines to a known, confined CoroutineDispatcher (such as Dispatchers.Main.immediate or an explicit single-threaded executor dispatcher) to prevent concurrent preemption in native Apple runtimes.

Code MUST isolate state mutation coroutines to a known, confined CoroutineDispatcher (such as Dispatchers.Main.immediate or an explicit single-threaded executor dispatcher) to prevent concurrent preemption in native Apple runtimes.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Engineers MUST NOT execute suspending, blocking, or I/O operations inside the StateFlow.update { } lambda.

Engineers MUST NOT execute suspending, blocking, or I/O operations inside the StateFlow.update { } lambda.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Services MUST instantiate StateFlow with immutable data classes where all properties are declared as val and contain deep-immutable collections (e.g., kotlinx.collections.immutable or persistent primitives).

Services MUST instantiate StateFlow with immutable data classes where all properties are declared as val and contain deep-immutable collections (e.g., kotlinx.collections.immutable or persistent primitives).

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Kotlin Multiplatform StateFlow Atomic Confinement and Thread-Safe Mutation Guardrail critical for AI coding assistants?

Guarantees thread-safe shared mutable state between JVM and Kotlin/Native runtimes by forbidding direct property assignment and unconfined coroutine execution on MutableStateFlow. Enforces atomic state transitions via .update{} and pins mutations to deterministic coroutine contexts to eliminate race conditions and memory model desynchronization across platforms. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Direct MutableStateFlow.value reassignments from arbitrary or unconfined coroutine scopes across JVM and Darwin runtimes) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

Related MOBILE Rules

View all rules →
1 Rule in Stack

Kotlin