This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kotlin-wasm-canvaskit-context-confinement

Kotlin Wasm-GC CanvasKit Context Confinement & Linear Memory Teardown

Enforces strict lexical lifecycle confinement and deterministic teardown for Skiko/CanvasKit native backing resources within Kotlin Wasm-GC runtimes. WebAssembly Garbage Collection collects managed references but does not trace or reclaim CanvasKit C++ linear memory allocations, causing unbounded cross-heap memory leaks during high-refresh multiplatform canvas rendering.

Prohibited Anti-Pattern: Leaking CanvasKit C++ Linear Memory via Unscoped Wasm-GC Allocation in Render Loops

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Kotlin Wasm-GC CanvasKit Context Confinement & Linear Memory Teardown - Enforces strict lexical lifecycle confinement and deterministic teardown for Skiko/CanvasKit native backing resources within Kotlin Wasm-GC runtimes. WebAssembly Garbage Collection collects managed references but does not trace or reclaim CanvasKit C++ linear memory allocations, causing unbounded cross-heap memory leaks during high-refresh multiplatform canvas rendering.
globs: **/wasmJsMain/**/*.kt, **/skiko/**/*.kt, **/*Canvas*.kt, **/*Renderer*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kotlin-wasm-canvaskit-context-confinement"
---

# Kotlin Wasm-GC CanvasKit Context Confinement & Linear Memory Teardown

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Leaking CanvasKit C++ Linear Memory via Unscoped Wasm-GC Allocation in Render Loops

## System Constraints
You are a Principal Mobile and WebAssembly Systems Architect enforcing strict memory invariants in Kotlin Wasm-GC and CanvasKit runtimes. In Kotlin Wasm-GC, references to CanvasKit/Skiko objects (such as Surface, Paint, Path, PictureRecorder, and Image) bridge the managed Wasm-GC heap with unmanaged Emscripten/C++ linear memory. The Wasm-GC collector NEVER frees Skia C++ linear memory backing buffers. You MUST mandate deterministic resource acquisition and release via lexical scopes, prevent per-frame transient allocations, and guarantee immediate surface teardown during resize or component disposal.

## Strict Guidelines
- Engineers MUST ALWAYS invoke `.close()` or `.delete()` deterministically within a `finally` block or use scoped resource blocks (e.g., `use { ... }`) for every Skiko/CanvasKit native wrapper (Path, Paint, Surface, Image, Picture).
- Engineers MUST NOT instantiate new `Paint`, `Path`, `Surface`, or `TextBlob` instances inside continuous animation, high-refresh render passes, or `onDraw` callbacks.
- Transient path mutations MUST reuse pre-allocated scratch objects via `.reset()` or `.rewind()` to maintain zero-allocation render cycles.
- CanvasKit surfaces and Skia contexts MUST be detached and deleted immediately upon DOM resizing, canvas detachment, or Compose `DisposableEffect` disposal.
- Interop wrappers bridging JavaScript CanvasKit handles MUST NEVER rely on finalizers or phantom references for unmanaged C++ memory reclamation.

## Bad vs Good Implementations
### Deterministic CanvasKit Native Resource Disposal

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Each invocation allocates C++ structs and raster buffers inside CanvasKit's Emscripten linear heap. Kotlin Wasm-GC manages the Kotlin proxy instance but cannot deallocate the underlying WebAssembly C++ linear memory. At 60-120fps, this leaks hundreds of megabytes within seconds.
```kotlin
import org.jetbrains.skia.*

class CanvasRenderer {
    // ANTI-PATTERN: Instantiating Skia native wrappers per frame inside Kotlin Wasm-GC.
    // Wasm-GC collects the Kotlin wrapper object, but the underlying CanvasKit C++
    // linear memory allocation remains uncollected, causing catastrophic OOM crashes.
    fun renderFrame(canvas: Canvas, width: Float, height: Float) {
        val paint = Paint().apply {
            color = Color.RED
            isAntiAlias = true
        }
        val path = Path().apply {
            moveTo(0f, 0f)
            lineTo(width, height)
        }
        canvas.drawPath(path, paint)
        // paint and path are abandoned to Wasm-GC; linear memory leaks instantly
    }
}
```

