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.
review -> repair [condition="verdict == 'reject'", weight="3", repair_budget="answer", repair_round="retry"]repair -> reviewWriting a loop is easy. The work is in stopping it.
How a loop stops
Section titled “How a loop stops”Orbital gives you two ways to stop a loop, and two ways to make a long loop cheaper.
Repair rounds
Section titled “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 entry gives the full rules.
In the editor, each edge that loops back shows its condition and weight.

Visit caps
Section titled “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.
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 covers the wait step itself.

Loop restart
Section titled “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.
Failure is not a loop
Section titled “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.
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 explains how to tell them apart in the app, and Error handling gives the exact rules.
Before you start
Section titled “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.
1. Download the example
Section titled “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 and unpack it into ~/.orbital/workflows/. Keep every relative path.
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.
2. Validate it
Section titled “2. Validate it”orbital validate ~/.orbital/workflows/bounded-retries.dot3. Run it
Section titled “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
Section titled “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
Section titled “Related”- Add branches: how edges choose the next step.
- Add gates and waits: pause a loop for time or for a person.
- Pass context between steps: what a loop restart clears from context.
- Error handling: how failed steps and repair budgets behave.