Introduction
The LADR Mental Model
The LADR Mental Model
How to think about ladders, the runtime, and execution. After reading this article, every other concept in LADR will make sense. Six ideas: a ladder is a program not a prompt, execution is a cursor walking steps, data flows through typed fields, control flow is declarative, memory is explicit, and safety is economic.
The three layers
LADR is not one thing. It is three things working together:
-
A language — a YAML dialect for declaring reasoning programs. You write a ladder the way you write a SQL query: you declare what structure you want, not how to execute it.
-
A runtime — a Go engine that loads your declaration and executes it. The runtime walks through the steps you declared, dispatches LLM calls, evaluates conditions, persists memory, and enforces hard resource ceilings. It owns the execution loop the way a database engine owns query execution.
-
A memory model — persistent state (slates) that survives across calls. Slates let a ladder learn from its mistakes, accumulate verified findings, and start each call already educated by prior experience.
These three layers map to the three things you interact with:
| Layer | You write | The system handles |
|---|---|---|
| Language | YAML config (steps, fields, gates, knobs) | Parsing, validation, schema checking |
| Runtime | Nothing — it's automatic | Execution, LLM dispatch, gate evaluation, budget enforcement |
| Memory | Slate declarations (folders, files, metatags) | Storage, retrieval, eviction, token budgeting |
The separation is deliberate. Because the runtime owns execution, it can enforce hard ceilings that make untrusted ladders safe. Because memory is explicit, the author controls exactly what persists. Because the language is declarative, a ladder is data — inspectable, analyzable, shareable — not code.
A ladder is a program, not a prompt
This is the single most important mental shift. A ladder is not a prompt template with variables. It is a multi-step program that orchestrates N LLM calls with data dependencies, conditional branching, loops, recursion, and persistent state.
A prompt template makes one LLM call. You fill in {{variables}}, send the assembled string, get a response. The "strategy" is in the prompt text.
A ladder makes many LLM calls in a structured sequence. Draft five candidates in parallel. Score each one. Prune the weak ones. Synthesize the survivors into a final answer. Persist what was learned. The "strategy" is in the structure — which calls to make, in what order, what data flows between them, what conditions gate transitions.
Side by side:
PROMPT TEMPLATE LADR LADDER
───────────────────── ─────────────────────────
One LLM call N LLM calls
Variables: {{question}} Fields: typed data sources
No control flow Gates: conditional pruning
No memory Slates: persistent state
No cost bounds Budget: hard spend ceiling
Strategy = prompt text Strategy = structure + promptsThe system prompts inside each step still matter — they're the instructions the LLM receives. But the leverage comes from the structure around them: parallel fan-out, verification gates, reflexion loops, accumulated memory. A one-shot prompt is a single attempt. A ladder is a search strategy.
The execution model: a cursor walking steps
The best mental model for LADR execution is a cursor that walks through an array of steps. At each step, the runtime performs a fixed sequence of operations, then decides where to go next.
Here is what happens at every step:
Step begins
│
▼
Resolve all declared fields ──────────────┐
│ │
▼ │
Assemble prompt from resolved fields │ each field is a typed
│ │ data source resolved
▼ │ independently
Dispatch N parallel LLM calls ────────────┘
│
▼
Store each output as a numbered node
│
▼
┌─ Step has a gate? ──┐
│ │
│ NO │ YES
│ │ │ Evaluate condition against each node
│ │ │ Prune non-matching nodes
│ │ │
│ │ ▼
│ │ ┌─ Any survivors? ──┐
│ │ │ │
│ │ │ YES │ NO
│ │ │ │ │ │
│ │ │ ▼ │ ▼
│ │ │ Fire THEN │ Fire ELSE
│ │ │ action │ action
│ │ └──────┬─────────────┘
│ │ │
│ ▼ ▼
┌─ What action? ──────────────────────────┐
│ continue → advance to next step │
│ jump → reposition cursor to target │
│ abort → terminate ladder │
│ write → write to slate, then advance │
└─────────────────────────────────────────┘Let's walk through each phase:
1. Resolve fields. Every step declares a list of fields. Each field has a type that determines where its data comes from (text from input, ingest from a prior step's node, slateRead from memory, etc). The runtime resolves every field to a concrete string before assembling the prompt.
2. Assemble the prompt. Resolved fields render as labeled lines in the user-role message. After all field lines, the step's systemPrompt is appended. There is no string interpolation — the LLM sees field lines and system instruction together.
3. Dispatch LLM calls. The nodes: field controls parallelism. If nodes: 5, five identical calls go out — each receives the same assembled prompt, except for nodeInfo fields which inject the node's own number.
4. Evaluate the gate (if declared). After all nodes complete, if the step has an if: block, the gate evaluates per node. Matching nodes survive; non-matching nodes are pruned. After pruning, the step-level action fires (then: if survivors remain, else: if not).
5. Apply the action. Four actions: continue (advance), abort (terminate), jump (reposition cursor), write (persist to slate then advance). Jumps are how reflexion loops work — a gate that fails can jump backward to retry.
6. Move to the next step. The cursor advances (or jumps) and the cycle repeats until all steps complete, a ceiling is hit, or an abort fires.
Data flows through fields, not interpolation
This is the second most important mental shift. In most LLM tooling, you pass data between calls via string interpolation:
# Typical prompt template approach
prompt = f"Question: {question}\nDraft: {draft}\nImprove the draft."In LADR, you never interpolate. You declare typed fields with explicit data sources:
fields:
- { name: Question, type: text, from: input.context }
- { name: Draft, type: ingest, from: { stepId: draft, loopRef: current } }
systemPrompt: "Improve the Draft. Answer the Question."The runtime resolves each field independently, renders them as labeled lines (Question: ..., Draft: ...), and sends the assembled message to the LLM. The system prompt references fields by name, not by placeholder syntax.
Why this matters:
-
Validation. The runtime knows what data each step needs. It can verify that every field reference resolves to an actual source before execution begins. An
ingestfield referencing a non-existent step fails at config load time, not mid-execution. -
Determinism. Given the same inputs and the same config, the same prompt is assembled every time. No hidden dependencies from interpolation that might silently change.
-
Observability. The trace shows exactly what each field resolved to, what the assembled prompt looked like, and what the LLM received. There are no black-box interpolation steps.
-
Composability. Fields are typed interfaces. A step that declares an
ingestfield from{ stepId: draft }can be reused with any upstream step nameddraft— the data plumbing is explicit, not implicit.
The field is the atomic unit of data flow. Every piece of information that reaches an LLM call passes through a declared field. There is no other path.
Control flow is declarative
There is no if, while, or for in LADR YAML. Control flow is expressed through four declarative primitives:
Gates (if:). A typed predicate evaluated after a step's nodes complete. The gate checks each node's output against one of twelve condition keys: nine leaf predicates (string equality, integer/number comparison, substring match, set membership, regex match, JSON schema validation) and three compound combinators (and, or, not) that nest recursively into boolean trees. Non-matching nodes are pruned. The gate fires an action (then: or else:) based on whether any survivors remain.
Jumps (then: { jump: ... }). A jump repositions the cursor. Forward jumps skip steps (early exit on high confidence). Backward jumps create loops (reflexion: generate, verify, reflect, generate again). Cross-ladder jumps hand off to a different ladder entirely (specialist composition). Every jump costs one hop against the ceiling.
Loops (the runtime repeating the steps array). The runtime's main loop iterates the steps array up to maxLoops times. Each iteration is a "loop." Steps in loop N can read outputs from loop N-1 via loopRef: previous, or from all prior loops via loopRef: accumulate.
Recursion (recursion: { maxDepth: N }). A step with a recursion block can spawn a child instance of the same ladder. The child runs the full steps array with the step's output as its input. When the child completes, its exit output replaces the parent's step output. Recursion is how a ladder decomposes problems.
The key insight: the runtime owns all four control-flow mechanisms. The author declares what should happen (the gate condition, the jump target, the recursion depth); the runtime handles how (cursor movement, child spawning, loop iteration). Because the runtime owns the loop, it can enforce hard ceilings that bound total execution.
Memory is explicit, not implicit
Most LLM applications that want "memory" do it by stuffing prior conversation into the prompt. This works until the prompt grows to 50K tokens and the model stops attending to relevant context.
LADR takes a different approach: slates. A slate is a persistent, declared memory store with:
- Folders — named containers, each with a token budget and eviction policy.
- Files — plain-text or JSON documents inside folders.
- Metatags — typed metadata attached to files (string, number, boolean, array, object). The retrieval and ranking layer.
The author declares the schema at config time. At runtime:
- Writes happen via
slateWrite(schema-matched: extract JSON from LLM output, validate against a JSON Schema, write only if valid) or gated writes (write unconditionally when a gate passes). - Reads happen via
slateReadfields (read specific files or metatags) orretrieve:blocks (LLM-driven on-demand fetch: the model sees the file index, requests specific files, the runtime fetches them within a bounded loop).
Slate: Project Memory
├── Folder: lessons
│ ├── failures.md
│ └── wins.md
└── Folder: facts
├── core.md
└── glossary.md
slateRead field ──→ failures.md ──→ "Prompt: Lessons"
slateRead field ──→ core.md ──→ "Prompt: Facts"
│
▼
Step systemPrompt
│
┌─────────┼─────────┐
▼ ▼
slateWrite retrieve: loopThe slate persists across calls within a session. On call 2, the ladder's generate step reads prior lessons and verified findings from the slate. The ladder starts already educated by its own past experience.
Why explicit memory matters:
- The author controls exactly what persists (no invisible context stuffing).
- Token budgets are enforced (folders have
tokenLimit; eviction policies prevent unbounded growth). - Schema matching ensures only structured, validated data hits the slate (no prose noise from unfiltered LLM output).
- Memory is inspectable (slates are visible in Foundry's inspector; files are plain text or JSON).
Safety is economic, not grammatical
LADR is Turing-complete. Ladders can loop, recurse, jump, and self-modify at runtime via dynamic steps. Termination cannot be statically guaranteed — the halting problem is undecidable for Turing-complete languages.
Most languages solve this by restricting the grammar: cap loops at N, ban backward jumps, forbid recursion. LADR doesn't. The grammar is unrestricted because real reasoning is unbounded: a reflexion loop on a hard problem might need 50 iterations; a recursive decomposition might need 8 levels.
Instead, every execution runs under four hard runtime ceilings:
| Ceiling | Default | Platform max | What it bounds |
|---|---|---|---|
| Spend | $5.00 | $50.00 | Total dollar cost per execution |
| Hops | 200 | 10,000 | Total step executions (loops + jumps + recursion) |
| LLM calls | 500 | 5,000 | Total API calls to any provider |
| Recursion depth | 5 | 20 | Maximum nesting depth |
The spend cap is the primary safety net. Every LLM call costs real money. No matter how cleverly a ladder loops or recurses, it cannot avoid paying for each call. The spend cap kills the execution the moment cumulative spend crosses the threshold. This is what makes untrusted published ladders safe to invoke: not grammar restrictions, but economics.
When a ceiling is hit, the runtime exits gracefully — it returns the best-so-far output (the exit step's output from the most recent completed loop), not an error. The caller sees a normal response with a flag indicating the cap was reached. The ladder did as much as it could within its budget.
A complete walkthrough
Let's trace a complete execution of a 4-step Tree-of-Thoughts ladder to see how all the pieces fit together.
The ladder: Draft 5 candidates, evaluate each, prune weak ones, synthesize survivors into a final answer.
The input: "What is the best approach to quantum error correction?"
Step 1: draft (5 nodes, parallel)
The cursor arrives at draft. The runtime:
-
Resolves fields for each of the 5 nodes:
Context(text, frominput.context) → the questionBranch Number(nodeInfo) → 1, 2, 3, 4, 5 respectivelyTotal Branches(knobInfo, frombranchesknob) → 5
-
Assembles 5 prompts (identical except for
Branch Number). -
Dispatches 5 parallel LLM calls.
-
Stores outputs as nodes 1-5 of the
draftstep.
No gate on this step → all 5 nodes survive → cursor advances.
Step 2: evaluate (5 nodes, parallel, with gate)
The cursor arrives at evaluate. The runtime:
-
Resolves fields for each of 5 nodes:
Draft(ingest, fromdraftstep,loopRef: current) → reads the matching draft node (node 1 reads draft node 1, node 2 reads draft node 2, etc.)
-
Dispatches 5 parallel LLM calls. Each evaluates its assigned branch.
-
Each outputs JSON:
{"score": 4, "verdict": "strong"}or similar. -
Gate evaluates per node:
if: { jsonMatches: { properties: { score: { minimum: 4 } } } }- Node 1:
{"score": 3}→ PRUNED (score < 4) - Node 2:
{"score": 5}→ SURVIVES - Node 3:
{"score": 4}→ SURVIVES - Node 4:
{"score": 2}→ PRUNED - Node 5:
{"score": 5}→ SURVIVES
- Node 1:
-
3 survivors remain →
then: continuefires → cursor advances.
Step 3: synthesize (1 node)
The cursor arrives at synthesize. The runtime:
-
Resolves fields:
Surviving Drafts(multi_ingest, fromevaluatewithnodeRef: accumulate) → reads all surviving evaluate outputs (nodes 2, 3, 5)
-
Assembles prompt with 3 numbered draft entries.
-
Dispatches 1 LLM call. The LLM synthesizes the 3 surviving drafts into one coherent answer.
-
No gate → cursor advances.
Step 4: answer (1 node, exit step)
The cursor arrives at answer. The runtime:
-
Resolves
Synthesizedfield (ingest fromsynthesizestep). -
Dispatches 1 LLM call to format the final answer.
-
Output stored as node 1 of
answer. -
answeris the exit step → its output is returned to the caller in OpenAI-compatible format.
Total: 12 LLM calls (5 draft + 5 evaluate + 1 synthesize + 1 answer). Cost: bounded by the execution budget. Time: roughly 3 LLM-rounds deep (draft+evaluate in parallel, synthesize, answer).
The caller sees a standard chat completion response. The intermediate steps, gate evaluations, and pruning decisions are all visible in the trace — but the API surface is identical to a direct model call.
What this mental model unlocks
Once you internalize these six ideas, every LADR feature becomes intuitive:
-
A ladder is a program, not a prompt → you think in terms of structure (steps, gates, data flow), not prompt engineering.
-
Execution is a cursor walking steps → you can trace any ladder's behavior by mentally walking the steps array.
-
Data flows through typed fields → you never interpolate; you declare sources and let the runtime resolve them.
-
Control flow is declarative → gates, jumps, loops, and recursion are structural declarations, not imperative code.
-
Memory is explicit → slates persist what you declare, nothing more.
-
Safety is economic → the spend cap bounds cost regardless of complexity.
Every article in the Language section deepens one of these six ideas. Every tutorial shows them in action. Every example demonstrates them composed into a working strategy.