#### Production Standard (Correct)
> **Standard**: Reuses native `Paint` and `Path` allocations across frames via `.reset()`, eliminating per-frame linear heap growth. Implements `AutoCloseable` to deterministically invoke `.close()` (which delegates to C++ `delete()`) when the renderer lifecycle terminates.
```kotlin
import org.jetbrains.skia.*

class CanvasRenderer : AutoCloseable {
    // PRODUCTION FIX: Pre-allocate scratch native resources and reuse across frames.
    private val framePaint = Paint().apply {
        color = Color.RED
        isAntiAlias = true
    }
    private val scratchPath = Path()

    fun renderFrame(canvas: Canvas, width: Float, height: Float) {
        scratchPath.reset()
        scratchPath.moveTo(0f, 0f)
        scratchPath.lineTo(width, height)
        canvas.drawPath(scratchPath, framePaint)
    }

    // Deterministic teardown of backing C++ linear memory allocations
    override fun close() {
        scratchPath.close()
        framePaint.close()
    }
}
```

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Kotlin Wasm-GC CanvasKit Context Confinement & Linear Memory Teardown

Kotlin Wasm-GC CanvasKit Context Confinement & Linear Memory Teardown defines an industrial-grade behavioral standard for mobile engineering. Enforces strict lexical lifecycle confinement and deterministic teardown for Skiko/CanvasKit native backing resources within Kotlin Wasm-GC runtimes. WebAssembly Garbage Collection collects managed references but does not trace or reclaim CanvasKit C++ linear memory allocations, causing unbounded cross-heap memory leaks during high-refresh multiplatform canvas rendering. 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: Leaking CanvasKit C++ Linear Memory via Unscoped Wasm-GC Allocation in Render Loops 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 DriftLeaking CanvasKit C++ Linear Memory via Unscoped Wasm-GC Allocation in Render LoopsCritical
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 invoke `.close()` or `.delete()` deterministically within a `finally` block or use scoped resource blocks (e.g., `use { ... }`) for every Skiko/CanvasKit native wrapper (Path, Paint, Surface, Image, Picture).

Engineers MUST ALWAYS invoke `.close()` or `.delete()` deterministically within a `finally` block or use scoped resource blocks (e.g., `use { ... }`) for every Skiko/CanvasKit native wrapper (Path, Paint, Surface, Image, Picture).

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 instantiate new `Paint`, `Path`, `Surface`, or `TextBlob` instances inside continuous animation, high-refresh render passes, or `onDraw` callbacks.

Engineers MUST NOT instantiate new `Paint`, `Path`, `Surface`, or `TextBlob` instances inside continuous animation, high-refresh render passes, or `onDraw` callbacks.

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

Transient path mutations MUST reuse pre-allocated scratch objects via `.reset()` or `.rewind()` to maintain zero-allocation render cycles.

Transient path mutations MUST reuse pre-allocated scratch objects via `.reset()` or `.rewind()` to maintain zero-allocation render cycles.

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

CanvasKit surfaces and Skia contexts MUST be detached and deleted immediately upon DOM resizing, canvas detachment, or Compose `DisposableEffect` disposal.

CanvasKit surfaces and Skia contexts MUST be detached and deleted immediately upon DOM resizing, canvas detachment, or Compose `DisposableEffect` disposal.

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

Interop wrappers bridging JavaScript CanvasKit handles MUST NEVER rely on finalizers or phantom references for unmanaged C++ memory reclamation.

Interop wrappers bridging JavaScript CanvasKit handles MUST NEVER rely on finalizers or phantom references for unmanaged C++ memory reclamation.

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 Wasm-GC CanvasKit Context Confinement & Linear Memory Teardown critical for AI coding assistants?

Enforces strict lexical lifecycle confinement and deterministic teardown for Skiko/CanvasKit native backing resources within Kotlin Wasm-GC runtimes. WebAssembly Garbage Collection collects managed references but does not trace or reclaim CanvasKit C++ linear memory allocations, causing unbounded cross-heap memory leaks during high-refresh multiplatform canvas rendering. 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 (Leaking CanvasKit C++ Linear Memory via Unscoped Wasm-GC Allocation in Render Loops) 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