# How Context Forking Determines the Sandbox Level in Quarkdown

> Discover how context forking in Quarkdown determines sandbox levels. Learn how it controls state mutations propagation across included files for isolation or read-only access.

- Repository: [Giorgio Garofalo/quarkdown](https://github.com/iamgio/quarkdown)
- Tags: deep-dive
- Published: 2026-04-29

---

**In Quarkdown, context forking is the mechanism that instantiates different `Context` subclasses based on the specified sandbox level, controlling whether state mutations from included files propagate bidirectionally, stay isolated, or remain read-only.**

Quarkdown evaluates documents within a **Context** object that encapsulates all compiler state, including variables, functions, and document metadata. When a file is included via `.include` or a context is manually forked, the system creates a child context whose isolation characteristics are determined by the **sandbox level**. According to the iamgio/quarkdown source code, this relationship is implemented through specific Kotlin subclasses that regulate state sharing between parent and child execution environments.

## The Forking Mechanism

The base **Context** interface defines the `fork()` method as the primary entry point for creating child contexts. As implemented in [`quarkdown-core/src/main/kotlin/com/quarkdown/core/context/Context.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdown/core/context/Context.kt) (lines 152-155), this method always produces a `ScopeContext`, establishing a baseline sandbox that prevents new declarations from leaking back to the parent:

```kotlin
// Context.kt - fork() implementation
fun fork(): Context = ScopeContext(this, fileSystem)

```

This default behavior creates a **scope**-level isolation. However, when including files via the standard library, the sandbox parameter expands this mechanism to support three distinct isolation policies, each mapping to a specific Context subclass.

## Three Sandbox Levels and Their Implementations

The **sandbox level** acts as a policy selector that determines which concrete Context class the fork instantiates. Each mode controls the direction and granularity of state sharing between the main document and the included file.

### SHARE Mode: Full Bidirectional Sharing

The **`SHARE`** sandbox creates a **SharedContext** ([[`SharedContext.kt`](https://github.com/iamgio/quarkdown/blob/main/SharedContext.kt)](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdown/core/context/SharedContext.kt)), which inherits all parent state and maintains bidirectional synchronization. Any mutation—whether setting variables, defining functions, or registering media—reflects immediately in both the parent and child contexts. This mode is implemented when `.include` is called without an explicit sandbox argument.

### SCOPE Mode: Isolated Declarations

**`SCOPE`** (the default for `Context.fork()`) instantiates **ScopeContext** ([[`ScopeContext.kt`](https://github.com/iamgio/quarkdown/blob/main/ScopeContext.kt)](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdown/core/context/ScopeContext.kt)). This class shares document information, attributes, and media storage with the parent but captures new variable and function definitions locally. Declarations made within a SCOPE-sandboxed include remain invisible to the parent document, preventing namespace pollution while maintaining access to shared runtime data.

### SUBDOCUMENT Mode: Complete Isolation

The **`SUBDOCUMENT`** sandbox produces a **SubdocumentContext** ([[`SubdocumentContext.kt`](https://github.com/iamgio/quarkdown/blob/main/SubdocumentContext.kt)](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdown/core/context/SubdocumentContext.kt)), granting the child its own document-info and media storage. The child inherits parent state strictly for reading—nothing writes back. This mode effectively treats the included file as a separate sub-document with independent identity, useful for processing auxiliary content without side effects.

## Mapping Sandboxing in the Include Implementation

The relationship between **context forking** and **sandbox level** becomes explicit in [`quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/Ecosystem.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/Ecosystem.kt) (lines 16-21). Here, the `.include` command selects the concrete context class based on the `sandbox` enum value:

```kotlin
// Ecosystem.kt - include implementation
val newContext: Context = when (sandbox) {
    ContextSandbox.SHARE       -> SharedContext(context, newFileSystem)
    ContextSandbox.SCOPE       -> ScopeContext(context, newFileSystem)
    ContextSandbox.SUBDOCUMENT -> SubdocumentContext(context, context.subdocument, newFileSystem)
}

```

The `newFileSystem` parameter provides isolated file system access, while the selected Context subclass controls memory and state isolation. Thus, **the sandbox level determines which forked context type to instantiate**, bridging the high-level policy with the low-level implementation.

## Practical Usage Examples

### Default Sharing Behavior

Including a file with the default SHARE sandbox allows full bidirectional mutation:

```qd
// main.qd
.include {chapter1.qd}

```

```kotlin
// Creates: SharedContext(parentContext, fileSystemForChapter1)
// Variables defined in chapter1.qd are visible in main.qd

```

### Lambda Bodies with Scope Sandboxing

When nesting includes inside lambdas, SCOPE prevents iteration variables from polluting the outer scope:

```qd
.foreach {item} {
    .include {item.qd} sandbox:{scope}
}

```

```kotlin
// Creates: ScopeContext(parentContext, fileSystemForItem)
// New declarations inside item.qd remain local to that iteration

```

### Subdocument Isolation

Use SUBDOCUMENT when including independent appendix files that should not affect the primary document state:

```qd
.include {appendix.qd} sandbox:{subdocument}

```

```kotlin
// Creates: SubdocumentContext(parentContext, parentContext.subdocument, fileSystemForAppendix)
// Changes in appendix.qd are completely isolated

```

### Manual Context Forking

Calling `fork()` directly always yields a ScopeContext, equivalent to the SCOPE sandbox:

```kotlin
val child = currentContext.fork()
// Equivalent to: ScopeContext(currentContext, currentContext.fileSystem)

```

## Summary

- **Context forking** is the technical mechanism that creates child execution contexts from a parent Context in Quarkdown.
- The **sandbox level** (`SHARE`, `SCOPE`, or `SUBDOCUMENT`) determines which concrete Context subclass (`SharedContext`, `ScopeContext`, or `SubdocumentContext`) the fork instantiates.
- `Context.fork()` always creates a `ScopeContext`, while the `.include` command selects the class based on its `sandbox` parameter as implemented in [`Ecosystem.kt`](https://github.com/iamgio/quarkdown/blob/main/Ecosystem.kt).
- **SHARE** allows bidirectional mutation, **SCOPE** isolates new declarations but shares data, and **SUBDOCUMENT** provides read-only inheritance with independent document info.

## Frequently Asked Questions

### What is the default sandbox level when forking a context in Quarkdown?

Calling `Context.fork()` directly always creates a `ScopeContext`, making **SCOPE** the default sandbox level for manual forks. This prevents new declarations from leaking back to the parent while maintaining shared access to existing state. The `.include` command defaults to **SHARE** unless explicitly overridden.

### How does the SCOPE sandbox differ from SHARE in Quarkdown?

**SCOPE** (implemented by `ScopeContext`) shares document information, media, and attributes with the parent but captures new variable and function definitions locally. **SHARE** (implemented by `SharedContext`) maintains full bidirectional synchronization where any mutation in the child immediately reflects in the parent context. Use SCOPE to prevent namespace pollution; use SHARE for seamless integration.

### When should I use the SUBDOCUMENT sandbox level?

Use **SUBDOCUMENT** when including files that should function as completely independent documents, such as appendixes or external references processed within the same runtime. This mode (via `SubdocumentContext`) gives the included content its own document-info and media storage, ensuring zero write-back to the parent state and complete isolation of side effects.

### Can I manually fork a context outside of the .include command?

Yes. The `Context` interface exposes the `fork()` method (defined in [`Context.kt`](https://github.com/iamgio/quarkdown/blob/main/Context.kt) lines 152-155), which you can call programmatically to create a `ScopeContext` without using the standard library's `.include` command. This is useful for custom extensions or programmatic document manipulation where you need scoped execution without file inclusion.