This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kmp-skie-swift-concurrency-cancellation-standard

KMP SKIE Bidirectional Structured Concurrency Cancellation Guardrail

Enforces strict bidirectional cancellation propagation across the Kotlin Multiplatform (KMP) and Darwin Swift C-interop boundary using Touchlab SKIE. Unbridged Darwin FFI transitions default to fire-and-forget or orphan Job scopes, leading to memory leaks, background battery drain, and stale UI updates when Swift Tasks or Kotlin Coroutines cancel prematurely.

Prohibited Anti-Pattern: Orphaned Coroutine Execution and Leaked Swift Task Subscriptions across KMP FFI Boundaries

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: KMP SKIE Bidirectional Structured Concurrency Cancellation Guardrail - Enforces strict bidirectional cancellation propagation across the Kotlin Multiplatform (KMP) and Darwin Swift C-interop boundary using Touchlab SKIE. Unbridged Darwin FFI transitions default to fire-and-forget or orphan Job scopes, leading to memory leaks, background battery drain, and stale UI updates when Swift Tasks or Kotlin Coroutines cancel prematurely.
globs: iosApp/**/*.swift, shared/src/iosMain/kotlin/**/*.kt, shared/src/commonMain/kotlin/**/*.kt, shared/build.gradle.kts
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-skie-swift-concurrency-cancellation-standard"
---

# KMP SKIE Bidirectional Structured Concurrency Cancellation Guardrail

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Orphaned Coroutine Execution and Leaked Swift Task Subscriptions across KMP FFI Boundaries

## System Constraints
You are a Principal Mobile Architect enforcing the KMP SKIE Swift Concurrency Cancellation Bridging Standard. In KMP projects targeting Darwin platforms, raw Objective-C export converts suspend functions into completion handlers that detach cancellation context. You must ensure SKIE is configured correctly, suspend functions are consumed as native async/await within structured Swift Task trees, Flow emissions use SKIE's SkieSwiftFlow wrapper without escaping Task lifecycles, and Kotlin Jobs accurately cancel when Swift Tasks are cancelled.

## Strict Guidelines
- Engineers MUST ALWAYS leverage SKIE-generated async/await signatures in Swift rather than raw completion-handler blocks generated by default Kotlin/Native obj-c export.
- Engineers MUST NOT discard or ignore Swift Task cancellation flags when bridging into asynchronous Kotlin calls; Task.checkCancellation() must be checked prior to boundary transitions.
- Kotlin repository and domain methods MUST NOT launch in an unconfined GlobalScope or NonCancellable context when serving Darwin consumers unless explicit fire-and-forget telemetry is required.
- Swift callers consuming SKIE SkieSwiftFlow or SkieSwiftOptionalFlow MUST consume sequences inside structured 'for await' loops wrapped within a manageable Task hierarchy, ensuring the underlying Kotlin Coroutine cancels when the Task terminates.
- SwiftUI views MUST bind SKIE flows using .task { } view modifiers instead of onAppear { Task { } } to guarantee automatic coroutine job cancellation on view unmount.

## Bad vs Good Implementations
### SwiftUI Consumption of Kotlin SKIE Flow with Automatic Cancellation

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Launching an unmanaged Swift Task inside .onAppear bypasses SwiftUI lifecycle bounds. When the view pops off the navigation stack, the loop continues iterating, and the underlying Kotlin Flow collector is never cancelled, exhausting network and memory resources.
```swift
struct ProfileView: View {
    @StateObject var viewModel = ProfileViewModel()
    
    var body: some View {
        Text(viewModel.state)
            .onAppear {
                // BAD: Task {} in onAppear is unmanaged and leaks across view destruction.
                // Calling default Kotlin obj-c export via completion handler drops cancellation.
                Task {
                    for await state in viewModel.repository.observeProfileUpdates() {
                        viewModel.state = state
                    }
                }
            }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Using SwiftUI's .task modifier ensures structured lifecycle integration. When the view disappears, Swift cancels the Task. SKIE intercepts this cancellation token over the Darwin FFI and calls Job.cancel() on the Kotlin CoroutineScope backing the Flow collector.
```swift
struct ProfileView: View {
    @State private var state: String = ""
    let repository: UserRepository
    
