Learn
How Mobius works
Mobius runs loops: versioned definitions that start from triggers, execute ordered steps, and leave behind inspectable runs.
If you remember one shape, make it this one:
organization
project: platform
agents: Triager, Scout
toolkits, skills, tables, environments
loop: morning-brief
version 1
triggers: schedule, event, http, manual
steps: agent, action, sleep, wait_for_event, interaction, loop
run: run_01...
step records, event stream, context, artifactsA project is the workspace boundary. A loop is the thing you author. A run is one execution of a published loop version. A step is one bounded piece of work inside that run.
Resource hierarchy
Everything customer-visible is scoped to a project. That includes loops, runs, agents, workers, integrations, secrets, tables, artifacts, API keys, roles, and audit records.
Loops are versioned. Publishing a version makes that version runnable and supersedes the previous published version. Existing runs keep their original version, so a run started on version 3 never changes behavior because you publish version 4.
Agents are project resources too. An agent can run inside a loop step, but it is not owned by that loop. The same agent can also have agent sessions, table grants, toolkits, skills, and managed environment access.
The run lifecycle
trigger fires
Mobius creates or reuses a trigger-fire record
Mobius starts a run against the latest runnable loop version
each step moves from pending to running
step output is saved into run context under save_as or the step key
later steps read inputs and context through templates
artifacts are published against the active run and step
the run ends as completed, failed, or cancelledThe run event stream is the operator view. It records events such as
run.started, step.started, action.completed, artifact.created,
limit.reached, run.completed, and run.failed with a per-run sequence
number. The app, CLI, and HTTP API all read the same event log.
Step types
| Step type | What runs | When to use it |
|---|---|---|
agent | One bounded agent turn. The agent receives instructions, may call tools, and returns an output. | Use this when judgment, tool use, or synthesis belongs inside the run. |
action | One deterministic action call with rendered parameters. It does not start an LLM turn. | Use this for Slack posts, GitHub labels, table writes, worker code, or any exact side effect. |
sleep | A timer. The run suspends until duration or until. | Use this when time is the condition. |
wait_for_event | A source-event subscription. The run suspends until event_type matches, optionally with source_id, match, condition, and payload_mapping. | Use this when another system needs to answer before the run continues. |
interaction | A request to humans or agents using request_information, request_approval, or request_review. | Use this when the run needs a decision or response in the Mobius inbox. |
loop | A fire-and-forget child run of another loop in the same project. | Use this when one loop should hand work to another loop without waiting for the child result. |
Authored specs cannot include cleanup steps. Mobius materializes cleanup
from the loop's cleanup block and from resources it allocated for the run.
When in doubt, start with one agent step followed by one action step. Add
waits only when the outside world actually needs to answer.
Triggers
Triggers belong to loops. When a trigger fires, Mobius starts one run or applies the loop's concurrency policy if another run is active.
| Trigger kind | Starts from | Use when |
|---|---|---|
schedule | A cron-like schedule. | The loop should run on a clock, such as morning-brief at 7:00 on weekdays. |
event | A public source event such as github.pull_request.opened or table.row.inserted. | A provider or Mobius resource already emits the event you need. |
http | An inbound POST to the trigger's delivery URL. | Another system can call Mobius directly. |
manual | A user, CLI command, or API request starts the run. | You want an operator-controlled start path. |
Trigger config becomes run input. For example, a GitHub event trigger starts a run with the event payload and metadata under the input envelope, while an HTTP trigger forwards the request payload.
Context and templates
Every step can receive an input object. String leaves in that object may use
Go text/template expressions against run inputs and prior step context:
input:
issue_title: "{{ .inputs.event.issue.title }}"
triage_label: "{{ .context.classify.label }}"Step output is saved under save_as. If save_as is omitted, Mobius uses the
step key. This keeps later prompts and action parameters explicit:
steps:
- key: classify
kind: agent
save_as: triage
config:
agent_id: agt_triager
instructions: "Classify the issue and return JSON."
- key: label
kind: action
config:
action_name: github.issue.add_labels
parameters:
label: "{{ .context.triage.label }}"Agent transcripts are not shared between steps by default. If one agent's work should guide a later step, have the producing step return a compact structured output and save it in context.
Artifacts
Artifacts are durable files produced by a run: markdown reports, JSON, images, audio, PDFs, source files, or anything else a worker publishes. They are separate from a managed environment's scratch filesystem.
Workers publish artifacts with an active lease token, so Mobius can derive the
run, step, worker session, attempt, and visibility. That is why the run page
can show "Scout produced brief.md during summarize" without trusting
caller-supplied lineage.
Use artifacts for outputs humans or later systems should inspect. Use run context for small structured values that later steps need.
Why every run halts
Mobius is built around unattended execution, so every path needs a stop condition. These are the bounds enforced today:
| Bound | What happens | Why it exists |
|---|---|---|
defaults.wall_clock_timeout | Mobius stamps a run deadline. The reaper fails the run with wall_clock_deadline_exceeded after the deadline passes. | A run should not occupy runtime state forever because one step wedged. |
| Step retries | retry.max_attempts defaults to 1. Retries stop when attempts are exhausted. | Transient failures get another chance, but an action cannot loop forever. |
| Per-run step cap | Billing policy rejects an oversized plan at start, and the engine emits limit.reached if a running plan crosses the cap. | Loop expansion and child-step materialization need a hard ceiling. |
| Concurrency policy | allow, queue, skip, or replace decides what a new trigger fire does while another run is active. | Overlap is a product choice, not an accident. |
| Suspended-step timeout | Waiting steps can set timeout.duration. Today timeout.on_timeout supports fail, so a timed-out wait_for_event or interaction fails the waiting step. | A run waiting on another person or system still needs a final state. |
| Cancellation | Operators can cancel a run. Mobius closes open waits and records run.cancelled. | Humans need a hard stop for obsolete or unsafe work. |
Older examples may use forbid for concurrency. The compiler accepts it as a
compatibility alias, but new specs should use skip.
Next
- Build your first loop in the quickstart.
- Read the step contract in steps.
- Learn how runs behave in runs.
- Choose trigger types in triggers.