# Overview > Agents that finish the job. Turn goals into finished work. You pick a workflow and a project, and describe what you want. Orbital runs the workflow one step at a time on your own machine, with agents such as Claude Code, Codex and OpenCode, and you watch, steer and collect the result. Use this prompt to get started faster: ```text Help me set up Orbital, an app that runs agent workflows on my own machine. Its docs index is https://docs.beta.runorbital.dev/llms.txt. Work through the steps below in order. Ask me one question at a time, and wait for my answer before you ask the next one. Don't change my files, except to register Orbital with you in step 3. 1. Install Orbital. Ask me which system I use. On a Mac, I download the app for Apple Silicon (https://releases.runorbital.dev/macos/Orbital-latest-arm64.dmg) or Intel (https://releases.runorbital.dev/macos/Orbital-latest-x64.dmg), drag it to Applications and open it. On Linux or Windows, follow https://docs.beta.runorbital.dev/get-started/install.md to install the `orbital` command, then run `orbital serve`. 2. Check for an agent. Orbital runs Claude Code, Codex or OpenCode, and ships none of them. Check whether `claude`, `codex` or `opencode` is installed and signed in. If none is, ask me which one I want, and help me install it and sign in. 3. Connect Orbital to you over MCP. Ask me to open Settings > MCP in Orbital, and to paste the endpoint address and the token. Register a server named `orbital` with that address and the header `Authorization: Bearer `, the way https://docs.beta.runorbital.dev/mcp/connect.md shows for your client. If your client needs a new session to see the server, tell me. 4. Pick a project. Call `list_projects`. If there is none, ask me to add a Git repository as a project in the app, then call it again. Ask me which project to use. 5. Start the first run. Call `start_run` with that project, the workflow `explain-repo` and the goal "Explain this repository: what it does, who it is for, how it is built and how to run it." The workflow only reads the repository. Follow the run with `wait_for_run` until it ends, then tell me what it found and where the app shows the run. ``` The prompt has your coding agent install Orbital, connect to it over MCP and start the read-only `explain-repo` workflow on a repository of yours. To do the same by hand, follow [Install Orbital](/get-started/install/) and [Start your first run](/get-started/first-run/). ![A run page with an agent step selected, showing the step's prompt, the agent's answer and the values it recorded, with run facts, pull requests and the list of steps on the right.](/screenshots/run-hero.webp?v=4fa21a573c) *A run shows every step: what the agent was asked, what it answered and what comes next.* ## What you can run | Kind of work | What Orbital does | Example | | --- | --- | --- | | Research and reporting | Researches a question on the web and writes a short report with its sources. | [Research report](/examples/research-report/) | | Triage | Proposes a label, a priority and a next step for each open issue. | [Triage issues](/examples/triage-issues/) | | Code | Implements a feature, delivers an epic ticket by ticket, or reviews pull requests. | [Implement a feature](/examples/feature-pull-request/) | Use Orbital for work you repeat. In one long agent session, you must remember when to research, draft, review, wait and stop. A workflow writes that process down once, so every run follows it. For a one-off question, a plain chat is enough, and Orbital has [chats](/chats/) too. ![You start a run from a project and a workflow. Each agent step runs on the harness and model chosen for it, and the run ends with a result such as a report, a summary, a triaged board or a pull request.](/overview.svg?v=b9455d0602) ## The parts A **run** carries out one workflow for one project, towards one goal. Each step runs on the harness and model that its role, its model settings or the run chose, so one run can use several. Orbital keeps every run's history, and a run carries on where it was when Orbital restarts. Read [Runs](/runs/). A **workflow** is a file of steps. Most steps are prompts for an agent. Others are checks a machine does better, such as "did anything change?" or "did the pull request merge?". The edges between steps say what happens next, so a workflow can branch, loop and wait. Orbital ships eight workflows, from `explain-repo`, which only reads, to `implement-epic`, which delivers a whole epic. Read [Workflows](/workflows/) and [Shipped workflows](/reference/shipped-workflows/). A **project** is a name and the folders a run works in. A folder can be a Git repository, or a plain folder of notes and documents. Plain folders work with every harness except Codex, which needs a Git repository. Read [Projects and folders](/projects/). A **harness** is the agent program that does an agent step: Claude Code, Codex or OpenCode. You sign in to it yourself, and Orbital uses your plan. [Roles and model settings](/roles/) choose the harness and model step by step. Read [Harnesses and models](/harnesses/). The **Supervisor** is an optional chat that acts as your delivery lead. A watcher wakes it when a run stops, fails or runs out of quota, and it steps in inside the limits you set. Read [Supervisor](/supervisor/). [How Orbital works](/concepts/how-orbital-works/) puts every idea on one page. ## How work reaches you Most results arrive as text. The agent's last reply is the answer, and each step's output stays in the run's **Context** tab. The research, follow-up and triage workflows work this way, and so does `explain-repo`. A workflow that changes code can add a worktree step. A **worktree** is an isolated copy of a repository on a branch of its own, so your own checkout stays untouched. Workflows that use one can then open a pull request, repair failing CI, conflicts and review comments, and wait for it to merge. Read [Worktrees and delivery](/projects/worktrees-and-delivery/). ## Where it runs Orbital runs on your machine. The Mac app starts its own server. On other systems, `orbital serve` starts the server and you use Orbital in a browser. Coding agents can drive Orbital too, over [MCP](/mcp/connect/). ## Find your way - [Get started](/get-started/install/): Install the app, add a project and start your first run. - [Runs](/runs/): Start, watch, message and stop a run, and read its result. - [Workflows](/workflows/): Write the steps an agent follows, with branches, loops and gates. - [Automate](/triggers/): Start runs from GitHub, Linear or a schedule, and let a supervisor lead delivery. - [Examples](/examples/feature-pull-request/): Complete workflows for code, research and triage, with downloads. - [Reference](/reference/attributes/): Look up attributes, the command line, the HTTP API and MCP tools. ## Related - [Install Orbital](/get-started/install/): get the Mac app or the `orbital` command and sign in to a harness. - [Start your first run](/get-started/first-run/): run `explain-repo` on a repository you already have. - [Build with AI](/get-started/build-with-ai/): connect coding agents, and give them these docs as Markdown. - [How Orbital works](/concepts/how-orbital-works/): every idea in Orbital, with a link to its page. --- # Install Orbital > Install the Mac app or the orbital command, then sign in to a harness You install Orbital, then sign in to at least one harness, the coding agent that does the work: Claude Code, Codex or OpenCode. At the end, **Settings > Harnesses** shows a harness ready to take a run. About 5 minutes. ## Before you start On a Mac, you need nothing else. Orbital.app starts its own server, updates itself and needs neither npm nor Node. On Linux or Windows, you install the `orbital` command with npm and run `orbital serve`. You need Node 24.19 or newer and `git`, and an invitation to the npm package during the beta. ## 1. Install Orbital **Mac app** 1. **Download the disk image for your Mac.** These addresses always serve the newest release: [Apple Silicon](https://releases.runorbital.dev/macos/Orbital-latest-arm64.dmg) or [Intel](https://releases.runorbital.dev/macos/Orbital-latest-x64.dmg). 2. **Drag Orbital to Applications.** Open the disk image and drag Orbital to the **Applications** folder. 3. **Open Orbital.** Open it from **Applications** or the Dock. The first launch asks you to set up a project. ![The first-launch wizard with the heading Give your agents a place to work, the steps Welcome, Project and First run, and a Set up a project button.](/screenshots/first-launch.webp?v=5ece35762f) *The first launch walks you through adding a project.* The app is signed and notarised. It reads your login shell's `PATH`, so it finds `git`, `claude`, `codex` and `opencode` where your terminal finds them. It serves on port 42121, or on a free port when another program holds that one. **Command line** The Mac app carries the `orbital` command. To use it from a terminal: 1. **Open the command line settings.** Open **Settings > Command line and skills**. 2. **Install the command.** Under **The orbital command**, choose **Install**. You can also choose **Orbital > Install Command Line Tool** from the menu bar. 3. **Give your password if asked.** Enter your administrator password if your Mac asks for it. 4. **Check the command.** Open a new terminal and run `orbital --help`. This links `/usr/local/bin/orbital` to the command inside the app. After the app updates, the command runs the new release without another install. `orbital run start` then starts runs in the open app. [The orbital command](/command-line/) explains every command. **npm** npm access is by invitation during the beta. The Mac app is the main way to get Orbital, and npm releases are on hold. To ask for access, contact the Orbital team through [runorbital.dev](https://runorbital.dev) and give your GitHub account name. Use npm on Linux or Windows, or on a Mac without the app. You need Node 24.19 or newer and `git`. Check them: ```sh node --version git --version ``` The package is `@flashingpumpkin/orbital` on GitHub Packages, which needs a token even to read it. Once your invitation gives your GitHub account read access: 1. **Create a token.** Create a [classic personal access token](https://github.com/settings/tokens) with only the `read:packages` scope. 2. **Add the registry to npm.** Add these two lines to `~/.npmrc`, with your token in place of ``: ```ini title="~/.npmrc" @flashingpumpkin:registry=https://npm.pkg.github.com //npm.pkg.github.com/:_authToken= ``` 3. **Install and check.** ```sh npm install --global @flashingpumpkin/orbital orbital --version ``` 4. **Start the server.** ```sh orbital serve ``` The server prints its address, `http://127.0.0.1:42121/` unless you change the port, and opens it in your browser. It listens only on your own machine. `orbital app` opens the same app in a window of its own instead. [The orbital command](/reference/command/#orbital-serve) lists the flags. With pnpm, allow Orbital's storage dependency to build: `pnpm add --global --allow-build=better-sqlite3 @flashingpumpkin/orbital`. This registry token only downloads the package. Orbital never reads it. Workflows that read GitHub use a separate GitHub token; see [Settings](/reference/settings/#github). :::caution The server has no login of its own. Keep it on your own machine unless you protect the network yourself. ::: ## 2. Install and sign in to a harness Orbital runs the harnesses you install yourself, and ships none of them. Install at least one. Sign in before you start Orbital, because a running server does not see credentials you export later. **Claude Code** Install it with `npm install -g @anthropic-ai/claude-code@latest`. Orbital works with version 2.1.280 or newer. To sign in, run `claude` and follow the login. Leave `ANTHROPIC_API_KEY` unset to use your subscription. The [Claude Code setup guide](https://code.claude.com/docs/en/setup) covers installing it on your system. **Codex** Install it with `npm install -g @openai/codex@latest`. Orbital works with version 0.156.0 or newer. To sign in, run `codex login`. The [Codex guide](https://learn.chatgpt.com/docs/codex/cli) covers installing it on your system. **OpenCode** Install it with `npm install -g opencode-ai@latest`. Orbital works with version 1.18.0 or newer. To sign in, run `opencode auth login` for your provider. The [OpenCode guide](https://opencode.ai/docs/cli/#auth) covers installing it on your system. ## 3. Check that Orbital finds the harness Check the harness in a terminal first: `claude --version`, `codex --version` or `opencode --version`. Then open **Settings > Harnesses** in Orbital. The status table shows whether each harness is installed, whether it is new enough and whether it is signed in. ![Settings, Harnesses page with a status table showing whether Claude, Codex and OpenCode are installed and signed in, followed by Claude's settings for turned on, program, default model, default effort and environment variables.](/screenshots/settings-harnesses.webp?v=6446e71ff0) *The status table shows which harnesses are ready.* Orbital looks for each harness in this order: 1. The program set under **Program** in **Settings > Harnesses**. 2. `claude`, `codex` or `opencode` on the `PATH` of your login shell. The Mac app reads that `PATH` from your shell when it opens, so Orbital finds any harness you can start in a terminal. A harness Orbital cannot find reads "Not installed", with the command to install it. Install it, then choose **Check again**. [Harnesses and models](/harnesses/) explains more. ## Upgrade Orbital **Mac app** The app checks for a new release when it starts and every hour. It downloads an update in the background and says "Orbital \ is downloading". When the update is ready, choose **Restart to update**, or it installs the next time you quit. **Settings > About** shows the version you are running, the last check and its result, and a **Check for updates** button. Orbital updates itself only from the **Applications** folder. Opened from the disk image, it says "Move Orbital to Applications to get updates". ![Settings, About page showing the running version, its updates and the path of the settings file.](/screenshots/settings-about.webp?v=ae05abacce) *About shows the version you run and whether an update is waiting.* **Command line** The `orbital` command installed from the Mac app is a link to the command inside the app. It updates with the app, and runs the new release without another install. **npm** The npm package checks for a newer release when the server starts and once an hour. In a terminal, `orbital serve` asks before it starts: ```text Orbital is available. You are running . Update before starting? [Y/n] ``` A server that is already running shows "Orbital \ is available." with an **Update and restart** button. Runs that are working carry on by themselves after the restart. You can also run `npm update --global @flashingpumpkin/orbital` and restart the server. Your data in `~/.orbital` survives upgrades and uninstalling. :::caution An older release refuses run history that a newer one has updated. Keep a copy of `~/.orbital/run-history.db` before you go back to an older release. ::: ## Next steps - [Start your first run](/get-started/first-run/): add a project and run `explain-repo`. - [Harnesses and models](/harnesses/): how Orbital picks and starts each harness. - [Command line](/command-line/): what each subcommand does. - [Troubleshooting](/troubleshooting/): fixes when the app or a harness will not start. --- # Start your first run > Add a repository as a project, run explain-repo and read its answer You add a repository you already have as a [project](/projects/) and run the `explain-repo` workflow on it. One agent step reads the repository and answers with its purpose, who it is for, how it is built and how to run it. About 10 minutes. The run itself usually takes one to three minutes. The run only reads. It makes no worktree, no branch and no pull request. ## Before you start You need a Mac for the app. On Linux or Windows, [install the `orbital` command](/get-started/install/) with npm and run `orbital serve` instead. The steps in the browser are the same. You also need a repository on your machine to explain. ## 1. Install Orbital and sign in to a harness 1. **Download Orbital.** Download it for [Apple Silicon](https://releases.runorbital.dev/macos/Orbital-latest-arm64.dmg) or [Intel](https://releases.runorbital.dev/macos/Orbital-latest-x64.dmg). 2. **Drag Orbital to Applications.** Open the disk image and drag Orbital to **Applications**. 3. **Install a harness and sign in.** Orbital ships no [harness](/harnesses/), so install one yourself. Any of the three runs this first run. **Claude Code** Run `npm install -g @anthropic-ai/claude-code@latest`. Then run `claude` in a terminal and follow the login. The [Claude Code setup guide](https://code.claude.com/docs/en/setup) has other ways to install it. **Codex** Run `npm install -g @openai/codex@latest`, then `codex login`. The [Codex guide](https://learn.chatgpt.com/docs/codex/cli) has other ways to install it. Codex starts only in a Git repository, so add a repository, not a plain folder, as your project. **OpenCode** Run `npm install -g opencode-ai@latest`, then `opencode auth login`. The [OpenCode guide](https://opencode.ai/docs/) has other ways to install it. 4. **Open Orbital.** Open it from **Applications**. Orbital finds `git`, `claude`, `codex` and `opencode` on your login shell's `PATH`. **Settings > Harnesses** shows each harness. A harness it cannot find reads "Not installed", with the command to install it. [Install Orbital](/get-started/install/) has the details for each harness. ## 2. Add the repository as a project The first time Orbital opens, it asks you to "Give your agents a place to work." ![The first-launch wizard with the heading Give your agents a place to work, the steps Welcome, Project and First run, and a Set up a project button.](/screenshots/first-launch.webp?v=5ece35762f) *The wizard opens on the first launch.* 1. **Start the project setup.** Choose **Set up a project**. 2. **Pick a folder.** Choose **Choose folder** and pick a repository on your machine. A plain folder works too, except on Codex; see [plain folders and Git](/projects/#plain-folders-and-git). Orbital names the project after the folder. ![The first-launch wizard while a project is being added, with the same Welcome, Project and First run steps.](/screenshots/add-project.webp?v=2d32cbc234) *Orbital names the project after the folder you pick.* 3. **Add the project.** Choose **Add project**. The project is ready when the app says "Your project is ready." 4. **Go to the first run.** Choose **Prepare first run**. Already past the first launch? Open **Projects** and choose **New project**. Name it, then choose **Add folder** on the project's page. The first folder you add is the project's primary folder. [Projects and folders](/projects/) explains more. ## 3. Run explain-repo 1. **Open a new session.** Choose **New task** in the sidebar. The project you added is selected at the top. 2. **Choose Workflow mode.** Make sure the switch under the text box says **Workflow**, not **Chat**. 3. **Pick the workflow.** Open the workflow menu and choose `explain-repo`. 4. **Write the goal.** In the text box, write what you want to know, for example `Explain this repository to a new team member`. This is the run's goal. ![The Start page asking what to do in a project, with the goal box, the harness picker, the workflow picker and the Chat and Workflow switch.](/screenshots/start-page.webp?v=0428f5d705) *Set the switch to Workflow, pick explain-repo, then write your goal.* 5. **Pick a harness.** In the harness picker, choose the harness you signed in to: Claude Code, Codex or OpenCode. 6. **Start the run.** Choose **Start run**. The step only reads the repository and explains it. Its prompt tells it not to change, create or delete any file, and to run only commands that read. It works the same on Claude Code, Codex and OpenCode. ![explain-repo has one step, explain, and then done.](/shipped/explain-repo.svg?v=900c23c839) The run page opens. The **Session** tab shows the `explain` step working: the files the agent reads and its reply as it writes it. [Watch a run](/runs/watch-a-run/) explains the rest of the page. ## 4. Read the answer The answer is the agent's last message in the **Session** tab, just above "Run ended: success". It has four parts: 1. Purpose: what the repository does. 2. Who it is for. 3. How it is built. 4. How to run it. The agent says where it found each fact. It also says so when the repository does not tell. The **Context** tab keeps the same answer as `response.explain`. ## If the run stops A run that cannot start its harness stops. It is [Paused](/runs/#standings), and its badge reads "Waiting on you". The step shows why, for example that the harness is not installed or not signed in. Fix that, then choose **Resume**. [Troubleshooting](/troubleshooting/) lists the common causes. ![A run page on the Session tab showing its steps one by one and ending in Stopped with an error, with run facts such as project, workflow, branch, harness, model, run ID and path on the right.](/screenshots/run-thread.webp?v=f4f203d47e) *The Session tab says where and why a run stopped.* ## Next steps - [Implement a feature](/examples/feature-pull-request/): deliver a change as a pull request. - [Research report](/examples/research-report/): turn a question into a written report. - [Runs](/runs/): what a run is and what you can do with one. - [Permissions](/roles/#permissions): what a run asks you before its agent acts, on each harness. - [How Orbital works](/concepts/how-orbital-works/): the ideas behind the run you just watched. - [Write your first workflow](/workflows/write-a-workflow/): write a workflow of your own. --- # Build with AI > Give agents and chat assistants Orbital's docs, its MCP endpoint and its skills You can hand Orbital to an AI tool in three ways: give an assistant the docs as Markdown, connect a coding agent to Orbital's MCP endpoint, or install the skills Orbital ships. The quickest start is to give your assistant the docs index: ```text https://docs.beta.runorbital.dev/llms.txt ``` ## Give an assistant the docs Every page of these docs is also published as plain Markdown, for assistants to read. | File | What it holds | | --- | --- | | [`llms.txt`](https://docs.beta.runorbital.dev/llms.txt) | An index of every page, with absolute links, grouped by tab: Docs, Examples and Reference. | | [`llms-full.txt`](https://docs.beta.runorbital.dev/llms-full.txt) | Every page in one file. | | A page's Markdown copy | One page. Add `.md` to the page's URL without its trailing slash, such as `/runs/start-a-run.md`. The home page is `/index.md`. | Each Markdown copy starts with a line that points to `llms.txt`, so an assistant that reads one page can find the rest. Every page also advertises its copy with ``, for tools that look for it. ### The Copy page button Every page has a **Copy page** button at the top. It copies the page's Markdown, ready to paste into a chat. Its menu has more: | Item | What it does | | --- | --- | | View as Markdown | Opens the page's Markdown copy. | | Open in GitHub | Opens the page's source file. | | Open in ChatGPT, Open in Claude, Open in T3 Chat, Open in Cursor | Opens the assistant with the prompt "Read *the page's .md URL*, I want to ask questions about it." | ## Connect a coding agent over MCP MCP, the Model Context Protocol, is how coding agents connect to tools. Orbital serves MCP over Streamable HTTP, on the same address as the app, at `/mcp`. A coding agent such as Claude Code, Codex or OpenCode that connects to it can do what you do in the app. It can list, start, follow, message and control runs, and read and validate workflows. It can also read where a run's pull request stands and what its worktree holds, and secure a run's work, without `gh` or `git` on the machine. The endpoint is on by default. Every call must carry a token, even for `orbital serve`. **Settings > MCP** shows the address and the token, and gives each client a ready-made setup to paste into a terminal. [Connect a coding agent](/mcp/connect/) shows the steps. ![Settings, MCP page showing a switch for each tool group: reading runs and workflows, starting runs, controlling runs, messaging and answering runs, archiving, and changing Orbital's settings.](/screenshots/settings-mcp-tools.webp?v=73920e6114) *Each tool group has its own switch.* Each tool group has a switch. Reading, starting, controlling, messaging and answering are on by default. Archiving and changing Orbital's settings are off until you turn them on. [MCP tools](/reference/mcp-tools/) describes every tool. This endpoint is for agents outside Orbital and for the built-in [Supervisor](/supervisor/). The agents inside a run use a separate MCP server of their own. ## Install the shipped skills A skill is a folder of instructions an agent loads when a task matches it. Orbital ships two: | Skill | What it does | | --- | --- | | `orbital-delivery-lead` | Delivers tickets and epics through Orbital runs, as a delivery lead. It refines tickets, starts runs, checks in on them, unsticks them and closes what they deliver. It needs the MCP endpoint connected. | | `orbital-create-workflow` | Designs a new Orbital workflow with you, one decision at a time. Then it writes the files and validates them. | Install them from **Settings > Command line and skills**, or with `orbital skill install`. They work for every project on your machine. [Use the shipped skills](/mcp/skills/) explains how. ![Settings, Command line and skills page showing the orbital command installed and the agent skills installed.](/screenshots/settings-command-line.webp?v=2453b7527a) *Install the skills and the orbital command from one page.* ## Choosing a route | You want | Use | | --- | --- | | An assistant that answers questions about Orbital | `llms.txt`, `llms-full.txt` or a page's Markdown copy | | A coding agent that starts, follows and steers your runs | The MCP endpoint | | A coding agent that delivers tickets or designs workflows the way Orbital expects | The shipped skills, with the MCP endpoint for delivery | ## Related - [Connect a coding agent](/mcp/connect/): register Claude Code, Codex, OpenCode or another client. - [Use the shipped skills](/mcp/skills/): install the skills and ask agents to use them. - [MCP tools](/reference/mcp-tools/): every tool, its arguments and its results. - [Lead delivery with a supervisor](/supervisor/lead-delivery/): the loop the delivery lead skill follows. --- # How Orbital works > How a run carries out a workflow, step by step, with the coding agents you already use You give Orbital a goal, a [workflow](/workflows/) and a [project](/projects/), and it starts a [run](/runs/). The run carries out the workflow one step at a time, hands each agent step to a coding agent you already use, and records every result, so you can follow it, steer it and pick it up after a restart. ![A run page with an agent step selected, showing the step's prompt, the agent's answer and the values it recorded, with run facts, pull requests and the list of steps on the right.](/screenshots/run-hero.webp?v=4fa21a573c) *Most ideas meet on the run page: the run, its workflow's steps, its harness and its project.* ## A run works one step at a time A workflow is a DOT file of steps and the edges between them. Its `entry` names the first step. From there, the run takes one step at a time: - An agent step fills in its prompt and sends it to a harness. - A command step records facts it reads from your machine, such as the state of a pull request. - The edges leaving a step choose what happens next. - A terminal step ends the run. The harness is the coding agent that does an agent step: Claude Code, Codex or OpenCode. A step takes its harness, model and effort together from one place: its model settings, its role or the workflow's blocks, else the run's harness. [Which harness a step uses](/harnesses/#which-harness-a-step-uses) gives the order. The transcript shows the harness and model each step used. ![The run page's Waterfall tab shows each step as a bar on a timeline with its duration, including repeated visits such as probe@2 and wait@2.](/screenshots/run-steps.webp?v=fee718c962) *Each visit to a step is its own bar; a repeat visit carries a number such as @2.* ## What a run remembers A run begins with its goal and declared inputs. Its context holds those values, accepted agent outputs, command facts and each step's outcome. Prompts read context. Conditions on edges only read values available on their path through the workflow. [Pass context between steps](/workflows/pass-context/) explains what a step can read. Agent outputs are checked, structured data. Orbital does not scrape them from the agent's prose. An `outputs` declaration tells Orbital which keys and types to accept. The agent's final response stays available under `context.response.`. Built-in probes only observe. A `parallelogram` with `probes="pr"` reads each repository folder's current pull request from GitHub. Script commands are different: they run your shell command once in the primary folder and declare their own typed facts. ![The run page's Context tab lists fourteen values held by the run, such as pr.state and summary, each with the step that set it on the right.](/screenshots/run-context.webp?v=3e79bd8607) *Each context value shows the step that set it.* ## Runs carry on after a restart Orbital keeps every run's progress, context and transcript in `~/.orbital/run-history.db`. `ORBITAL_HOME` moves it to `$ORBITAL_HOME/.orbital`. Closing the browser leaves the server running. When Orbital restarts, every run you did not pause carries on from its last finished activity, with the context it had: - A finished step does not run again. - A wait wakes at the time it first set. - An agent turn continues its session. The message an agent was writing when Orbital restarted may be lost. A tool call that was running may run again. A paused run stays paused until you resume it. **Resume** carries the step on in the same agent session. **Retry** starts the step again as a new visit. A run keeps the workflow as it was when the run started, and the harness you picked for it. Orbital rereads a prompt file before a later visit. If your edit is invalid, it keeps the checked prompt. [Durability after restarts](/runs/durability/) covers restarts in full. ## You can steer a live run Open the run and read the active step and its transcript. Send a message when the agent needs a correction. A message you queue reaches the agent whole in its next turn that can take it, even when the history sent with that turn is shortened. Harnesses differ in whether they can take a message during a live turn. [Message a run](/runs/message-a-run/) shows how. [Pause](/runs/control-a-run/) the run to stop its current work before you change direction. Answer questions and permission requests on the run page. During a wait, skip the remaining time only when the observation should run again now. :::caution A manual jump changes the next step and may lack context that normal routing would have produced. Read its warnings before you confirm. Use normal routing when it already expresses the correction you need. ::: ## When a step fails What happens depends on where the failure is: | Failure | Result | | --- | --- | | A step fails and a failure edge matches | The run takes the edge and may recover. | | A step fails and no failure edge matches | The run is Paused and shows "Waiting on you" until you act. | | A terminal step fails | The run ends as Failed. | These are different [standings](/runs/#standings), so check both the standing and the transcript. [Error handling](/reference/error-handling/) gives the exact rules. [Troubleshooting](/troubleshooting/) explains how to recognise each state in the app. ![The run page's Waterfall tab highlights a failed command step with its error output and a red Stopped with an error box, followed by Run ended: failed.](/screenshots/run-current-step.webp?v=cf3386ca9f) *The failed step is highlighted, with the error that stopped it.* ## Quick reference | Idea | What it is | | --- | --- | | [Run](/runs/) | One workflow carried out for one project, towards one goal. Orbital keeps its full history, and you can follow, message, pause and resume it. | | [Workflow](/workflows/) | One DOT file of steps and the edges between them. Orbital loads, validates and runs it, and ships eight of its own. | | [Node](/reference/node-types/) | One step of a workflow. Its shape decides what it does. There are eight kinds, from agent steps to gates and fan-outs. | | [Gates and waits](/workflows/gates-and-waits/) | A gate stops a run until a person lets it continue. A wait stops it for a set time. Both survive a restart. | | [Run queue](/runs/queue/) | Orbital works on at most a set number of runs at once, two unless you change it. Other runs wait and start as slots free up. | | [Durability](/runs/durability/) | When Orbital restarts, every run you did not pause carries on where it was. Queued runs keep their place. | | [Project](/projects/) | A name and the folders a run works in. A folder can be a Git repository or a plain folder. Every run belongs to one project. | | [Worktree](/projects/worktrees-and-delivery/) | An isolated copy of a repository on a branch of its own. Code workflows use it to open a pull request and repair it until it merges. | | [Harness](/harnesses/) | The coding agent that does an agent step: Claude Code, Codex or OpenCode. You install and sign in to each yourself, so each step runs on your own plan. | | [Role](/roles/) | Who a step's agent is, which model it runs on and which tools it may use, bundled under one name. | | [Chat](/chats/) | A conversation with one coding agent in a project, with no workflow behind it. Use it for work you do not repeat. | | [Trigger](/triggers/) | A rule that acts on runs when something happens in GitHub, Linear or on a schedule. It can start a run, or stop, pause or message runs already working. | | [Supervisor](/supervisor/) | An optional chat that acts as your delivery lead inside Orbital. When a run needs help, it steps in within the limits you set. | ## Related - [Start your first run](/get-started/first-run/): see runs, projects and harnesses work together. - [Pass context between steps](/workflows/pass-context/): what goes into context and what a step can read. - [Pause, stop or resolve a run](/runs/control-a-run/): take control of a live run. - [Error handling](/reference/error-handling/): the exact rules for failed steps. - [Settings](/settings/): where you change how each part behaves. --- # Runs > A run carries out one workflow for one project, towards one goal You start a run to carry out one [workflow](/workflows/) for one [project](/projects/), towards one goal. While it works, you can follow it, send its agent a message and pause it, and it picks up again after a restart. Orbital keeps each run's full history. Each step runs on the harness and model that its role, its model settings or the run chose, so one run can use several harnesses and models. [Harnesses and models](/harnesses/) gives the order. ![A run page with an agent step selected, showing the step's prompt, the agent's answer and its recorded values, with the run's facts, pull requests and steps in the dock on the right.](/screenshots/run-hero.webp?v=4fa21a573c) *A run page: the selected step in the middle, the run's facts and steps on the right.* ## Standings Each run has one standing at a time. The runs list, the run page and the MCP tools use the same names. The one exception: the MCP tools report a run at a gate as Paused. | Standing | Meaning | | --- | --- | | Created | Recorded but not started. | | Queued | Waiting for a free run slot in the [queue](/runs/queue/). | | Running | A step is working. | | Question | The agent asked you something and waits for the answer. | | Waiting | The run waits for time or at a [human gate](/workflows/gates-and-waits/). A timed wait shows "Wakes in" and the time left. A run at a gate shows "Waiting:" and the gate's label, such as "Waiting: The pull request is waiting for review or merge". The Status filter calls it **At a gate**. It carries on by itself. | | Paused | Nothing moves until you act. A run you paused shows "Paused". A run that stopped by itself shows "Waiting on you". It stops by itself when a step fails and the workflow has no route for the failure, when the harness is missing or out of quota, when a step reaches its visit limit, or when Orbital cannot read the workflow. A paused run holds no run slot. | | Succeeded | It reached a terminal step that ends in success. | | Failed | It reached a terminal step that ends in failure, or spent the workflow's `max_visits`. | | Aborted | Someone aborted it. | ## The phase field The HTTP run listing and the MCP `list_runs` and `get_run` tools return a `phase` field beside the standing. The phase says which part of a step the run is in. Use the standing to decide what to do. Use the phase when you need the finer detail. | Phase | Meaning | Standing | | --- | --- | --- | | Created | Recorded but not started. | Created | | Queued | Waiting for a free run slot. | Queued | | Entering | Starting a step. | Running, Question or Waiting | | Executing | A step is working. | Running, Question or Waiting | | Selecting | Choosing the next step from the edges. | Running, Question or Waiting | | AwaitingOperator | At a human gate, or stopped by itself. | Waiting at a gate, or Paused ("Waiting on you") | | Interrupted | You paused it. | Paused | | Ended | Reached a terminal step or was aborted. | Succeeded, Failed or Aborted, from the run's `outcome` | The MCP tools report a run at a gate with the standing Paused. Its phase is AwaitingOperator, as for a run that stopped by itself. For a run at a gate, the HTTP listing also returns `waitingOn`, the gate's label. ## Read the result A run ends at a terminal step. The **Session** tab then shows "Run ended: success". A run that failed or was aborted shows "Run ended: failed" or "Run ended: aborted" as an alert. What the run delivered depends on its workflow: | Workflow | Where the result is | | --- | --- | | Answers or writes, such as `explain-repo` or a research report | The agent's reply. It is the last message in the **Session** tab, above "Run ended". The **Context** tab keeps each step's reply as `response.`, and the latest one as `last_response`. | | Delivers code, through a worktree and a pull request | The dock's **Pull requests** section lists its pull requests. The **Changes** tab shows the branch's changes. | ![The dock's Pull requests list, showing open and merged pull requests with their branch names.](/screenshots/pull-requests-panel.webp?v=612ac483b1) *A code run's pull requests, listed in the dock.* ## Titles Orbital titles a run the moment you start it. It uses a ticket's identifier and title, a link's short form, or the first line of the goal. A small model then writes a better title from the ticket and from the first step's work. To rename a run, choose its title in the header. Orbital never replaces a title you gave. ## Quick reference | You want | Where to look | | --- | --- | | Whether a run needs you | Its standing, in the runs list or on the run page | | Finer detail for a script or an agent | The `phase` field from the HTTP run listing or the MCP tools | | A run's answer | The last message in the **Session** tab, or `response.` in the **Context** tab | | A run's pull requests | The dock's **Pull requests** section, and the **Changes** tab | | A new title | The run's title in the header | ## Related - [Start a run](/runs/start-a-run/): give a project a goal and a workflow. - [Watch a run](/runs/watch-a-run/): find runs and read the run page. - [Message a run](/runs/message-a-run/): steer the agent or answer its question. - [Pause, stop or resolve a run](/runs/control-a-run/): every control and what it does. - [The run queue](/runs/queue/): how many runs work at once, and in what order. --- # Workflows > A workflow is one file of steps and edges that Orbital runs You describe the work a run does as a workflow: one DOT file that Orbital loads, validates and runs. The smallest complete one has a `graph` attribute line, one agent and one terminal: ```dot title="minimal.dot" digraph hello { graph [version="1", entry="hello"] hello [tool_access="Read only", prompt="Reply with one sentence explaining the goal: {{ inputs.goal }}."] done [shape="Msquare"] hello -> done } ``` A workflow declares a version, an entry node, the [nodes](/reference/node-types/) themselves and the edges between them. [Write your first workflow](/workflows/write-a-workflow/) walks through this one. Keep the file in your project's `.orbital/` folder. Give every node a stable ID, because the run transcript, visit counts and manual jumps all address nodes by ID. Write each prompt for someone arriving without prior conversation: state the expected result and the allowed actions. ## Why DOT? DOT writes nodes and edges directly, so the file has the same shape as the workflow. Graphviz can draw a diagram from the same definition that Orbital runs. You can review changes to that file in Git. Orbital is loosely based on the [StrongDM Attractor specification](https://github.com/strongdm/attractor/blob/main/attractor-spec.md#12-why-dot-syntax). Its DOT rationale describes these benefits. Orbital has its own supported syntax and does not promise full Attractor compatibility. The workflow language may evolve; DOT is the format to use today. ## Where Orbital looks for a workflow Orbital searches these places in order and takes the first match: | Order | Place | | --- | --- | | 1 | Custom workflows saved in the browser. | | 2 | The `.orbital/` folder of the project's primary folder. | | 3 | The `.orbital/` folder of each remaining folder. | | 4 | Your library, `~/.orbital/workflows/`. | | 5 | The workflows shipped with Orbital. | Because the first match wins, a custom workflow named `hello` shadows a file named `hello.dot`. Use a distinct name while experimenting. Pass an explicit path to `orbital validate` when you diagnose a collision. ## Shipped workflows Orbital ships eight workflows. They appear in the new-run menu of every project. A workflow of the same name in any other place takes a shipped one over. | Workflow | What it does | What it needs | | --- | --- | --- | | `explain-repo` | Reads the project's primary folder in one step that only reads, and explains the repository. No worktree, no pull request. | An optional goal | | `implement-ticket` | Plans, implements and reviews one ticket, then delivers it to merge. | A ticket URL, as `ticket_url` | | `implement-epic` | Delivers an epic's leaf tickets one at a time, then closes the epic. | An epic URL, as `ticket_url` | | `implement-epic-with-panel` | Delivers an epic the same way, with a panel of parallel reviewers. | An epic URL, as `ticket_url` | | `improve-codebase` | Finds and delivers one worthwhile improvement, then stops. | An optional goal | | `reduce-complexity` | Simplifies the function most worth simplifying. | An optional goal | | `pursue-goal` | Implements and validates a goal in a loop, then delivers it. | A goal in plain words | | `port-project` | Ports an application to a new stack, one proved slice per pull request. | A goal naming the source repository and the target stack | [Shipped workflows](/reference/shipped-workflows/) describes each one, with its diagram. ## The Workflows page Click **Workflows** in the sidebar to list every workflow the selected project offers. A badge says where each one comes from. ![The Workflows page lists four workflows, each with a source badge such as Project, Custom or Home, and Clone, Export or Revert to built-in buttons. Search, Import and New workflow sit at the top.](/screenshots/workflows-page.webp?v=991e5fde13) *Each workflow's badge says where it lives and whether you can edit it here.* | Badge | Where it lives | Can you edit it here? | | --- | --- | --- | | Custom | Saved in Orbital from this page. | Yes. | | Project | The `.orbital/` folder of one of the project's folders. | No. Edit the file. | | Home | Your library, `~/.orbital/workflows/`. | No. Edit the file. | | Shipped | Comes with Orbital. | Yes. Saving stores a Custom copy. | A Custom copy of a shipped workflow shows **Takes over built-in**. Reverting it deletes your copy, and the shipped workflow is back. Orbital never changes the file it ships, so a later release still reaches every name you have not taken over. On this page you can create a workflow, import one, clone one, export one as a file, and open one in the editor. Export writes one self-contained file with the prompts inlined. **Import** opens a box where you paste DOT text. Nothing is stored until you save. Prompt files the text refers to become empty prompts for you to fill in. ![The Import a workflow page has one large DOT text box with Import and Cancel buttons below it.](/screenshots/import-workflow.webp?v=b5c29c87bd) *Paste the DOT text; nothing is stored until you save.* ### The editor The editor draws the workflow and validates it as you edit. The **Problems** bar at the bottom lists what validation found. The **Inspector** on the right holds the workflow's description, entry, inputs, turn budget and permissions. Move around the canvas with a two-finger swipe, or drag an empty area. Pinch or hold ⌘ while scrolling to zoom. Work in progress is kept as a Draft, even while it is not valid yet. A Draft cannot be run. It becomes a Custom workflow once you save it as a valid workflow. The browser workflow library also creates and edits custom workflows, and saving validates them. Inspect the selected workflow before you start a run. ![The workflow editor shows a read-only project workflow as nodes and edges on a canvas, with the Inspector panel on the right and a Problems bar reading No problems at the bottom.](/screenshots/workflow-editor.webp?v=85a9c6275a) *A project workflow is read-only here; choose Clone to edit to change it.* ## Declared inputs `inputs="work,notes?"` requires `work` and makes `notes` optional. `goal` is built in. Starting a run requires the goal and every required input. So a workflow states what it needs, and you do not have to remember it. `input_labels` and `input_hints` explain an input on the new-run form, for example `input_labels="ticket_url='Ticket URL'"`. Without them, the form shows the input's name. [Pass context between steps](/workflows/pass-context/) covers what happens to those values once the run starts. ## Validate before you run Validation reads the file, resolves imports and prompt files, and checks routing without calling an agent. ```sh orbital validate .orbital/hello.dot ``` Fix errors before you add more nodes. Warnings identify unreachable nodes, unused inputs and other suspicious but loadable declarations. Add one decision at a time. A choice output with two explicit weighted routes is easier to inspect than a prompt that silently decides whether to publish. Validate again after you edit imports and prompt files, because those are separate files that the workflow only names. ## Choosing where to keep a workflow | You want | Keep it | | --- | --- | | The workflow beside the code it works on, reviewed in Git | The `.orbital/` folder of the project's folder | | The workflow in every project | Your library, `~/.orbital/workflows/` | | To build it in the app, without files | A Custom workflow, from the Workflows page | | A changed shipped workflow | A Custom copy of it. Revert to get the shipped one back. | ## Related - [Write your first workflow](/workflows/write-a-workflow/): copy the smallest complete workflow and change it. - [Node types](/reference/node-types/): the kinds of step a workflow is made of. - [How Orbital works](/concepts/how-orbital-works/): how a run carries out a workflow one step at a time. - [Shipped workflows](/reference/shipped-workflows/): each shipped workflow with its diagram. - [The orbital command](/command-line/): `orbital validate` and the other commands. --- # Durability after restarts > Every run carries on where it was when Orbital starts again You do not need to do anything when Orbital restarts. Every [run](/runs/) you did not pause carries on where it was, and nothing that finished runs again. Orbital restarts when you quit the app, when it updates, or when the computer restarts. ## What Orbital keeps An activity is one unit of a run's work: an agent turn, a command, a check, a wait or a worktree step. Orbital saves each run's progress as the run goes, in `~/.orbital/run-history.db`. It keeps: - the result of every finished activity; - the time each wait wakes; - every control you sent; - whether you paused the run. ## What happens at the next start 1. Orbital stops any agent programs that were still running from before the restart. 2. It carries on every unfinished run you did not pause, from its last finished activity. 3. Runs that were working take their slots back. Queued runs keep their place in the [queue](/runs/queue/). 4. A run that reached a new step while Orbital was shutting down starts that step now. Each kind of run comes back as follows: | Run | After the restart | | --- | --- | | At a timed wait | The wait wakes at the time it first set. | | Paused | It stays paused. | | At a gate | It stays at the gate. | | Aborting | An abort that was under way ends as aborted. | | With a half-applied control | The control is finished when you send it again. | ## A step that was under way A step that was under way when Orbital restarted had no result yet, so Orbital picks it up again. The run's thread shows both attempts. | Activity | What happens | | --- | --- | | An agent turn | It continues its session. If the harness cannot continue that session, the step starts again in the same worktree with the same prompt. The harness sets two limits: the message the agent was writing may be lost, and a tool call that was running may run again. | | A command marked `repeat_safety="idempotent"` or `"reconcilable"` | It runs again. | | A command left with the default, `uncertain` | It waits for you instead of running again, because running it twice could do harm. The run is Paused and shows "Waiting on you". | ![A run held back by a restart, with a Paused badge in its header.](/screenshots/run-interrupted.webp?v=700d8cf233) *A run held back after a restart waits for you to act.* For a command that did not run again, check what it did. Then choose **Retry step** or **Resume**. ## Pause and resume **Pause** marks the run as paused, stops its agent and frees its slot. The run stays paused across restarts. **Resume** carries the same step on: an agent turn continues the same session in the same worktree. **Retry step** starts the step again instead. See [Pause, stop or resolve a run](/runs/control-a-run/). ## Chats A [chat](/chats/) that was answering when Orbital stopped goes back to waiting, with a note that the answer failed. Send a message to carry on. ## Keep a copy Your data survives upgrades and uninstalling. :::caution Keep a copy of `~/.orbital/run-history.db` before you go back to an older release. An older release cannot open run history that a newer one has updated. ::: ## Related - [Pause, stop or resolve a run](/runs/control-a-run/): the controls Orbital records. - [The run queue](/runs/queue/): how slots come back after a restart. - [Error handling](/reference/error-handling/): what a run does when a step fails. - [Troubleshooting](/troubleshooting/): fixes for runs that stop or stall. --- # Projects and folders > A project is a name and the folders its runs work in You add a project to tell Orbital where your runs work. A project is a name and the folders a run works in. A folder can be a Git repository, or a plain folder that is not one, such as research reports. A project can hold several folders, such as a web app and its API. Every run belongs to one project. The **Projects** page lists your projects in a table, with each project's folders and runs. Its row menu has **Rename** and **Delete**. [Create a project](/projects/create-a-project/) shows how to make one. ![The Projects page shows a table of projects with their folders and runs, and a New project button.](/screenshots/projects-page.webp?v=8b88b77800) *Each row lists a project's folders and runs.* ## Folders The project's page lists its folders first, with their name, path, type and role. | Column | Values | | --- | --- | | **Type** | "Repository", with its base branch, or "Plain folder" for a folder that is not a Git repository. | | **Role** | "Primary" or "Secondary". | The first folder you add is the **primary folder**. Each agent step starts in it: it is the step's working directory. The other folders are open to the agent as additional folders it can read and change. A folder's menu has **Make primary**, **Rename**, **Change path** and **Remove**. When you remove the primary folder, another folder takes its place. Runs already under way keep the folders they started with. If you move or rename a folder on disk, use **Change path** to point the project at where it is now. Runs already under way follow it: each step reads the folder from the project when it starts, and their worktrees are repaired to find the repository in its new place. A step that needs a folder that no longer exists waits for you, so you can update the folder's path and resume the run. A workflow that makes a worktree makes one for each repository folder, all on the same branch name. Plain folders are used where they are. [Worktrees and delivery](/projects/worktrees-and-delivery/) explains this. ![A project page shows a table of folders with name, path, type and role, then the Runs limit and each folder's files to copy.](/screenshots/project-details.webp?v=a56bdefccc) *The folders come first, then the project's settings.* ## Project settings The project's settings sit below the folders, in groups. | Group | What it holds | | --- | --- | | **Runs** | The run limit and the project's own supervisor. | | One group per folder, headed by its name | For a repository, the files to copy into new worktrees. | The settings save as you change them. The page header says "All changes saved" once they have. An info button beside a label explains the setting. ## Plain folders and Git A project does not need a Git repository. Plain folders work with Claude Code and OpenCode. Codex is the exception: it refuses to start in a folder that is not a Git repository. So a step on Codex needs its primary folder to be one. A workflow that changes code also needs a Git repository, because it works in a worktree. A worktree is a copy of a repository on a branch of its own. The shipped workflows that deliver a pull request need one for that reason, and so do the examples that make a worktree. A workflow that only reads or writes files runs in a plain folder. Examples are `explain-repo` and the research example. ## Runs at a time A project can limit how many of its runs work at once, with **Run limit** under **Runs** on its page. When a project reaches its limit, its next runs wait, and other projects' runs go first. [Create a project](/projects/create-a-project/#limit-runs-at-a-time) shows how to set it. ## Custom actions Custom actions are commands and links you add to a project for its runs. They appear in each run's **Run actions** menu. A command runs on the machine that runs Orbital, in the run's folder you choose. A link opens a URL template. [Create a project](/projects/create-a-project/#add-custom-actions) shows how to add one. ## Choose a project The project selector at the top of the sidebar chooses "All projects" or one project. The runs list, the Workflows page and the new-run composer follow it. Search with "Search projects…". ## Workflows a project offers A project offers these workflows: - the workflows in the `.orbital/` folder of each of its folders - your library in `~/.orbital/workflows/` - the Custom workflows you saved in Orbital - the shipped workflows [Workflows](/workflows/#where-orbital-looks-for-a-workflow) explains which one wins when two share a name. ## More on projects With the Triggers feature on, a project's page also holds its [trigger rules](/triggers/). With the Supervisor feature on, a project can have [its own supervisor](/supervisor/#a-supervisor-for-one-project). ## Quick reference | You want to | Use | | --- | --- | | Choose where agent steps start | **Make primary** in a folder's menu | | Copy files into new worktrees of a repository | The folder's files to copy | | Limit how many of the project's runs work at once | **Run limit** under **Runs** | | Add a command or link to every run | [Custom actions](/projects/create-a-project/#add-custom-actions) | | Run on Codex, or deliver code | A Git repository as the primary folder | ## Related - [Create a project](/projects/create-a-project/): add a project, its folders, a run limit and custom actions. - [Worktrees and delivery](/projects/worktrees-and-delivery/): how code workflows use worktrees and open pull requests. - [The run queue](/runs/queue/): how project limits work with the limit in Settings. --- # Harnesses and models > A harness is the coding agent that does each agent step You choose which coding agent does each agent step of a run. That agent is the step's harness: Claude Code, Codex or OpenCode. You install and sign in to each harness yourself, so each step runs on your own plan. Orbital starts the harness, gives it the step's prompt and settings, and records what it does. A run can move from one harness to another. ## Which harness a step uses A step takes its harness, model and effort together, from one source. When a step's model settings, role or the workflow's blocks name a harness, the step uses that harness. It takes the model and effort from the same place. A model or effort that names another harness is never paired with it. When nothing on the step names a harness, the step uses the run's harness. The run's harness is the first of these that is set: 1. the harness you picked when you started the run 2. the workflow's own `harness` 3. **Default harness** in **Settings > General** So a harness you pick for the run applies only to steps whose model also comes from the run. A model that belongs to another harness is refused before the turn starts. The step stops with a reason such as "Model `gpt-6-astra` runs on codex, not on claude, so the turn was not started." Change the step's model settings or move it to that harness, then retry. After a restart, Orbital works out each step's harness afresh. The run's header and the composer footer name the harness and model of the same step: the step running now, or the latest one. A run that used more than one pairing says so, for example "Codex · gpt-6-astra, +1 other". [Roles, personas, model settings and tool access](/roles/) and [Use different models in one workflow](/harnesses/use-different-models/) explain model settings. [Attributes](/reference/attributes/#resolution-order) gives the full order. ## See a harness's status Open **Settings > Harnesses**. The status table shows, for each harness, whether it is installed, whether it is signed in, and when Orbital last checked. Choose **Check again** after you install or sign in. Orbital also checks every five minutes. ![Settings, Harnesses page shows a status table for Claude, Codex and OpenCode, then Claude's settings: turned on, program, default model, default effort and environment variables.](/screenshots/settings-harnesses.webp?v=6446e71ff0) *The status table comes first, then each harness's settings.* | Status | Meaning | | --- | --- | | Installed, *version* | Orbital found the program and it answered. | | Not installed | Orbital found the program neither at the path set in Settings nor on the `PATH` of your login shell. The message gives the command to install it. | | Too old, *version* | The program is older than the oldest version Orbital works with. The message names that version and the command to update it. | | Could not start | The program is there, but it failed to start. | | Off | You turned the harness off. | | Signed in, Signed out, Sign-in not reported | What the harness says about its login. | Pickers add the reason after a harness's name, such as "(turned off)" or "(not installed)". ## Harness settings Each harness has its own group in **Settings > Harnesses**: **Turned on**, **Program**, **Default model**, **Default effort** and **Environment variables**. See [Settings](/reference/settings/#harnesses). ## Differences between harnesses **Claude Code** Claude Code works in plain folders as well as Git repositories. Claude has usage, rate and weekly limits. When it reaches one, the step stops as [out of quota](#when-a-harness-runs-out-of-quota). **Codex** Codex refuses to start in a folder that is not a Git repository. So a step on Codex needs its primary folder to be one. See [plain folders and Git](/projects/#plain-folders-and-git). Codex reads files through commands. A Codex step with Read files but not Run commands keeps commands available for reading in a read-only sandbox that prevents writes. See [Tool access](/roles/#tool-access). **OpenCode** OpenCode works in plain folders as well as Git repositories. To use OpenRouter models through OpenCode, export `OPENROUTER_API_KEY` in the shell that starts Orbital, then restart it. ## A missing or switched-off harness A step whose harness is turned off, not installed, or not set up does not fail. The run stops on that step with the reason on the step. It is [Paused](/runs/#standings), and its badge reads "Waiting on you". To carry on: 1. **Fix the cause.** Install the harness, sign in, or turn it back on under **Turned on** in **Settings > Harnesses**. 2. **Check again.** Choose **Check again** in **Settings > Harnesses**. 3. **Resume the run.** Open the run and choose **Resume**. Or [move the run to a harness that works](/harnesses/switch-harness/). A harness that is off takes no new turns anywhere. Runs that need it stop, and its chats take no new turns. ## When a harness runs out of quota Claude and other harnesses have usage, rate and weekly limits. When a harness reaches one, the step stops with an "out of quota" reason. The reason names the harness and, when it is known, when the quota resets. The run is Paused, with the badge "Waiting on you". You can then [switch the run's harness](/harnesses/switch-harness/) yourself. Or let the Supervisor do it with a [quota fallback](/harnesses/switch-harness/#quota-fallbacks). ## Switch a run's harness You can move a stopped run to another harness and retry its current step there. Finished steps, context and file changes stay. [Switch a run's harness](/harnesses/switch-harness/) gives the steps and what carries over. ## A step that goes silent A turn that writes nothing for the **Stalled turn limit** is stopped and started again in the same session. The limit is five minutes unless you change it in **Settings > General**. After two such restarts in one step, the run is Paused, with the badge "Waiting on you". ## Quick reference | | Claude Code | Codex | OpenCode | | --- | --- | --- | --- | | Install | `npm install -g @anthropic-ai/claude-code@latest` | `npm install -g @openai/codex@latest` | `npm install -g opencode-ai@latest` | | Sign in | `claude` | `codex login` | `opencode auth login` | | Oldest version Orbital works with | 2.1.280 | 0.156.0 | 1.18.0 | | Plain folders | Yes | No, the primary folder must be a Git repository | Yes | ## Related - [Switch a run's harness](/harnesses/switch-harness/): retry a step on another harness, by hand or by quota fallback. - [Use different models in one workflow](/harnesses/use-different-models/): give steps their own harness, model and effort. - [Install Orbital](/get-started/install/): install and sign in to a harness. - [Settings reference](/reference/settings/#harnesses): every harness setting. --- # Roles and tool access > Named building blocks decide who a step's agent is and what it uses You decide three things for each agent step: who the agent is, which model it runs on, and which tools it may use. Orbital gives each its own named building block, and a role bundles all three under one name. | Building block | What it decides | Example | | --- | --- | --- | | Persona | Who the agent is: a prompt it receives with the step's own prompt. | Strict reviewer: "You review work you did not write…" | | Model settings | The harness, model and effort. | Deep thinking: effort high, on any harness. | | Tool access | Which kinds of tool the step may use. | Read only: read files and nothing else. | | Role | One persona, one model settings and one tool access together. | Reviewer: Strict reviewer, Deep thinking, Full access. | The same name means the same thing on every step. Change the model settings called Deep thinking, and every step that uses it changes. ![Settings, Roles page lists roles such as Implementer, Planner and Researcher, each with its persona, model settings and tool access.](/screenshots/settings-roles.webp?v=cfaa8fe138) *Each role names one persona, one model settings and one tool access.* ## Tool access Tool access has five switches. The harness enforces them, not the prompt. [Permissions](#permissions) explains how they differ from the `permissions` attribute. | Switch | Kind | What it covers | | --- | --- | --- | | Read files | `read` | Read, list and search files. | | Change files | `edit` | Create, change and delete files. | | Run commands (tests, git, scripts) | `shell` | Run programs in a terminal. | | Browse the web | `web` | Fetch pages and search the web. | | Use connected services | `mcp` | Tools from MCP servers, including Orbital's own. | A step whose tool access leaves a kind out does not see those tools at all. Blocks add up: a kind blocked anywhere on the way to a step stays blocked. Two limits come from the harnesses. A step that can run commands can still read files through them. Codex reads files only through commands, so it keeps commands available for reading even when Tool access withholds Run commands. Without Change files, Codex runs those commands in a read-only sandbox and cannot write to the repository. ![Settings, Tool access page shows the Full access entry with a start-from choice and a switch for each kind of tool.](/screenshots/settings-tool-access.webp?v=64acfcd2b2) *One switch for each kind of tool.* ## Permissions Tool access decides which tools a step has. Permissions decide whether the harness asks before it uses them. Neither the prompt nor `permissions` limits tools: only tool access does. :::caution[A prompt is not a limit] A prompt that says "do not change files" is a request, and the agent can ignore it. Use tool access to take a tool away. ::: A workflow sets `permissions="auto-accept"` or `permissions="full"` for all its steps. The default is `auto-accept`. When a harness asks you something, the run shows as Question until you answer. See [standings](/runs/#standings). ### Claude Code | Setting | What happens | | --- | --- | | `auto-accept` | Claude Code's auto mode approves what it judges safe. Whatever auto mode does not settle, the run asks you as a Question. | | `full` | Claude Code bypasses its permission checks. Nothing is asked. | | Tool access | A step does not get the tools of a kind it lacks. | ### Codex | Setting | What happens | | --- | --- | | `auto-accept` | Codex defaults to `workspace-write` and `on-request`: it can change files in the workspace and run sandboxed commands, and decides when to ask for approval to act outside the sandbox. Each approval request appears as a Question for you to approve or decline. Set `codex_sandbox_mode` and `codex_approval_policy` to override these defaults, subject to the tool access limit below. | | `full` | Codex runs with full access and never asks, whatever `codex_sandbox_mode` and `codex_approval_policy` say. | | Tool access | A step without Change files runs in a read-only sandbox and never asks, whatever `permissions` says. Codex reads files through its sandboxed shell, so it keeps the shell for reading even without Run commands. | [Harness-specific attributes](/reference/attributes/#harness-specific-attributes) lists the sandbox modes and approval policies. ### OpenCode | Setting | What happens | | --- | --- | | `auto-accept` | Every permission OpenCode raises is asked as a Question. Your OpenCode permission settings decide what it raises. | | `full` | Every permission is approved. Nothing is asked. | | Tool access | A step sees only the tools of its kinds. OpenCode refuses any other call before asking you. | ### Shipped workflows Of the shipped workflows, `pursue-goal` and `port-project` use `full`. The others use `auto-accept`. Every shipped role but Supervisor has Full access, so a step in those roles can change files and run commands whatever its permissions say. ## What ships Orbital ships these entries. You can edit each one in **Settings**. | Role | Persona | Model settings | Tool access | | --- | --- | --- | --- | | Planner | Planner | Deep thinking | Full access | | Implementer | Implementer | Balanced | Full access | | Reviewer | Strict reviewer | Deep thinking | Full access | | Researcher | Researcher | Balanced | Full access | | Supervisor | Supervisor | Deep thinking | Supervisor | | Tool access | Kinds | | --- | --- | | Read only | Read files | | Research | Read files, Browse the web | | Full access | Every kind | | Supervisor | Read files, Browse the web, Use connected services | Deep thinking sets effort high and Balanced sets effort medium. Neither names a harness or model, so they run on whichever harness the run uses. The Supervisor role is the one [the Supervisor](/supervisor/) speaks in. [Shipped defaults](/reference/attributes/#shipped-defaults) lists every shipped entry and each persona's prompt. ## The library and a workflow's own blocks The **library** holds the entries in **Settings > Roles**, **Personas**, **Model settings** and **Tool access**. Every workflow can use them by name. ![Settings, Personas page lists persona prompts by name.](/screenshots/settings-personas.webp?v=a5e3cd0f3e) *A persona is a named prompt.* A workflow can also define its own blocks. A block with the same name as a library entry replaces it for that workflow only. In the editor, saving a role asks whether to "Change it everywhere" or "Only in this workflow". Orbital adds the shipped entries on every start. It never overwrites an entry you edited. **Reset to default** returns an edited entry to what ships. Deleting a shipped entry hides it until you choose **Restore**. [Give steps a role](/roles/configure-steps/#edit-the-shipped-defaults) gives the details. ## Choose them for a step In the workflow editor, select the step and pick a **Role**, **Persona**, **Model settings** or **Tool access** in the inspector. In a workflow file, set `role`, `persona`, `model_settings` or `tool_access` on the step. A step's own choice beats its role's, and its role's beats the workflow's. To make a step read-only, give it `tool_access="Read only"`. ## Quick reference | To decide | In the editor | In the workflow file | | --- | --- | --- | | Persona, model settings and tool access together | **Role**, on the step | `role` on the step | | Who the agent is | **Persona** | `persona` | | Harness, model and effort | **Model settings** | `model_settings` | | Which kinds of tool the step has | **Tool access** | `tool_access` | | Whether the harness asks before it uses a tool | The workflow's **Inspector** | `permissions` on the workflow | ## Related - [Give steps a role](/roles/configure-steps/): a worked example in the editor. - [Use different models in one workflow](/harnesses/use-different-models/): model settings on several harnesses. - [Attributes](/reference/attributes/#roles-model-settings-and-tool-access): the block grammar and the full resolution order. - [Settings](/reference/settings/#roles): the library pages. --- # Start a run > Give a project a goal, pick a workflow and start a run You start a [run](/runs/) from the Start page: you pick a project, write a goal and choose a workflow. The run then carries out that one workflow for that one project, towards that goal. ## Before you start You need a [project](/projects/create-a-project/) and a [harness](/harnesses/) that is set up. A run whose harness is missing stops and waits on you. ## Start a run from the app 1. **Open the Start page.** Choose **New task** in the sidebar. 2. **Check the project.** The project shows at the top. Choose it to pick another, or press ⌘K. 3. **Leave the switch on Workflow.** The switch sits under the text box. Set to **Chat**, it starts a [chat](/chats/) instead. 4. **Write the goal.** In the text box that reads "Describe what the run should achieve", type what the run should achieve. 5. **Choose a workflow and fill in its inputs.** The strip offers up to three workflows: the ones this project runs most, then the default. **Choose a workflow** lists every workflow the project offers, with its description. If the workflow has inputs, fill them in. For example, `implement-ticket` asks for a "Ticket URL". 6. **Pick a harness and model, if you want.** **Default harness** follows the workflow and your settings. Steps whose role or model settings name their own harness keep that harness whatever you pick. 7. **Choose Start run.** ![The Start page asking what to do in a project, with the goal box, the harness picker, the workflow picker and the Chat and Workflow switch.](/screenshots/start-page.webp?v=0428f5d705) *The goal box, with the workflow and harness pickers beneath it.* ## If you choose no workflow The run uses the workflow the project ran last. If the project has never run one, it uses the **Default workflow** in [Settings](/reference/settings/#general). ## What happens next The new run joins the [queue](/runs/queue/) and starts when a run slot is free. Follow it on the [run page](/runs/watch-a-run/). ## Other ways to start a run You can also start a run without opening the app. | From | How | | --- | --- | | A terminal | [`orbital run start`](/reference/command/#orbital-run-start) | | A coding agent | Over [MCP](/mcp/connect/) | | An event or a schedule | A [trigger](/triggers/) | ## Related - [Runs](/runs/): what a run is and the standings it moves through. - [Watch a run](/runs/watch-a-run/): follow the run you started. - [The run queue](/runs/queue/): when a queued run starts. - [Permissions](/roles/#permissions): what a run asks you before its agent acts, on each harness. - [Start your first run](/get-started/first-run/): a guided first run for new users. --- # Watch a run > Find a run, see what it is doing and read its changes You follow your [runs](/runs/) in three places in the app. The **Runs** page lists them all, the **Attention** page lists the ones that wait for you, and a run's own page shows it in full. ## Find a run on the runs page Choose **Runs** in the sidebar to list every run. Search with "Search runs…", and filter by **Status**, **Time**, **Repo** and **Workflow**. Turn on **Show archived** to include archived runs. **Columns** chooses what the list shows: Status, Harness, Repo, Title, Workflow, Created, Size and PR. The sidebar also lists **Recent runs** and **Recent chats**. **View all** opens the runs page. ![The Runs page, a table of runs with status, harness, repository, title, workflow, created, size and pull request columns, with a search box and filter chips above it.](/screenshots/runs-page.webp?v=cfabb3ed64) *Search and the filter chips sit above the table of runs.* ![The Runs page with the Status filter open, listing Created, Queued, Running, Question, Waiting, At a gate, Paused, Succeeded, Failed and Aborted.](/screenshots/runs-status-filter.webp?v=859f79421d) *Each filter chip opens a list of the values you can pick.* ## See the runs that need you The **Attention** page lists the runs in the selected project that wait for you: the runs with the standing Question or Paused. When nothing waits, the page says "Nothing needs attention". ![The Attention page, listing the runs in a project that are waiting for an answer or an action from you.](/screenshots/attention-page.webp?v=541ff6c55b) *Only the runs that wait for you are listed.* ## Read the run page Choose a run to open its page. The page has a header, six tabs and a dock. The header shows the run's title, workflow, branch and harness. The **Run actions** menu (⋯) holds the [run controls](/runs/control-a-run/). It also holds **Copy run ID**, **Download run JSON** and your project's [custom actions](/projects/create-a-project/#add-custom-actions). **Open in** opens the run's folder in another app, such as your editor. ![A run page on the Session tab, showing route, wait and command steps one after another and ending in Stopped with an error, with the run's facts, pull requests and steps in the dock.](/screenshots/run-thread.webp?v=f4f203d47e) *The Session tab tells the run's story step by step.* | Tab | What it shows | | --- | --- | | Session | The conversation: the goal, each step, the agent's messages and tool calls, questions and notices. | | Waterfall | A timeline of the steps. Choose a step to see its status, input and output. | | Context | The values steps have written, such as outputs and facts. | | Logs | The run's recorded events, with search. | | Workflow | The workflow as a diagram and as source, frozen as the run started it. **Open in editor** opens it. | | Cost | Two tables. **Tokens by step** gives each step's model, tokens in and out, run time and cost, with a total. **Tokens by model** adds them up for each model. | ![The Waterfall tab, a timeline of the run's steps drawn as bars with their durations.](/screenshots/run-steps.webp?v=fee718c962) *Longer bars are steps that took longer.* ## Use the dock The dock sits beside the tabs and has three tabs of its own. | Dock tab | What it shows | | --- | --- | | Run | The run's project, workflow, branch, harness, model, run ID and path; its **Pull requests**; its **Steps**; and the **Agent** of the current turn, with its settings and tools. | | Changes | The files the run changed. Choose **Uncommitted**, **Staged** or **Branch**. Show the diff side by side or with word wrap. | | Files | The files in the run's folder, with a viewer. Use **Find a file** to search. | Choose **Hide dock** or **Expand dock** to change its size. ![The dock's Changes tab, with a tree of changed files on one side and the diff of the selected file.](/screenshots/run-changes.webp?v=a2e436d9fe) *Pick a file in the tree to see its diff.* ## Related - [Runs](/runs/): standings, the phase field and where the result appears. - [Message a run](/runs/message-a-run/): steer the agent from the Session tab. - [Pause, stop or resolve a run](/runs/control-a-run/): the controls in the Run actions menu. - [Pass context between steps](/workflows/pass-context/): where the values on the Context tab come from. --- # Message a run > Steer a working agent or answer its question from the composer You can write to a [run](/runs/)'s agent while the run works, from the composer at the bottom of the run page. Your message either steers the step that is working or waits for the next turn. ## Send a message 1. **Open the run.** Choose the run on the **Runs** page or under **Recent runs**. The run page opens on the **Session** tab. ![A run page for a running run, with a Running badge and Quiet for 4h 49m beside the tabs, and the harness, model, workflow and branch in a footer at the bottom.](/screenshots/run-running.webp?v=216080729b) *A running run shows how long its agent has been quiet.* 2. **Write in the composer.** The composer sits at the bottom of the **Session** tab. 3. **Send the message.** While a step is working, your message steers it. The composer then says "Message the agent". When no step is working, the message waits for the next turn. The composer then says "Message the agent's next turn". ## Answer a question When the agent asks a question, the run's standing becomes Question and the composer says "Answer the question or message the agent". Choose one of the options, or write your own answer. ![An agent's question with several options to choose from and a box for a free-text answer.](/screenshots/agent-question.webp?v=92fd44eff4) *Pick an option, or write your own answer.* The **Attention** page lists every run with a question waiting. See [See the runs that need you](/runs/watch-a-run/#see-the-runs-that-need-you). ## Related - [Watch a run](/runs/watch-a-run/): find the run and read its Session tab. - [Pause, stop or resolve a run](/runs/control-a-run/): the controls that sit beside the composer. - [Gates and waits](/workflows/gates-and-waits/): steps where a run waits for you. - [Chats](/chats/): talk to an agent without a workflow. --- # Pause, stop or resolve a run > Pause, resume, retry, abort, archive or resolve a run You pause, stop or resolve a [run](/runs/) from its run page. **Pause** and **Resume** sit on the composer, and every other control sits in the **Run actions** menu (⋯) in the header. The app offers a control only when it applies. ![A run page with the Run actions menu open, listing the controls that apply to the run.](/screenshots/run-actions-menu.webp?v=5686eaf17c) *The Run actions menu lists only the controls that apply now.* ## Pause and resume a run Choose **Pause** to pause the step the run is on and hand it back to you. The run frees its [run slot](/runs/queue/) and shows "Paused" until you act. Choose **Resume** to carry the same step on from where it stopped, in the same agent session. To start the step again from the beginning instead, choose **Retry step**. A run can also stop by itself and show "Waiting on you". [Standings](/runs/#standings) lists when that happens. The same controls carry it on. ![A run paused at a step, with a Waiting on you badge in the header.](/screenshots/run-paused.webp?v=66bbab501c) *A run that stopped by itself shows Waiting on you.* ## Abort a run Aborting a run stops its step and ends the run. :::caution You cannot resume an aborted run. To try the same work again, choose **Restart as new run**. ::: 1. **Open the Run actions menu.** Choose ⋯ in the run page's header. 2. **Choose Abort run.** Orbital asks first. 3. **Confirm.** Choose **Abort run** in the dialog, or **Cancel** to keep the run going. ![The Abort confirmation dialog, saying the run will stop the step it is on and end, and cannot be resumed afterwards.](/screenshots/abort-run-dialog.webp?v=80364622a4) *The dialog warns that the run cannot be resumed.* ## Resolve a failed or aborted run You can mark a run that ended Failed or Aborted as resolved. Choose **Mark as resolved** in the **Run actions** menu, and the run leaves the cards at the top of **Recent runs**. ## All run controls | Control | What it does | | --- | --- | | Pause | Pauses the step the run is on and hands it back to you. The run frees its slot. | | Resume | Carries the same step on from where it stopped, in the same agent session. | | Retry step | Starts the step again from the beginning, as a new visit. | | Retry with another harness… | Starts the step again on another harness. See [Switch a run's harness](/harnesses/switch-harness/). | | Jump to step… | Moves the run to another step. | | Skip | Ends a wait now, or opens a [gate](/workflows/gates-and-waits/). | | Abort run | Stops the step and ends the run. You cannot resume it afterwards. Orbital asks first. | | Restart as new run | Runs the same frozen workflow, goal and inputs again, as a new run. | | Archive, Unarchive | Hides a finished run from the list, or brings it back. Archiving removes the run's worktrees. | | Delete permanently | Removes the run and its history. | | Move to front of queue | On the runs list, starts a queued run next. | | Mark as resolved | Takes a Failed or Aborted run off the cards at the top of **Recent runs**. | ## Controls survive a restart A control you send while Orbital restarts still applies. [Durability after restarts](/runs/durability/) explains how. ## Related - [Runs](/runs/): the standings a control moves a run between. - [Message a run](/runs/message-a-run/): steer the agent instead of stopping it. - [Switch a run's harness](/harnesses/switch-harness/): retry a step on another harness. - [The run queue](/runs/queue/): what happens to a slot when you pause or resume. - [Durability after restarts](/runs/durability/): how controls and paused runs survive a restart. --- # The run queue > Orbital runs a set number of runs at once and queues the rest You set how many [runs](/runs/) Orbital works on at once, two unless you change it. Each working run holds one run slot, and the other runs wait in the queue until a slot frees up. A project can also have [its own limit](#limit-one-projects-runs). ## Change the number of runs at work 1. **Open Settings.** Go to **Settings > General**. 2. **Set Concurrent runs.** Under **Concurrent runs**, choose − or +, or type a number from 1 to 16. ![The General settings page, with Appearance, Concurrent runs, Queue method, Default workflow, Default harness and other settings.](/screenshots/settings-general.webp?v=cb0897c671) *Concurrent runs sets how many runs work at once.* The page shows how many slots are in use now. A change applies at once. Lowering the number never stops a run that is already working. The queue waits longer instead. Each run can make many agent turns at once, for example in parallel steps. The number counts runs, not turns. ## Choose the queue method The queue method decides what a run does with its slot while it waits for you or for a pull request. 1. **Open Settings.** Go to **Settings > General**. 2. **Choose a Queue method.** Under **Queue method**, choose one: - **Interleave**, the default. While a run waits for you or for a pull request, another run can use its slot. - **Finish first**. A new run starts only when an earlier run has finished. The choice saves at once and applies at the next decision, without a restart. Switching to **Interleave** lets queued runs into the slots that paused runs were holding. Switching to **Finish first** stops nothing that is already working. Under **Finish first**, a queued run's page says "Waiting for a run to finish", with its place. A paused run, or one at a gate, says "Holding its slot until it finishes". ## Limit one project's runs Give a project its own limit, so a project with a long backlog does not take every slot. 1. **Open the project.** Open **Projects** and choose the project. 2. **Set the run limit.** Under **Runs**, set **Run limit** with **−** and **+**, or type a number and press Enter. It saves at once. ![A project page with its folders table and the Runs limit field.](/screenshots/project-details.webp?v=a56bdefccc) *The run limit applies to this project only.* Leave the field empty for no limit of its own. The number in **Settings > General** stays the upper limit across all projects, and the project page shows it, for example "Orbital allows 4 at a time overall." A project limit at or above that number changes nothing, and the page says so. A change applies at the next decision, without a restart. Lowering the limit never stops a run that is already working. A queued run held back by its project's limit shows "Queued · waiting for a free slot in *project*" instead of its place. ## What holds a slot A run takes a slot when it starts. It keeps the slot through every step: agent turns, commands, checks, parallel branches, timed waits and questions the agent asks you. Under **Interleave**, a run gives its slot back when: - it reaches a [human gate](/workflows/gates-and-waits/) (Waiting, "At a gate"); - it stops by itself and shows "Waiting on you", or you pause it (Paused); - it ends. A delivery workflow waiting for its pull request to be reviewed sits at a human gate, so it holds no slot. Many runs can wait on review while others work. Under **Finish first**, a run keeps its slot until it ends, through gates, pauses and waits on you. Stopping a run, or resolving it, frees its slot under either method. ## Order in the queue When a slot frees, Orbital skips projects that are at their own limit. It gives the slot to the project that has waited longest since it last got one, so projects take turns. Within one project, runs start in the order they joined the queue. A queued run's badge shows its place, such as "Queued · 2nd". To start a queued run next, open **Runs**, open its row's menu (⋯) and choose **Move to front of queue**. A run that comes back from a gate or a pause joins the back of the queue and shows "Continuing when a slot is free". A paused run therefore loses its place. When you resume it, it joins the back. ## After a restart The queue is stored with the run history. After a restart, the runs that were working take their slots back first, even beyond the number you set. Queued runs keep their order. :::tip Queue only what you can follow. A long queue behind a stuck run holds everything up. [Lead delivery with a supervisor](/supervisor/lead-delivery/#rules-that-keep-it-safe) recommends at most one run queued beyond those working. ::: ## Related - [Runs](/runs/): the standings a run moves through, including Queued. - [Pause, stop or resolve a run](/runs/control-a-run/): pause a run to free its slot. - [Projects and folders](/projects/): the project page where the run limit lives. - [Settings](/reference/settings/#general): every General setting, including Concurrent runs. - [Durability after restarts](/runs/durability/): what happens to the queue when Orbital restarts. --- # Write your first workflow > Run the smallest complete workflow: one agent step and one terminal Write a [workflow](/workflows/) with one agent step and one terminal, then run it. It is the smallest workflow that does anything. ```dot title="minimal.dot" digraph hello { graph [version="1", entry="hello"] hello [tool_access="Read only", prompt="Reply with one sentence explaining the goal: {{ inputs.goal }}."] done [shape="Msquare"] hello -> done } ``` `hello` is an agent step. It has the Read only tool access, so it can read files but cannot change them. Codex reads files through commands that its read-only sandbox prevents from writing. `done` is a terminal, and the edge between them is the whole route. Every other guide builds on this with more steps and edges. ![hello → done. One prompt followed by a successful terminal.](/minimal.svg?v=b911ee569f) ## Before you start You need a harness you have signed in to, and a project in Orbital. Any project works. It does not need to be a Git repository, unless you run the example with Codex, which needs one. See [Projects and folders](/projects/). ## 1. Save the workflow [Download the example](/minimal.zip) and unpack it into your workflow library, `~/.orbital/workflows/`. Keep every relative path. You can also [download minimal.dot](/examples/minimal/minimal.dot) on its own. ## 2. Validate it ```sh orbital validate ~/.orbital/workflows/minimal.dot ``` Validation calls no agent. ## 3. Start a run Choose **New task** and pick any project. Choose the `minimal` workflow and your harness. One project serves every example. ![The Start page asks what to do in the selected project, with a box for the goal, a harness picker, a workflow picker and a Chat or Workflow switch.](/screenshots/start-page.webp?v=0428f5d705) *Pick the workflow and harness beside the goal box.* Set the goal to "Explain what a workflow is", and start the run. The `hello` step returns a sentence, then `done` ends the run successfully. ## Related - [Write prompts](/workflows/write-prompts/): read run values in a prompt. - [Add branches](/workflows/add-branches/): send a run to different steps. - [Node types](/reference/node-types/): the kinds of step you can add. - [Research report](/examples/research-report/): a workflow that touches no code. - [Workflows](/workflows/): where Orbital finds workflows and how to validate them. --- # Write prompts > Read run values in a prompt, and use conditions, loops and filters Write the text an agent step receives, and let Orbital fill in the run's values before each visit. One prompt can then say something different on a retry, after a review or in a different project. ```dot title="review.dot" review [prompt="Review the change against this goal: {{ inputs.goal }}"] ``` The template language is [Nunjucks](https://mozilla.github.io/nunjucks/templating.html). Every example below shows the prompt and the text it renders to. ## Inline prompts and prompt files A short prompt fits in the node's `prompt` attribute, such as `prompt="Review the goal for risks."`. The attribute sits inside double quotes, so write strings inside the template with single quotes, as every example here does. A longer prompt belongs in its own file. `prompt_file="prompts/review.md"` names a file relative to the workflow file, and a `prompts` folder beside the workflow is the usual place. A node takes one or the other, never both. Orbital reads a prompt file again before each later visit, so an edit reaches a running workflow. If the edited file is invalid, Orbital reports the problem and keeps the prompt it last checked. ## Read a value `{{ … }}` prints a value. The goal is `inputs.goal`, each declared input is `inputs.`, and anything the run has gathered is under `context`. [Template variables](/reference/template-variables/) lists every name. ```jinja title="prompt" Work towards this goal: {{ inputs.goal }} Extra notes: {{ inputs.notes }} ``` This run was started without the optional `notes` input, so that line ends empty: ```text title="renders" Work towards this goal: Add a dark mode toggle to the settings page Extra notes: ``` A value the run does not hold renders as nothing, never as an error or the word `undefined`. Validation catches most mistakes first. A prompt that reads a context value some earlier path never writes fails `orbital validate`, and so does a name that is not a template variable. ## Conditionals `{% if %}` includes text only when its test holds. `{% elif %}` tries another test and `{% else %}` covers the rest. Compare with `==` and `!=`, and join tests with `and`, `or` and `not`. An empty value counts as false. ```jinja title="prompt" {% if context.verdict == 'reject' -%} Fix every finding the reviewer listed. {% elif context.verdict == 'pass' -%} Tidy the change and write the summary. {% else -%} Review the change from the start. {% endif -%} ``` With `verdict` set to `reject`: ```text title="renders" Fix every finding the reviewer listed. ``` The dashes in `-%}` stop each tag from leaving a blank line behind. [Whitespace control](#whitespace-control) explains them. ## Loops `{% for %}` repeats its text for each item. Inside it, `loop.index` counts from 1, `loop.first` and `loop.last` mark the ends, and `loop.length` is the number of items. `{% else %}` inside a loop renders when there is nothing to loop over. ```jinja title="prompt" Check each folder: {% for name, folder in run.folders -%} {{ loop.index }}. {{ name }} at {{ folder.path }} {% else -%} This project has no folders. {% endfor -%} ``` In a project with the folders `web` and `api`: ```text title="renders" Check each folder: 1. web at /work/shop/web 2. api at /work/shop/api ``` Context values are single pieces of text, numbers or true and false, so a loop has two useful sources: the folders under `run.folders` or `project.folders`, and a list written in the prompt itself, such as `{% for area in ['tests', 'types'] %}`. A `json` output is stored as text, and a loop over it walks its characters. ## Filters A filter changes a value on its way into the prompt. Write it after a `|`, and chain as many as you need. | Filter | What it does | | --- | --- | | `default('x')` | Uses `x` when the value is missing. `default('x', true)` also replaces empty text. | | `trim` | Removes spaces and line breaks from both ends. | | `join(', ')` | Joins a list into one piece of text. | | `length` | Counts the items in a list, the folders in `run.folders`, or the characters in text. | | `upper` and `lower` | Change the case. | | `replace('a', 'b')` | Replaces every `a` with `b`. | ```jinja title="prompt" Branch: {{ inputs.title | trim | lower | replace(' ', '-') }} Priority: {{ inputs.priority | default('normal') | upper }} Last failure: {{ context.failure_reason | default('none', true) }} Folders: {{ run.folders | length }} Checks: {{ ['lint', 'tests', 'types'] | join(', ') }} ``` With `title` set to ` Fix Login Redirect `, no `priority` input, no failure yet and one folder: ```text title="renders" Branch: fix-login-redirect Priority: NORMAL Last failure: none Folders: 1 Checks: lint, tests, types ``` `failure_reason` is empty text after a success rather than missing, which is why that line needs `default('none', true)`. Nunjucks has more filters; its [list of built-in filters](https://mozilla.github.io/nunjucks/templating.html#builtin-filters) covers them all. ## Whitespace control Every line break in the prompt stays in the output, including the ones around `{% %}` tags. A condition that is false leaves an empty line where it stood: ```jinja title="prompt" Review the change. {% if context.failure_reason %} The last attempt failed: {{ context.failure_reason }} {% endif %} Report what you found. ``` ```text title="renders" Review the change. Report what you found. ``` A dash inside a tag removes the spaces and line breaks on that side. `{%-` trims before the tag and `-%}` trims after it. `{{-`, `-}}`, `{#-` and `-#}` work the same way. ```jinja title="prompt" Review the change. {%- if context.failure_reason %} The last attempt failed: {{ context.failure_reason }} {%- endif %} Report what you found. ``` ```text title="renders" Review the change. Report what you found. ``` When the step fails and comes back, the same prompt adds the failure on its own line between the other two. ## Comments `{# … #}` holds a note for whoever edits the prompt. The agent never sees it. ```jinja title="prompt" {# The reviewer reads this on a phone, so keep the prompt short. -#} Review the change in three sentences or fewer. ``` ```text title="renders" Review the change in three sentences or fewer. ``` ## Write a literal `{{` Text between `{% raw %}` and `{% endraw %}` renders exactly as written. Use it when the agent must see template syntax of its own, such as a placeholder in a file it edits. ```jinja title="prompt" Add a greeting to the email template. It must say: {% raw %}Hello {{ customer.name }}, your order has shipped.{% endraw %} ``` ```text title="renders" Add a greeting to the email template. It must say: Hello {{ customer.name }}, your order has shipped. ``` ## What a prompt cannot do A prompt cannot read files, so `include`, `import` and `extends` are unavailable, and it cannot run shell commands. Orbital checks everything a prompt reads before the run starts. A prompt that pulled in another file or a command's output would read things that check never saw. To give a prompt a file's contents or a command's result, add a [command node](/reference/node-types/#command) before it. Its facts land in context, where the prompt reads them. To share text between prompts, put it in one prompt file and point several nodes at it. ## Patterns ### Show the failure only on a retry Orbital writes `context.failure_reason` after every step. It is empty on success and holds the reason on failure. A step reached again through a failure edge can explain what went wrong, while its first visit reads as normal. ```jinja title="prompt" Implement the goal: {{ inputs.goal }} {%- if context.failure_reason %} Your last attempt failed: {{ context.failure_reason }} Fix that first. {%- endif %} ``` After the tests failed: ```text title="renders" Implement the goal: Fix the login redirect Your last attempt failed: npm test exited with code 1: 2 of 48 tests failed Fix that first. ``` On the first visit, the prompt ends after the goal. ### Use the goal and a declared input With `inputs="ticket_url"` on the workflow, the new-run form asks for a ticket URL and every prompt can read it. ```jinja title="prompt" Goal: {{ inputs.goal }} Implement the ticket at {{ inputs.ticket_url }}. Keep the change to what the ticket asks for. ``` ```text title="renders" Goal: Ship the ticket as one pull request Implement the ticket at https://github.com/acme/shop/issues/42. Keep the change to what the ticket asks for. ``` `inputs.ticket_url` always renders what the run started with. `context.ticket_url` renders the current value, which a later step may have changed. ### List each branch's result after a fan-in After a [fan-in](/workflows/run-steps-in-parallel/), each branch's result sits under `context.parallel.results`, in edge order, with its `node`, `outcome`, `failure_reason` and `context`. Quote the index. Read each branch on its own line. Validation checks every value a prompt reads, and it cannot check a loop over the results. ```jinja title="prompt" Two reviewers read the change. Combine their findings into one list. {{ context.parallel.results['0'].node }}: {{ context.parallel.results['0'].context.assessment }} {{ context.parallel.results['1'].node }}: {{ context.parallel.results['1'].context.assessment }} ``` ```text title="renders" Two reviewers read the change. Combine their findings into one list. security: The coupon code is not escaped before the query. tests: Nothing covers an expired coupon. ``` ### Point at a folder `run.folders..path` is where the run works on that folder now, which is a worktree once the run has one. `project.folders..path` is the folder as the project holds it. The name is the folder's name in the project. ```jinja title="prompt" Work only inside {{ run.folders.web.path }}. Do not edit {{ project.folders.web.path }}. That folder belongs to the person who started the run. ``` ```text title="renders" Work only inside /home/me/.orbital/worktrees/r7Kq2/web. Do not edit /work/shop/web. That folder belongs to the person who started the run. ``` ### Reuse an earlier agent's answer `context.response.` is the final prose of the agent step called ``, without the outputs it handed over. It is empty until that step completes, and it names agent steps only. ```jinja title="prompt" The planner wrote this plan: {{ context.response.plan }} Implement it step by step. ``` ```text title="renders" The planner wrote this plan: 1. Read the return URL from the query string. 2. Redirect there after sign-in. Implement it step by step. ``` ## Quick reference | Write | To | | --- | --- | | `{{ context.verdict }}` | Print a value. | | `{% if context.verdict == 'reject' %}` … `{% endif %}` | Include text only when a test holds. | | `{% for name, folder in run.folders %}` … `{% endfor %}` | Repeat text for each item. | | `{{ inputs.title \| trim }}` | Change a value with a filter. | | `{%-` and `-%}` | Trim the line break before or after a tag. | | `{# note #}` | Leave a note the agent never sees. | | `{% raw %}` … `{% endraw %}` | Print template syntax as written. | ## Related - [Template variables](/reference/template-variables/): every name a prompt can read. - [Pass context between steps](/workflows/pass-context/): how steps write the values prompts read. - [Context](/reference/context/): what each context value holds and when it exists. - [History and threads](/reference/history-and-threads/): what a prompt already carries from earlier steps. --- # Add branches > Send a run to different steps with conditions on its edges Send a run to different steps depending on what it has just learned. Put a `condition` on each edge that leaves the step, and the run follows the first one that matches. ```dot title="conditional.dot" classify [prompt="If the goal asks for a greeting, choose greet. Otherwise choose explain.", outputs="route:choice"] classify -> greet [condition="route == 'greet'", weight="2"] classify -> explain [condition="route == 'explain'", weight="1"] ``` ## How conditions choose the next step Routing lives on edges, not in a decision node. Each edge that leaves a step either carries a `condition` or is the fallback. A condition compares one context value with a literal, such as `verdict == 'pass'`, or tests a value on its own, such as `pr.mergeable`. When a step succeeds, Orbital tries its conditioned edges from the highest `weight` down and takes the first match. Only then does it consider the fallback. Two conditioned edges from one step cannot share a weight. You cannot combine conditions or compare two context values, so every route stays readable in the workflow. The [`condition`](/reference/attributes/#condition) entry lists every form a condition takes. ![The workflow editor canvas shows edges leaving an agent step, each labelled with a condition and a weight such as verdict == 'reject' · w3, and a dashed edge labelled else.](/screenshots/workflow-editor.webp?v=85a9c6275a) *Each conditioned edge shows its condition and weight; the dashed else edge is the fallback.* ## Choose what to branch on Decide first what produces the value you branch on. The answer changes the cost and the reliability of the branch. - Use a built-in probe when Orbital already observes the fact. The [ticket workflow](/examples/workflows/ticket/) routes on pull request state without asking an agent whether a merge happened. - For another exact observation, use a script command with declared `bool`, `number` or `text` facts. Its last line of output must be the JSON object exactly as declared. Keep the script read-only when it only inspects something. - For judgement, give an agent a `choice` output. Orbital takes the allowed values from the conditions on the step's outgoing edges and refuses any other answer. So `outputs="verdict:choice"` with edges for `pass` and `reject` accepts nothing else. A probe or a script gives an exact answer at no agent cost. The example below branches on judgement, so an agent makes the choice. ## Cover every case A branching step needs a fallback, an edge with no condition, unless its conditions already cover every value of its `choice` output. A run where no edge matches stops with an error, so validation asks for the fallback before the run starts. :::note A fallback handles successful results outside your named cases. It never handles a failed step. Route a failure with its own condition, `outcome == 'failed'`, as [Add a loop](/workflows/add-a-loop/) and [Error handling](/reference/error-handling/) explain. ::: ## Before you start You need a harness you have signed in to, and a project in Orbital. Any project works. It does not need to be a Git repository, unless you run the example with Codex, which needs one. See [Projects and folders](/projects/). ## 1. Download the example [Download the example](/conditional.zip) and unpack it into `~/.orbital/workflows/`. Keep every relative path. ![classify selects greet or explain; both reach done.](/conditional.svg?v=56bd802163) ```dot title="conditional.dot" digraph conditional { graph [version="1", entry="classify"] classify [tool_access="Read only", prompt="If the goal asks for a greeting, choose greet. Otherwise choose explain.", outputs="route:choice"] greet [tool_access="Read only", prompt="Write a friendly greeting."] explain [tool_access="Read only", prompt="Explain the goal in one sentence."] done [shape="Msquare"] classify -> greet [condition="route == 'greet'", weight="2"] classify -> explain [condition="route == 'explain'", weight="1"] greet -> done explain -> done } ``` `classify` declares `outputs="route:choice"`. Orbital takes the allowed values, `greet` and `explain`, from the conditions on its outgoing edges, and refuses any other answer before it reaches context. The two edges carry different weights, so the higher one is tried first. The conditions cover every allowed value, so this step needs no fallback. Every step has the Read only tool access, so none of them can change files. Codex can run commands to read files because its read-only sandbox prevents writes. [Download conditional.dot](/examples/conditional/conditional.dot) ## 2. Validate it ```sh orbital validate ~/.orbital/workflows/conditional.dot ``` ## 3. Run it Choose **New task**, pick any project, and choose the `conditional` workflow and your harness. One project serves every example. Use a greeting as the goal to select `greet`, or any other goal to select `explain`. Exactly one response step runs before `done`. The run's **Context** tab shows each value the run holds and the step that set it. ![The run page's Context tab lists the values held by the run, each with the step that set it on the right.](/screenshots/run-context.webp?v=3e79bd8607) *The Context tab shows which step set each value.* :::caution A later step that reads a value produced on only one branch fails validation. Check [Error handling](/reference/error-handling/) before you join branches back together. ::: ## Related - [`condition`](/reference/attributes/#condition): every form a condition takes, and the fallback rules. - [Add a loop](/workflows/add-a-loop/): send rejected work back for repair, with a bound. - [Pass context between steps](/workflows/pass-context/): the values a condition can read. - [Error handling](/reference/error-handling/): what happens when a step fails. --- # Add a loop > Send work back to an earlier step, and stop the loop after a set number of rounds Send work back to an earlier step when a review rejects it. A loop is any edge that points back at an earlier step, and nothing else marks it. ```dot title="bounded-retries.dot" review -> repair [condition="verdict == 'reject'", weight="3", repair_budget="answer", repair_round="retry"] repair -> review ``` Writing a loop is easy. The work is in stopping it. ## How a loop stops Orbital gives you two ways to stop a loop, and two ways to make a long loop cheaper. ### Repair rounds A repair budget limits how many times one piece of work goes back for correction. It is the bound to reach for when an agent may need several corrections but must not correct forever. You name the budget on three kinds of edge: | Round | Edge | What it does | | --- | --- | --- | | `retry` | The edge that sends work back for repair. | Starts a repair round. | | `exhausted` | A partner with the same condition and budget, and a different weight. | Runs once after five unsuccessful rounds, instead of the retry. | | `reset` | The edge taken when the work has passed. | Clears the budget, so the next piece of work gets five rounds again. | Put the retry and exhausted edges after the review that decides. Never put them on a fan-out or inside a parallel branch. The [`repair_round`](/reference/attributes/#repair_round) entry gives the full rules. In the editor, each edge that loops back shows its condition and weight. ![The workflow editor shows a pull-request workflow in which conditioned edges such as review_verdict == 'reject' lead from later steps back to earlier ones.](/screenshots/workflow-editor-loop.webp?v=4dacb0ba6a) *Edges that loop back carry a condition and a weight like any other edge.* ### Visit caps `max_visits` on a node caps every visit to that node for the life of the run, whatever the reason. `max_visits` on the workflow caps the run's agent turns, 200 by default. Commands and waits do not spend it. | Limit reached | Result | | --- | --- | | The workflow's limit | The run ends Failed. | | A step's own limit | That visit fails, and the run is Paused with "Waiting on you", whatever failure edges the step has. | Visit caps stay independent of repair budgets. Treat them as the outer guard, not as the way the loop is meant to stop. See [`max_visits`](/reference/attributes/#max_visits). ### Waits A poll loop watches something outside the run, such as a pull request, a build or a queue. Put an `insulator` wait in it. Waiting spends no agent visits, and you can choose **Skip** to check again now. [Add gates and waits](/workflows/gates-and-waits/) covers the wait step itself. ![The run page's Waterfall tab shows a poll loop: probe, route and wait steps repeat as probe@2, route@2, wait@2 and so on, each with its duration.](/screenshots/run-steps.webp?v=fee718c962) *Each pass of a poll loop adds a new visit to the probe, route and wait steps.* ### Loop restart `loop_restart="true"` on the edge back clears the values the loop wrote to context, the history of finished steps that later prompts show, and the agents' sessions. It keeps the original inputs, the run's identity, the visit counts and the repair budgets. Use it when the next pass is a new piece of work, not another attempt at the same one. Without it, each prompt in the loop carries the history of every earlier pass. See [`loop_restart`](/reference/attributes/#loop_restart). ## Failure is not a loop A failed step takes a matching conditioned edge and never the ordinary fallback. When the workflow knows how to recover, route `outcome == 'failed'` to a repair prompt that reads `{{ context.failure_reason }}`. Then return to the observation or review that proves the recovery worked. :::caution Read the failure reason before you decide to retry. An authentication failure needs credentials. Asking the same unauthenticated harness again cannot repair it. ::: Make exhaustion visible with a terminal or a step that reassesses the work. A failed terminal ends the run as Failed. A failed step with no edge for the failure leaves the run Paused, with "Waiting on you". [Troubleshooting](/troubleshooting/) explains how to tell them apart in the app, and [Error handling](/reference/error-handling/) gives the exact rules. ## Before you start You need a harness you have signed in to, and a project in Orbital. Any project works. It does not need to be a Git repository, unless you run the example with Codex, which needs one. See [Projects and folders](/projects/). ## 1. Download the example This workflow reviews an answer and sends rejections to a repair step. It gives up after five unsuccessful rounds instead of looping forever. [Download the example](/bounded-retries.zip) and unpack it into `~/.orbital/workflows/`. Keep every relative path. ![review passes to done or rejects into repair; repair returns to review. Five unsuccessful repair rounds lead to exhausted.](/bounded-retries.svg?v=32a54bfbd3) ```dot title="bounded-retries.dot" digraph retries { graph [version="1", entry="review", max_visits="30"] review [tool_access="Read only", prompt="Review the goal and any previous repair. Choose pass or reject.", outputs="verdict:choice"] repair [tool_access="Read only", prompt="Revise the proposed answer to the goal."] done [shape="Msquare"] exhausted [shape="Msquare", outcome="failed"] review -> repair [condition="verdict == 'reject'", weight="3", repair_budget="answer", repair_round="retry"] review -> exhausted [condition="verdict == 'reject'", weight="2", repair_budget="answer", repair_round="exhausted"] review -> exhausted review -> done [condition="verdict == 'pass'", weight="1", repair_budget="answer", repair_round="reset"] repair -> review } ``` Four edges leave `review`, and three of them name `repair_budget="answer"`: | Edge | Condition | What it does | | --- | --- | --- | | `retry` | `verdict == 'reject'`, weight 3 | Starts a repair round. | | `exhausted` | The same condition, weight 2 | Runs once after five unsuccessful rounds. | | `reset` | `verdict == 'pass'`, weight 1 | Clears the budget, so a later review that rejects gets five rounds again. | The fourth edge, `review -> exhausted`, has no condition, so it is the fallback. Validation does not count the retry and exhausted edges as answering `reject`, so `review` needs a fallback for the case where neither round is open. It points at `exhausted`, so a rejection that no round can take still ends the run Failed. Both steps have the Read only tool access, so the repair revises its answer without changing files or running commands. [Download bounded-retries.dot](/examples/bounded-retries/bounded-retries.dot) ## 2. Validate it ```sh orbital validate ~/.orbital/workflows/bounded-retries.dot ``` ## 3. Run it Choose **New task**, pick any project, and choose the `bounded-retries` workflow and your harness. One project serves every example. Write the goal the review step should judge, and start the run. The first passing verdict ends the run successfully. A rejection starts up to five repairs. A sixth rejecting review ends at `exhausted` with a failed outcome. ## Choosing how to stop a loop | You want to | Use | | --- | --- | | Allow a few corrections of one piece of work | A repair budget with `retry`, `exhausted` and `reset` edges. | | Guard against a loop that never ends | `max_visits` on the node or the workflow. | | Check on something outside the run | A wait in the loop. | | Start each pass as new work | `loop_restart="true"` on the edge back. | | Recover from a failed step | An `outcome == 'failed'` edge to a repair prompt. | ## Related - [Add branches](/workflows/add-branches/): how edges choose the next step. - [Add gates and waits](/workflows/gates-and-waits/): pause a loop for time or for a person. - [Pass context between steps](/workflows/pass-context/): what a loop restart clears from context. - [Error handling](/reference/error-handling/): how failed steps and repair budgets behave. --- # Run steps in parallel > Run independent review steps at once and collect their results Run independent steps at the same time with a fan-out, then collect their results with a fan-in. In practice, independent work means reviews. ```dot title="parallel-reviews.dot" reviews [shape="component", max_parallel="2"] collect [shape="tripleoctagon", prompt="Summarise these reviews: {{ context.parallel.results['0'].context.assessment }} and {{ context.parallel.results['1'].context.assessment }}"] reviews -> clarity reviews -> risks clarity -> collect risks -> collect ``` Orbital keeps each branch's context apart, but not its files. Two branches that write to the same checkout will fight over it. ## How the fan-out works `reviews` is a `component`, a [fan-out](/reference/node-types/#fan-out). Each of its bare outgoing edges starts a branch, up to `max_parallel` at a time. Every branch must reach the same `tripleoctagon` fan-in without a cycle, a nested fan-out or an edge that leaves the branch. Each branch keeps its own context, so `clarity` cannot read what `risks` produced. After the fan-in, results arrive in edge order under `parallel.results`, with node, outcome, failure reason and context. The fan-in prompt reads `{{ context.parallel.results['0'].context.assessment }}`. The index is quoted, and the complete example validates that spelling. ![The editor shows a fan-out step with a maximum of three, three parallel steps below it, and a prompted fan-in step that combines their findings.](/screenshots/workflow-editor-roles.webp?v=3ff23d4252) *Every branch leaves the fan-out and meets again at the fan-in.* Set `max_parallel` to the number of steps your harness can run at once. If branches must write, give them separate files and review the combined result after the fan-in. :::caution If a branch fails and the fan-out has no conditioned failure edge, the run is Paused. It waits for you and names the failed branches. Add a failure edge on the fan-out when partial completion has a safe recovery. See [Runs](/runs/). ::: ## Before you start You need a harness you have signed in to that can run two steps at once, and a project in Orbital. Any project works. It does not need to be a Git repository, unless you run the example with Codex, which needs one. See [Projects and folders](/projects/). The branches share a folder, so every step has the Read only tool access and cannot change it. ## 1. Download the example [Download the example](/parallel-reviews.zip) and unpack it into `~/.orbital/workflows/`. Keep every relative path. ![reviews forks into clarity and risks; both meet at collect, which summarises them and reaches done.](/parallel-reviews.svg?v=13456a7d81) ```dot title="parallel-reviews.dot" digraph reviews { graph [version="1", entry="reviews"] reviews [shape="component", max_parallel="2"] clarity [tool_access="Read only", prompt="Review the goal for clarity.", outputs="assessment:text"] risks [tool_access="Read only", prompt="Review the goal for risks.", outputs="assessment:text"] collect [shape="tripleoctagon", tool_access="Read only", prompt="Summarise these reviews: {{ context.parallel.results['0'].context.assessment }} and {{ context.parallel.results['1'].context.assessment }}"] done [shape="Msquare"] reviews -> clarity reviews -> risks clarity -> collect risks -> collect collect -> done } ``` [Download parallel-reviews.dot](/examples/parallel-reviews/parallel-reviews.dot) ## 2. Validate it ```sh orbital validate ~/.orbital/workflows/parallel-reviews.dot ``` Validate again whenever you change where a branch starts or ends. ## 3. Run it Choose **New task**, pick any project, and choose the `parallel-reviews` workflow and your harness. One project serves every example. Write the goal both reviews should assess, and start the run. Both reviews run. Their `assessment` outputs stay inside `parallel.results`. The `collect` step summarises both before `done`. ## Related - [Fan-out](/reference/node-types/#fan-out) and [fan-in](/reference/node-types/#fan-in): what the two steps take and do. - [Parallel branches](/reference/history-and-threads/#parallel-branches): the exact rules for branches, their context and their results. - [Pass context between steps](/workflows/pass-context/): why a branch cannot read its sibling's outputs. - [Give steps a role](/roles/configure-steps/): give each branch its own role. --- # Reuse a workflow > Move a shared sequence of steps into its own workflow file and import it When several workflows share a sequence of steps, move the sequence into its own workflow file and import it. One node with an `import` attribute stands in for the whole file. ```dot title="epic.dot" ticket [import="subgraphs/ticket.dot"] ``` ## How a sub workflow works A sub workflow is an ordinary workflow file that another workflow imports. It has its own version, entry and terminals, so you can validate and run it on its own before any parent depends on it. It has to be a complete workflow, not a fragment. The node that names it is a placeholder. Before validation, Orbital replaces the placeholder with the imported workflow, so the editor, the validator and the run all see one flattened workflow. - Imported step IDs take the placeholder's name as a prefix, so `ticket.review` is the imported `review` step. - Edges into the placeholder reach the imported entry. - The imported terminals become exits, and the placeholder's outgoing edges replace them. - Outputs and probe fact names keep their original names. That is what lets a parent condition read a value the sub workflow produced. With one exit, the parent can use a bare outgoing edge. With several, each outgoing edge names one with `exit`, and every exit is bound exactly once. The parent's edge out of the placeholder carries only `exit` and `loop_restart`. Conditions, weights and repair budgets belong on the edges inside the imported workflow. Relative imports and prompt files resolve from the file that names them. The [import placeholder](/reference/node-types/#import-placeholder) reference gives the rest of the rules. ## Share settings with the steps inside A placeholder can carry `role`, `persona`, `model_settings` and `tool_access`. Each applies to every step inside that does not choose its own, and a step's own choice always wins. [Give steps a role](/roles/configure-steps/) explains how. A placeholder cannot override the prompts of the steps inside. The imported workflow's own workflow-level settings describe its standalone use and do not become parent defaults. Put shared configuration on the placeholder or in the parent workflow. ## Before you start You need the [epic example](/epic.zip), unpacked into `~/.orbital/workflows/` with every relative path intact. It holds the parent and the sub workflow shown below. ## 1. Write the sub workflow This one implements and reviews a single piece of work. Its two terminals, `delivered` and `rejected`, become the exits the parent binds. ```dot title="subgraphs/ticket.dot" digraph ticket { graph [version="1", entry="implement", description="Implement and locally review one sub ticket"] implement [prompt="Implement the current work described below. Satisfy only its requirements. {{ current_work }}", outputs="implementation_summary:text"] review [prompt="Review the implementation against the current work described below. Choose pass when it satisfies the work, or reject when it does not. {{ current_work }}", outputs="verdict:choice"] delivered [shape="Msquare", outcome="success"] rejected [shape="Msquare", outcome="failed", label="Five reviews rejected the same sub ticket"] implement -> review review -> delivered [condition="verdict == 'pass'", weight="3", repair_budget="sub_ticket", repair_round="reset"] review -> implement [condition="verdict == 'reject'", weight="2", repair_budget="sub_ticket", repair_round="retry"] review -> rejected review -> rejected [condition="verdict == 'reject'", weight="1", repair_budget="sub_ticket", repair_round="exhausted"] } ``` It reads `{{ current_work }}` rather than a declared input, so the parent decides what one piece of work is. Any parent that sets current work can use it. ## 2. Validate it alone ```sh orbital validate ~/.orbital/workflows/subgraphs/ticket.dot ``` ## 3. Add a placeholder to the parent `ticket [import="subgraphs/ticket.dot"]` is the placeholder. The path is relative to the parent, because relative imports resolve from the file that names them. ```dot title="epic.dot" digraph epic { graph [version="1", entry="select", inputs="work", description="Work through an epic one sub ticket at a time", max_visits="200"] select [prompt="Read the epic at {{ inputs.work }} and every sub ticket under it. Choose the next unfinished sub ticket and report it as the current work. Choose ticket while one remains, or none once every sub ticket is finished.", outputs="current_work:work,remaining:choice"] ticket [import="subgraphs/ticket.dot"] finished [shape="Msquare", outcome="success"] stalled [shape="Msquare", outcome="failed", label="A sub ticket could not be delivered"] select -> ticket [condition="remaining == 'ticket'", weight="2"] select -> finished [condition="remaining == 'none'", weight="1"] ticket -> select [exit="delivered", loop_restart="true"] ticket -> stalled [exit="rejected"] } ``` ## 4. Bind every exit Each edge leaving the placeholder names the exit it binds. Here `delivered` returns to the select step, and `rejected` ends the run. ## 5. Validate the parent ```sh orbital validate ~/.orbital/workflows/epic.dot ``` Orbital replaces the placeholder with the imported workflow and prefixes every imported node ID, so the flattened workflow contains `ticket.implement` and `ticket.review`. The editor draws the placeholder as one import node, with each exit on its own edge. Choose **Expand imports** to see a flat, read-only view with each import expanded, and **Show authored workflow** to return. ![The workflow editor shows an import node for landing.dot, with two outgoing edges labelled exit closed and exit merged, and an Expand imports button above the canvas.](/screenshots/workflow-editor-imports.webp?v=3f00eb3b61) *The import node binds each exit on its own edge.* ## Share it Include every imported file and prompt file, and keep their relative paths. The [epic example](/epic.zip) shows the layout. The [ticket workflow](/examples/workflows/ticket/) is a parent with two sub workflows and twelve prompt files, shipped as one archive. If someone pastes the parent into **Import** on the Workflows page instead, the prompt files it refers to become empty prompts to fill in. ![The Import a workflow page has one large DOT text box with Import and Cancel buttons below it.](/screenshots/import-workflow.webp?v=b5c29c87bd) *Pasted DOT text is not stored until you save.* ## Related - [Import placeholder](/reference/node-types/#import-placeholder): the rules for placeholders, names and exits. - [Ticket to merge](/examples/workflows/ticket/): a parent with two sub workflows. - [Outer loop over an epic](/examples/workflows/epic/): the same parent and sub workflow, explained step by step. - [Give steps a role](/roles/configure-steps/): set roles on imported steps. --- # Pass context between steps > Hand a value from one step to the next through the run's context Hand a value from one step to a later one by declaring it as an output, then reading it from context. Context is the one store a run reads and writes as it moves through the workflow. ```dot title="review.dot" review [prompt="Review the change.", outputs="verdict:choice,findings:json"] fix [prompt="Address these findings: {{ context.findings }}"] review -> fix [condition="verdict == 'reject'", weight="1"] ``` Context starts with the goal and the declared inputs. It then gains an entry for every accepted agent output, every command fact and each step's `outcome`. Prompts read it, conditions read it, and nothing else carries information between steps. The run page's **Context** tab lists every value the run holds and the step that set it. ![The run page's Context tab lists fourteen values held by the run, such as next, summary, pr and pr.state, each with the step that set it on the right.](/screenshots/run-context.webp?v=3e79bd8607) *Each value shows the step that set it, such as select, implement or a probe visit.* ## What goes in | Source | What it adds | | --- | --- | | Declared inputs | Copied into context when the run starts. | | An agent step | Only the outputs it declares. | | A probe command | Per-folder facts such as `web.pr.raw`, and rolled-up facts such as `pr.state`. | | A script command | The dotted names in its own `facts` attribute, and nothing else. | | Every step | Its `outcome`. | `{{ inputs.work }}` always renders the original input value. `{{ context.work }}` renders whatever the run holds now, which a later step can change. [Workflows](/workflows/) explains how to declare inputs. An agent contributes only what it declares. `outputs="verdict:choice,findings:json"` accepts exactly those two keys with those two types. The agent finishes its turn by calling the `handoff` tool with them, and the tool refuses undeclared keys. The final prose stays readable as `{{ context.response.review }}`, named after the node. [Context](/reference/context/#command-facts) in the reference lists the facts each probe writes. ## What a step can read The validator checks, before a run starts, that a value is produced on every path that leads to the step reading it. This catches the common mistake: a prompt that reads a verdict only one branch produces. It fails validation rather than rendering empty at three in the morning. Two consequences follow: - A parallel branch cannot read its sibling's outputs. After the fan-in, read them through `parallel.results`. See [Run steps in parallel](/workflows/run-steps-in-parallel/). - A repair prompt reached by a failure edge can read `{{ context.failure_reason }}`, because Orbital writes it whenever a step fails. ## Clear context on purpose An edge with `loop_restart="true"` clears the values the loop wrote to context, among other things. Use it when a loop starts a new piece of work, so the next pass does not reason about the last one. [Add a loop](/workflows/add-a-loop/) lists what it clears and what it keeps. ## Related - [Template variables](/reference/template-variables/): every name a prompt can read. - [Context](/reference/context/): the exact fact names and output kinds. - [Add branches](/workflows/add-branches/): how edges read context to choose the next step. - [How Orbital works](/concepts/how-orbital-works/): how context is saved and restored across a restart. --- # Add gates and waits > Stop a run for a person with a gate, or for a set time with a wait Stop a run until a person lets it continue with a human gate, or for a set time with a wait. Use a gate where the process needs a person, and a wait where it needs time to pass. ```dot title="release.dot" approve [shape="hexagon", label="Approve the release"] pause [shape="insulator", duration="30m"] ``` Both survive a restart. | | Human gate | Wait | | --- | --- | --- | | Shape | `hexagon` | `insulator` with a `duration` | | Ends when | A person lets it continue, or a watched pull request changes | The duration passes | | Run slot | Given up | Kept | | After a restart | Stays at the gate | Wakes at the time it first set | ## Human gates A human gate is a step with `shape="hexagon"`. It does no work. When a run reaches one, its standing is Waiting. It shows a clock and "Waiting:" followed by the gate's label, not the pause mark, so you can tell it apart from a run you paused. The runs list's Status filter calls it **At a gate**. It holds no run slot, and it stays at the gate across a restart. To let the run continue, open it and choose **Continue** or **Skip** on the gate, or **Resume**. The run then joins the back of the queue, continues when a slot is free, and follows the gate's edge. A coding agent can do the same with the `control_run` MCP tool and the `SkipWait` or `Resume` action. A gate needs an outgoing edge. A gate inside a parallel branch is refused. ### The pull request gate The shipped delivery workflows stop at a gate labelled "The pull request is waiting for review or merge". The run page shows how long it has waited and when Orbital checks next. ![A gate step reads The pull request is waiting for review or merge, with the time waited, the time until the next check, and a Skip button.](/screenshots/gate-wait.webp?v=fdf72f63d4) *The gate shows how long it has waited and when it checks next.* When the run's probes have seen a pull request, Orbital looks at it once a minute. The run continues by itself once the `pr` probe's rolled-up `pr.state` is no longer `WAITING`: the pull request merged, closed, conflicts, failed CI or needs changes. It then joins the queue and follows the gate's edge, as when you let it continue. An approved pull request that is not merged yet goes back to the gate. [Worktrees and delivery](/projects/worktrees-and-delivery/) explains the whole loop. ## Waits A wait is a step with `shape="insulator"` and a `duration`, such as `duration="30m"`. The run sleeps for that time, then continues. A wait keeps its run slot, but waiting costs no agent visits. The run page shows a countdown. Choose **Skip** to end the wait now. ![A wait step reads Wakes in 16m 48s, with a progress bar and a Skip button.](/screenshots/wait-countdown.webp?v=ece53f53a5) *Choose Skip to end the wait early.* Orbital records the skip, so it still applies if Orbital restarts at that moment. A wait that was running during a restart wakes at the time it first set. The epic workflows, for example, wait thirty minutes when every remaining ticket is blocked, then look again. [Add a loop](/workflows/add-a-loop/) explains why a poll loop needs a wait. ## Questions from the agent An agent can also stop a run by asking you something. The run stands as Question until you answer in the composer. That is not a gate, because the workflow did not plan it. See [Message a run](/runs/message-a-run/). ![An agent's question card asks which cap to rename, offers two options to pick from, and has a box to write your own answer with a Send button.](/screenshots/agent-question.webp?v=92fd44eff4) *Pick an option or write your own answer.* ## Related - [Human gate](/reference/node-types/#human-gate) and [wait](/reference/node-types/#wait): the syntax for both steps. - [Add a loop](/workflows/add-a-loop/): use a wait inside a poll loop. - [Worktrees and delivery](/projects/worktrees-and-delivery/): the delivery loop around the pull request gate. - [Pause, stop or resolve a run](/runs/control-a-run/): resume a run by hand. --- # Triggers > Rules that start, stop, pause or message runs when something happens A trigger rule acts on your [runs](/runs/) when something happens: a pull request opens, a Linear issue moves, or a time of day comes round. You write the rule once, and it can start a run, or stop, pause or message the runs already working on that item. :::caution[Beta] Triggers are an experimental feature. They are off until you turn them on in **Settings > Experimental features**. ::: Rules belong to a [project](/projects/). They are never part of a workflow file. ![The Triggers page lists rules with their source, an on or off switch, what each rule does and its recent firings, and one rule shows an error.](/screenshots/triggers-page.webp?v=5fe1ad8f4b) *Each rule shows its source, whether it is on, and what it did lately.* ## Turn triggers on 1. **Open the experimental features.** Open **Settings > Experimental features**. 2. **Turn on Triggers.** Turn on **Triggers**. Its switch reads "Rules that start, pause, stop or message runs when GitHub, Linear or a schedule says so." 3. **Find your rules.** **Triggers** now appears in the sidebar. Each project's page also gets a section for its trigger rules. ![Settings, Experimental features, shows switches for the Supervisor, Triggers and Accounts.](/screenshots/settings-experimental.webp?v=e8e4d1269c) *The Triggers switch turns the whole feature on or off.* While the feature is off, Orbital checks nothing and the Triggers page says "Triggers are turned off". Orbital keeps your rules, and when you turn the feature back on, each rule carries on from where it left off. ## What can set a rule off Each rule has one source, and the source says what event the rule waits for. | Source | When | What it needs | | --- | --- | --- | | Schedule | "At set times, such as every weekday morning." Under "How often?", pick a preset, choose "Pick days and a time" and select the days and time, or write a cron expression. It uses the time zone of the machine that runs Orbital. | Nothing. | | GitHub | "When a pull request or issue changes": opened, closed, reopened, labelled, unlabelled, assigned, unassigned, or a review requested. Choose issues, pull requests or both. | A GitHub token; see [Settings](/reference/settings/#github). | | Linear | "When a Linear issue changes": created, moved to another state, handed to someone else, a label added or removed. | A Linear key in **Settings > Linear**. | Orbital checks each source on a schedule, not by webhooks. You choose how soon it notices a change: "Within about a minute (recommended)", within five or fifteen minutes, or within an hour. ## What a rule does Each rule has one action. | Action | What it does | | --- | --- | | Starts a run | "Orbital starts a workflow and hands it a goal." Choose the workflow, and optionally the harness and model. | | Stops runs | "Stops the runs already working on this item." | | Pauses runs | "Pauses the runs working on this item so you can look." | | Messages runs | "Sends the runs working on this item a note." | Stop, pause and message act only on runs a trigger started for the same pull request or issue. "A schedule can only start runs." The goal of a started run is a template that can use the values the event carries: | Source | Values | | --- | --- | | Schedule | `{{ date }}`, `{{ time }}` | | GitHub | `{{ url }}`, `{{ number }}`, `{{ title }}`, `{{ author }}`, `{{ repository }}`, `{{ head_branch }}`, `{{ base_branch }}`, `{{ event }}` | | Linear | `{{ identifier }}`, `{{ url }}`, `{{ title }}`, `{{ team }}`, `{{ state }}`, `{{ assignee }}`, `{{ event }}` | For example, `Review {{ url }}: {{ title }}`. ## Conditions Add conditions under "Only if" to narrow a rule down, so it acts only on what matters. | Part | What it does | | --- | --- | | Value | What the condition looks at, such as the author, a label or the target branch. | | Comparison | "is any of", "is none of" or "contains". | | "and" or "or" | Combines the conditions in one group. | | "Or when…" | Adds another group. Any group that matches sets the rule off. | ## Quick reference | You want to | Source | Action | | --- | --- | --- | | Review every new pull request | GitHub, a pull request is opened | Starts a run | | Improve the codebase every weekday morning | Schedule | Starts a run | | Stop a run when its Linear issue is cancelled | Linear, an issue moves to another state | Stops runs | | Look at a run before it goes further | GitHub or Linear | Pauses runs | | Tell a working run that its item changed | GitHub or Linear | Messages runs | ## Related - [Make a trigger rule](/triggers/make-a-rule/): create, test and follow a rule. - [Review pull requests](/examples/review-pull-requests/): a complete rule that reviews every new pull request. - [Settings](/reference/settings/#github): where Orbital finds your GitHub token. - [Troubleshooting](/troubleshooting/): fixes for a rule that does not fire. --- # Make a trigger rule > Create a rule that acts on runs, test it, and follow what it does You make a [trigger rule](/triggers/) in a project, test it against the last seven days, and save it. From then on it starts, stops, pauses or messages runs when GitHub, Linear or a schedule says so. ## Before you start - Turn on **Triggers** in **Settings > Experimental features**. See [Turn triggers on](/triggers/#turn-triggers-on). - For a GitHub rule, save a GitHub token. See [Settings](/reference/settings/#github). - For a Linear rule, save a Linear key in **Settings > Linear**. ## Make a rule 1. **Open the rules.** Open **Triggers** in the sidebar, or the trigger rules section of a project's page. ![A project page with triggers turned on shows a section for the project's trigger rules.](/screenshots/project-triggers.webp?v=f689fb22f3) *A project's page lists the rules that belong to it.* 2. **Start a rule.** Choose **New rule**. Or start from a ready-made rule: "Review every new pull request", "Improve the codebase every weekday morning" or "Stop the run when its Linear issue is cancelled". 3. **Name the rule and pick what sets it off.** Give the rule a name. Then pick its source and the event it waits for. [Sources and events](#sources-and-events) shows the events for each source. 4. **Pick what the rule does.** Choose whether it starts a run, or stops, pauses or messages runs. To start a run, choose the workflow and write its goal. You can also pick the harness and model. [What a rule does](/triggers/#what-a-rule-does) lists the values the goal can use. 5. **Add conditions if you need them.** Under "Only if", add the conditions that narrow the rule down. See [Conditions](/triggers/#conditions). 6. **Try it and save.** Choose **Try it and save**. Orbital looks back over the last seven days and reports what the rule would have done. Saving turns the rule on. :::caution A saved rule acts on its own. It can start, stop or pause runs without you pressing anything. Read what **Try it and save** reports before you rely on the rule. ::: ## Sources and events **GitHub** A GitHub rule sets off "When a pull request or issue changes". Choose issues, pull requests or both, and the event: opened, closed, reopened, labelled, unlabelled, assigned, unassigned, or a review requested. ![The New rule dialog set to GitHub pull requests, with the event 'A pull request is opened' chosen.](/screenshots/trigger-rule-github.webp?v=c4800c5727) *Pick pull requests, issues or both, then the event.* **Linear** A Linear rule sets off "When a Linear issue changes". Choose the event: created, moved to another state, handed to someone else, or a label added or removed. ![The New rule dialog set to Linear, with the event 'An issue moves to another state' chosen.](/screenshots/trigger-rule-linear.webp?v=3097911bdd) *Pick the change to the Linear issue that sets the rule off.* **Schedule** A schedule rule sets off "At set times, such as every weekday morning." Under "How often?", pick a preset, choose "Pick days and a time" and select the days and time, or write a cron expression. It uses the time zone of the machine that runs Orbital. A schedule can only start runs. ![The New rule dialog set to a schedule of every weekday at 09:00, with the next run times listed.](/screenshots/trigger-rule-schedule.webp?v=4af59dea16) *The dialog lists the next times the rule will fire.* ## Follow a rule The rules list shows whether each rule is on and when Orbital last checked. It also shows what the rule did, such as "Started a run" or "Fired 2 times in the last 7 days". A rule that cannot check its source says why, for example that GitHub or Linear refused the saved token. ## Related - [Triggers](/triggers/): sources, actions, goal values and conditions. - [Review pull requests](/examples/review-pull-requests/): a complete rule, from start to finish. - [Start a run](/runs/start-a-run/): what a started run does. - [Permissions](/roles/#permissions): what a run asks you before its agent acts, on each harness. - [Troubleshooting](/troubleshooting/): fixes for a rule that does not fire. --- # Supervisor > A standing chat that watches your runs and steps in when one needs help You turn on the Supervisor to have a [chat](/chats/) that looks after your runs while you are away. It acts as your delivery lead inside Orbital: when a run needs help, it steps in, it asks you about what it cannot decide, and it tells you what happened. A watcher wakes it, and it acts within the limits you set. :::caution[Beta] The Supervisor is an experimental feature. It is off until you turn it on in **Settings > Experimental features**. ::: This page describes the built-in Supervisor. To run a delivery lead from a coding agent instead, or to learn the loop a delivery lead follows, see [Lead delivery with a supervisor](/supervisor/lead-delivery/). Orbital attaches its own MCP server to chats as `orbital_app`. On Claude, load its tools with `ToolSearch`, for example `select:mcp__orbital_app__list_runs`. This server is separate from an `orbital` server you register in your own agent configuration. ## Turn it on 1. **Open the experimental features.** Open **Settings > Experimental features**. 2. **Turn on the Supervisor.** Turn on the **Supervisor** switch. It reads "A standing chat that watches your runs and steps in when one needs help." 3. **Open its chat.** A chat named **Supervisor** now appears, pinned at the top of your chat list. It is never archived. While the feature is off, no part of the Supervisor runs or shows. ## What it watches Orbital keeps a record of every run. A built-in watcher reads that record, and the watcher spends no model tokens. It wakes the Supervisor only for these reasons: | Reason | What counts | | --- | --- | | Needs a decision | A run stops by itself and is Paused, with the badge "Waiting on you". A run at a gate, such as one waiting for a pull request review, does not count, because that is your process working. | | Failed | A run ends Failed. | | Out of quota | A harness runs out of quota. | | Refused tools | A step keeps trying tools its tool access refuses: three refused calls in one step. | The watcher also wakes the Supervisor to look over runs that are still going, every hour unless you change it. It also wakes the Supervisor when you approve or decline something it asked about, and for a daily summary if you turn that on. The watcher looks at the record every 15 seconds. The same reason wakes the Supervisor only once. If the app was closed, the watcher catches up when you open it again. A restart neither repeats a wake-up nor loses one. Orbital retries a turn that stalls by itself, twice in one step, and wakes the Supervisor only when that does not help. A run stops, and the watcher wakes the Supervisor, in three cases: Orbital gives up on a stalled turn, a step reaches its visit limit, or the working folder cannot be used. Your runs already handle some problems themselves, so the Supervisor leaves them alone: failing checks, merge conflicts and review changes that the delivery workflow repairs, and closing tickets. A run that waits days for a person to review its pull request is normal. The Supervisor does not chase it. ## What it will and will not do The Supervisor acts only inside Orbital. It never acts in your delivery process. | It can | It does not | | --- | --- | | Send a running step a message, or answer its question | Comment on, review, approve or merge pull requests | | Pause, resume, retry, jump, skip a wait, restart or abort a run | Turn on auto-merge | | Revise a run's goal | Ping reviewers | | Move a run to another harness | Post in Slack or anywhere else | | Commit a run's uncommitted work on the run's own branch and push it, never with force and never to main | Create, edit, comment on or close tickets | | Start runs | | | Archive runs | | | Check what a run delivered | | It follows these safety rules: - It secures a run's work before it aborts the run, and aborts only a run that holds no unpushed work. - It never deletes a branch that holds work nobody has pushed. - It does what you asked and no more. Each action appears in the run's thread as coming from the Supervisor, with its reason. A short line also appears in the Supervisor chat, for example "Supervisor paused run …". To have an action undone, ask in the chat. When something outside Orbital needs doing, the Supervisor drafts the text and you send it. When the same problem keeps coming back, it drafts a change to your workflow for you to review in the editor. If the problem is in Orbital itself, it drafts a bug report for you to send. The Supervisor can read connected sources, such as your tracker or GitHub, to answer a real question. It never writes through them. ## How its calls are scoped The Supervisor acts through Orbital's own [MCP tools](/reference/mcp-tools/), the same ones a coding agent uses. Its [tool access](/roles/), Supervisor, allows reading, the web and those tools, and nothing else. It cannot change files or run commands. Orbital checks every call it makes against these rules: - It must give a reason with every action. The reason is recorded on the run and in its chat. - It acts only on runs it owns. - Each action follows **What it may do**: **Do it**, **Ask me first** or **Never**. - It stops acting when it is off, paused for today, or over its daily limit. [MCP tools](/reference/mcp-tools/#how-a-supervisors-calls-are-scoped) lists every rule. ## Talk to it Open the pinned Supervisor chat and write to it as you would to a delivery lead. For example: - "Start the triggers epic." - "Why is this run stuck?" - "What landed today?" You can attach files, as in any chat. ![The Supervisor chat shows a watcher message about a stalled run, the Supervisor's reply, and the Ask the Supervisor box.](/screenshots/supervisor-chat.webp?v=3491a90299) *The watcher's message and the Supervisor's reply share one chat.* When an action is set to **Ask me first**, the Supervisor asks before it acts. A card appears in the chat, marked "Waiting for you". It says what the Supervisor wants to do, where and why. Choose **Approve** to let it act once, or **Decline** to refuse. Either way, the Supervisor carries on with your answer. If desktop notifications are on, a notification opens the chat at that card. ### Start it fresh The Supervisor carries on one session from turn to turn, so an old conclusion can linger. Choose **Start fresh** in its chat header while it is waiting. The chat keeps its messages, and the next turn begins a new session. Its notes carry over. The Supervisor tries Orbital's tools before it says they are unavailable. If a turn cannot connect to Orbital, or Orbital refuses the connection, the chat shows it. Before the next turn, Orbital checks its own tools again, even after a restart, and tells the Supervisor whether they answered. ## A supervisor for one project A [project](/projects/) can have a Supervisor of its own. Open the project's page and choose **Add supervisor**. Its pinned chat is named "Supervisor · " followed by the project's name. It reads only that project. Each run has one owner. If the run's project has its own Supervisor, that Supervisor looks after the run. Otherwise the Orbital-wide Supervisor does. A project's Supervisor can hand a matter, or a single run, to the Orbital-wide Supervisor. The run records the handover. To stop, choose **Remove supervisor** on the project's page. Its runs go back to the Orbital-wide Supervisor. Its chat is archived, and its history stays readable. You can still act on any run yourself. ## Its settings Open **Settings > Supervisor**. Each change saves as you make it. ![Settings, Supervisor, shows its status and pause control, the watched projects, and what it may do.](/screenshots/settings-supervisor.webp?v=282bbd433b) *Each action can be set to Do it, Ask me first or Never.* A picker at the top chooses which Supervisor you change: Orbital-wide, or a project's own Supervisor. A project's Supervisor follows the Orbital-wide settings until you change a field. Such a field shows "Same as Orbital-wide". A changed field has a button to go back to the Orbital-wide value. The page shows these groups, in this order: | Group | What you can set | | --- | --- | | **Status** | The **Supervisor** switch turns it on or off. **Pause for today** stops it acting until the next 07:00. | | **Watched projects** | **Watches**: All projects, or "Only the projects I pick". Only the Orbital-wide Supervisor has this group. | | **What it may do** | For each action: **Do it**, **Ask me first** or **Never**. Every action starts at **Do it**. | | **What it may read** | A switch for each connected source. | | **What wakes it** | A switch for each of the four reasons, and **Look over runs at work**: Every hour, Every few hours or Off. | | **Who it is** | **Role**. The Supervisor role sets its persona, model and tool access. See [Give steps a role](/roles/configure-steps/). | | **Quota fallbacks** | **Move a run to**: the harnesses to move a run to when its harness runs out of quota, tried first to last. See [The fallback harness](#the-fallback-harness). | | **Spending** | **Daily limit**: No limit, tokens a day, or an estimated cost a day in USD. It counts only the Supervisor's own turns. **Runs it started, at work at once**: No cap, or at most a number you choose. | | **Notifications** | **Tell me about**: Decisions only, Decisions and failures, or Every action. **Quiet hours**: during these hours you only hear about decisions it needs from you. **Desktop notifications**: On or Off. | | **Reports** | **When it acts, say**: Brief says what it did, Detailed also says why. **Daily summary**: Off, or every day at a time you choose. | | **Notes** | What the Supervisor keeps in mind between conversations. It writes them itself. You can change or clear them. | | **Your instructions** | **For all projects**, and one field for each project. | | **Housekeeping** | **Archive finished runs**: Never, or after a number of days. **Keep its activity log**: how many days to keep it. | | **Start over** | Resets every field to its default. For a project's Supervisor, it resets to the Orbital-wide settings. | The actions under **What it may do** are these: | Action | | --- | | Message and control runs | | Save and push a run's work to its own branch | | Switch a run's harness | | Start runs | | Archive runs | When the Supervisor reaches its daily limit, it stops acting for the rest of the day. It says so once in its chat. ## The fallback harness When a run's harness runs out of quota, the Supervisor can move the run to the first harness in its **Quota fallbacks** list that can take it. The run continues in place and keeps its work. A fallback whose harness is not installed or not signed in shows as unavailable, and the Supervisor skips it. [Switch a run's harness](/harnesses/switch-harness/) describes the switch, where to set the list, and why the fallback's **On model** is not applied yet. ## Choosing how to switch it off Choose the lightest option that does what you need. The options run from lightest to heaviest. | Option | How | What happens | | --- | --- | --- | | Stop one kind of action | Under **What it may do**, set that action to **Never**. | It no longer takes that action. | | Pause it for the day | Under **Status**, choose **Pause for today**. | It carries on by itself at 07:00, or when you choose **Resume now**. | | Turn the Supervisor off | Under **Status**, turn off the **Supervisor** switch. | It takes no actions and nothing wakes it. You can still talk to it in its chat. | | Remove a project's own Supervisor | On the project's page, choose **Remove supervisor**. | Its runs go back to the Orbital-wide Supervisor. | | Turn off the feature | Open **Settings > Experimental features** and turn off **Supervisor**. | No part of the Supervisor runs or shows. Orbital keeps its settings for when you turn it back on. | ## Related - [Lead delivery with a supervisor](/supervisor/lead-delivery/): the loop a delivery lead follows, and how to run it. - [MCP tools](/reference/mcp-tools/#how-a-supervisors-calls-are-scoped): every rule Orbital applies to the Supervisor's calls. - [Switch a run's harness](/harnesses/switch-harness/): how a quota fallback moves a run. - [Chats](/chats/): how the Supervisor's chat works like any other chat. - [Troubleshooting](/troubleshooting/): fixes when the Supervisor does not act. --- # Lead delivery with a supervisor > Put a delivery lead over your runs, as a coding agent or the Supervisor You put a delivery lead over your [runs](/runs/) so you can keep the product decisions and hand off the rest. The delivery lead makes tickets ready, writes goals, starts runs on them and checks in on a steady cadence. It unsticks runs without losing work, and checks what they delivered before it closes anything. The delivery lead is either a coding agent connected over MCP or the built-in [Supervisor](/supervisor/). Both follow the same loop. [Choosing a coding agent or the Supervisor](#choosing-a-coding-agent-or-the-supervisor) compares them. ## The loop The delivery lead repeats these six steps. 1. **Refine the ticket against main.** An open ticket is not always ready to build. Before any run starts on it, check these points: | Check | What to do | | --- | --- | | Is it already delivered? | Search merged pull requests for its number, and read the code on the default branch. Close a delivered ticket with a pointer to the pull request. | | Is it still true? | Recent merges rename files and remove what a ticket relies on. Fix every stale name and claim. | | Is it measured? | A ticket about speed, size or cost carries today's numbers. | | Does it fit the product now? | Check it against the repository's rules files, such as `CLAUDE.md` or `AGENTS.md`, and recent decisions. | | Is it one piece of work? | Split a ticket too large for one run. | | Can a run prove it is done? | Every "done when" item must be something a check or a test can show. | 2. **Write a goal that carries every rule.** Most work runs on `pursue-goal`. Its validate step sees only the goal and the code, so write every rule validate needs into the goal: - Link the ticket and say "exactly as the ticket describes". - Tell the Implementer to commit at the end of every round, and never to leave work uncommitted between rounds. - Tell validate to check every "done when" item one by one, and to run the repository's checks. Tell it that a blocker the agent cannot fix is never a pass. - Name the repository's own checks and rules. - Ask for one closing keyword per ticket in the pull request. For several tickets in one run, list them in order. Tell validate to reject with the next ticket's name while tickets remain. 3. **Start runs.** Start the run with the goal. Start runs that touch the same files one after another, and independent runs side by side. Keep the [queue](/runs/queue/) short: at most one run queued beyond those working. 4. **Check in on a cadence.** Check every active run about every 30 minutes. For each run, read its standing, the steps it entered, validate's verdicts and feedback, its worktree's commits and uncommitted changes, and its pull request's checks. A run is making progress when it adds commits, when validate's findings shrink, or when its pull request moves. Treat it as stuck when: - validate repeats the same findings for three rounds with no new commit; - uncommitted work sits in the worktree across two rounds; - the implement step is within three visits of its cap; - a step has been silent for over 20 minutes with no tool calls; - it loops between two steps with nothing new; - it is [Paused](/runs/#standings) with a reason, such as the badge "Waiting on you"; - its pull request fails checks, or GitHub will not merge it; - it is queued behind slots that stuck runs hold. A run whose badge reads "Waiting: The pull request is waiting for review or merge" for days is not stuck. That is your review process working. 5. **Unstick without losing work.** Secure the work first. When a stopped run has uncommitted changes, commit and push them to its own branch with `secure_run_work`. Then take the least disruptive step that puts the run back on track, in this order: | Order | Step | | --- | --- | | 1 | Message the running step with the concrete next action. | | 2 | Resume a paused run whose cause is fixed. | | 3 | Retry a failed step whose cause has passed. | | 4 | Split an oversized ticket, and start a new run from the pushed branch. | | 5 | Fix a small cause yourself, when it is in reach. | | 6 | File a bug for an Orbital defect, with the run and the evidence. | A message reaches only the step that is running. When the scope changes, revise the run's goal as well. Record the change on the ticket, where validate reads it. 6. **Verify and close.** Never take a run's word for it: - Confirm the pull request merged and every ticket it names is closed. - Check the result yourself on the default branch: read the code and run the proving check. - Close an epic when every sub-issue is closed. - Archive finished runs, and remove worktrees they left once nothing in them is unpushed. ## Rules that keep it safe These rules apply to any delivery lead, the skill or the built-in Supervisor. | Rule | What it means | | --- | --- | | Never abort unpushed work. | Abort a run only when its work is committed and pushed and a replacement run has started from that branch, or when it holds no work at all. | | Keep the queue short. | At most one run queued beyond those working. A long queue behind a stuck run holds everything up. | | A blocker is never a pass. | Validate keeps rejecting while a blocker stands, and says so. The delivery lead fixes or escalates the blocker; it never lowers the bar. | | Respect the review process. | The delivery lead does not approve or merge pull requests, and does not chase reviewers. | | Never check out a run's branch. | A run's worktree holds it. | | Give a reason with every action. | It is recorded on the run. | ## Set up the delivery lead **Coding agent over MCP** 1. **Connect the agent.** [Connect the agent to Orbital over MCP](/mcp/connect/). 2. **Install the skills.** [Install the shipped skills](/mcp/skills/) from **Settings > Command line and skills**, or run `orbital skill install`. 3. **Start a session.** Start a new session of the agent in the repository. 4. **Ask it to lead.** Give it the ticket, the project and the cadence. For example: ```text Use the orbital-delivery-lead skill. Deliver https://github.com/acme/shop/issues/412 with Orbital in the Shop project. Refine the ticket first, then start a pursue-goal run and check in every 30 minutes until it merges. ``` ![Settings, MCP, shows a switch for each tool group, with Archiving and Changing Orbital's settings off.](/screenshots/settings-mcp-tools.webp?v=73920e6114) *Archiving is its own tool group; turn it on so the agent can archive and resolve runs.* The agent works through the loop with Orbital's [MCP tools](/reference/mcp-tools/), plus `gh` and `git`: | Loop step | Tools | | --- | --- | | Refine | `gh issue view`, `gh pr list --search`, reading the code | | Start | `list_projects`, `list_workflows`, `start_run` | | Check in | `list_runs`, `get_run`, `read_run_history`, `read_run_thread`, `wait_for_run`, `read_run_worktree`, `read_pull_request` | | Unstick | `secure_run_work`, `message_run`, `revise_goal`, `control_run` | | Close | `gh pr view`, `gh issue close`, `archive_run`, `resolve_run` | `archive_run` and `resolve_run` belong to the Archiving tool group, which is off until you turn it on in **Settings > MCP**. To keep the cadence, ask the agent to repeat its check-in, for example with Claude Code's `/loop` command. Or use `wait_for_run`, which returns as soon as a run's standing changes. **Built-in Supervisor** 1. **Turn on the Supervisor.** Open **Settings > Experimental features** and turn on **Supervisor**. A chat named **Supervisor** appears, pinned at the top of your chats. 2. **Choose what it may do.** Open **Settings > Supervisor**. Each action is **Do it**, **Ask me first** or **Never**. Start with **Ask me first** for **Start runs** and **Switch a run's harness** until you trust it. 3. **Write your rules.** Under **Your instructions**, write your rules, such as your goal template and "Never abort a run that holds unpushed work." 4. **Talk to it.** Ask it for work in its chat. For example: ```text Start implement-ticket on https://github.com/acme/shop/issues/412 in Shop, and tell me when the pull request is ready for review. ``` ![The Supervisor chat shows a watcher message about a stalled run, the Supervisor's reply, and the Ask the Supervisor box.](/screenshots/supervisor-chat.webp?v=3491a90299) *The watcher wakes the Supervisor in its chat, and you answer there.* It asks you only for decisions that are yours. With **Ask me first**, a card marked "Waiting for you" appears with **Approve** and **Decline**. The watcher reads the run record every 15 seconds and spends no tokens. It wakes the Supervisor when a run stops and needs a decision, except at a gate. It also wakes it when a run fails, a harness runs out of quota, or a step keeps trying tools its tool access refuses. The hourly look over runs at work wakes it too, and so does your answer to something it asked about. It does not wake the Supervisor for what workflows already handle: failing CI, conflicts, review changes. Orbital retries a stalled turn itself. The run stops, and the watcher wakes the Supervisor, once Orbital gives up, a step reaches its visit limit, or the working folder cannot be used. [What it watches](/supervisor/#what-it-watches) has the details. Inside the limits you set, the Supervisor messages and controls runs, answers their questions and revises goals. It [switches a run to a quota fallback](/harnesses/switch-harness/), starts runs and archives runs. Every action carries a reason, shows on the run's thread, and appears as a line in its chat. It cannot change files or run commands, and it never writes to your tracker or pull requests. For those, it drafts the text and you send it. [The Supervisor](/supervisor/) describes every setting. ## A worked example
From a ticket to a merged pull request Issue #412 in `acme/shop` asks for a "Copy link" button on each invoice row. The delivery lead is Claude Code with the `orbital-delivery-lead` skill. **Refine.** The agent reads #412 and searches merged pull requests for "412": none. It reads `src/invoices/InvoiceRow.tsx` on main and finds the ticket names `InvoiceTable.tsx`, which a recent pull request renamed. It fixes the name in the ticket, and adds a measurable "done when": "a unit test covers the link format". **Write the goal.** From the one-ticket template: ```text Implement https://github.com/acme/shop/issues/412 exactly as the ticket describes. Read the ticket first. Follow CLAUDE.md. Commit your progress at the end of every implement round, even when the work is not finished. Never leave work uncommitted between rounds. Validate checks every "done when" item of the ticket one by one and runs npm test. It rejects when any item is missing and says which, with the evidence. A blocker you cannot fix is never a pass: say so in the feedback and keep rejecting. It passes only when every item holds and npm test passes. Put `Closes #412` in the pull request description. ``` **Start.** `list_projects` gives the Shop project's id. `start_run` starts `pursue-goal` with that goal and the title "Copy link on invoice rows". Orbital answers with the run's id and its page. **Check in.** Thirty minutes later, `get_run` shows the run in its fourth validate round. `read_run_history` shows validate rejecting three times with the same finding, "no unit test for the link format". There has been no new commit since the first round. Three rounds of the same findings with no new commit is a stuck signal. **Unstick.** The agent sends `message_run` with delivery `Resume`: "Commit your progress now. Then add the unit test validate asks for in src/invoices/link.test.ts." Its reason: "validate repeated one finding for three rounds with no new commit". The next round adds a commit with the test, and validate passes. **Delivery.** The run opens pull request #418 with `Closes #412`, and stops at the gate. Its badge reads "Waiting: The pull request is waiting for review or merge". It holds no slot while it waits. CI fails on a lint rule; the run's fix-CI step repairs it and publishes again. A person reviews and merges #418. **Verify and close.** The agent confirms #418 merged and #412 closed, pulls main and runs `npm test`: it passes. It checks the new test is in main. It archives the run with `archive_run`, and tells you: "#412 delivered in #418, merged and verified on main."
## Choosing a coding agent or the Supervisor | | A coding agent over MCP | The built-in Supervisor chat | | --- | --- | --- | | What it is | Claude Code, Codex or OpenCode, with the `orbital-delivery-lead` skill, connected to Orbital's MCP endpoint. | A standing chat inside Orbital, woken by a watcher. See [The Supervisor](/supervisor/). | | What wakes it | You, or a loop you set up in the agent. | The watcher, when a run stops, fails, runs out of quota or keeps trying refused tools, and for an hourly look over runs at work. | | What it can do | Everything in [the loop](#the-loop): tickets, runs, `git`, `gh`, fixes. | Act on runs inside Orbital, and nothing else. It never writes to your tracker or pull requests. It drafts that text for you. | | Best for | Driving a backlog end to end, including refining tickets. | Keeping runs moving while you are away. | Many people use both: a coding agent to plan and start the work, and the Supervisor to watch it overnight. ## Related - [The Supervisor](/supervisor/): every setting of the built-in Supervisor. - [Connect a coding agent](/mcp/connect/): connect Claude Code, Codex or OpenCode to Orbital. - [Use the shipped skills](/mcp/skills/): install the `orbital-delivery-lead` skill. - [MCP tools](/reference/mcp-tools/): every tool the delivery lead can call. - [Deliver an epic](/examples/deliver-an-epic/): a delivery lead working through an epic. --- # Chats > Talk to one coding agent in its own worktree, with no workflow You start a chat to talk to one coding agent in a [project](/projects/), with no workflow behind it. The agent works in a worktree of its own, and you can attach files to your messages. Use a chat for work you do not repeat, such as a question, a quick fix or an explanation. Use a [run](/runs/) when you want the same process every time. ## Start a chat 1. **Open the Start page.** Choose **New task** in the sidebar. 2. **Switch to chat.** Set the switch under the text box to **Chat**. 3. **Write your first message.** Type it in the box that reads "Ask for a change, a fix, or an explanation". You can also pick a [harness](/harnesses/) and model. 4. **Start the chat.** Choose **Start chat**. The chat appears under **Recent chats** in the sidebar. ![The Start page in chat mode shows the message box, the harness picker and the Chat and Workflow switch set to Chat.](/screenshots/start-chat.webp?v=d7a51fc836) *With the switch on Chat, the Start page starts a chat, not a run.* ## Where a chat works Each chat gets a worktree of every repository folder in its project, on a branch named after the chat. The agent works in the primary folder's worktree, so nothing it does touches your own checkout. A chat takes no [run slot](/runs/queue/), and its agent's permission requests are accepted for it. It can use Orbital's own [MCP tools](/reference/mcp-tools/), unless its [tool access](/roles/) leaves out connected services. ## Talk to the agent Each message starts a turn. While the agent is answering, a new message steers it, where the harness allows. Choose **Interrupt** to stop the answer but keep the session, so your next message carries on the same conversation. ![A chat with an agent shows the conversation, the workflows started from the chat, and the message box at the bottom.](/screenshots/chat-page.webp?v=e0ef602f2c) *Write in the box at the bottom to start the next turn.* The agent can ask you a question with options to choose from. ![A chat where the agent asks a question and offers options to choose from.](/screenshots/chat-question.webp?v=0a554c8359) *Pick an option to answer the agent.* ## Attach files Attach files to a chat message with the attach control, or paste an image. You can attach images (PNG, JPEG, GIF, WebP), PDFs, text files and other documents. | Limit | Value | | --- | --- | | Files per message | 100 | | Size of one image | 10 MB | | Images per message, in total | 80 MB | | Size of any other file | 50 MB | Orbital shrinks an image that is too large before it sends it. Each harness receives some kinds of file directly, and gets the rest as a file path it can read. | Harness | Receives directly | | --- | --- | | Claude Code | Images and PDFs | | Codex | Images | | OpenCode | Images, PDFs and text files | Orbital keeps attachments in `~/.orbital/attachments/`. Runs have no attach control, so attach files in a chat. ## Start a workflow from a chat Open the chat's **Start workflow** tab and choose a workflow. The run works in the chat's worktree and shows as a tab of the chat, not in the runs list. You can keep chatting while it runs. ## Archive a chat Archive a chat when you are done with it. Archiving removes its worktree and branch, and keeps the conversation. :::caution Archiving removes the chat's worktree and branch, whatever they hold. Push anything you want to keep before you archive. ::: ## After a restart A chat that was answering when Orbital restarted goes back to waiting, with a note that the answer failed. Nothing resumes by itself. Send a message to carry on. ## The Supervisor chat With the Supervisor feature on, a Supervisor chat is pinned at the top of the chat list. You cannot archive or delete it. See [The Supervisor](/supervisor/). ## Choosing a chat or a run | | Chat | Run | | --- | --- | --- | | Behind it | No workflow. You steer each turn. | A [workflow](/workflows/), the same process every time. | | Where it works | A worktree of its own, on a branch named after the chat. | A worktree when its workflow makes one, otherwise your own folders. See [Worktrees and delivery](/projects/worktrees-and-delivery/). | | Run slot | Takes none. | Holds one while it works. See [The run queue](/runs/queue/). | | Attachments | Yes. | No attach control. | | After a restart | Goes back to waiting. Send a message to carry on. | Carries on where it was, unless you paused it. See [Durability after restarts](/runs/durability/). | You can have both: start a workflow from a chat, and the run works in the chat's worktree. ## Related - [Runs](/runs/): when to use a run instead of a chat. - [The Supervisor](/supervisor/): the pinned chat that looks after your runs. - [Worktrees and delivery](/projects/worktrees-and-delivery/): how worktrees keep your checkout safe. - [Harnesses and models](/harnesses/): the agents a chat can use. --- # Create a project > Add a project and its folders, then set its run limit and actions Create a [project](/projects/) for each piece of work you hand to agents, such as a product with a web app and an API. A project is a name and the folders a run works in. Start by choosing **Projects** in the sidebar. On first launch, Orbital opens a setup wizard that adds your first project for you. [Start your first run](/get-started/first-run/) walks through it. Use the steps below for every project after that. ![The first-launch wizard reads Give your agents a place to work, with the steps Welcome, Project and First run, and a Set up a project button.](/screenshots/first-launch.webp?v=5ece35762f) *On first launch, the wizard adds your first project.* ## Before you start - Install Orbital. See [Install Orbital](/get-started/install/). - Know where each folder lives on the machine that runs Orbital. A folder can be a Git repository or a plain folder. [Plain folders and Git](/projects/#plain-folders-and-git) says when you need a repository. ## Create a project and add folders 1. **Open Projects.** Choose **Projects** in the sidebar. 2. **Make the project.** Choose **New project** and give it a name, such as `Platform`. 3. **Add a folder.** Open the project and choose **Add folder**. Browse the machine that runs Orbital, or type the folder's absolute path. 4. **Name the folder, if you want.** Give the folder a short name: lowercase letters, digits and underscores. Leave it blank to use the folder's own name. 5. **Add the other folders.** Choose **Add folder** beside the folder list for each further folder. The first folder you add is the primary folder, where each agent step starts. ![A project page shows its folders with name, path, type and role, then the Runs group and a settings group for each folder.](/screenshots/project-details.webp?v=a56bdefccc) *Each folder shows its type and whether it is primary.* ## Limit runs at a time A project can limit how many of its runs work at once. On the project's page, find **Run limit** under **Runs**. It reads, for example, "At most 2 runs at a time". Choose **−** or **+**, or type a number and press Enter. Each change saves at once. Leave it empty for no limit of its own. When a project reaches its limit, its next runs wait, and other projects' runs go first. [The run queue](/runs/queue/#limit-one-projects-runs) explains how the limit works with the one in Settings. ## Add custom actions Custom actions are commands and links you add to a project for its runs. They appear in each run's **Run actions** menu. 1. **Add the action.** On the project's page, choose **Add action**. 2. **Name it.** Give it a **Key**, such as `build`, and a **Label**, such as `Build`. 3. **Choose its kind.** Pick a **Kind** from the table below, and fill in its command or URL template. | Kind | What it holds | Example | | --- | --- | --- | | **Command** | A command. It runs on the machine that runs Orbital, in the run's folder you choose. | `pnpm build` | | **Link** | A URL template. | `https://example.com/{path}` | Saving an action runs nothing. ## Related - [Projects and folders](/projects/): what folders, roles and project settings mean - [Start a run](/runs/start-a-run/): give the new project its first goal - [The run queue](/runs/queue/): how project limits and the Settings limit work together --- # Worktrees and delivery > Code workflows work in a worktree, open a pull request and see it merged Give a workflow a worktree step when it must change files without touching your own checkout. A worktree is an isolated copy of a repository, on a branch of its own. This workflow makes one, changes it, and removes it only when nothing would be lost: ```dot title="worktree.dot" digraph worktree { graph [version="1", entry="checkout"] checkout [shape="folder", action="create"] implement [prompt="Make the change the goal describes and commit it."] cleanup [shape="folder", action="remove"] done [shape="Msquare"] kept [shape="Msquare", outcome="failed"] checkout -> implement implement -> cleanup cleanup -> done [condition="worktree.clean == true", weight="1"] cleanup -> kept } ``` A worktree is optional. A run gets one only when its workflow has a worktree step. `explain-repo`, the research, follow-up and triage guides, and most workflows that only read and write text have none. The shipped code workflows create a worktree first. When the work passes review, they open a pull request and repair it until it merges or closes. Then they remove the worktree, unless that would lose work. ## Worktrees A worktree step with `action="create"` makes one worktree for each repository folder of the [project](/projects/). All of them share one branch name, `orbital/`. They live under `~/.orbital/worktrees/`. Plain folders, which are not Git repositories, are used where they are. See [plain folders and Git](/projects/#plain-folders-and-git). From then on, each agent step starts in the primary folder's worktree and reaches the other worktrees as additional folders. Before a worktree exists, a step works in your own folders. That is why `explain-repo`, which makes no worktree, only reads. Its prompt tells it not to change files. The dock's **Changes** tab shows what the run changed in its worktree, and **Files** shows the worktree's files. **Open in** opens the worktree in your editor. ## Removing a worktree A worktree step with `action="remove"` removes the run's worktrees only when nothing in them would be lost. It keeps a worktree that holds uncommitted changes or commits nobody has pushed, and records why in `worktree.status`. The shipped workflows then end on a terminal that says the worktree was kept, so you can look. Never delete a kept worktree to make a run look finished. Commit and push what you need first. Archiving a run also removes its worktrees. ## Delivery The shipped delivery workflows share one delivery sub workflow, `pr-delivery`. It runs these stages: | Stage | What happens | | --- | --- | | Publish | An agent step pushes the branch and opens the pull request, following the repository's conventions. When the step ends, Orbital asks GitHub to squash-merge the pull request once its checks and reviews allow it. A pull request that can merge already merges at once. If the repository does not allow auto-merge, a rule blocks it, or the token lacks permission, the run's history records that and delivery carries on. | | Watch | A probe reads the pull request's state from GitHub. | | Wait at a gate | While the pull request waits for review or merge, the run waits at a human gate, "The pull request is waiting for review or merge". It shows a clock and "Waiting: The pull request is waiting for review or merge", and takes no run slot. Orbital looks at the pull request once a minute and carries on by itself when something changes. | | Repair | Conflicts, failing CI and requested changes each have a repair step. A repair that changes the branch is reviewed against its reason alone, then published again. | | End | A merged pull request ends the run Succeeded. A pull request closed without merging ends it Failed. | Choose **Skip** at the gate, or **Resume**, to make the run look at the pull request at once. [Add gates and waits](/workflows/gates-and-waits/) explains gates. ![A gate step reads The pull request is waiting for review or merge, with the waiting time, the next check and a Skip button.](/screenshots/gate-wait.webp?v=fdf72f63d4) *Skip makes the run check the pull request now.* :::caution[Pull requests can merge with no review] Delivery never approves a pull request. It asks GitHub to merge it once your branch rules allow. On a repository that requires no review and no checks, the pull request merges as soon as it opens. Your branch rules decide, so keep required reviews and checks on the branches you care about. ::: ### Turn auto-merge off No setting turns auto-merge off. It comes from `auto_merge="squash"` on the publish step of `pr-delivery`. That is a sub workflow, not a workflow you can start or open by name. Each delivery workflow imports its own file, `subgraphs/pr-delivery.dot`, from beside it. A separate `pr-delivery` of your own is never used. Instead, take your own copy of each delivery workflow you start, and delete `auto_merge="squash"` from that copy's `pr-delivery`. Every shipped workflow except `explain-repo` delivers through `pr-delivery`. `implement-ticket` and `implement-epic` reach it through `deliver-ticket`. To do it in the app: 1. **Open the workflow.** Open **Workflows** and open the shipped workflow you use, for example `pursue-goal`. 2. **Find the delivery file.** Open the **Prompt docs** tab and select `subgraphs/pr-delivery.dot`. It shows "Imported by delivery", or "Imported by a nested workflow" for `implement-ticket` and `implement-epic`. 3. **Remove the attribute.** Delete `auto_merge="squash"` from the `publish` line, then save. Saving stores your own copy, with its imported files, under the same name. The list shows it as Custom with "Hides Shipped". **Revert** brings the shipped one back. To do it with files, copy the workflow's `.dot` file, with its `subgraphs/` and `prompts/` folders, into `~/.orbital/workflows/` or the project's `.orbital/` folder. Delete `auto_merge="squash"` from the copy's `subgraphs/pr-delivery.dot`. A workflow there wins over a shipped workflow of the same name. The [Ticket to merge](/examples/workflows/ticket/) example holds the same delivery files. [Auto-merge](/reference/attributes/#auto-merge) describes the attribute. ### When GitHub never runs a check Sometimes GitHub does not run a required check at all, for example because of billing or no runner. The run is then Paused on the probe step, with GitHub's reason and the check names. See [standings](/runs/#standings). Fix the cause on GitHub, then choose **Resume**. ### Several repositories A project with several repository folders gets one pull request per repository that changed. The run treats the set as one delivery. It merges only when every pull request merged, and it repairs the worst state first. The dock's **Pull requests** section lists them all. ![The dock's Pull requests list shows open and merged pull requests, each with its branch.](/screenshots/pull-requests-panel.webp?v=612ac483b1) *Every pull request of the run, with its state.* ## The GitHub token Delivery reads pull requests, checks and review threads through a GitHub token. Orbital tries these in order and uses the first it finds: 1. the token saved in **Settings > GitHub** 2. `GH_TOKEN` 3. `GITHUB_TOKEN` 4. an existing `gh auth login` The page shows which one is in use. See [Settings](/reference/settings/#github). ![Settings, GitHub page shows a saved token and the token sources in order: saved token, GH_TOKEN, GITHUB_TOKEN and gh auth login.](/screenshots/settings-github.webp?v=f898ede9d3) *The page names the token source in use.* ## Related - [Add gates and waits](/workflows/gates-and-waits/): how the run waits for a pull request - [Implement a feature](/examples/feature-pull-request/): a delivery workflow from goal to merge - [Ticket to merge](/examples/workflows/ticket/): the delivery sub workflow file by file - [Troubleshooting](/troubleshooting/#a-worktree-remains-after-cleanup): what to do when a worktree remains --- # Switch a run's harness > Retry a run's current step on another harness, by hand or automatically Move a run to another [harness](/harnesses/) part way through when its harness runs out of quota, is missing, or does a step badly. Open the run's **Run actions** menu (⋯) and choose **Retry with another harness…**. The current step starts again on the new harness, and the rest of the run stays as it is. ## Before you start - Install and sign in to the harness you want to move to. **Settings > Harnesses** shows its status. ## Switch the harness 1. **Stop the step.** If the step is still working, choose **Pause** and wait until the step stops. A run that has already stopped needs no pause. 2. **Open the dialog.** Open the **Run actions** menu (⋯) and choose **Retry with another harness…**. 3. **Pick the harness and retry.** Pick the new harness under **Harness**, then choose **Change harness and retry**. ![The Retry with another harness dialog with a Harness picker and a Change harness and retry button.](/screenshots/change-harness-dialog.webp?v=ddbef0b3da) *Pick the harness, then retry.* ## What changes and what stays The current step starts again on the new harness, in a fresh session. The new harness then does every later step of the run too, even a step whose workflow names another harness. | Part of the run | After the switch | | --- | --- | | Tool access of each step | Kept. | | Settings made for the new harness, such as `codex_model` and `codex_effort` | Kept. | | A model or effort that names no harness | Dropped. The new harness uses its own default model and effort, or the **Default model** and **Default effort** you set for it. | | Finished steps, the run's context and history | Kept as they are. | | Worktrees and file changes | Kept as they are. | | The old harness's session | Does not carry over. | :::caution[Retrying can repeat work] Retrying the step can repeat what it already did outside Orbital, such as a comment it posted. Orbital does not undo it. ::: ## Switch from a coding agent A coding agent can do the same with the [`control_run`](/reference/mcp-tools/#control_run) MCP tool and the `ChangeHarness` action. That action takes an optional `model`, but Orbital refuses the call when you give one. It changes a run's harness, not yet its model. ## Quota fallbacks A quota fallback lets [the Supervisor](/supervisor/) switch a run for you when its harness [runs out of quota](/harnesses/#when-a-harness-runs-out-of-quota). Set it under "When a harness runs out of quota" in **Settings > Harnesses**: **Switch to** names the harness to move to. The Supervisor settings show the same list as **Quota fallbacks**, with **Move a run to**. A change in one place changes the other. The Supervisor switches a run only to a harness in that list. It does so only when its **Switch a run's harness** permission allows it. That permission is **Do it** unless you change it. Without the Supervisor, Orbital never moves a run by itself. The fallback also has an **On model** field. Orbital does not apply it yet. The switched run uses the new harness's default model, as described above. ## Related - [Harnesses and models](/harnesses/): which harness a step uses and what each status means - [Pause, stop or resolve a run](/runs/control-a-run/): pause a step before you switch - [Supervisor](/supervisor/): the agent that applies quota fallbacks - [MCP tools](/reference/mcp-tools/#control_run): the `ChangeHarness` action --- # Use different models > Run different steps on different harnesses, models and efforts Run cheap steps on a small model and expensive steps on a large one by giving each step its own Model settings. Name the settings on the workflow, then pick one per step: ```dot title="ship.dot" digraph ship { graph [ version="1", entry="implement", model_settings="Balanced { effort: medium; } Codex review { harness: codex; model: gpt-6-sol; effort: high; }" ] implement [model_settings="Balanced", prompt="Implement this goal: {{ inputs.goal }}"] review [model_settings="Codex review", prompt="Review the change. Report what you find. Do not change files."] done [shape="Msquare"] implement -> review review -> done } ``` A step can also get its Model settings through a Role, which brings a Persona and Tool access too. [Roles and tool access](/roles/) defines these, and [Give steps a role](/roles/configure-steps/) shows how to pick them in the editor. Orbital works out the harness, model, effort, connection and provider separately for every step. So a step that changes only its effort keeps the model it would have had. ## Before you start - Sign in to each harness you want to use. Follow [Install Orbital](/get-started/install/#2-install-and-sign-in-to-a-harness) before you try a model. ## Name models in Model settings The library holds named Model settings in **Settings > Model settings**. Each can set a harness, a model and an effort. Give a step one of them, directly or through its Role. ![Settings, Model settings page lists Balanced and Deep thinking, each with its harness and effort.](/screenshots/settings-model-settings.webp?v=01d7f10060) *Each entry sets a harness, model or effort under one name.* The harness names are claude, codex and opencode. Effort accepts minimal, low, medium, high, xhigh, max and ultra, but each harness accepts only some of them. Orbital refuses a harness and effort that do not go together before any turn starts, so a run does not fail halfway through. ## Pick a model for the run The harness and model picker in the new-run composer starts at **Default harness**, which follows the workflow and your settings. Pick a harness's default model, such as "Codex default model", to make that the run's harness. Or pick a model by name. [Which harness a step uses](/harnesses/#which-harness-a-step-uses) explains which steps follow the run's harness. Choosing a model sets the workflow's default for that harness. A step's own model, its Model settings and its Role still come first. Changing the harness clears the model you chose. The picker shows each model's reference price in USD per million input and output tokens, when Orbital knows it. A model in the picker is a suggestion: your sign-in decides whether you can use it. [Models and prices](/reference/models/) lists every suggested model and its price, and explains how Orbital estimates a run's cost. ## Resolution order Orbital decides each setting on its own, and the most specific choice wins: 1. what a step sets itself 2. the Model settings it names 3. its Role 4. what an import placeholder chooses for it 5. the model stylesheet 6. the workflow's choices 7. the run's choice and the harness default A setting for one harness, such as `codex_model`, beats a plain one, such as `model`. [Attributes](/reference/attributes/#resolution-order) gives the exact order. Harness-specific attributes such as `claude_model`, `codex_effort` and `opencode_provider` work on workflows, agent steps, Model settings blocks and stylesheet rules. Orbital checks their names and values even when the step runs on another harness. So a typo shows up when you validate the workflow, not on the turn that needs it. [Attributes](/reference/attributes/#harness-specific-attributes) lists them all. The settings inside an imported workflow apply when you run that workflow on its own. They never become defaults for the workflow that imports it. Put shared settings in the parent workflow, or choose them on the placeholder. See [Reuse a workflow](/workflows/reuse-a-workflow/). :::note Permissions default to auto-accept. Each harness treats `permissions` differently. Tool access, not permissions or the prompt, limits what a step can do. [Permissions](/roles/#permissions) explains both. ::: ## See which model a run used The run page names the harness and the model the latest turn reported. They appear in the footer under the composer, beside the workflow, the branch and the pull request. Where the composer is not on screen, such as on an ended run, the header names them instead. Before a turn reports a model, the page shows the model the turn asked for, marked "(configured)", or "Model not known yet". A run that used several models shows the latest with a count of the others. The transcript records the settings each turn ran with. It is the fastest way to check that your settings did what you meant. The **Tokens by model** table on the run's **Cost** tab lists each model the run used. ![A run's Cost tab shows tokens and cost for each step and a Tokens by model table.](/screenshots/run-billing.webp?v=01a803de81) *Tokens by model lists every model the run used.* ## Change harness during a run You can move a stopped run to another harness and retry its step there. [Switch a run's harness](/harnesses/switch-harness/) describes how, what carries over, and what the quota fallback does. ## Advanced: profiles and the model stylesheet Workflows written before roles existed may use profiles and a model stylesheet. Both still work. In the editor they sit under **More** and **Advanced**, where you edit them as text. For new workflows, prefer Model settings and Roles, which the editor offers as pickers. [Attributes](/reference/attributes/#advanced-profiles-and-model-stylesheet) gives the syntax. ## Related - [Give steps a role](/roles/configure-steps/): pick Model settings and Roles in the editor - [Roles and tool access](/roles/): the building blocks and what ships - [Harnesses and models](/harnesses/): which harness a step uses - [Switch a run's harness](/harnesses/switch-harness/): move a stopped run to another harness - [Models and prices](/reference/models/): suggested models and how cost is estimated - [Attributes](/reference/attributes/#resolution-order): the exact resolution order --- # Give steps a role > Choose each step's role, persona, model settings and tool access in the editor Give a step a Role to decide in one choice who its agent is, which model it runs on and which tools it may use. Select the step in the workflow editor, then choose a Role under **Role** in the inspector. ![plan leads to implement, implement leads to review, and review reaches done.](/roles.svg?v=41e56bc93b) ## Before you start - Have a workflow open in the editor. See [Write your first workflow](/workflows/write-a-workflow/). - Know what the building blocks are. [Roles and tool access](/roles/) defines them. ## Pick a role for a step Only steps that take a turn can have a Role. A command, a wait or an end does not. 1. **Select the step.** Open the workflow in the editor and select the step. 2. **Choose a Role.** In the inspector, under **Role**, choose a Role from the list. Orbital ships Planner, Implementer, Reviewer, Researcher and Supervisor. 3. **Check the pickers below.** The Persona, Model settings and Tool access pickers below it fill in from the Role. Each says where its value comes from, such as "from role Reviewer". ![The workflow editor shows a fan-out of parallel steps, each labelled with its role, such as Researcher, Reviewer and Implementer, and a Roles legend.](/screenshots/workflow-editor-roles.webp?v=3ff23d4252) *Each step on the canvas shows its role.* Each picker shows one of these origins: | Origin | Meaning | | --- | --- | | "set on this step" | You chose it on the step. | | "from role Reviewer" | The step's Role brought it. | | "from role Reviewer via ticket" | The import step `ticket` brought it through its Role. | | "from the workflow" or "from the workflow's role Implementer" | The workflow chose it for every step. | | "default" | Nothing chose it, so Orbital's defaults apply. | **Clear** removes the step's own choice, and the picker returns to what the step inherits. Exact values, such as a model name or single tool switches, sit under **More** below the picker. ## The three building blocks A step is configured by three building blocks. - A **Persona** says who the agent is. It is a named prompt, such as "You review work you did not write", that the agent receives along with the step's own prompt. - **Model settings** say which harness runs the step, which model it uses, and how hard it thinks. A harness is the agent program Orbital starts, such as Claude, Codex or OpenCode. Effort runs from minimal to ultra. - **Tool access** says what the agent may use. It is five switches: Read files, Change files, Run commands (tests, git, scripts), Browse the web and Use connected services. A step can use each building block directly. It can also use a **Role**, a shortcut that bundles one Persona, one set of Model settings and one Tool access under one name. A Role may leave any of the three out. Every building block has a name, and the same name means the same thing on every step. Change the Model settings called Deep thinking, and every step that uses it changes too. ## Read the step summary Select a step in the workflow editor. The inspector shows one line that sums up how the step is configured, for example: Reviewer · Strict reviewer · Codex, gpt-6-sol, high · Read only Each part, from left to right, means: - **Reviewer** is the step's Role. It is missing when the step has no Role. - **Strict reviewer** is the Persona. - **Codex, gpt-6-sol, high** are the Model settings: the harness, the model and the effort. A part the step leaves to its defaults is not shown. - **Read only** says what the step may use, after every Tool access that applies to it. It reads "Everything", "Read only", "Everything except running commands" or "No tools", or else lists the switches that are on, such as "Read files, browse the web". Select a part to open its definition. ## Assign a role to several steps 1. **Select the steps.** Select several steps on the canvas. 2. **Choose a Role.** Choose a Role in the panel that appears. 3. **Assign it.** Select **Assign role**. Steps that take no turn are skipped, and the panel names them. ## Customise one step Choose a different value in one picker, for example Tool access. Only that part changes. The step keeps the rest from its Role, and the picker now says "set on this step". In the example workflow, `implement` uses the workflow's Role, Implementer, but its Tool access is "No web". It can still read and change files and run commands, but it cannot browse. ## Change a role everywhere or only in this workflow Roles, Model settings and Tool access live in Orbital's library, which every workflow shares. A workflow may also keep its own copy. 1. **Open the Roles tab.** Open the **Roles** tab in the editor. It lists what this workflow uses, and then the library. 2. **Change an entry.** Open an entry and change it. 3. **Save it.** Select **Save**. Orbital asks "Change Reviewer everywhere?". 4. **Choose where the change applies.** Choose **Change it everywhere** to change the library entry for every workflow. Choose **Only in this workflow** to keep a copy that only this workflow uses. :::caution[Change it everywhere affects every workflow] **Change it everywhere** changes the library entry. Every workflow that uses that name changes too, unless it keeps its own copy. ::: An entry this workflow changed shows the badge "Changed for this workflow". **Use the library version** removes the copy, and the workflow goes back to the library entry. An entry the library does not have shows "Only in this workflow". ![The editor's Roles tab shows a role changed only for this workflow, marked Changed for this workflow.](/screenshots/workflow-editor-role-override.webp?v=77d73eb071) *The badge marks a change kept in this workflow only.* Personas are kept in **Settings > Personas**. ## Edit the shipped defaults Orbital ships five Roles: | Role | Persona | Model settings | Tool access | | --- | --- | --- | --- | | Planner | Planner | Deep thinking | Full access | | Implementer | Implementer | Balanced | Full access | | Reviewer | Strict reviewer | Deep thinking | Full access | | Researcher | Researcher | Balanced | Full access | | Supervisor | Supervisor | Deep thinking | Supervisor | Every shipped Role but Supervisor has Full access, so its steps can run commands such as `gh` and `git`. To restrict a Role, give it the Read only or Research Tool access. The Supervisor Role is the one [the Supervisor](/supervisor/) speaks in. Its Tool access, also called Supervisor, allows reading, the web and Orbital's own MCP tools, and nothing else. Deep thinking thinks hard, and Balanced thinks less. Neither names a harness or a model, so they run on whichever harness you choose for the run. [Shipped defaults](/reference/attributes/#shipped-defaults) lists every shipped entry and each persona's prompt. Edit them in **Settings**, under **Roles**, **Personas**, **Model settings** and **Tool access**. Your edits survive upgrades: Orbital never overwrites an entry you changed. - **Reset to default** returns a shipped entry to Orbital's version. - Deleting a shipped entry hides it. It stays hidden after upgrades. Select **Restore** under **Deleted defaults** to bring it back. - Renaming a shipped entry keeps the new name as your own entry, and hides the shipped one. ## Imported steps An import step brings in another workflow. You can choose a Role, a Persona, Model settings and Tool access on the import step itself. They apply to every step inside that does not choose its own. The pickers of the steps inside then say "via" and the import step's name. A choice on a step inside always wins over the import step's choice. ## Which setting wins Each setting, such as the model or the effort, is decided on its own. The more specific choice wins: 1. What the step sets itself, such as its own model. 2. The Persona, Model settings or Tool access the step names. 3. The step's Role. 4. What the import step that brought the step in chooses. 5. What the workflow chooses for every step: its Persona, Model settings and Tool access, then its Role, then its own settings. 6. The harness you choose for the run, and your defaults in Settings. So a step with the Role Reviewer and its own Model settings Fast uses Fast, and still takes its Persona and Tool access from Reviewer. Blocked tools are different. A tool blocked anywhere stays blocked. When the workflow's Tool access blocks the web, no step can use the web, whatever its Role says. [Resolution order](/reference/attributes/#resolution-order) gives the exact order, including the advanced settings. ## When a name is missing A step may name a Role, Persona, Model settings or Tool access that neither the workflow nor the library defines. That is a warning, not an error. The step still runs with its defaults, and the editor says which name is missing, for example: "No persona called `Critic` in this workflow or the library. The step uses its defaults." ## Try the example The example is a plan, implement and review workflow. It defines its own copies of the Roles, Model settings and Tool access it uses, so it runs the same way on any Orbital. The Personas come from the library. Delete the definitions from the workflow to use your library entries instead. - `plan` uses the Role Planner. It thinks hard and only reads. - `implement` uses the workflow's Role, Implementer, with the Tool access "No web". - `review` uses the Role Reviewer. It thinks hard and only reads. 1. **Download the files.** [Download the files](/roles.zip). 2. **Unpack them.** Unpack the download into your workflow library, `~/.orbital/workflows/`. Keep every relative path. 3. **Check the workflow.** Run `orbital validate ~/.orbital/workflows/roles.dot`. Open the `roles` workflow in the editor and select each step to see its summary line. Then choose **New task**, pick any project, choose the `roles` workflow and write a goal. One practice project serves every example. :::caution[This example changes your folder] `implement` changes files in the project's folder itself, because the workflow makes no worktree. Pick a practice project, not one you care about. ::: See [plain folders and Git](/projects/#plain-folders-and-git) for when a project must be a Git repository. ## Expected behaviour `plan` writes a plan without changing files. `implement` changes the files. `review` reports what it finds without changing files. The run page shows each step's Role and the settings it ran with. ## Complete source ### roles.dot ```dot title="roles.dot" digraph roles { graph [ version="1", entry="plan", role="Implementer", roles="Planner { persona: Planner; model_settings: Deep thinking; tool_access: Read only; } Implementer { persona: Implementer; model_settings: Balanced; tool_access: Full access; } Reviewer { persona: Strict reviewer; model_settings: Deep thinking; tool_access: Read only; }", model_settings="Deep thinking { effort: high; } Balanced { effort: medium; }", tool_access="Read only { tools: read; } Full access { tools: read, edit, shell, web, mcp; } No web { tools_blocked: web; }" ] plan [role="Planner", prompt="Plan a small change that meets this goal: {{ inputs.goal }}. Do not change files.", outputs="plan:text"] implement [tool_access="No web", prompt="Implement this plan: {{ context.plan }}"] review [role="Reviewer", prompt="Review the change against this plan: {{ context.plan }}. Report what you find. Do not change files."] done [shape="Msquare"] plan -> implement implement -> review review -> done } ``` [Download roles.dot](/examples/roles/roles.dot) [Attributes](/reference/attributes/#roles-model-settings-and-tool-access) describes the text form of every attribute in this file. ## Quick reference | To | Do this | | --- | --- | | Give one step a Role | Select the step and choose a Role under **Role** in the inspector. | | Give several steps a Role | Select them on the canvas, choose a Role and select **Assign role**. | | Change one part of a step | Choose another value in that part's picker. **Clear** undoes it. | | Change an entry for every workflow | Open the **Roles** tab, change the entry, select **Save**, then **Change it everywhere**. | | Change an entry for this workflow only | Do the same, but choose **Only in this workflow**. | | Bring back a deleted shipped entry | Select **Restore** under **Deleted defaults** in **Settings**. | ## Related - [Roles and tool access](/roles/): the building blocks, tool access and permissions - [Use different models](/harnesses/use-different-models/): run steps on different harnesses and models - [Reuse a workflow](/workflows/reuse-a-workflow/): how import steps pass settings to the steps inside - [Attributes](/reference/attributes/#resolution-order): the exact resolution order --- # Settings > Where Settings is and what each of its sections is for Change how Orbital runs, harnesses, roles, connected services and the app itself work in Settings. Open **Settings** from the sidebar, then pick a section from the list on the left. Every value saves as soon as you finish with it. A text field saves when you press Enter or leave the field. A **Reset** button appears next to a label whose value you changed from its default. A line under a field says when the change applies, for example "Applies to runs started afterwards." ![Settings, General page with Appearance set to match the computer, light or dark, followed by concurrent runs, default workflow, default harness, keep harness stream, stalled turn limit and open worktrees in.](/screenshots/settings-general.webp?v=cb0897c671) *General holds the appearance and the settings for runs, worktrees and the server.* ## The sections | Section | What it is for | | --- | --- | | [General](/reference/settings/#general) | Appearance on this device, and the settings for runs, worktrees and the server. | | [Harnesses](/reference/settings/#harnesses) | Whether each harness can start, how Orbital starts it, and where a run goes when its harness runs out of quota. | | [Supervisor](/reference/settings/#supervisor) | How the Supervisor looks after your runs. It appears only while the Supervisor experimental feature is on. | | [Roles](/reference/settings/#roles) | Roles that give a step a persona, model settings and tool access in one choice. | | [Personas](/reference/settings/#personas) | Persona prompts that agent turns can use. | | [Model settings](/reference/settings/#model-settings) | Named choices of harness, model and effort for a step. | | [Tool access](/reference/settings/#tool-access) | Which kinds of tool a step can use. | | [GitHub](/reference/settings/#github) | The GitHub token Orbital uses for its GitHub requests, and where it looks for one. | | [Linear](/reference/settings/#linear) | The Linear key that Linear trigger rules use to check Linear. | | [MCP](/reference/settings/#mcp) | The MCP endpoint that lets coding agents outside Orbital read and control your runs. | | [Costs](/reference/settings/#costs) | The prices, in USD per million tokens, that Orbital uses to estimate what runs cost. | | [Command line and skills](/reference/settings/#command-line-and-skills) | Installs the `orbital` command and Orbital's agent skills. | | [Experimental features](/reference/settings/#experimental-features) | Switches for features still being tried out. Each is off until you turn it on. | | [Recent changes](/reference/settings/#recent-changes) | The newest changes to your settings, with who or what made each one and when. | | [About](/reference/settings/#about) | The version that is running, its updates, and where your settings are kept. | ## Harnesses **Settings > Harnesses** shows whether each [harness](/harnesses/) is installed and signed in. Below the status table, each harness has its own settings, such as its program and default model. ![Settings, Harnesses page with a status table showing whether Claude, Codex and OpenCode are installed and signed in, followed by Claude's settings for turned on, program, default model, default effort and environment variables.](/screenshots/settings-harnesses.webp?v=6446e71ff0) *Check here first when a run cannot start its harness.* ## GitHub **Settings > GitHub** saves a token for Orbital's GitHub requests. It also lists the places Orbital looks for a token, in order. The first source with a token is used. ![Settings, GitHub page showing a saved token and the token sources in order: saved token, GH_TOKEN, GITHUB_TOKEN and gh auth login.](/screenshots/settings-github.webp?v=f898ede9d3) *The token sources list shows which token Orbital uses.* ## Related - [Settings reference](/reference/settings/): every field, its default and its allowed values - [Harnesses and models](/harnesses/): how Orbital picks and starts each harness - [Roles and tool access](/roles/): what the library sections do for steps - [Connect a coding agent](/mcp/connect/): use the MCP section to register a client - [Troubleshooting](/troubleshooting/): fixes that name each switch in Settings --- # Connect a coding agent > Register Claude Code, Codex, OpenCode or any MCP client with Orbital Connect Claude Code, Codex, OpenCode or another MCP client to Orbital, so the agent can list, start, follow, message and control your runs from a conversation. Copy your client's setup from **Settings > MCP** and run it in a terminal. With your address and token in place of `` and ``, it looks like this: **Claude Code** ```sh claude mcp remove --scope user orbital >/dev/null 2>&1; claude mcp add --transport http --scope user orbital '' --header 'Authorization: Bearer ' ``` **Codex** ```sh codex mcp add orbital --url '' && printf '%s\n' '' '[mcp_servers.orbital.http_headers]' 'Authorization = "Bearer "' >> ~/.codex/config.toml ``` This adds the server, then adds the token to `~/.codex/config.toml`: ```toml [mcp_servers.orbital.http_headers] Authorization = "Bearer " ``` :::caution The command appends this table each time you paste it. A file with the table twice is not valid. Before you paste it again, for example after rotating the token, delete the old `[mcp_servers.orbital.http_headers]` table from `~/.codex/config.toml`. ::: **OpenCode** ```sh opencode mcp add orbital --url '' --header 'Authorization=Bearer ' ``` **Other client** Any client that speaks Streamable HTTP works. Give it the address and one header: ```text URL: Authorization: Bearer ``` Each setup registers Orbital for your user, not one folder. It replaces an earlier Orbital entry, so pasting it again leaves one entry. The Codex setup is the exception, as its tab says. MCP, the Model Context Protocol, is how coding agents connect to tools. Orbital serves MCP over Streamable HTTP, on the same address as the app, at `/mcp`. A connected agent works with your runs the same way you do in the app. It can also [lead delivery with a supervisor](/supervisor/lead-delivery/) through the [`orbital-delivery-lead` skill](/mcp/skills/#orbital-delivery-lead). ## Connect a client 1. **Turn the endpoint on.** Open **Settings > MCP**. Check that **Turned on** is on. It is on unless you turned it off. **Status** should say "Listening". ![Settings, MCP page with the endpoint turned on, its status Listening, the endpoint address and token, and a Copy setup button for each client.](/screenshots/settings-mcp.webp?v=ab5e9e42f5) *Check that the endpoint is turned on and listening.* 2. **Find the address and the token.** Under **Connection**, find the **Endpoint address**, such as `http://127.0.0.1:42121/mcp`, and the **Token**. Choose **Reveal** to see the token and **Copy** to copy it. Every call must carry the token as `Authorization: Bearer `. That holds even for `orbital serve`, whose app needs no login. 3. **Copy your client's setup.** Under **Register a client**, Settings shows one ready-made setup for each client, with your address and token already in it. Choose **Copy setup** for your client. 4. **Paste it into a terminal.** Paste the setup into a terminal and run it. Then start a new session of that agent. 5. **Check that it works.** In the new session, ask `List my Orbital projects.` The agent calls `list_projects` and answers with your projects and their folders. If the agent cannot see the tool, check three things. The endpoint is on. The address matches the one in Settings. You started a new session after registering. [Troubleshooting](/troubleshooting/#an-mcp-client-cannot-reach-orbital) has more fixes. ## Choose what agents may do **Settings > MCP** has a switch for each tool group. A tool in a group that is off answers that the group is off in Settings. | Tool group | Tools | Default | | --- | --- | --- | | Reading runs and workflows | `list_runs`, `get_run`, `list_projects`, `read_run_thread`, `read_run_history`, `read_run_log`, `export_run`, `list_workflows`, `read_workflow`, `validate_workflow`, `wait_for_run`, `read_pull_request`, `read_run_worktree` | On | | Starting runs | `start_run` | On | | Controlling runs | `control_run`, `revise_goal`, `secure_run_work` | On | | Messaging and answering runs | `message_run`, `answer_question` | On | | Archiving | `archive_run`, `resolve_run` | Off | | Changing Orbital's settings | `change_setting` | Off | :::caution With the defaults, a connected agent can start, pause, retry, restart and abort your runs, and message them. Turn off a group you do not want agents to use. ::: Turn on **Archiving** if you want an agent to tidy up finished runs. Turn on **Changing Orbital's settings** if an agent should change settings. [MCP tools](/reference/mcp-tools/) describes every tool, its arguments and when to use it. ## Which tool for which job | You want the agent to | It uses | | --- | --- | | Find a project and its workflows | `list_projects`, `list_workflows` | | Start a run | `start_run`, with a project, a goal and optionally a workflow and its inputs | | See how a run is doing | `get_run`, then `read_run_thread` or `read_run_history` for detail | | Wait for a run to change | `wait_for_run`, which returns when the standing changes, the agent asks a question, or the run ends | | Find out why a step failed | `read_run_log` | | Steer the step at work | `message_run` with delivery `Resume` | | Leave a note for the next step | `message_run` with delivery `Hold` | | Change what a run should achieve | `revise_goal` | | Answer a run's question | `answer_question` | | Pause, resume, retry, jump, [switch harness](/harnesses/switch-harness/), restart or abort | `control_run` with the position from `get_run` | | Check a run's pull request: its checks, reviews and merge state | `read_pull_request` | | See a run's uncommitted changes and commits ahead | `read_run_worktree` | | Save a stopped run's work before replacing it | `secure_run_work`, which commits on the run's branch and pushes without force | | Mark a failed run as dealt with, or archive a finished one | `resolve_run`, `archive_run` | Run content, such as the thread and the log, came from agents. An agent reading it treats it as data, not as instructions. ## The address and the token | Topic | What to know | | --- | --- | | The port | The Mac app serves on port 42121 at every launch, so a registration keeps working after you quit and reopen it. When another program holds that port, the app starts on a free port and **Settings > MCP** says so. Update each registration with the address it shows. | | The host rule | When Orbital listens only on your own machine, which is the default, the endpoint answers only to the names `localhost`, `127.0.0.1` and `[::1]`. When you start the server with `--bind` on a network address, it answers to any host name, and the token is still required. It always refuses a change that another web site asks for. | | Where the token is kept | The token is kept in `~/.orbital/mcp-token`, readable only by you. It stays the same across restarts until you regenerate it. | ### Rotate the token Choose **Regenerate** next to the token in **Settings > MCP**. Then copy each client's setup again and paste it, which replaces the old entry. :::caution The old token stops working at once. Every client you registered with it loses access until you paste its new setup. ::: ## The Supervisor's calls The built-in [Supervisor](/supervisor/) uses the same tools, but under its own rules instead of the tool group switches. It must give a reason with every action. It acts only on runs it owns. Each action follows its **What it may do** setting. See [how a supervisor's calls are scoped](/reference/mcp-tools/#how-a-supervisors-calls-are-scoped). Orbital attaches its own server to agent turns as `orbital_app`. Keep the `orbital` name in your client configuration above. The two names let your configured connection and the turn-specific connection coexist. ## Related - [MCP tools](/reference/mcp-tools/): every tool, its arguments and its results - [Use the shipped skills](/mcp/skills/): give a connected agent Orbital's delivery lead and workflow designer skills - [Lead delivery with a supervisor](/supervisor/lead-delivery/): the loop a connected agent can follow to deliver tickets - [Troubleshooting](/troubleshooting/#an-mcp-client-cannot-reach-orbital): fixes when a client cannot reach Orbital --- # Use the shipped skills > Install Orbital's two agent skills and ask coding agents to use them Install Orbital's two skills so your coding agent can deliver tickets through Orbital runs and design workflows with you. From a terminal, run: ```sh orbital skill install ``` A skill is a folder of instructions an agent, such as Claude Code or Codex, loads when a task matches it. Orbital ships these two: | Skill | What it is for | When an agent uses it | | --- | --- | --- | | `orbital-delivery-lead` | Delivering tickets and epics through Orbital runs, as a delivery lead. | You ask it to deliver tickets with Orbital, to start or watch runs, or to act as delivery lead. | | `orbital-create-workflow` | Designing a new Orbital workflow with you, one decision at a time. | You ask it to turn a process into a workflow, or to design or change one. | ## Install them **App** 1. **Open the skills settings.** Open **Settings > Command line and skills**. 2. **Install each skill.** Under **Agent skills**, choose **Install** next to each skill. Choose **Update** when a newer version ships. ![Settings, Command line and skills page showing the orbital command installed and the agent skills installed.](/screenshots/settings-command-line.webp?v=2453b7527a) *Each skill shows whether it is installed and up to date.* **Command line** ```sh orbital skill install ``` The command installs every shipped skill and reports on each one: | Report | What it means | | --- | --- | | Installed | The skill was not there, and now is. | | Updated | The skill now matches the version this release ships. | | Already up to date | Nothing changed. | | Kept | Your copy differs, so Orbital left it alone. | | Replaced | With `--replace`, Orbital installed its own and names the backup of yours. | | Could not install | Something went wrong, and the report gives the reason. | The command exits with an error when a skill could not be installed. See [`orbital skill install`](/reference/command/#orbital-skill-install). ## Where they go Each skill is copied to `~/.agents/skills/` and linked from `~/.claude/skills/`. Claude Code finds it through the link. Other agents that read skills from `~/.agents/skills` find the copy. The skills work for every project on your machine. ## When a different copy is there Orbital never overwrites a copy you changed. When the installed copy, or the link, differs from the skill Orbital ships, Orbital keeps it and says so. In Settings the skill reads "A different copy with this name is installed. Orbital will keep it unless you replace it." To replace it, choose **Replace…** in Settings and confirm, or run: ```sh orbital skill install --replace orbital-delivery-lead ``` Orbital moves your copy to a dated backup under `~/.agents/skill-backups` or `~/.claude/skill-backups`, then installs its own. ## orbital-delivery-lead The delivery lead refines tickets until they are true and worth building, and starts Orbital runs on them. It checks in on the runs, unsticks them without losing work, and verifies and closes what they deliver. It uses Orbital's MCP tools, and `gh` and `git`. [Lead delivery with a supervisor](/supervisor/lead-delivery/) describes the loop it follows. The skill adds rules of its own for keeping delivery safe. It needs Orbital's MCP endpoint connected to the agent. See [Connect a coding agent](/mcp/connect/). Without MCP, it falls back to Orbital's [HTTP API](/reference/http-api/). :::caution The delivery lead starts runs and acts on them by itself, within what its MCP tool groups allow. Check the switches in **Settings > MCP** before you ask it to lead. ::: Ask it, for example: ```text Use the orbital-delivery-lead skill to deliver the open sub-issues of https://github.com/acme/shop/issues/400 with Orbital, one run per ticket. Refine each ticket against main first. Check in every 30 minutes. ``` It comes with goal templates for one ticket and for several tickets in order. Each template carries the rules a run's validate step needs: commit every round, check every "done when" item, and a blocker is never a pass. ## orbital-create-workflow The workflow designer turns a process you run by hand into an Orbital [workflow](/workflows/). It interviews you one decision at a time, with a recommendation for each. It covers the goal and inputs, the unit of work, whether the workflow ends or runs on, and what each step may do. It also covers parallel work, failures and retries, and waits. It draws the workflow as it grows and asks you to confirm the design. It asks you to approve each prompt before it writes it. Then it writes the files and validates them without running them. It carries the complete workflow format and worked examples, so it needs nothing else. Ask it, for example: ```text Use the orbital-create-workflow skill. Every Monday I read our open support tickets, group them by product area and write a summary for the team. Turn that into an Orbital workflow. ``` Check the result yourself before you run it. For a workflow saved in your library as `.dot`: ```sh orbital validate ~/.orbital/workflows/.dot ``` Save the finished workflow in a repository's `.orbital/` folder or in your library, `~/.orbital/workflows/`. It then appears in the new-run menu. [Workflows](/workflows/) explains where Orbital looks. ## Related - [Connect a coding agent](/mcp/connect/): give the delivery lead the tools it needs - [Command line](/command-line/): install skills and validate workflows from a terminal - [Settings reference](/reference/settings/#command-line-and-skills): the fields in **Command line and skills** - [Write your first workflow](/workflows/write-a-workflow/): check what the workflow designer writes --- # The orbital command > Run Orbital, validate workflows and start runs from a terminal Use the `orbital` command to run Orbital from a terminal. For example, start the server and the browser app, then check a workflow without running it: ```sh orbital serve orbital validate ~/.orbital/workflows/.dot ``` The command also opens Orbital in a window of its own, starts runs and changes settings on a running server, and installs the shipped [skills](/mcp/skills/). Run `orbital` with no arguments to print its help. ## Before you start The Mac app carries the command. Install it from **Settings > Command line and skills**, or with **Orbital > Install Command Line Tool** in the menu bar. On Linux or Windows, install it with npm. [Install Orbital](/get-started/install/#1-install-orbital) gives both routes. ![Settings, Command line and skills page showing the orbital command installed and the agent skills installed.](/screenshots/settings-command-line.webp?v=2453b7527a) *The orbital command shows as installed once the link is in place.* ## The subcommands | Subcommand | What it does | | --- | --- | | [`orbital serve`](/reference/command/#orbital-serve) | Runs the server and the browser app on one port, prints its address and opens it in your browser. | | [`orbital app`](/reference/command/#orbital-app) | Opens Orbital in a window that runs its own server. Closing the window stops the server. | | [`orbital validate`](/reference/command/#orbital-validate) | Checks a workflow without running it. It starts neither the server nor an agent. | | [`orbital run start`](/reference/command/#orbital-run-start) | Queues a run on the server running on this machine, the desktop app's or `orbital serve`'s, and prints the run's ID and page address. | | [`orbital settings set`](/reference/command/#orbital-settings-set) | Changes one setting on the server running on this machine. **Settings > Recent changes** lists the change. | | [`orbital skill install`](/reference/command/#orbital-skill-install) | Installs every skill that ships with Orbital, for agents and for Claude. | Every subcommand takes `--version`, which prints the version the command is running. A usage error, such as an unknown flag, prints the problem and the help. [The command line reference](/reference/command/) lists every flag, exit code and environment variable. ## Related - [Command line reference](/reference/command/): every flag, exit code and environment variable - [Install Orbital](/get-started/install/): install the command from the Mac app or with npm - [Use the shipped skills](/mcp/skills/): what `orbital skill install` gives your agents - [Start a run](/runs/start-a-run/): the other ways to start a run - [Permissions](/roles/#permissions): what a run asks you before its agent acts, on each harness --- # Troubleshooting > Find the fix for a symptom, or report a bug with the right files Find the symptom in the contents list and apply the fix under it. Each fix says what causes the problem and what to do. To check that Orbital and your harness work at all, run `explain-repo` as in [Start your first run](/get-started/first-run/). It only reads. ## Install and harnesses A [harness](/harnesses/) is the coding agent program that runs a step, such as Claude Code, Codex or OpenCode. ### Installation fails A 401 or 403 from GitHub Packages points at your setup. Check the registry entry, and check that your own classic token has the `read:packages` scope. A 404 can mean your account lacks package access, or that the scope points at the wrong registry. npm access is by invitation during the beta. [Install Orbital](/get-started/install/#1-install-orbital) says how to ask. :::danger Never paste tokens into workflow prompts or logs. ::: If the server cannot load better-sqlite3 after a pnpm install, repeat the install with `--allow-build=better-sqlite3`. If pnpm has no global bin directory, run `pnpm setup` and open a new shell. Then follow [Install Orbital](/get-started/install/). ### The first turn fails Read the command or harness named in the failure. Check its version and login in the shell that starts the server. The server does not inherit credentials exported after it started. Restart it after you change environment variables, then retry the stopped step. If authentication succeeds but model discovery fails, check the selected model and provider. ### A harness is missing or switched off A step does not fail when its harness is not installed, not signed in, not set up or turned off. The run stops on that step and names the cause. The run is [Paused](/runs/#standings), and its badge reads "Waiting on you". 1. **Read the status table.** Open **Settings > Harnesses** and find the harness the step uses. ![Settings, Harnesses page with a status table showing whether Claude, Codex and OpenCode are installed and signed in, followed by Claude's settings.](/screenshots/settings-harnesses.webp?v=6446e71ff0) *The status table says whether each harness is installed and signed in.* 2. **Install the harness if it says "Not installed".** Install it in the shell that starts Orbital, or set its **Program** to the full path. The Mac app reads your login shell's `PATH`. Restart the app after you change it. 3. **Sign in if it says "Signed out".** Run `claude`, `codex login` or `opencode auth login`. 4. **Turn it on if it says "Off".** Turn **Turned on** back on. 5. **Check again and resume.** Choose **Check again**. Then open the run and choose **Resume**. To carry on with another harness instead, [switch the run's harness](/harnesses/switch-harness/). A Codex step on Linux can also stop because Codex's sandbox needs bubblewrap. Install your distribution's `bubblewrap` package, then resume. ### A harness ran out of quota The step stops with an "out of quota" reason. The reason names the harness and, when known, when the quota resets. Wait and choose **Resume**, or [switch the run's harness](/harnesses/switch-harness/). That page also explains how the Supervisor can switch it for you. ### Codex rejects the working folder Codex needs a Git repository. Other harnesses work in a plain folder. See [plain folders and Git](/projects/#plain-folders-and-git). Open the project's page and check the primary folder. Its **Type** must be "Repository". To use a plain folder with Codex, run `git init` in it first, then retry the failed step. ## Runs and workflows ### Validation rejects the workflow Run `orbital validate` with the explicit saved file path. Then match the error: | Error | What to do | | --- | --- | | Missing entry | Give the ID of a declared node. | | Unknown attributes | Look for retired syntax or a typo. | | Missing prompt file or import | Put the companion file at the path relative to the file that names it. | | Read before production | Add an earlier producer on every incoming path. Do not fabricate a default. | For choice routing, give each conditioned edge a distinct weight. Cover every accepted choice, or provide a fallback. See [node types](/reference/node-types/) and [add branches](/workflows/add-branches/). ### Starting reports missing inputs Fill in the goal and every required workflow input on the new-run page. Optional inputs end in a question mark in the declaration. If the workflow is not the one you expected, check for a custom workflow that shadows a file of the same name. Confirm that the project includes the folder that holds the workflow. ### A run appears stopped A server restart does not stop a run. The next start carries on every run you did not pause, from its last finished activity. A run that stays Paused was either paused by you, or stopped by itself and shows the badge "Waiting on you". ![A run page for an interrupted run, with a Paused badge in its header.](/screenshots/run-interrupted.webp?v=700d8cf233) *A stopped run shows the Paused badge.* Open the transcript to find the last active step. Then choose one: | Action | What it does | | --- | --- | | **Resume** | Carries the step on in its session when it can. | | **Retry** | Starts the step again in a new session. | Neither is the same as starting an unrelated run. See [how Orbital works](/concepts/how-orbital-works/). A run whose harness ran out of quota is Paused too. [The Supervisor can switch its harness](/harnesses/switch-harness/) for you. ### A failed step does not take its fallback That is intentional. A failed step takes only a matching conditioned edge. With no matching failure route, the run is Paused, with the badge "Waiting on you". Add an explicit failure route if recovery belongs in the workflow. Otherwise resolve the cause and retry. A failed terminal ends the run and records a terminal failure. A probe that keeps retrying is still inside its step. Check GitHub authentication, repository access and the diagnostic before you change workflow edges. ### A step cannot stop a process That is intentional. Every process a step starts can signal only the processes it started itself. So no step can stop the Orbital server or another run. | System | What the step sees | | --- | --- | | Linux | The step runs in its own process namespace through bubblewrap. `ps` and `pgrep` see only the step's processes, and `kill` answers `No such process` for any other. | | macOS | A sandbox profile refuses the signal, and `kill` answers `Operation not permitted`. | The step's output, in the run's transcript, shows what was refused. Record the PID of anything a step starts, and stop it by that PID. :::caution On Linux without bubblewrap, or where bubblewrap cannot create a user namespace, steps run without this protection. The server logs a warning at start. Install your distribution's `bubblewrap` package. ::: ### A worktree remains after cleanup Inspect `worktree.status` and the cleanup step. Orbital keeps all checkouts if removal could lose work. Commit and push the changes you need, or complete the right pull request action, then retry cleanup. Do not delete the directory to make a run look successful. [Worktrees and delivery](/projects/worktrees-and-delivery/) says when Orbital removes a worktree and when it keeps one. ## MCP, triggers and the Supervisor ### An MCP client cannot reach Orbital First check that **Turned on** is on in **Settings > MCP**, and that **Status** says "Listening". While the endpoint is off, `/mcp` answers 404. ![Settings, MCP page with the endpoint turned on, its status Listening, the endpoint address and the token.](/screenshots/settings-mcp.webp?v=ab5e9e42f5) *Check the endpoint switch, its status and its address.* Then match what the client reports: | The client reports | Cause and fix | | --- | --- | | 401 | The token is missing or old. Copy the client's setup from **Settings > MCP** again, paste it, and start a new session. Regenerating the token makes every old registration fail. | | 403 "this server does not answer to that host" | The client used a host name other than `localhost`, `127.0.0.1` or `[::1]`, while Orbital listens only on your machine. Use the address Settings shows. | | A tool's group "is off in Settings" | Turn that tool group on in **Settings > MCP**. | The Mac app moves to a free port when port 42121 is taken. **Settings > MCP** then says so. Register the new address. See [Connect a coding agent](/mcp/connect/). ### A trigger rule does nothing - Check that **Triggers** is on in **Settings > Experimental features**. - Open **Triggers** and read the rule's line. A rule that cannot check its source says why, for example that GitHub or Linear refused the saved token. Fix the token in **Settings > GitHub** or **Settings > Linear**. Then open the rule and save it again. - Stop, pause and message rules act only on runs that a trigger started for the same item. - Choose **Try it and save** to see what the rule would have done over the last seven days. ![The Triggers page listing rules with their source, an on or off switch, what each does and its recent firings, with an error shown on one rule.](/screenshots/triggers-page.webp?v=5fe1ad8f4b) *A rule that cannot check its source shows the error on its line.* ### The Supervisor does not act - Check that **Supervisor** is on in **Settings > Experimental features**. Then check that its **Status** in **Settings > Supervisor** is on and not paused for today. - Check "What it may do". An action set to "Never" is refused. An action set to "Ask me first" waits for your approval in its chat. - Check "Spending". At its daily limit it stops acting for the day, and says so once in its chat. - A run at a gate does not wake it. That is your process working. ## Reporting a bug Send two files with the report: | File | Where to find it | | --- | --- | | The server's log | `~/.orbital/logs/server/orbital.log`. In the desktop app, **Help > Show Logs** opens its folder. | | The export of the run that went wrong | Open the run, then choose **Download run JSON** from its **Run actions** menu (⋯). | Leave out the macOS system log. It holds the operating system's messages and none of Orbital's server or engine activity. ## Related - [Install Orbital](/get-started/install/): install the app or the command and sign in to a harness - [Harnesses and models](/harnesses/): how a step picks its harness and what each status means - [Switch a run's harness](/harnesses/switch-harness/): carry a paused run on with another harness - [Pause, stop or resolve a run](/runs/control-a-run/): resume, retry or end a run that stopped - [Error handling](/reference/error-handling/): failure routes, fallbacks and repair budgets - [Connect a coding agent](/mcp/connect/): register a client and choose its tool groups - [Make a trigger rule](/triggers/make-a-rule/): set up a rule and try it before you save - [Supervisor](/supervisor/): what the Supervisor watches and what it may do - [Settings](/settings/): where each switch named on this page lives --- # Implement a feature > Turn a short goal or a ticket into a reviewed, merged pull request In this example you have Orbital build a small feature and deliver it as a merged pull request. You start one shipped workflow with the feature in plain words, or with the address of the ticket that describes it. About five minutes to set up. The run then works on its own until the pull request merges. ## Before you start Both workflows ship with Orbital, so there is nothing to download. You need: - a project whose primary folder is a Git repository with a GitHub remote you can push to, because the run works in a [worktree](/projects/worktrees-and-delivery/) and delivers a pull request; - a GitHub token Orbital can use: a `gh auth login`, or a token in **Settings > GitHub**; - a harness that is installed and signed in; For `implement-ticket` with a Linear ticket, the agent reads it with the Linear tools your harness has, such as Linear's MCP server. :::caution Orbital turns on auto-merge for the pull request. Where your branch rules require no review, the pull request merges as soon as its required checks pass. With no required checks, it merges as soon as it opens. Protect the branch before you start, or start your own copy of the workflow with auto-merge removed from its delivery. [Turn auto-merge off](/projects/worktrees-and-delivery/#turn-auto-merge-off) shows how. ::: The steps run with Full access. `pursue-goal` also runs with `permissions="full"`, so its agent is never asked before it changes files or runs commands. `implement-ticket` uses `auto-accept`. [Permissions](/roles/#permissions) explains what each one asks you. ## 1. Choose a workflow | Use | When | | --- | --- | | `pursue-goal` | You can say what you want in a few sentences, and there is no ticket. | | `implement-ticket` | The work is written up in a GitHub or Linear issue. | Both work in a worktree, review their own work and verification, open a pull request, and repair it until it merges. ## 2. Start the run Follow the part for the workflow you chose. ### Deliver a goal with pursue-goal **App** 1. Choose **New task**, pick the project and choose the `pursue-goal` workflow. ![The Start page asking what to do in a project, with a goal box, a harness picker, a workflow picker and a Chat or Workflow switch.](/screenshots/start-page.webp?v=0428f5d705) *Pick the workflow, then write the goal in the box.* 2. Write the goal. Say what should change, how you will know it works, and any rule the result must follow. For example: ```text Add a "Copy link" button to each invoice row. Clicking it copies the invoice's public address. Add a unit test for the address format. Follow the conventions in CONTRIBUTING.md. ``` 3. Choose **Start run**. **Command line** Run `orbital run start` with your project's id and the goal. [The orbital command](/reference/command/) lists every flag. ```sh orbital run start --workflow pursue-goal --project proj_r3tQMqBPBB5p \ --goal "Add a Copy link button to each invoice row. Clicking it copies the invoice's public address. Add a unit test for the address format." ``` ![pursue-goal: create_worktree, implement, validate, then delivery, cleanup and the terminals.](/shipped/pursue-goal.svg?v=dc2465a951) What the run does: | Step | What it does | | --- | --- | | **create_worktree** | Makes a worktree on a new branch. | | **implement** | Changes the code and commits. | | **validate** | Checks the work against every point of the goal, using the change and verification. If something is missing, it sends feedback back to **implement**. This loop runs at most 20 times. | | **delivery** | Opens the pull request and waits for it to merge, repairing CI, conflicts and review comments. | | **cleanup** | Removes the worktree once nothing in it would be lost. | Implementation goes straight to validation. A rejected validation returns its feedback to the next implementation round; a pass starts pull request delivery. There is no separate checks or prerequisite gate. ### Deliver a ticket with implement-ticket **App** 1. Choose **New task**, pick the project and choose the `implement-ticket` workflow. 2. Fill in **Ticket URL**, such as `https://github.com/acme/app/issues/628`. 3. Write a goal, or leave the ticket to speak for itself. 4. Choose **Start run**. **Command line** Give the ticket's address as the `ticket_url` input. ```sh orbital run start --workflow implement-ticket --project proj_r3tQMqBPBB5p \ --input ticket_url=https://github.com/acme/app/issues/628 ``` ![implement-ticket: scope, then the deliver-ticket sub workflow.](/shipped/implement-ticket.svg?v=82fe3ab50a) The first step reads the ticket and writes the scope that every later step works from. Then the run plans, implements, and has the change reviewed against the ticket and verification. A rejected review revises the plan and the implementation, and the review runs again. Once the review passes, the run delivers the pull request the same way as `pursue-goal`. It completes the ticket after the merge. ## 3. Follow the run | Where | What it shows | | --- | --- | | The **Session** tab | Each step and the agent's work. | | The dock's **Changes** tab | The diff so far. | | The dock's **Pull requests** section | The pull request, once it is open. | ![A run page on the Session tab showing the steps one by one, with Run facts, Pull requests and Steps in the dock on the right.](/screenshots/run-thread.webp?v=f4f203d47e) *The Session tab shows each step; the dock lists the run's pull requests.* To steer the run, write in the composer. To change what the run should do, send it a message with the new requirement. See [Message a run](/runs/message-a-run/). ## 4. Review the pull request While the pull request waits for review, the run waits at a gate, "The pull request is waiting for review or merge". It takes no run slot. Review the pull request as you normally would. You do not need to merge it: once your branch rules allow, GitHub merges it, because Orbital turned on auto-merge. Orbital looks at the pull request once a minute and carries on by itself. ![A gate step saying the pull request is waiting for review or merge, with the waiting time, the next check and a Skip button.](/screenshots/gate-wait.webp?v=fdf72f63d4) *The run waits here without taking a run slot.* ## What you get The run ends Succeeded when the pull request merges. It ends Failed when someone closes the pull request without merging. See [standings](/runs/#standings). ![The dock's Pull requests list with open and merged pull requests and their branches.](/screenshots/pull-requests-panel.webp?v=612ac483b1) *A merged pull request shows as merged in the dock.* Once the publish step ends, Orbital turns on auto-merge for the pull request itself, with your GitHub token. The agent never runs `gh` for it, so this works on every harness and permission setting. When the repository does not allow auto-merge, a rule blocks it, or the token lacks permission, the run's history says so and the run carries on. Then merge that pull request yourself. [Turn auto-merge off](/projects/worktrees-and-delivery/#turn-auto-merge-off) explains how to stop Orbital asking. ## Related - [Worktrees and delivery](/projects/worktrees-and-delivery/): how the run makes a worktree, opens the pull request and merges it - [Shipped workflows](/reference/shipped-workflows/): every step of `pursue-goal` and `implement-ticket` - [Lead delivery with a supervisor](/supervisor/lead-delivery/): have an agent start and follow such runs for you - [Deliver an epic](/examples/deliver-an-epic/): deliver many tickets in one run --- # Deliver an epic > Deliver an epic's tickets in dependency order, one merged pull request each In this example you deliver a whole epic with one run. You start the shipped `implement-epic` workflow with the epic's address. The run picks the next ticket that nothing blocks, delivers it as one merged pull request, and picks again until every ticket is done. Then it closes the epic. About five minutes to start, once the tickets are ready. The run itself can take days. ![implement-epic: select picks a ticket, ticket delivers it, and the loop repeats until close_epic.](/shipped/implement-epic.svg?v=6755d01c84) ## Before you start The workflow ships with Orbital, so there is nothing to download. You need the same as for [one feature](/examples/feature-pull-request/#before-you-start): a project on a Git repository, because each ticket is built in a worktree, a GitHub token and a signed-in harness. :::caution Orbital turns on auto-merge for each pull request. Where your branch rules require no review, each pull request merges as soon as its required checks pass. The run then moves on to the next ticket without waiting for you. Protect the branch before you start, or start your own copy of `implement-epic` with auto-merge removed from its delivery. [Turn auto-merge off](/projects/worktrees-and-delivery/#turn-auto-merge-off) shows how. ::: The steps have Full access and run with `permissions="auto-accept"`. [Permissions](/roles/#permissions) explains what that asks you. ## 1. Prepare the tickets The epic needs tickets the agent can read: | Tracker | What the epic needs | | --- | --- | | GitHub | Sub-issues of the epic, with "blocked by" links where order matters. | | Linear | Sub-issues, read with the Linear tools your harness has. | Each ticket should be one piece of work that one pull request can deliver, with a clear "done when". The run follows the tickets, so refine them first. [Lead delivery with a supervisor](/supervisor/lead-delivery/#the-loop) explains how. ## 2. Start the run **App** 1. Choose **New task**, pick the project and choose `implement-epic`. ![The Start page asking what to do in a project, with a goal box, a harness picker, a workflow picker and a Chat or Workflow switch.](/screenshots/start-page.webp?v=0428f5d705) *Choose the workflow here before you fill in the epic.* 2. Fill in **Ticket URL** with the epic's address. 3. Write a goal with any rule every ticket must follow, or leave the epic to speak for itself. 4. Choose **Start run**. **Command line** Give the epic's address as the `ticket_url` input. [The orbital command](/reference/command/) lists every flag. ```sh orbital run start --workflow implement-epic --project proj_r3tQMqBPBB5p \ --input ticket_url=https://github.com/acme/app/issues/600 ``` The run then works through the epic: | Step | What it does | | --- | --- | | **select** | Reads the epic and its tickets and picks the next leaf ticket that is open and not blocked. | | **ticket** | Delivers that ticket exactly as [`implement-ticket`](/examples/feature-pull-request/#deliver-a-ticket-with-implement-ticket) does: worktree, plan, implement, review, pull request, merge, complete the ticket. | | Back to **select** | The run starts again with a clean slate for the next ticket. | | Wait | When every remaining ticket is blocked, the run waits thirty minutes and looks again. | | **close_epic** | When no ticket is left, closes the epic, and the run ends Succeeded. | A pull request closed without merging does not stop the run. The run moves on to the next ticket, and you decide what to do with the closed one. ## 3. Review each pull request Each ticket's pull request appears in the dock's **Pull requests** section. The run takes no run slot while it waits at each pull request's review gate. Review each pull request as you normally would. Once your branch rules allow, GitHub merges it, because Orbital turned on auto-merge. ![The dock's Pull requests list with open and merged pull requests and their branches.](/screenshots/pull-requests-panel.webp?v=612ac483b1) *Each ticket adds its own pull request to the list.* An epic run can take days. Check in on it, or let [the Supervisor](/supervisor/) or a coding agent with the [`orbital-delivery-lead` skill](/mcp/skills/#orbital-delivery-lead) watch it for you. ## What you get A merged pull request for each ticket the run delivered, with that ticket completed, and the epic closed. The run ends Succeeded when no ticket is left. ## With a review panel `implement-epic-with-panel` delivers an epic the same way, but reviews each change differently. Eight reviewers look at the change in parallel, each for one kind of problem: the change's lifecycle, workflows, integrations, the frontend, tests, complexity, new comments and coding standards. The Implementer fixes what they find, and one more reviewer checks the fix. There are at most two fix rounds. Use it when you want a broader review of each ticket and can spend more agent time on it. ![implement-epic-with-panel: parallel reviewers, triage, fix and a second review for each ticket.](/shipped/implement-epic-with-panel.svg?v=6d0a9eabf9) ## Related - [Shipped workflows](/reference/shipped-workflows/#implement-epic): every step of `implement-epic` and its panel variant - [Outer loop over an epic](/examples/workflows/epic/): a smaller epic workflow you can read file by file - [Implement a feature](/examples/feature-pull-request/): how one ticket is delivered - [Supervisor](/supervisor/): let an agent watch a long run for you --- # Review pull requests > Start a review run each time someone opens a pull request In this example you have an agent review every new pull request. You add the `review-pull-request` workflow to your library, then make a [trigger](/triggers/) rule that starts it when a pull request opens. The run reads the pull request and leaves one review comment. About ten minutes to set up. After that, each review run starts within about a minute of a pull request opening. ```dot title="review-pull-request.dot" digraph review_pull_request { graph [version="1", entry="review", description="Review one pull request and leave the review as comments, without changing, approving or merging it"] review [role="Reviewer", tools="read, shell", prompt_file="prompts/review-pull-request/review.md"] done [shape="Msquare", outcome="success"] review -> done } ``` ![review has one step, then done.](/review-pull-request.svg?v=86d090e8cf) ## Before you start You need: - the Triggers feature turned on: **Settings > Experimental features > Triggers**; - a GitHub token that can read the repository and comment on its pull requests, from `gh auth login` or **Settings > GitHub**; - the `gh` command on the machine that runs Orbital; - a project whose primary folder is the repository; - the workflow: [download the archive](/review-pull-request.zip). ## 1. Add the workflow 1. Unpack the archive into your workflow library, `~/.orbital/workflows/`. Keep every relative path. 2. Check it: ```sh orbital validate ~/.orbital/workflows/review-pull-request.dot ``` Every project now offers `review-pull-request`, and it shows on the **Workflows** page. ![The Workflows page listing workflows with their source, Clone and Export actions, and Import and New workflow buttons.](/screenshots/workflows-page.webp?v=991e5fde13) *Workflows in your library show here for every project.* The archive holds the workflow above and its prompt: ```md title="prompts/review-pull-request/review.md" Review this pull request: {{ inputs.goal }} Read the pull request with the GitHub command line, `gh`: its description, the tickets it names, its diff, its checks and the review comments already on it. Read the repository's instructions files, such as AGENTS.md or CLAUDE.md, and follow its review conventions. Look for defects, missed requirements, missing tests and risks. For each finding, give the file and line, what is wrong, why it matters and the evidence. Do not report style a formatter would fix. Leave one review on the pull request with `gh pr review --comment`, holding every finding. Never approve, request changes on, merge or close the pull request, and do not push commits or change any file. If you cannot read the pull request, say so and stop. End with a short summary of the review you left, with the pull request's address. ``` ## 2. Understand what the agent can do The step runs as the Reviewer role, but `tools="read, shell"` replaces the role's Full access. The step can read files and run commands, because it needs `gh` to read the pull request and post the review. It has no Change files, no web and no connected services. The workflow makes no worktree. Its prompt tells it never to approve, merge or change anything. The pull request's title, description and diff come from whoever opened it. The agent reads them, so text in them can try to steer it. The prompt tells the agent not to change or push anything, but a prompt is only a request: [tool access, not the prompt, enforces limits](/roles/#permissions). Taking away Change files stops the agent editing files directly. It can still run commands. So the rule in the next step should also start runs only for pull requests from people you trust. ## 3. Make the rule 1. Open **Triggers** in the sidebar and choose the ready-made rule "Review every new pull request". It fills in: - source: GitHub, for pull requests that are opened; - action: Starts a run; - goal: `Review {{ url }}: {{ title }}`. ![The New rule dialog set to GitHub pull requests, with the event A pull request is opened.](/screenshots/trigger-rule-github.webp?v=c4800c5727) *The ready-made rule listens for opened pull requests.* 2. Choose the project and the `review-pull-request` workflow. 3. Limit it to people you trust. Under **Only if**, add "Author's team is any of" and pick a team of your organisation, written as `organisation/team`, such as `acme/engineering`. The rule then starts runs only for pull requests that team's members open, not for one from a stranger on a public repository. Add more conditions if you need them. For example, "Target branch is any of `main`", or leave out draft pull requests. 4. Choose **Try it and save**. Orbital shows what the rule would have done over the last seven days. Saving turns the rule on. :::caution Once saved, the rule starts runs on its own, with no one choosing **Start run**. Each opened pull request that passes **Only if** starts one. ::: ## 4. Open a pull request When someone opens a pull request, the rule starts a run within about a minute. The run's goal holds the pull request's address and title. The rules list shows when each rule last checked and what it did, such as "Started a run". ![The Triggers page listing rules with their source, an on or off switch, what each does and its recent firings.](/screenshots/triggers-page.webp?v=5fe1ad8f4b) *Each rule's line shows its last check and what it did.* ## What you get The review appears on the pull request as a comment. The run's last message in the **Session** tab summarises it. ## Keep it under control Each opened pull request starts one run, and each run takes a run slot while it works. Keep **Concurrent runs** in mind, and narrow the rule with conditions on a busy repository. See [The run queue](/runs/queue/). A rule can also stop, pause or message the runs that a trigger started for the same pull request. For example, a second rule on "closed" with the action **Stops runs** ends a review that is no longer needed. ## Related - [Triggers](/triggers/): what a rule watches and what it can do to runs - [Make a trigger rule](/triggers/make-a-rule/): build a rule of your own step by step - [Settings](/reference/settings/#experimental-features): turn on the Triggers feature - [The run queue](/runs/queue/): how many runs work at once --- # Improve a codebase > Deliver small improvements one at a time, or one focused refactor, without a ticket In this example you have Orbital improve a codebase without a ticket. Start the shipped `improve-codebase` workflow for one worthwhile improvement, or `reduce-complexity` for one focused refactor. About five minutes to start. Both workflows stop after one improvement, or immediately when nothing worthwhile is found. ![improve-codebase: discover a candidate, implement and deliver it, then stop; exit immediately when there is nothing to do.](/shipped/improve-codebase.svg?v=fa58f0a81a) ## Before you start :::caution Both workflows turn on auto-merge. Where your branch rules require no review, each pull request merges as soon as its required checks pass. See [turn auto-merge off](/projects/worktrees-and-delivery/#turn-auto-merge-off). ::: ## 1. Choose a workflow | Use | When | | --- | --- | | `improve-codebase` | You want one worthwhile improvement supported by evidence. | | `reduce-complexity` | You want one refactor of the most complex code. | ## 2. Start the run Follow the part for the workflow you chose. ### Run improve-codebase 1. Choose **New task**, pick the project and choose `improve-codebase`. ![The Start page asking what to do in a project, with a goal box, a harness picker, a workflow picker and a Chat or Workflow switch.](/screenshots/start-page.webp?v=0428f5d705) *Choose the workflow, then point it somewhere in the goal box.* 2. Point it somewhere, or leave the default goal, "Find and deliver one worthwhile improvement anywhere in the codebase." To narrow it, write your own, such as `Improve test coverage of the billing module`. 3. Choose **Start run**. The run finds one worthwhile improvement, delivers it as a merged pull request, and stops after cleanup: | Step | What it does | | --- | --- | | **discover** | Looks for one worthwhile improvement and says why. | | Deliver | If it finds one, the run makes a worktree, implements the improvement with a review loop, and delivers the pull request to merge. | | Clean up | The run cleans up and ends. | | No candidate | If it finds nothing worth doing, the run ends immediately without creating a worktree. | ### Run reduce-complexity 1. Choose **New task**, pick the project and choose `reduce-complexity`. 2. Optionally write a goal, such as `Only look at the src/api folder`. 3. Choose **Start run**. ![reduce-complexity: pick a target, refactor it in a worktree, deliver it.](/shipped/reduce-complexity.svg?v=68f0751032) The pick step measures complexity and picks the function most worth simplifying. The run then refactors it in a worktree, delivers one pull request and stops. ## What you get `improve-codebase` delivers at most one improvement per run. When discovery finds nothing worthwhile, it succeeds without creating a worktree or pull request. `reduce-complexity` leaves one pull request with the refactor, except in two cases: | Outcome | When | | --- | --- | | Succeeded, without a worktree | Nothing is worth simplifying. | | Failed, without a pull request | The refactor was not safe. | ## Run it on a schedule With the Triggers feature on, the ready-made rule "Improve the codebase every weekday morning" starts a run at 9:00 on weekdays. Its goal is "Find and make one small improvement to the codebase". Choose `improve-codebase`, `reduce-complexity` or your own workflow for it. Each scheduled run of `improve-codebase` considers one improvement and ends. See [triggers](/triggers/). ![The New rule dialog set to a schedule of every weekday at 09:00, with the next run times listed.](/screenshots/trigger-rule-schedule.webp?v=4af59dea16) *A schedule rule lists its next runs before you save it.* ## Related - [Shipped workflows](/reference/shipped-workflows/): every step of `improve-codebase` and `reduce-complexity` - [Worktrees and delivery](/projects/worktrees-and-delivery/): how each improvement reaches a merged pull request - [Make a trigger rule](/triggers/make-a-rule/): start runs on a schedule --- # Triage issues > Propose a label, priority and next step for each open issue, without changing the tracker In this example you triage a backlog. The `triage-issues` workflow reads the issues you name and answers with one table that gives each issue a proposed label, a priority and a next step. It changes nothing in the tracker. You decide what to apply. About five minutes to set up. ```dot title="triage-issues.dot" digraph triage_issues { graph [version="1", entry="triage", description="Read an issue tracker's open issues and propose a label, a priority and a next step for each, changing nothing", defaults="goal='Triage the open issues opened in the last seven days.'", tool_access="Triage { tools: read, web, mcp; }"] triage [role="Researcher", tool_access="Triage", prompt_file="prompts/triage-issues/triage.md"] done [shape="Msquare", outcome="success"] triage -> done } ``` ![triage has one step, then done.](/triage-issues.svg?v=9444b8fdb4) ## Before you start You need: - a signed-in harness with the connected service for your tracker. The agent reads the tracker with the connected services your harness has. For GitHub Issues or Linear, set up that service's MCP server in your harness first, for example Linear's MCP server in Claude Code; - any project. The workflow does not read the project's files, and the project does not need to be a Git repository, unless you run it with Codex, which needs one. See [plain folders and Git](/projects/#plain-folders-and-git); - the workflow: [download the archive](/triage-issues.zip). The workflow defines its own [tool access](/roles/#tool-access), Triage. It allows reading files, browsing the web and using connected services. It does not allow changing files or running commands. Triage is not one of the tool access blocks Orbital ships. :::note A connected service can still change an issue, so the prompt also forbids changing any issue. ::: ## 1. Add the workflow 1. Unpack the archive into your workflow library, `~/.orbital/workflows/`. Keep every relative path. 2. Check it: ```sh orbital validate ~/.orbital/workflows/triage-issues.dot ``` ![The Workflows page listing workflows with their source, Clone and Export actions, and Import and New workflow buttons.](/screenshots/workflows-page.webp?v=991e5fde13) *Once unpacked, the workflow shows on the Workflows page.* The archive holds the workflow above and its prompt: ```md title="prompts/triage-issues/triage.md" Triage the issues this goal names: {{ inputs.goal }} Read the issues with the tools your tracker offers, such as its connected service or its web pages. Read each issue's title, description and comments. Do not label, assign, comment on, close or otherwise change any issue, and do not change any file. For each issue, propose: 1. a label: bug, feature, question, documentation or duplicate; 2. a priority: urgent, soon or later, with one sentence on why; 3. a next step: ask the reporter a named question, reproduce it, link the duplicate, or ready to build. Answer with one table: issue, title, label, priority, next step. Under the table, list the issues you could not read and why. The table is the run's final answer, so end with it and nothing else. ``` ## 2. Start the run 1. Choose **New task**, pick any project and choose the `triage-issues` workflow. 2. Write which issues to triage, or leave the default: "Triage the open issues opened in the last seven days." For example: ```text Triage the open issues in the ENG team in Linear that have no label. ``` 3. Choose **Start run**. ## 3. Read the table The answer is the step's reply, the last message in the **Session** tab. It is one table with the issue, its title, the proposed label, priority and next step. Under the table, the agent lists any issue it could not read and why. ![A run page with an agent step selected, showing its prompt, the agent's answer and recorded values, with Run facts and a Steps list in the dock.](/screenshots/run-hero.webp?v=4fa21a573c) *The agent's answer holds the triage table.* ## 4. Apply what you agree with The run changes nothing in the tracker. Apply the labels, priorities and next steps you agree with in your tracker yourself. ## Make it yours - Change the labels and priorities in `triage.md` to the ones your team uses. - Run it every morning with a Schedule [trigger](/triggers/). - The workflow defines the `Triage` tool access in its `graph` line, and the `triage` step uses it. [Roles and tool access](/roles/#tool-access) explains the block. ## Related - [Roles and tool access](/roles/#tool-access): how a tool access block limits a step - [Make a trigger rule](/triggers/make-a-rule/): run the triage every morning - [Write a research report](/examples/research-report/): another workflow that changes no files --- # Research report > Research a question on the web and write a short report with sources In this example you turn a question into a short written report with sources. One step of the `research-report` workflow researches the question on the web and lists what it found. A second step writes the report from those findings alone. About five minutes to set up. ```dot title="research-report.dot" digraph research_report { graph [version="1", entry="research", description="Research a question on the web and write a short report with sources"] research [role="Researcher", tool_access="Research", prompt_file="prompts/research-report/research.md", outputs="findings:text"] write [role="Researcher", tool_access="Research", prompt_file="prompts/research-report/write.md"] done [shape="Msquare", outcome="success"] research -> write write -> done } ``` ![research passes its findings to write, then done ends the run.](/research-report.svg?v=f9f6c28b0d) ## Before you start You need a signed-in harness and the workflow: [download the archive](/research-report.zip). The workflow touches no code. Neither step can change a file or run a command: both have the Research [tool access](/roles/#tool-access), which allows reading files and browsing the web and nothing else. ## 1. Add the workflow 1. Unpack the archive into your workflow library, `~/.orbital/workflows/`. Keep every relative path. 2. Check it: ```sh orbital validate ~/.orbital/workflows/research-report.dot ``` Every project now offers `research-report`. The archive holds the workflow above and its two prompts: ```md title="prompts/research-report/research.md" Research this question: {{ inputs.goal }} Search the web and read at least three independent sources. Prefer primary sources, such as official documentation, standards, papers or the organisation's own pages, over summaries of them. Do not change, create or delete any file. Return `findings`: a list of facts. Give each fact its source's title and address. Mark each fact as agreed by several sources, stated by one source, or disputed. Note what you could not find out. ``` ```md title="prompts/research-report/write.md" Write a short report that answers this question: {{ inputs.goal }} Use only these findings: {{ context.findings }} Write in plain language, in short sentences. Put the answer first, in two or three sentences. Then give the details under short headings. Say where the sources disagree and what nobody could confirm. End with a numbered list of sources, each with its title and address. Do not change, create or delete any file. The report is the run's final answer, so end with it and nothing else. ``` ## 2. Make a place for it to work Every run belongs to a [project](/projects/), and a step starts in the project's primary folder. Any project works, because the workflow reads no project files. The project does not need to be a Git repository, unless you run it with Codex, which needs one. See [plain folders and Git](/projects/#plain-folders-and-git). If you have no project yet, make one: 1. Make a folder such as `~/research`. 2. In Orbital, open **Projects**, choose **New project** and name it `Research`. 3. Choose **Add folder** and pick `~/research`. It shows as a Plain folder. ## 3. Start the run 1. Choose **New task**, pick any project and choose the `research-report` workflow. 2. Write the question as the goal. For example: ```text What are the main differences between the EU AI Act's obligations for providers and for deployers of high-risk AI systems? ``` 3. Choose a harness in the harness picker, and choose **Start run**. ![The Start page asking what to do in a project, with a goal box, a harness picker, a workflow picker and a Chat or Workflow switch.](/screenshots/start-page.webp?v=0428f5d705) *The harness picker sits beside the workflow picker.* ## 4. Read the report The **research** step's findings appear in the **Context** tab as `findings`. Each fact comes with its source and whether sources agree. ![A run's Context tab with a table of context keys, their values and the step that set each one.](/screenshots/run-context.webp?v=3e79bd8607) *Each value shows the step that set it.* The report is the **write** step's reply: the last message in the **Session** tab, above "Run ended: success". It gives the answer first, then the details, then a numbered list of sources. :::caution Check the sources yourself before you rely on the report. The agent can misread a page, and the web changes. ::: ## Make it yours - Ask for a different shape of report by editing `write.md`, for example a one-page brief for a manager. - Add a review step with the Reviewer role that checks each fact in the report against its source. [Add branches](/workflows/add-branches/) shows how to send a rejected report back. - Start it every Monday with a Schedule [trigger](/triggers/). ## Related - [Pass context between steps](/workflows/pass-context/): how `findings` reaches the write step - [Add branches](/workflows/add-branches/): send a rejected report back for another try - [Triage issues](/examples/triage-issues/): another workflow that changes no files --- # One-shot implementation > The smallest workflow that delivers real work, from worktree to pull request In this example you run the smallest workflow that delivers real work. It takes an isolated checkout, implements once, reviews once, publishes a pull request and puts the checkout away. There is no repair loop and no waiting on the pull request. About five minutes to set up. ```dot title="one-shot.dot" digraph one_shot { graph [version="1", entry="checkout", inputs="work", description="Implement one change in an isolated checkout, review it once and publish it"] checkout [shape="folder", action="create"] implement [prompt="Implement the change described by {{ inputs.work }}. Follow the conventions already in the repository. Do not commit or push.", outputs="implementation_summary:text"] review [prompt="Review the change in the working folder. The Implementer reported: {{ context.implementation_summary }}. Choose pass when the change is complete and correct, or reject when it is not.", outputs="verdict:choice"] publish [prompt="Commit the reviewed change on its own branch, push it and open a pull request that explains why the change exists.", outputs="publication_summary:text"] cleanup [shape="folder", action="remove"] published [shape="Msquare", outcome="success"] rejected [shape="Msquare", outcome="failed", label="Checkout kept for inspection"] retained [shape="Msquare", outcome="failed", label="Checkout retained to preserve local work"] checkout -> implement implement -> review review -> publish [condition="verdict == 'pass'", weight="2"] review -> rejected [condition="verdict == 'reject'", weight="1"] publish -> cleanup cleanup -> published [condition="worktree.clean == true", weight="1"] cleanup -> retained } ``` ![checkout → implement → review. A passing review publishes and cleans up; a rejected review keeps the checkout.](/one-shot.svg?v=b5fcd92391) Use it when the change is small enough that a rejected review means you want to look at it yourself. ## Before you start You need: - a project whose primary folder is a Git repository with a GitHub remote you can push to, because the run works in a worktree and opens a pull request; - Git credentials, and a signed-in harness that can open a pull request; - the workflow: [download the archive](/one-shot.zip), or just [one-shot.dot](/examples/one-shot/one-shot.dot). ## 1. Add the workflow 1. Unpack the archive into your workflow library, `~/.orbital/workflows/`. Keep every relative path. 2. Check it: ```sh orbital validate ~/.orbital/workflows/one-shot.dot ``` ![The workflow editor showing a workflow as nodes joined by edges, with an Inspector panel on the right and a Problems bar.](/screenshots/workflow-editor.webp?v=85a9c6275a) *The editor draws the workflow's steps; the Problems bar lists what validation rejects.* ## 2. Read how it works `checkout` is a `folder` node with `action="create"`. It makes one isolated checkout per repository folder on a generated branch, so the run never touches your working copy. `cleanup` is the matching `action="remove"`. `review` declares `outputs="verdict:choice"`, and its two outgoing conditions tell Orbital that `pass` and `reject` are the only answers it will accept. A rejection ends the run at a failed terminal and skips cleanup on purpose. Removal would refuse anyway with uncommitted work in the checkout, and the point of a rejection is that you want to read the diff. Cleanup routes on `worktree.clean`. Removal measures every checkout first and removes none if any would lose dirty files or unpushed work. So the fallback ends at a failed terminal that names what happened, rather than pretending the run succeeded. ## 3. Start the run Choose **New task** and pick the project on the repository. Choose the `one-shot` workflow, set work to a description or ticket URL, and write the goal. ## What you get | Review | Result | | --- | --- | | Passes | A pull request is published, and the run ends Succeeded with the checkout removed. | | Rejects | The run ends Failed with the checkout intact for you to inspect. | | Passes, but removal is refused | The run ends Failed and keeps the checkout. | ## Related - [Implementer with a local step](/examples/workflows/local-step/): add a local check before the end - [Ticket to merge](/examples/workflows/ticket/): follow the pull request through to merge - [Worktrees and delivery](/projects/worktrees-and-delivery/): how `folder` nodes create and remove checkouts --- # Implementer with a local step > An Implementer checked by a command step, routing on what the machine observed In this example you put a shell command between the Implementer and the end of the run, so the run routes on what the machine observed rather than on what the agent said. An agent that reports success is not evidence of success. About five minutes to set up. ```dot title="local-step.dot" digraph local_step { graph [version="1", entry="implement", inputs="work", description="Implement a change and prove it with a local command before finishing", max_visits="40"] implement [prompt="Implement the change described by {{ inputs.work }}. Change the files the work needs. Do not commit.", outputs="implementation_summary:text"] observe [shape="parallelogram", repeat_safety="idempotent", script="if git status --porcelain | grep -q .; then echo '{\"change.present\":true,\"change.summary\":\"the working folder has uncommitted changes\"}'; else echo '{\"change.present\":false,\"change.summary\":\"the working folder is unchanged\"}'; fi", facts="change.present:bool,change.summary:text", timeout="2m"] repair [prompt="A local command observed that {{ context.change.summary }}. The reported work was: {{ context.implementation_summary }}. Change the files the work actually needs.", outputs="repair_summary:text"] observed [shape="Msquare", outcome="success"] unchanged [shape="Msquare", outcome="failed", label="No change after five repairs"] broken [shape="Msquare", outcome="failed", label="The local command itself failed"] implement -> observe observe -> broken [condition="outcome == 'failed'", weight="4"] observe -> repair [condition="change.present == false", weight="3", repair_budget="local_check", repair_round="retry"] observe -> unchanged [condition="change.present == false", weight="2", repair_budget="local_check", repair_round="exhausted"] observe -> observed [condition="change.present == true", weight="1", repair_budget="local_check", repair_round="reset"] observe -> broken repair -> observe } ``` ![implement → observe. The command routes an unchanged folder into repair and a changed one into a successful terminal.](/local-step.svg?v=b6fe3585b1) ## Before you start You need: - a signed-in harness; - a project whose primary folder is a Git repository. It needs no remote. It must be a Git repository because the check asks `git status` whether the folder changed; - the workflow: [download the archive](/local-step.zip), or just [local-step.dot](/examples/local-step/local-step.dot). :::caution The workflow makes no worktree, so the agent changes files in the project's folder itself. Use a practice repository. ::: ## 1. Add the workflow 1. Unpack the archive into your workflow library, `~/.orbital/workflows/`. Keep every relative path. 2. Check it: ```sh orbital validate ~/.orbital/workflows/local-step.dot ``` ![The workflow editor showing a workflow as nodes joined by edges, with an Inspector panel on the right and a Problems bar.](/screenshots/workflow-editor.webp?v=85a9c6275a) *The editor draws the workflow's steps; the Problems bar lists what validation rejects.* ## 2. Read how it works `observe` is a `parallelogram` with a `script`. The script runs once through `bash -c` in the primary working folder, inherits the server environment, and prints one JSON object as its final stdout line. `facts="change.present:bool,change.summary:text"` declares exactly the keys that object contains, and Orbital rejects a final line that does not match. The check here asks git whether the working folder has uncommitted changes. Replace it with your project's own check, such as its test or lint command, and declare whatever facts that command can report. Keep the script read-only when it only inspects something, and give it a `timeout` shorter than the default ten minutes when the command is quick. Four edges leave `observe`, and each covers a different outcome: | Edge | What it does | | --- | --- | | `outcome == 'failed'` | Catches the script itself failing on a timeout, a non-zero exit or a malformed final line. | | `change.present == false` | Starts a repair round, bounded by `repair_budget="local_check"` and its `exhausted` partner. | | `change.present == true` | Succeeds and resets the budget. | | The bare fallback | Required, because a command node cannot infer its own coverage the way a choice-producing agent can. | ## 3. Make a practice repository Run `git init ~/orbital-practice` and add that folder as a project. ## 4. Start the run Choose **New task** and pick the project. Choose the `local-step` workflow, set work to what you want changed, and write the goal. ## What you get An Implementer that changes a file reaches the successful terminal on the first observation. An Implementer that changes nothing is sent to repair and observed again, up to five rounds, before the run ends Failed. A script that cannot run at all ends the run Failed at a separate terminal, so you can tell the two causes apart in the transcript. ## Related - [Context](/reference/context/): how to declare the facts a command reports - [Error handling](/reference/error-handling/): how repair budgets bound a loop - [One-shot implementation](/examples/workflows/one-shot-implementation/): the smallest workflow that delivers a pull request --- # Outer loop over an epic > An outer loop that delivers an epic's tickets through a sub workflow In this example you work through all of an epic's sub tickets in one run. The outer loop selects the next unfinished sub ticket, hands it to an imported delivery workflow, and comes back to select again. It stops when the select step reports that nothing remains. About five minutes to set up. The run can take a long time, one delivery per sub ticket. ```dot title="epic.dot" digraph epic { graph [version="1", entry="select", inputs="work", description="Work through an epic one sub ticket at a time", max_visits="200"] select [prompt="Read the epic at {{ inputs.work }} and every sub ticket under it. Choose the next unfinished sub ticket and report it as the current work. Choose ticket while one remains, or none once every sub ticket is finished.", outputs="current_work:work,remaining:choice"] ticket [import="subgraphs/ticket.dot"] finished [shape="Msquare", outcome="success"] stalled [shape="Msquare", outcome="failed", label="A sub ticket could not be delivered"] select -> ticket [condition="remaining == 'ticket'", weight="2"] select -> finished [condition="remaining == 'none'", weight="1"] ticket -> select [exit="delivered", loop_restart="true"] ticket -> stalled [exit="rejected"] } ``` ![select routes a remaining ticket into the imported sub workflow and returns; no remaining ticket ends the run, and a sub ticket the reviewer will not pass ends it failed.](/epic.svg?v=31ecfb9da4) ## Before you start You need: - a signed-in harness that can read your tracker; - a project whose primary folder holds the code the sub tickets change. The example makes no worktree, so the agent changes files in that folder itself. A plain folder works, except with Codex. See [plain folders and Git](/projects/#plain-folders-and-git); - the workflow: [download the archive](/epic.zip). It holds `epic.dot` and `subgraphs/ticket.dot`. ## 1. Add the workflow 1. Unpack the archive into your workflow library, `~/.orbital/workflows/`. Keep every relative path, so `subgraphs/ticket.dot` sits beside `epic.dot`. 2. Check it: ```sh orbital validate ~/.orbital/workflows/epic.dot ``` ![The workflow editor showing a workflow with an imported sub workflow drawn as one node.](/screenshots/workflow-editor-imports.webp?v=3f00eb3b61) *The imported sub workflow shows as one node in its parent.* ## 2. Read how it works `select` declares two outputs. `remaining:choice` decides the route, and Orbital infers `ticket` and `none` from the two outgoing conditions. `current_work:work` matters more. The `work` kind accepts a validated work scope rather than free text. A select step that returns a vague description, or a ticket URL that is its own parent, fails validation instead of sending the Implementer somewhere unhelpful. Any prompt downstream renders that scope with `{{ current_work }}`. `ticket [import="subgraphs/ticket.dot"]` is an import placeholder. Orbital flattens the imported workflow into the parent before validation, and its two terminals become the exits the parent's edges bind: `delivered` returns to the select step, and `rejected` ends the run. Every exit has to be bound exactly once. The return edge carries `loop_restart="true"`. That clears the previous sub ticket's context, the history of its steps and its agent sessions. It keeps the original inputs, the run identity, visit counts and repair budget state. Without it, the tenth sub ticket would carry nine sub tickets of history into every prompt. Bounding the inner review loop needs a repair budget rather than a visit cap. `max_visits` counts every visit to a node for the life of the run, and a restarted loop keeps those counts. So a cap of four on `implement` would bound the whole epic at four sub tickets rather than four attempts at one of them. The sub workflow puts `repair_budget="sub_ticket"` on all three edges out of `review` instead: `retry` sends the work back, `exhausted` gives up after five rounds, and `reset` on the passing edge clears the budget so the next sub ticket starts fresh. The workflow's `max_visits` remains the outer guard on total agent turns. ```dot title="subgraphs/ticket.dot" digraph ticket { graph [version="1", entry="implement", description="Implement and locally review one sub ticket"] implement [prompt="Implement the current work described below. Satisfy only its requirements. {{ current_work }}", outputs="implementation_summary:text"] review [prompt="Review the implementation against the current work described below. Choose pass when it satisfies the work, or reject when it does not. {{ current_work }}", outputs="verdict:choice"] delivered [shape="Msquare", outcome="success"] rejected [shape="Msquare", outcome="failed", label="Five reviews rejected the same sub ticket"] implement -> review review -> delivered [condition="verdict == 'pass'", weight="3", repair_budget="sub_ticket", repair_round="reset"] review -> implement [condition="verdict == 'reject'", weight="2", repair_budget="sub_ticket", repair_round="retry"] review -> rejected review -> rejected [condition="verdict == 'reject'", weight="1", repair_budget="sub_ticket", repair_round="exhausted"] } ``` [Download epic.dot](/examples/epic/epic.dot) or [ticket.dot](/examples/epic/subgraphs/ticket.dot) on its own. Replace the sub workflow with your own delivery workflow. The [ticket workflow](/examples/workflows/ticket/) is the realistic one: it publishes a pull request, watches its state and merges. ## 3. Start the run Choose **New task** and pick the project that holds the code the sub tickets change. Choose the `epic` workflow, set work to the epic's URL and write the goal. :::tip Watch the first two iterations before you leave the run alone. ::: ## What you get Each iteration selects one sub ticket, implements and reviews it, then returns to the select step with a cleared context. A select step that reports none ends the run Succeeded. A reviewer that rejects the same sub ticket five times spends its repair budget, and the run ends Failed at the failed terminal, naming the sub ticket that stalled. ## Related - [Reuse a workflow](/workflows/reuse-a-workflow/): how a sub workflow's terminals become exits - [Add a loop](/workflows/add-a-loop/): the other ways to bound a loop - [Ticket to merge](/examples/workflows/ticket/): a realistic delivery workflow to import instead - [Deliver an epic](/examples/deliver-an-epic/): the shipped epic workflow --- # Ticket to merge > A complete workflow that takes a ticket to a merged pull request, like the shipped delivery sub workflows In this example you run the complete delivery workflow. It implements a ticket, publishes a pull request, watches it, repairs what CI and reviewers find, and merges. It imports two sub workflows and ships twelve prompt files. About ten minutes to set up, including reading the prompts. The run then works until the pull request merges or closes. ```dot title="ticket.dot" digraph implement_ticket { graph [version="1", entry="create_worktree", inputs="work", description="Deliver one ticket through reviewed implementation and a merged PR"] create_worktree [shape="folder", action="create"] implementation [import="subgraphs/implementation.dot"] delivery [import="subgraphs/pr-delivery.dot"] complete_ticket [prompt_file="prompts/complete-ticket.md", outputs="completion_summary:text"] cleanup_merged [shape="folder", action="remove"] cleanup_closed [shape="folder", action="remove"] complete [shape="Msquare", outcome="success"] closed [shape="Msquare", outcome="failed", label="PR closed without merging"] worktree_retained [shape="Msquare", outcome="success", label="Delivered; worktree retained for the unpushed commits worktree.status names"] cleanup_pending [shape="Msquare", outcome="failed", label="Worktree retained to preserve local work"] create_worktree -> implementation implementation -> delivery [exit="approved"] delivery -> complete_ticket [exit="merged"] delivery -> cleanup_closed [exit="closed"] complete_ticket -> cleanup_merged cleanup_merged -> complete [condition="worktree.clean == true", weight="1"] cleanup_merged -> worktree_retained cleanup_closed -> closed [condition="worktree.clean == true", weight="1"] cleanup_closed -> cleanup_pending } ``` ![create_worktree → implementation → delivery → complete_ticket → cleanup. Delivery loops through PR observations and repairs until merged or closed. Cleanup retains unsafe worktrees.](/ticket.svg?v=1780408f93) ## Before you start You need: - a project whose primary folder is the Git repository the ticket changes, with a GitHub remote you can push to, because the run works in a worktree and opens a pull request. An empty practice repository is not enough for this workflow; - a GitHub token and a signed-in harness. The agent needs access to the repository and the tracker, and permission to publish and repair the pull request; - the workflow: [download the archive](/ticket.zip). Read all prompts before running. They are listed under [complete source](#complete-source). :::caution Like the shipped workflows, the publish step sets `auto_merge="squash"`, so Orbital turns on auto-merge for the pull request. Where your branch rules require no review, the pull request merges as soon as its required checks pass. Delete that attribute from `subgraphs/pr-delivery.dot` to merge by hand. See [turn auto-merge off](/projects/worktrees-and-delivery/#turn-auto-merge-off). ::: ## 1. Add the workflow 1. Unpack the archive into your workflow library, `~/.orbital/workflows/`. Keep every relative path. 2. Check it: ```sh orbital validate ~/.orbital/workflows/ticket.dot ``` ![The workflow editor showing a pull-request workflow whose conditioned edges loop back to earlier steps.](/screenshots/workflow-editor-loop.webp?v=4dacb0ba6a) *Conditioned edges loop back until the pull request merges or closes.* ## 2. Start the run Choose **New task**, pick the project and choose the `ticket` workflow. Set work to the ticket URL and write the delivery goal. ## What you get Implementation plans, implements and reviews until approved. Delivery publishes the pull request, reads its state, handles CI, conflicts and reviews, and waits at a human gate while the pull request waits for review or merge. The waiting run shows a clock and "Waiting: The pull request is waiting for review or merge", takes no run slot, and carries on by itself when the pull request changes. An approved pull request that is not merged yet returns to the gate. | The pull request | Result | | --- | --- | | Merges | The run completes the ticket, cleans up and ends Succeeded. | | Merges, but the worktree still holds commits the remote never saw | The run ends Succeeded and keeps the worktree. `worktree.status` names those commits. | | Closes without merging | The run cleans up and ends Failed, labelled "PR closed without merging". | | Closes, and cleanup would lose work | The run ends Failed and keeps the worktree. | ## The review loop has no repair budget `subgraphs/implementation.dot` loops `local_review → plan_review → implement_review → local_review` until the review passes. It has no `repair_budget`, because it mirrors the shipped `implementation` sub workflow, which has none. The workflow's `max_visits` bounds the loop instead. It defaults to 200 agent visits for the whole run, and when they are spent the run ends Failed. For a tighter bound, put a `repair_budget` pair on the edges out of `local_review`: 1. Give the reject edge `repair_budget="review"` and `repair_round="retry"`. 2. Add an edge to a failed terminal with the same condition and budget, `repair_round="exhausted"` and a different weight. 3. Give the pass edge `repair_round="reset"` with the same budget. After five rejected rounds the run then takes the exhausted edge. [Error handling](/reference/error-handling/#bounded-repair) describes the rules, and [outer loop over an epic](/examples/workflows/epic/) uses such a budget. ## Complete source Every DOT file below is a complete workflow, and the archive holds the same files. The Markdown files are prompts the workflows name, not separate workflows. `ticket.dot` is at the top of this page. [Download ticket.dot](/examples/ticket/ticket.dot) on its own. ### subgraphs/implementation.dot ```dot title="subgraphs/implementation.dot" digraph implementation { graph [version="1", entry="plan", inputs="work", description="Plan, implement and locally review one work scope"] plan [prompt_file="../prompts/implementation/plan.md", outputs="implementation_plan:text"] implement [prompt_file="../prompts/implementation/implement.md", outputs="implementation_summary:text"] local_review [prompt_file="../prompts/implementation/review.md", outputs="review_verdict:choice, review_findings:json"] plan_review [prompt_file="../prompts/implementation/plan-review.md", outputs="implementation_plan:text"] implement_review [prompt_file="../prompts/implementation/implement-review.md", outputs="implementation_summary:text"] approved [shape="Msquare", outcome="success"] plan -> implement implement -> local_review local_review -> plan_review [condition="review_verdict == 'reject'", weight="2"] local_review -> approved [condition="review_verdict == 'pass'", weight="1"] plan_review -> implement_review implement_review -> local_review } ``` [Download implementation.dot](/examples/ticket/subgraphs/implementation.dot) ### subgraphs/pr-delivery.dot ```dot title="subgraphs/pr-delivery.dot" digraph pr_delivery { graph [version="1", entry="publish", inputs="work", description="Publish a change, then wait on its review and publish each repair until the PR is merged or closed"] publish [prompt_file="../prompts/pr-delivery/publish.md", outputs="publication_summary:text", auto_merge="squash"] probe_pr [shape="parallelogram", repeat_safety="idempotent", probes="pr,threads"] address_reviews [prompt_file="../prompts/pr-delivery/address-reviews.md", outputs="repair_summary:text, repair_result:choice"] fix_ci [prompt_file="../prompts/pr-delivery/fix-ci.md", outputs="repair_summary:text, repair_result:choice"] resolve_conflicts [prompt_file="../prompts/pr-delivery/resolve-conflicts.md", outputs="repair_summary:text, repair_result:choice"] review_repairs [prompt_file="../prompts/pr-delivery/review-repairs.md", outputs="review_verdict:choice, review_findings:json"] revise_repairs [prompt_file="../prompts/pr-delivery/revise-repairs.md", outputs="repair_summary:text"] await_review [shape="hexagon", label="The pull request is waiting for review or merge"] merged [shape="Msquare", outcome="success"] closed [shape="Msquare", outcome="failed"] publish -> probe_pr probe_pr -> closed [condition="outcome == 'failed'", weight="7"] probe_pr -> merged [condition="pr.state == 'MERGED'", weight="6"] probe_pr -> closed [condition="pr.state == 'CLOSED'", weight="5"] probe_pr -> resolve_conflicts [condition="pr.state == 'CONFLICTS'", weight="4"] probe_pr -> fix_ci [condition="pr.state == 'CI_FAILED'", weight="3"] probe_pr -> address_reviews [condition="pr.state == 'CHANGES_REQUIRED'", weight="2"] probe_pr -> await_review await_review -> probe_pr address_reviews -> review_repairs [condition="repair_result == 'changed'", weight="2"] address_reviews -> publish [condition="repair_result == 'unchanged'", weight="1"] fix_ci -> review_repairs [condition="repair_result == 'changed'", weight="2"] fix_ci -> publish [condition="repair_result == 'unchanged'", weight="1"] resolve_conflicts -> review_repairs [condition="repair_result == 'changed'", weight="2"] resolve_conflicts -> publish [condition="repair_result == 'unchanged'", weight="1"] review_repairs -> revise_repairs [condition="review_verdict == 'reject'", weight="2"] review_repairs -> publish [condition="review_verdict == 'pass'", weight="1"] revise_repairs -> publish } ``` [Download pr-delivery.dot](/examples/ticket/subgraphs/pr-delivery.dot) ### prompts/complete-ticket.md ```markdown title="prompts/complete-ticket.md" Discover and use relevant installed skills for completing tickets and communicating delivery results. Ticket: {{ context.work }} Publication summary: {{ context.publication_summary }} Confirm from the remote that the PR delivering this work has merged. Verify that the merged change satisfies the ticket and goal. Identify any ticket-related changes that remain unpublished before marking the work complete. Mark the supplied ticket done using its tracker’s workflow. If the ticket is already complete, verify the recorded delivery evidence and avoid duplicate updates. Leave parent, sibling and unrelated tickets unchanged. This step may update the supplied ticket's completion record. Leave repository files, commits, branches and pull requests unchanged. Return `completion_summary` with the ticket’s confirmed status, merged PR link and delivery evidence. A follow-up step cleans up the worktree. ``` [Download complete-ticket.md](/examples/ticket/prompts/complete-ticket.md) ### prompts/implementation/implement-review.md ```markdown title="prompts/implementation/implement-review.md" Discover and use relevant installed skills for implementation and verification. Work scope: {{ context.work }} Revised implementation plan: {{ context.implementation_plan }} Previous implementation summary: {{ context.implementation_summary }} Review findings: {{ context.review_findings | dump }} Inspect the current changes and applicable repository instructions. Apply the revised plan and address every blocking finding. Preserve correct work and remove changes the plan identifies as outside scope. Verify each correction and run the relevant checks for the complete change. Close all remaining defects, unmet requirements or verification gaps in the work scope and the plan. Address all required changes from the plan and review. Create or update local commits using the repository’s conventions. This step may change repository files, run checks and create local commits. Later steps own local review and publication. The caller owns any tracker updates. Return an updated `implementation_summary` that replaces the previous one. Describe the change as it stands now, then how this round corrected and verified each finding above, then anything still unresolved, stated once. Leave out earlier rounds, their findings and any dispute already settled: later steps read only this summary and the code, so history in it only makes every later prompt longer. ``` [Download implement-review.md](/examples/ticket/prompts/implementation/implement-review.md) ### prompts/implementation/implement.md ```markdown title="prompts/implementation/implement.md" Discover and use relevant installed skills for implementation and verification. Work scope: {{ context.work }} Implementation plan: {{ context.implementation_plan }} Implement the planned change in the prepared worktree. If the work scope names an open pull request, check out its branch first and build on it, so publication updates that pull request instead of opening another. Follow applicable repository instructions and preserve unrelated work. Satisfy every requirement in the work scope and goal. If the code reveals an error in the plan, make the necessary adjustment within that scope and explain it. Run the relevant checks. Record what each check establishes and any failures or verification gaps. Create or update local commits using the repository’s conventions. This step may change repository files, run checks and create local commits. Later steps own publication. The caller owns any tracker updates. Return `implementation_summary` with the changes, verification evidence, deviations from the plan and any unmet requirements. ``` [Download implement.md](/examples/ticket/prompts/implementation/implement.md) ### prompts/implementation/plan-review.md ```markdown title="prompts/implementation/plan-review.md" Discover and use relevant installed skills for planning and communicating the plan. Work scope: {{ context.work }} Previous implementation plan: {{ context.implementation_plan }} Implementation summary: {{ context.implementation_summary }} Review findings: {{ context.review_findings | dump }} Read the work scope, applicable repository instructions and current changes. Investigate each blocking finding against the code and available evidence. Revise the implementation plan to address every blocking finding. Specify each correction and how it will be verified. Preserve work that already satisfies the requirements and identify any changes that must be removed or revised. Keep the revised plan complete enough for implementation. Cover the work scope and goal without adding unrelated work. State assumptions and material risks. Return the revised plan as `implementation_plan`. Leave repository files and tracker records unchanged. Later steps will implement and publish the changes. The caller owns any tracker updates. ``` [Download plan-review.md](/examples/ticket/prompts/implementation/plan-review.md) ### prompts/implementation/plan.md ```markdown title="prompts/implementation/plan.md" Discover and use relevant installed skills for planning and communicating the plan. Work scope: {{ context.work }} Read the supplied work scope, relevant linked context and applicable repository instructions. Inspect the affected code and tests. For ticket-based work, read the identified ticket. Plan the changes needed to satisfy the work scope. Use the goal to guide decisions without expanding that scope. Choose the smallest implementation that meets every requirement. Explain the required behaviour, affected components, implementation order and how each acceptance criterion will be verified. Ground decisions in the inspected code. Return the plan as `implementation_plan`. Leave repository files and tracker records unchanged. Later steps will implement and publish the changes. ``` [Download plan.md](/examples/ticket/prompts/implementation/plan.md) ### prompts/implementation/review.md ```markdown title="prompts/implementation/review.md" Discover and use relevant installed skills for code review and communicating findings. Work scope: {{ context.work }} Implementation plan: {{ context.implementation_plan }} Implementation summary: {{ context.implementation_summary }} Review the current change against the work scope, goal, plan and applicable repository instructions. Inspect the complete branch diff against its target, including uncommitted changes, affected code and relevant tests. Review only requirements and defects that can be verified through automated checks or code inspection. For each rejection, identify the requirement, concrete evidence, required correction and machine-checkable completion condition. Use the work scope, plan and summary as context. Verify their claims against the code and available evidence. Reject unmet requirements, correctness or security defects, violations of mandatory repository rules, missing verification of required behaviour and changes made which are outside the work scope. Treat preferences as non-blocking unless repository instructions require them. Human reviews, manual acceptance, visual approval and user sign-off are non-blocking. Report them as outstanding without requiring their completion to pass. Compare each finding with previous reviews and attempted corrections. If the same finding returns without new evidence or a materially different actionable correction, recognise that the review is looping. Do not reject again for that finding. Report the unresolved concern and explain why another repair pass would not advance it. For each blocking finding, identify the requirement or defect, supporting evidence and the correction needed. This step is read-only. Leave files, commits, branches, pull requests and tracker records unchanged. Return `review_verdict` as `reject` only when actionable, machine-checkable blocking findings remain. Otherwise return `pass`. Keep outstanding human or manual checks and concerns excluded. Return `review_findings` as an array containing those findings, or an empty array when the review passes. ``` [Download review.md](/examples/ticket/prompts/implementation/review.md) ### prompts/pr-delivery/address-reviews.md ```markdown title="prompts/pr-delivery/address-reviews.md" Discover and use relevant installed skills for addressing pull request feedback, verification and communicating with reviewers. Work scope: {{ context.work }} Publication summary: {{ context.publication_summary }} The review threads still open, one repository at a time: {% for name, folder in run.folders %}{% if context[name].threads.open %}### {{ name }} {{ context[name].threads.open }} {% endif %}{% endfor %} Inspect the current PR, its published head, review decisions and unresolved threads. Read the affected code and applicable repository instructions. Address feedback within the work scope and goal. Make the required local changes, verify them and create or update local commits using the repository’s conventions. Preserve unrelated work. For feedback already satisfied by the published change, reply with supporting evidence and resolve the thread. For feedback requiring a new local change, keep the thread unresolved until that correction is published and verified. Check existing replies before posting to avoid duplicates. Explain with evidence when a requested change is unnecessary or outside scope. If the PR has merged or closed, report that state without modifying it. This step may change repository files, run checks, create local commits, reply to reviews and resolve addressed threads. Later steps own local review and publication. The caller owns any tracker updates. Return `repair_summary` with the feedback addressed, changes, verification evidence, published corrections confirmed, and threads awaiting publication or further resolution. Return `repair_result` as `changed` when this step changed the local branch or its files, otherwise `unchanged`. A changed repair is reviewed against its reason before it is published. An unchanged one goes straight to publication. ``` [Download address-reviews.md](/examples/ticket/prompts/pr-delivery/address-reviews.md) ### prompts/pr-delivery/fix-ci.md ```markdown title="prompts/pr-delivery/fix-ci.md" Discover and use relevant installed skills for diagnosing CI failures, implementing corrections and verification. Work scope: {{ context.work }} Publication summary: {{ context.publication_summary }} Get the PR's required CI checks green. Inspect the current PR, published head and failing checks. Read their logs, affected code and applicable repository instructions. Confirm the failures belong to the current published change. Identify the cause of each failure. Use focused local checks where possible and compare against the target branch when needed to distinguish defects in this change from existing failures or external problems. Correct failures within the work scope and goal. Preserve unrelated work. Rerun a failed remote check when the evidence supports a transient failure; do not rerun checks repeatedly without investigating. Verify the corrections and create or update local commits using the repository’s conventions. If the PR has merged or closed, report that state without modifying it. This step may change repository files, run checks, rerun remote CI and create local commits. Later steps own local review and publication. The caller owns any tracker updates. Return `repair_summary` with the failed checks, diagnosis, corrections, verification results and anything still unresolved. Return `repair_result` as `changed` when this step changed the local branch or its files, otherwise `unchanged`. A changed repair is reviewed against its reason before it is published. An unchanged one goes straight to publication. ``` [Download fix-ci.md](/examples/ticket/prompts/pr-delivery/fix-ci.md) ### prompts/pr-delivery/publish.md ```markdown title="prompts/pr-delivery/publish.md" Discover and use relevant installed skills for publishing pull requests and communicating the change. Work scope: {{ context.work }} Inspect the prepared worktree, current branch and any associated pull request. Push the intended local commits. Update the existing open PR, or create one against the repository’s intended target branch. Describe the complete change, its relationship to the work scope and the verification performed. When the commit messages record review decisions, state each decision and its reason in the description. Keep an existing `Open items` section, and remove an item only when the published change resolves it. Use ticket references only when the work scope identifies an existing ticket. When it does, write `Closes #N` in the description if the change completes that ticket, and `Part of #N` if it only advances it. If the associated PR is already merged and the branch contains follow up changes, open a new PR. Confirm the PR URL from the remote. If publication fails, inspect the current remote state before retrying. Preserve local work and avoid creating duplicate PRs. This step owns publication. Leave implementation files and tracker records unchanged. Later steps handle PR feedback, CI and conflicts. The caller owns any tracker updates. Return `publication_summary` with the PR URL, confirmed remote state and published commits. ``` [Download publish.md](/examples/ticket/prompts/pr-delivery/publish.md) ### prompts/pr-delivery/resolve-conflicts.md ```markdown title="prompts/pr-delivery/resolve-conflicts.md" Discover and use relevant installed skills for resolving merge conflicts and verifying the resulting change. Work scope: {{ context.work }} Publication summary: {{ context.publication_summary }} Make the PR mergeable by resolving its conflicts with the target branch. Inspect the current PR, its target branch, published head and prepared worktree. Read applicable repository instructions. If the PR has merged or closed, report that state without modifying it. Fetch the latest target branch and integrate it using the repository’s conventions. Prefer rebase. Resolve conflicts by understanding both changes. Preserve the required behaviour of the work scope, relevant target-branch changes and unrelated work. Verify that the local branch integrates cleanly with the fetched target. Run the relevant checks and create or update local commits using the repository’s conventions. If the conflict has already disappeared, report the evidence without introducing unnecessary changes. This step may fetch remote changes, merge or rebase locally, resolve conflicts, run checks and create local commits. Later steps own local review and publication. The caller owns any tracker updates. Return `repair_summary` with the target revision, conflicts resolved, resulting changes, verification evidence and anything still unresolved. Return `repair_result` as `changed` when this step changed the local branch or its files, otherwise `unchanged`. A changed repair is reviewed against its reason before it is published. An unchanged one goes straight to publication. ``` [Download resolve-conflicts.md](/examples/ticket/prompts/pr-delivery/resolve-conflicts.md) ### prompts/pr-delivery/review-repairs.md ```markdown title="prompts/pr-delivery/review-repairs.md" Discover and use relevant installed skills for code review and communicating findings. Publication summary: {{ context.publication_summary }} Repair summary: {{ context.repair_summary }} The published head each repair started from, and the reason for the repair, one repository at a time: {% for name, folder in run.folders %}### {{ name }} Started from: {{ context[name].pr.head }} Reason: {{ context[name].pr.state }} {% if context[name].pr.state == "CHANGES_REQUIRED" %} The review threads the repair answers: {{ context[name].threads.open }} {% endif %} {% endfor %} A `CI_FAILED` repair answers the failing checks on the pull request. A `CONFLICTS` repair answers the merge conflict with the target branch. A `CHANGES_REQUIRED` repair answers the review threads above. A repository whose reason is `WAITING` needed no repair. Review only what the repair step changed. Inspect the diff from the head it started from to the current local head, including uncommitted changes. When the repair rebased onto the target branch, compare the change before and after the rebase, for example with `git range-diff`, and review only the resolution. Read the failing checks, the conflict or the review threads to judge the repair against its reason. The complete change passed review before it was published. Do not review it again against the ticket, and do not search for defects the repair did not introduce. Verify the corrections and verification claims in the repair summary against the code and available evidence. Reject a repair only when it is wrong, incomplete for its reason, breaks a mandatory repository rule, or changes things outside its reason. Treat preferences as non-blocking unless repository instructions require them. This step is read-only. Leave files, commits, branches, pull requests and tracker records unchanged. Return `review_verdict` as `reject` when blocking findings remain, otherwise `pass`. Return `review_findings` as an array of blocking findings, each with `requirement`, `evidence` and `correction`. A pass returns an empty array. A pass publishes the repair. A reject goes to one revision step, which fixes what it can, records the rest as open items in the pull request description and publishes without another review. ``` [Download review-repairs.md](/examples/ticket/prompts/pr-delivery/review-repairs.md) ### prompts/pr-delivery/revise-repairs.md ```markdown title="prompts/pr-delivery/revise-repairs.md" Discover and use relevant installed skills for implementation and verification. Work scope: {{ context.work }} Publication summary: {{ context.publication_summary }} Previous repair summary: {{ context.repair_summary }} Review findings: {{ context.review_findings | dump }} Inspect the current changes and applicable repository instructions. Address each blocking finding you can fix within this step's permissions. Preserve correct work and remove changes identified as outside the repair's reason. Verify each correction and run the relevant checks for the complete change. Create or update local commits using the repository’s conventions. Some findings cannot be fixed here: they need a tracker change, a decision from a person, or a change this step may not make. Record each one, and each finding you judged wrong, as an open item in the pull request description. Keep the rest of the description, add the items under an `Open items` heading, and state for each the finding and why it was not fixed here. Do not repeat an item already listed. This step may change repository files, run checks, create local commits and edit the pull request description. Leave the PR's commits, reviews and tracker records otherwise unchanged. Return an updated `repair_summary` that replaces the previous one. Describe the repair as it stands now, then how this step corrected and verified each finding above, then the open items it recorded, stated once. Leave out earlier rounds and any dispute already settled: later steps read only this summary and the code, so history in it only makes every later prompt longer. The next step publishes these changes without another review and continues monitoring the PR. ``` [Download revise-repairs.md](/examples/ticket/prompts/pr-delivery/revise-repairs.md) ## Related This is the workflow the shorter examples build up to. - [One-shot implementation](/examples/workflows/one-shot-implementation/): compare it to see what waiting on a pull request adds - [Outer loop over an epic](/examples/workflows/epic/): run this kind of delivery once per sub ticket - [Add gates and waits](/workflows/gates-and-waits/): how the run waits on the pull request - [Worktrees and delivery](/projects/worktrees-and-delivery/): how the pull request is published and merged --- # Attributes > Every attribute a workflow, node or edge takes, with its values and defaults Look up any attribute you can write on a workflow, a node or an edge, with its values and default. ```dot title="review.dot" digraph review { graph [version="1", entry="review"] review [tool_access="Read only", prompt="Review the goal.", outputs="verdict:choice"] done [shape="Msquare"] rejected [shape="Msquare", outcome="failed"] review -> done [condition="verdict == 'pass'", weight="2"] review -> rejected [condition="verdict == 'reject'", weight="1"] } ``` Workflow attributes go in `graph [...]`, node attributes after a node ID, and edge attributes after an edge. Unknown attributes are errors everywhere. Node IDs are unique, and imported IDs take a dotted prefix when the workflow loads. ## Workflow attributes ### `version` The version of the workflow format. ```dot graph [version=""] ``` | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `version` | string | Yes | None | Use `1` for a new workflow. | Every complete workflow declares at least `version` and `entry`. ```dot title="minimal.dot" graph [version="1", entry="hello"] ``` Related: [Write your first workflow](/workflows/write-a-workflow/). ### `entry` The first step of a run. ```dot graph [entry=""] ``` | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `entry` | node ID | Yes | None | Names a node or an import placeholder. | A run starts at the node `entry` names. ```dot title="plan.dot" graph [version="1", entry="plan"] plan [prompt="Write a plan for the goal."] ``` Related: [Write your first workflow](/workflows/write-a-workflow/). ### `description` A description shown with the workflow. ```dot graph [description=""] ``` | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `description` | string | No | None | Shown with the workflow. | ```dot title="syntax.dot" graph [version="1", entry="plan", description="Attribute reference wrapper"] ``` Related: [Workflows](/workflows/). ### `inputs` The inputs a run takes besides its goal. ```dot graph [inputs=",?"] ``` | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `inputs` | comma-separated names | No | None | A trailing `?` makes an input optional. Names are identifiers. | `inputs="work,notes?"` requires `work` and makes `notes` optional. Names cannot use engine or probe namespaces, and `goal` is built in, so do not declare it again. Starting a run needs a goal and every required input, and refuses inputs the workflow does not declare. An absent optional input renders empty. Orbital copies the inputs into context when the run starts. `{{ context.work }}` can then change while `{{ inputs.work }}` keeps the original, and a loop restart restores the original inputs. ```dot title="deliver.dot" graph [version="1", entry="plan", inputs="ticket_url,notes?"] ``` Related: [Pass context between steps](/workflows/pass-context/), [Context](/reference/context/). ### `defaults` Literal values for the goal and declared inputs. ```dot graph [defaults="='',=''"] ``` | Name | Type | Required | Default | Description | | --- | --- | --- | --- | --- | | `defaults` | comma-separated assignments | No | None | Each assignment is `name='value'`. Double a single quote inside a value. | Defaults may name `goal` and declared inputs only. A value given when the run starts replaces the default. ```dot title="review.dot" graph [version="1", entry="review", inputs="notes?", defaults="goal='Review this project',notes='Owner''s notes'"] ``` Related: [Context](/reference/context/). ### `input_labels` The label the new-run form shows for a declared input. ```dot graph [input_labels="='