Gates
Gate Actions: continue, abort, jump, write
Gate Actions: continue, abort, jump, write
Under then: and else:, four actions are accepted. Each action determines what the runtime cursor does after the gate evaluates. The default for then: is continue; the default for else: is abort. Jumps and writes are how gates become productive — they turn a pass/fail decision into cursor movement or persistent memory.
continue
Advance the cursor to the next step. The default for then:.
then: continueThis is the most common action. Survivors (if any) are visible to downstream steps. The cursor moves to the next step in declaration order.
- id: verify
nodes: 5
if:
jsonMatches: { properties: { score: { minimum: 4 } } }
then: continue # survivors advance; pruned nodes disappear
# else defaults to abortWhen then: continue fires after per-node pruning, only the surviving nodes are visible to the next step. A 5-node step where 3 pass the gate means the next step sees 3 inputs (via multi_ingest: { nodeRef: accumulate }), not 5.
abort
Terminate the ladder. The exit step's output from the most recent completed loop is returned to the caller. The default for else:.
else: abortAbort does not produce an error. The caller receives a normal OpenAI-compatible response with the best-so-far output. The trace shows the abort event so the caller can distinguish "completed normally" from "aborted by gate."
- id: safety_check
systemPrompt: 'Output "safe" or "unsafe".'
if:
equals: "safe"
then: continue # proceed
else: abort # stop immediately, return best-so-farWhen else: abort fires, the runtime returns the exit step's output from the previous loop iteration. If this is the first loop, the exit step may not have run yet — in that case, the output is empty or the best-so-far output from whatever step did complete.
Abort vs graceful ceiling exit. Abort is an author-declared termination. Ceiling exhaustion is a runtime-enforced termination. Both return best-so-far output without error. The difference: abort is intentional (the gate decided to stop), ceiling exhaustion is forced (the budget ran out).
{ jump: ... }
Reposition the cursor. Within-ladder jumps take a stepId; cross-ladder jumps take a ladderId (and optionally a stepId).
then: { jump: { stepId: refine } } # within-ladder
else: { jump: { ladderId: "@alice/helper" } } # cross-ladder
else: { jump: { ladderId: "@alice/helper", stepId: "step3" } } # cross-ladder mid-entryWithin-ladder jumps move the cursor to any step in the current ladder's steps: array. Forward jumps skip steps (early exit). Backward jumps create loops (reflexion). The hop ceiling bounds total jumps, so loops are safe.
# Reflexion loop: critique fails → jump back to draft
- id: critique
if:
jsonMatches: { properties: { verdict: { enum: [good] } } }
then: continue
else: { jump: { stepId: draft } } # backward jump → retry loopCross-ladder jumps terminate the current ladder and start the target ladder via a tail-call handoff. The firing step's output becomes the target ladder's input.context. The target's exit-step output becomes the user-visible result.
See Jumps for the full reference, including passFields for wiring data into jump targets.
{ write: ... }
Write a source verbatim to a slate. No schema matching; just write the named source. Used for "if the gate passes, persist the relevant content" patterns.
then:
write:
to: { slate: "Memory", folder: trace, file: verified.md }
from: previous # all survivors of the previous step
on: append # append | overwrite | mergeByKeyThe from: field uses the unified source vocabulary (previous, output, { stepId, ... }, { slate, ... }, or a literal string). See Reads and Writes for the full reference.
Gated write vs slateWrite. A gated write action writes unconditionally when the gate fires — no JSON extraction or schema validation. A slateWrite block on the step extracts JSON from the output and validates against a schema before writing. Use gated writes when you want to persist raw text; use slateWrite when you want to persist only validated structured data.
# Gated write: persist survivors verbatim when gate passes
- id: verify
if:
jsonMatches: { properties: { score: { minimum: 4 } } }
then:
write:
to: { slate: "Memory", folder: winners, file: best.md }
from: previous
on: append
else: continue
# slateWrite: persist validated JSON after the step runs
- id: extract
slateWrite:
to: { slate: "Memory", folder: facts, file: core.md }
field: fact
on: append
match:
type: object
required: [fact]
properties:
fact: { type: string, minLength: 5 }The gated write fires only when the gate passes and writes raw text. The slateWrite runs after every execution of the step and writes only validated JSON.