Guides
Best Practices
Best Practices
Do's and don'ts for authoring safe, efficient, and maintainable ladders. Hard-won from the reference implementation and the Crucible composition.
Safety + budgets
Do always set an executionBudget before publishing. The platform default ($5/call) is generous; consider tighter defaults for public ladders to protect callers from unexpected spend.
Don't publish a ladder without a maxSpend ceiling. An open-ended ladder is an open-ended spend commitment.
Do test your ladder at the boundary of its budget. Set maxSpend: 0.01 temporarily and verify the engine handles exhaustion gracefully (best-so-far output, best-so-far output returned (ceiling exhaustion is graceful)).
Don't rely on the platform maximums ($50, 10k hops, 5k LLM calls) as your defaults. Those exist for first-party ladders; directory ladders should be much tighter.
Do declare your real ceiling needs honestly. If your ladder needs maxHops: 30, declare it. If it works in 10, declare 10. Inflated ceilings invite callers to spend more than necessary.
Don't set maxRecursion higher than you actually use. Most ladders never need >3. Each level multiplies cost.
Do prefer hops-bounded reflexion (jumps) over recursion for retry loops. Recursion spawns child ladders; jumps just reposition the cursor. Jumps are cheaper and easier to reason about.
Don't combine dynamic: true with recursion in the same generated step config; validation rejects it (nested dynamic + recursion is a runaway risk).
Prompting inside a ladder
Do use field names as labels in your prompt. The engine renders fields as Name: <value>; design the names so they read naturally in the prompt.
Don't inject knob values as plain numbers without context. Use a system prompt that explains what the knob means; the model reads the rendered Branch Count: 5 field line, so your prompt can reference "the Branch Count field above" to give it meaning.
Do give the LLM explicit output format instructions when you intend to gate or persist via JSON. "Output JSON: {...}" beats "output JSON."
Don't ask the LLM to emit JSON in one step and parse it informally in another. Use if.jsonMatches to verify before consuming. Failed validation is a silent no-op (no error, no event). Inspect the slate after execution to verify writes occurred.
Do use few-shot examples in the system prompt when the output shape is non-obvious. One concrete example beats five lines of description.
Don't stuff entire slate contents into the prompt unconditionally. Use retrieve: for on-demand fetch, or read specific files/metatags rather than the whole folder.
Do use the nodeInfo field when running multi-node steps; telling each node "you are node 3 of 5" produces more varied drafts than anonymous parallel calls.
Don't expect multi_ingest to deduplicate for you. If your source step has duplicate outputs, dedupe upstream with a gate or downstream in the synthesizer's system prompt.
Slates + memory
Do declare metatags for any property you'll want to filter or rank on later. Metatags are the discovery layer; file contents are the data layer.
Don't use file contents to encode structured data when metatags will do. Metatags are typed, schema'd, and checkable at validation time. File contents are freeform.
Do use mergeByKey when maintaining a registry / index / knowledge graph as a JSON file. append duplicates; overwrite wipes; mergeByKey updates by key.
Don't use evictionPolicy: reject on a folder that receives schema-matched writes. The write will silently no-op every time the file is full, with no easy way to recover.
Do set per-folder token limits that reflect what's actually useful. A 5000-token lessons folder bloats every prompt that reads it.
Don't share slates across ladders via cross-ladder jumps without thinking through concurrency. The current model is single-writer-per-call; cross-ladder writes are advanced.
Do test slate persistence by calling the ladder twice with related inputs and verifying the second call sees the first call's writes.
Don't rely on slate state for correctness without a fallback. Slates can be empty (first call), corrupted (sync edge cases), or evicted (FIFO/LRU). Code defensively.
Observability + debugging
Do mark timeline-significant steps with timeline: circle. Noise steps (intermediate parsing, schema-checking) should be left unmarked or marked timeline: init.
Don't rely on log-reading for debugging. Studio and Foundry's live timelines are the primary debugging surface; every step, node, gate evaluation, and slate write is rendered visually.
Do inspect the trace when a ladder behaves unexpectedly. Look for error, node_failed, and NodeDependencyError events. Schema-matched write failures are silent no-ops — inspect the slate to verify writes occurred.
Don't ship a ladder that hits ceilings on common inputs. If your typical call hits maxHops, either raise the ceiling or restructure the ladder.
Do version-pin in production. Call with @author/name@v3 instead of @author/name so a buggy new version doesn't break your app.
Don't assume a ladder is deterministic. LLMs are stochastic; multi-node ladders more so. If you need reproducibility, set temperature to 0 in the model parameters (passed through to the provider).
Publishing + sharing
Do set config_visibility: public only if you're comfortable with the full config being disclosed. Public-free ladders are AGPL; the config is the source.
Don't publish a ladder you wouldn't be comfortable seeing called 100,000 times. Iterate privately first.
Do include a one-line description that explains what the ladder does. The library surfaces this in listings.
Don't use clickbait descriptions. Accurate descriptions help callers find the right ladder for their task.
Do pin a stable version (@v3) once you have production callers. Latest is fine for experimentation; pinned is correct for production.
Don't break old versions when shipping new ones. The platform preserves all versions; new versions are new slugs (@v4). Existing pinned callers keep working.