fractal-engine is a recursive language-model compute engine for long-context,
long-horizon work. A root session keeps working state across turns. The model writes
Clojure, the host evaluates it in a persistent SCI context, and the turn ends only
when the model calls FINAL.
Recursion is not a separate API surface or scoring mode. It is the engine's way to split work while preserving durable state, bounded delegation, and an auditable state graph.
The recursive harness gives the model ordinary Clojure plus this small surface:
flowchart TB
ROOT["Root session REPL"] --> FINAL["FINAL value"]
ROOT --> EXACT["Ordinary Clojure"]
ROOT --> LEAF["lm"]
ROOT --> LEAFS["map-lm"]
ROOT --> CHILD["rlm"]
ROOT --> CHILDS["map-rlm"]
ROOT --> ATTACH["attach-rlm"]
LEAF --> ONECALL["One bounded provider call"]
LEAFS --> MANYCALLS["Ordered bounded leaf calls"]
CHILD --> CHILDSESSION["Fresh child session"]
CHILDS --> MANYCHILDREN["Ordered child sessions"]
ATTACH --> DERIVED["Fresh child restored from selected head"]
| Function | Use it for |
|---|---|
FINAL | Return the current turn's value and close the turn. |
lm | Make one bounded semantic leaf call over one bounded input. |
map-lm | Run independent bounded leaf calls in parallel, preserving input order. |
rlm | Start one fresh child session for a sub-problem that needs its own loop. |
map-rlm | Start independent child sessions in parallel for naturally separate lanes. |
attach-rlm | Derive a fresh child from a selected prior session/head and run one new task there. |
Everything else is engine machinery. Model code should not depend on hidden context, ambient mutable session objects, or storage paths.
Embedders can configure SDK surfaces that add qualified world/API calls beside the built-in recursive functions. These are not hidden tools or prompt-only affordances; they are real SCI namespace vars supplied by the host and gated by capability.
flowchart TB
Parent["Parent session"] --> ParentFns["Built-ins plus allowed surfaces"]
ParentFns --> SurfaceCall["(git/tracked-files {})"]
Parent --> Child["rlm child session"]
Child --> ChildClamp["Surface functions clamped by parent profile"]
ChildClamp --> ChildCall["(git/read-file path {})"]
Parent --> Leaf["lm leaf call"]
Leaf --> LeafPrompt["Leaf prompt may receive surface context"]
Leaf -. no host fns .-> NoCall["No direct surface calls inside leaf"]
The recursive rules are:
:surface/fns against the parent profile;lm and map-lm leaves remain one provider call, not a REPL, so they cannot
call surface functions directly;:surface/prompts :leaf, for
example how to classify snippets returned by the root or a child;This is the intended way to make a fractal-shaped Git, issue-tracker, archive, or MCP-backed world without bloating the kernel. The host owns the world code and policy. The engine owns the recursive shape, prompt placement, and durable identity checks.
Use deterministic Clojure for exact work:
FINAL follows from observed valuesUse lm or map-lm when the work needs one bounded semantic judgment:
map-lmLeaves do not get a REPL, persistent vars, child lineage, or a multi-step loop. A leaf is one provider call. If the task needs exploration, inspection, repair, or multi-step state, it is not a leaf.
Use rlm or map-rlm when a sub-problem needs its own working loop:
Children are full sessions. Each child has its own persistent SCI context, its own turn loop, inherited-and-clamped capability, durable heads, and lineage back to the caller. The parent receives an envelope and should read the child result from the envelope value, then validate and aggregate in Clojure.
The root session is the operator-steered working state. It can build maps, ledgers, drafts, checks, partial answers, and assumptions over many turns. Later turns resume from the current head and can continue from that state without replaying completed work.
Child sessions are branches. A child should receive:
FINALChild outputs are claims, not facts. Before a root composes load-bearing claims, it should re-ground them against returned data or source material, recompute exact totals locally, and carry explicit missingness when a lane failed or returned weak evidence.
For map calls, partial fan-out failures are values. A failed slot is represented as
an index-aligned sentinel such as {:fractal/failed true ...} while successful slots
remain available. Split successes from sentinels before aggregating; one failed lane
should not erase the rest of the batch.
attach-rlm is for reusing useful prior cognitive state without mutating it. It
selects a source session/head, restores that selected immutable head into a fresh
child, runs one new task in the child, and records a derivation edge.
It is different from nearby concepts:
| Concept | Meaning |
|---|---|
| Resume | Reopen the same durable session from its authoritative current head. |
| Fork | Start a separate logical branch from a selected head. |
| Continuation | Informal description of continuing a line of thought. It is not a storage primitive. |
| Attach | Create a fresh derived child from selected source state without advancing the source. |
The source session does not move under attach. Passing a session handle means "attach from that session's current head." Passing a head handle means "attach from this exact immutable point." That distinction matters when a later current head has moved past an older useful branch.
Use attach when:
Do not use attach to hide mutation, replay old provider calls, or make the source session appear to have continued in place.
Represent before delegating. Build a Clojure value that describes the work units, their ids, expected counts, and boundaries. Use that value as the source of child tasks and as the ledger for final aggregation.
Delegate with contracts. A child task should ask for a concrete value, not a general
analysis. Prefer "return {:id id :answer value :evidence [...] :missing [...]}" to
"look into this."
Rejoin deterministically. After map-lm or map-rlm, aggregate with ordinary
Clojure. Sort by stable ids, count successes and failures, validate shapes, and only
then construct FINAL.
Keep branch state meaningful. A child is justified when the branch's own state helps settle the problem. If a child would only run one bounded read, use a leaf. If it would only run one exact expression, use Clojure.
Attach from named evidence. Keep handles or returned envelopes for useful heads so a later turn can derive from them directly instead of asking the model to remember a prose summary.
Good recursive work is boring at the join point. The root has a ledger. Each branch
owns a bounded lane. Leaves make bounded judgments. Children return exact shapes.
Failures are explicit values. The root validates, reconciles, and returns one compact
FINAL built from observed state rather than expectation.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |