Skip to content
Orbital

One-off workflows

Let an agent propose a workflow for one piece of work, and approve it before it runs

Some work needs a process no saved workflow has, and is not worth saving. A One-off Workflow is written for one run. An agent proposes its TOML text, Orbital checks it, and the run waits for you as a held Run until you approve it. It is an experimental feature: turn on One-off workflows in Settings > Experimental.

A One-off Workflow is the same version = 3 TOML as any workflow file, with a few extra rules so one run cannot grow without bounds:

  • it is at most 64 KiB and has at most 40 steps;
  • it is self-contained: no import, no prompt_file and no [repeat];
  • it sets a workflow-level max_visits of at most 50, and no failure route loops back without a bound.

Text from a source Orbital does not trust also may not use auto_merge or permissions = "full". An agent checks text with validate_workflow and its source argument before proposing it. Text that breaks a rule is refused with each problem named, and no run is created.

A One-off Workflow never appears in a workflow list, the start composer, the trigger picker or list_workflows. Its run keeps its own snapshot, so Resume, Retry, Jump, Restart and Run Export never look its name up, even if a saved workflow has the same name.

A held Run is listed as One-off and Needs approval, and raises a desktop notification when notifications are on. Its page shows what you need to decide:

  • where the text came from, with a link, and whether Orbital trusts that source;
  • the canvas, the harnesses and styles its steps use, and its warnings;
  • the keys with effects outside the worktree, such as command scripts, git.push, github.pr.open, worktree.remove, auto_merge and permissions = "full". Every agent step has every tool, so read the prompts too;
  • a cost caution, the TOML and when the approval expires.

Start the run to approve it: it runs exactly the text you read. Abort it to decline it. A held Run nobody decides on within 24 hours ends Aborted with “Approval expired”. A project holds at most 10 runs waiting for approval.

Source Trusted What happens
The delivery lead’s chat Yes Always held, whatever Start runs says, unless it says Never, which refuses it. A new proposal under the same name replaces the earlier one.
A trigger rule Yes, once you approve the rule The rule pins the text, and each firing runs it without asking.
A watcher wake-up No Always held, with the wake-up’s reason. The delivery lead is woken with your decision.
A step of a run No Always held as a child run of the run that proposed it.
An agent outside Orbital No Settings > MCP decides.

Ask the delivery lead for work no saved workflow fits. It writes a One-off Workflow, checks it and proposes it with start_run and source. The held Run appears in the chat’s Run tab. If you ask for changes, its next proposal under the same name replaces the earlier one, which ends Aborted with “Replaced by a newer version”.

A trigger rule’s Start action can pin a One-off Workflow’s text and name. Every firing runs exactly that text, so the rule itself is what you approve. A rule an agent creates or changes with pinned text is saved switched off and marked Needs approval, and only you can switch it on, in the app. A rule never takes a definition from the event that fired it: event fields reach the run only through the goal, as for any rule.

When the watcher wakes the delivery lead about a run, such as one that failed, and it proposes a One-off Workflow before you have written anything, the proposal is untrusted. It is held with the reason it woke and the run it was about, and a newer proposal about the same run replaces it. The delivery lead is woken again with what you decided.

A step can propose a child run with the run.propose action. A planner step writes the TOML into the run’s context, and the action proposes it:

[steps.propose]
kind = "action"
do = "run.propose"
source = "{{ context.plan }}"
name = "triage"
goal = "Triage the failing tests"
wait = true
routes = [
{ if = "run.child.outcome == 'succeeded'", to = "steps.done" },
]
next = "steps.failed"

The child is held in the same project and links back to the step that proposed it. The step writes run.child.id. With wait = true, the run gives back its Run Slot and parks until the child ends, then writes run.child.outcome: succeeded, failed, aborted, declined or expired. A proposed run cannot propose another, and one run proposes at most 3 children. Invalid text fails the step and creates no child.

An agent connected over MCP proposes with start_run and source. One-off workflows from agents outside Orbital in Settings > MCP decides what happens: Ask me first, the default, holds the run; Do it starts it at once; Never refuses it. The run shows the client name the agent reported, marked as unverified. Restarting such a run holds it for approval again.

A One-off Workflow that worked can become a saved workflow. Open the run, then choose Save as workflow from its Actions menu. The text opens in the editor as an Import, and you save it as any workflow.