How Context Forking Determines the Sandbox Level in Quarkdown

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 (lines 152-155), this method always produces a ScopeContext, establishing a baseline sandbox that prevents new declarations from leaking back to the parent:

// 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/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/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/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 (lines 16-21). Here, the .include command selects the concrete context class based on the sandbox enum value:

// 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:

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

// 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:

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

// 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:

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

// 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:

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.
  • 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →