Build
Steps
A step is one meaningful unit of work inside a loop. Steps make a process easier to understand by showing where judgment, action, waiting, and approval happen.
Step boundaries are useful for more than execution. They give operators a clear timeline, preserve useful intermediate results, and let you retry or change one responsibility without rewriting the whole process.
Most loops should begin with one agent step. Split work when a boundary makes responsibility or evidence clearer, not merely because the prompt has several sentences.
Choose a step by responsibility
| Step | kind | Required config | Use it when |
|---|---|---|---|
| Agent | agent | instructions | The work needs judgment, synthesis, or flexible tool use. |
| Action | action | action_name | The work needs one exact operation, such as posting a message or updating a record. |
| Check | check | checks | The loop needs evidence that a condition is true. |
| Sleep | sleep | duration or until | Time owns the next move. |
| Wait for event | wait_for_event | event_type | Another system owns the next move. |
| Interaction | interaction | protocol and targets | A human or agent must answer, review, or approve. |
| Child loop | loop | loop_id | Another reusable process should begin. |
When in doubt, keep judgment in an agent step and side effects in an action step. A review agent can recommend a label; a following action can apply it. The run then shows exactly which decision led to which change.
Author the step object
Every entry in steps has kind and config. It may also have a stable id,
a display name, a step-level if, retry, and timeout. For example, this
deterministic action runs only for high-severity classifications:
steps:
- id: apply-label
name: Apply label
kind: action
if: steps.classify.output.severity == "high"
config:
action_name: github.issue.add_labels
execution_location: managed
parameters:
repo_full_name: "${{ event.repository.full_name }}"
issue_number: "${{ event.issue.number }}"
labels:
- "${{ steps.classify.output.label }}"
retry:
max_attempts: 3
delay: 2s
timeout:
duration: 30sAction inputs always belong under config.parameters. Use
config.execution_location only when routing matters: managed, worker, or
environment. Pass the complete loop file to mobius loops create -f or
mobius loops update -f; invalid kinds now report the full accepted set, and
common action-input aliases such as input and params point to parameters.
Expressions and templates
String leaves in step configuration use ${{ ... }} interpolation. The
expression environment has four roots:
| Root | Contents |
|---|---|
event | The triggering event or manually supplied event data. |
meta | Trigger and routing metadata. |
config | The loop's resolved run configuration. |
steps.<id>.output | The output of a prior step. steps[0].output also works. |
The step-level if field is a bare expression over the same roots. It must
evaluate to true or false; false records the step as skipped. Write
if: event.severity == "high", not a quoted template. A surrounding
${{ ... }} is accepted for convenience but is unnecessary. Later-step
outputs are unavailable because they do not exist yet, and an unknown path
fails instead of rendering an empty value.
Templates support comparisons, boolean and arithmetic operators, indexing,
optional access, and value-first pipelines such as
${{ event.labels | map(it.name) | join(", ") }}. Available helpers are:
- Core:
len,string,int,float,bool,contains,has,keys,entries,lower,upper, andsprintf. - Strings, collections, and math:
trim,split,join,replace,startsWith,endsWith,first,last,sum,slice,sort,reverse,min,max,abs,floor,ceil, andround. - Collection forms:
map,filter,flatMap,sortBy,any,all,find,count,if, andtry. - Formatting and structure:
coalesce,get,dig,trimPrefix,trimSuffix,title,capitalize,truncate,indent,quote,slug,lines,uniq,bullets,json,prettyJSON,fromJSON,now, andtoday.
Static objects and arrays in parameters keep their types while Mobius
recursively renders their string leaves. An interpolation that returns an
object or array becomes compact JSON text; Mobius does not parse rendered JSON
text back into a structured parameter. Use a static object with templated
leaves when the action expects an object. Use fromJSON(raw).field when a
provider value is a JSON string and you need one field from it.
Connect steps through results
Each completed step can save a result for later steps. A step named
classify, for example, might return a label, severity, and summary. A later
notification step can use those values without receiving the first agent's
whole transcript.
Prefer small, structured results between steps. They are easier to inspect, safer to reuse, and more stable than passing long prose or hidden conversation history through the process.
Mobius also gives an agent step the run's inputs, triggering event, metadata, and earlier results as context. Use templates when a value must appear in a specific field or sentence; use context for the broader information the agent needs to understand the task.
Use agent and action steps together
Agent and action steps solve different problems:
- An agent decides what should happen when the answer depends on context.
- An action performs a named operation with explicit inputs.
Keeping them separate gives you a clean place to add a check or interaction before an irreversible change. It also makes retries safer because the run can show whether the decision or the side effect failed.
An agent may call actions as tools within its own step when exploration is part of the task. Use a separate action step when the operation is a required part of the process and should be visible on the timeline.
Pause without losing the process
Some work cannot continue immediately. A sleep step waits for time. A wait-for- event step waits for another system. An interaction waits for a person or agent to provide information, approval, or review.
While a step is waiting, the run is suspended, not broken. Its state is saved and it resumes when the expected answer arrives. Use a timeout when no answer should leave the work open forever.
Add checks where evidence matters
A check turns “this looks good” into a visible decision point. Put it after the step that creates the evidence and before the action that depends on it.
Checks can stop the run, let it continue with a recorded failure, or open an approval gate. Prefer evidence from action results, test output, or artifacts over an agent's own claim that its work succeeded.
Conditions keep optional work visible
A condition lets a step run only when it is relevant. For example, a loop can skip escalation for low-severity findings while still showing the skipped step on the timeline.
Use a condition for process logic that operators should be able to see. Keep domain judgment inside an agent step when the choice cannot be expressed as a clear rule.
Retries and timeouts
Retries are for temporary failures. Timeouts keep a step from waiting or running without a useful end. Both are safety controls, not substitutes for a well-defined responsibility.
Be cautious when retrying side effects. Mobius can retry a step, but it cannot undo a message already sent or a record already changed. Make external actions idempotent when the same request might be attempted again.
Build steps in the app
Open Build > Loops, choose a loop, and add steps in the order a person would explain the process. Name each step after its responsibility, such as Classify issue, Request approval, and Apply label.
Run the loop manually and read the timeline. If one step hides several important decisions or side effects, split it. If several steps always change together and add no useful checkpoint, combine them.
The interactive API reference contains exact step fields, expression syntax, retry options, and per-kind configuration.
Next
- Put steps in a reusable process with loops.
- Inspect their execution with runs.
- Pause for a decision with interactions.
- Design limits and checks with guardrails.