    var body: some View {
        Text(state)
            .task {
                // GOOD: .task modifier is structurally bound to SwiftUI view lifecycle.
                // SKIE bridges SkieSwiftFlow directly into Swift's AsyncSequence.
                // When view unmounts, the Task cancels, propagating cancellation down to the Kotlin Coroutine Job.
                do {
                    for await profile in repository.observeProfile() {
                        self.state = profile.displayName
                    }
                } catch is CancellationError {
                    // Normal structured teardown across FFI boundary
                } catch {
                    // Handle domain errors
                }
            }
    }
}
```

### Kotlin Side Suspend Handling with Yields and Cooperative Cancellation

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: If the Swift caller cancels the bridged Task, the Kotlin Coroutine cannot cancel cooperatively because Dispatchers.Default performs blocking work without yield() or ensureActive() checkpoints.
```kotlin
class UserRepositoryImpl : UserRepository {
    // BAD: Using an independent GlobalScope detached from Swift caller
    override suspend fun syncUserData(): UserData {
        return withContext(Dispatchers.Default) {
            // Blocking long-running task without cooperative cancellation checkpoints
            val rawData = fetchRawPayloadBlocking()
            parseHugePayload(rawData)
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Checking coroutineContext.ensureActive() guarantees that when SKIE receives the Darwin cancellation signal, the Kotlin coroutine terminates immediately rather than performing expensive compute work.
```kotlin
class UserRepositoryImpl : UserRepository {
    override suspend fun syncUserData(): UserData = withContext(Dispatchers.Default) {
        // GOOD: ensureActive() checks Job.isActive propagated from Swift Task cancellation
        coroutineContext.ensureActive()
        val rawData = fetchRawPayloadSuspend()
        
        coroutineContext.ensureActive()
        parseHugePayloadWithCheckpoints(rawData)
    }
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: KMP SKIE Bidirectional Structured Concurrency Cancellation Guardrail

KMP SKIE Bidirectional Structured Concurrency Cancellation Guardrail defines an industrial-grade behavioral standard for mobile engineering. Enforces strict bidirectional cancellation propagation across the Kotlin Multiplatform (KMP) and Darwin Swift C-interop boundary using Touchlab SKIE. Unbridged Darwin FFI transitions default to fire-and-forget or orphan Job scopes, leading to memory leaks, background battery drain, and stale UI updates when Swift Tasks or Kotlin Coroutines cancel prematurely. 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: Orphaned Coroutine Execution and Leaked Swift Task Subscriptions across KMP FFI Boundaries 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 DriftOrphaned Coroutine Execution and Leaked Swift Task Subscriptions across KMP FFI BoundariesCritical
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 leverage SKIE-generated async/await signatures in Swift rather than raw completion-handler blocks generated by default Kotlin/Native obj-c export.

Engineers MUST ALWAYS leverage SKIE-generated async/await signatures in Swift rather than raw completion-handler blocks generated by default Kotlin/Native obj-c export.

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 NOT discard or ignore Swift Task cancellation flags when bridging into asynchronous Kotlin calls

Engineers MUST NOT discard or ignore Swift Task cancellation flags when bridging into asynchronous Kotlin calls; Task.checkCancellation() must be checked prior to boundary transitions.

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

Kotlin repository and domain methods MUST NOT launch in an unconfined GlobalScope or NonCancellable context when serving Darwin consumers unless explicit fire-and-forget telemetry is required.

Kotlin repository and domain methods MUST NOT launch in an unconfined GlobalScope or NonCancellable context when serving Darwin consumers unless explicit fire-and-forget telemetry is required.

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

Swift callers consuming SKIE SkieSwiftFlow or SkieSwiftOptionalFlow MUST consume sequences inside structured 'for await' loops wrapped within a manageable Task hierarchy, ensuring the underlying Kotlin Coroutine cancels when the Task terminates.

Swift callers consuming SKIE SkieSwiftFlow or SkieSwiftOptionalFlow MUST consume sequences inside structured 'for await' loops wrapped within a manageable Task hierarchy, ensuring the underlying Kotlin Coroutine cancels when the Task terminates.

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

SwiftUI views MUST bind SKIE flows using .task { } view modifiers instead of onAppear { Task { } } to guarantee automatic coroutine job cancellation on view unmount.

SwiftUI views MUST bind SKIE flows using .task { } view modifiers instead of onAppear { Task { } } to guarantee automatic coroutine job cancellation on view unmount.

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 KMP SKIE Bidirectional Structured Concurrency Cancellation Guardrail critical for AI coding assistants?

Enforces strict bidirectional cancellation propagation across the Kotlin Multiplatform (KMP) and Darwin Swift C-interop boundary using Touchlab SKIE. Unbridged Darwin FFI transitions default to fire-and-forget or orphan Job scopes, leading to memory leaks, background battery drain, and stale UI updates when Swift Tasks or Kotlin Coroutines cancel prematurely. 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 (Orphaned Coroutine Execution and Leaked Swift Task Subscriptions across KMP FFI Boundaries) 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

KMP