Dynamic Steps
The Manifest Field
The Manifest Field
The manifest field injects a runtime snapshot of the ladder's state into the prompt. It is the primary input for dynamic step generation — the planner LLM reads the manifest to know what sources it can reference. Manifest fields support targeted views via from:, or a configurable default bundle declared at the config level.
Declaring a manifest field
The manifest field is declared like any other field, with type: manifest. An optional from: selects a specific view; without from:, the field uses the config-level defaultManifest bundle (or the full dump if no default is declared).
fields:
# Specific view — just the current time
- name: Now
type: manifest
from: time
# Specific view — file listing for one folder
- name: Facts
type: manifest
from: { index: { slate: Memory, folder: facts } }
# Bare — uses defaultManifest bundle (or full dump)
- name: Available
type: manifestManifest views
The from: property selects what data the manifest injects. Each view is a single-purpose snapshot — you inject only what the step needs, keeping token cost minimal.
from: value | What it injects | Approximate cost |
|---|---|---|
time | Current timestamp(s): UTC now, optional local time + timezone | ~30 tokens |
structure | Slate titles + folder names, no files | ~3 tokens per folder |
{ index: { slate, folder } } | Flat file list for one folder: [{ name, size }, ...] | ~5 tokens per file |
{ index: { slate, depth: N } } | File lists across all folders in the slate, capped at N files per folder | bounded |
{ tree: { slate, depth: full } } | Full recursive tree of all folders and files | scales with slate size |
{ tree: { slate, depth: N } } | Recursive tree, depth-limited to N | bounded |
bare (no from:) | defaultManifest bundle, or full dump if no default declared | varies |
Time view injects the current timestamp(s). Useful for time-aware reasoning without a separate text field.
- name: Now
type: manifest
from: timeRenders as:
{ "now": "2026-07-26T12:00:00Z", "localNow": "2026-07-26T08:00:00-04:00", "timezone": "America/New_York" }Structure view injects slate titles and folder names — the skeleton of available memory, without any file details. Useful when the planner needs to know what slates exist but doesn't need file-level granularity.
- name: Layout
type: manifest
from: structureIndex view injects a flat file listing. With folder, it lists files in one folder. With depth: N, it lists files across all folders, capped at N files per folder to bound token cost.
- name: Facts
type: manifest
from: { index: { slate: Memory, folder: facts } }
- name: AllFiles
type: manifest
from: { index: { slate: Memory, depth: 10 } }Tree view injects a recursive folder tree. depth: full includes everything; depth: N limits recursion depth.
- name: MemoryTree
type: manifest
from: { tree: { slate: Memory, depth: full } }Default manifest bundle
When a step declares a bare type: manifest (no from:), the engine resolves it using the config-level defaultManifest bundle. This lets you declare a standard set of views once at the top of the ladder and reuse them everywhere.
ladrVersion: "0.1"
name: My Ladder
exit: answer
allowedTargets: { strategy: universal }
knobs: {}
steps: [ ... ]
defaultManifest:
- time
- structure
- index: { slate: Memory, folder: facts }Now any step with a bare manifest field gets exactly these three views — time, structure, and the facts folder listing — instead of the expensive full dump.
Resolution rule:
| Step declares | Config has defaultManifest | Result |
|---|---|---|
type: manifest + from: time | (ignored) | Just the time view |
type: manifest (bare) | Yes | The defaultManifest bundle |
type: manifest (bare) | No | Full dump (backward compat) |
Full dump (backward compat)
When a bare manifest field has no from: AND the config declares no defaultManifest, the engine injects the full runtime snapshot — the original behavior. This includes:
- Inputs — all non-internal input keys (
input.context,input.systemTime, etc.) - Knobs — resolved knob values
- Steps — every step's ID, loop index, node count, and survivor count
- Slates — every slate's title, folders, and files (with sizes)
- Time — current UTC time (+ local time/timezone if supplied)
This is the most expensive option. For production ladders, declare a defaultManifest with targeted views to keep token costs bounded.
Using the manifest for planning
The manifest is the planner's map. The planner LLM reads it, decides what to do, and emits a step config that references only sources listed in the manifest.
- id: plan_next
fields:
- { name: Available, type: manifest }
- { name: Context, type: text, from: input.context }
systemPrompt: |
You are a planning agent. Based on Available sources and the Context,
decide what to do next.
Emit a JSON step config:
{
"systemPrompt": "...",
"fields": [
{ "name": "...", "type": "ingest", "from": { "stepId": "...", "loopRef": "current" } },
{ "name": "...", "type": "slateRead", "from": { "slate": "...", "folder": "...", "file": "..." } }
],
"nodes": 3
}
Reference ONLY sources listed in Available. Do not invent step IDs or slate names.
if:
jsonMatches:
type: object
required: [systemPrompt, fields]
properties:
systemPrompt: { type: string, minLength: 10 }
fields: { type: array, minItems: 1 }
nodes: { type: integer, minimum: 1, maximum: 5 }
then: continue
else: abortThe if: jsonMatches gate self-validates the emitted config before the dynamic step loads it. If the planner's output doesn't match the schema, the ladder aborts (or fires else:).
Validation against the manifest: The runtime validates that every field reference in the generated config resolves to something in the manifest. If the planner references a step ID that doesn't exist, or a slate that wasn't declared, the dynamic step load fails.