> ## Documentation Index
> Fetch the complete documentation index at: https://www.shipfox.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Gates: Conditional Success and Automatic Restart

> A gate decides whether a run step succeeded and can restart the job from an earlier step when it fails — building bounded retry loops with no external scripting.

A **gate** turns a step's exit code into a decision: did this step succeed, and
if not, what should happen? Gates let you build **bounded retry loops** — run a
step, check a condition, and restart from an earlier step until it passes — without
any external scripting or orchestration. This is how Shipfox turns an agent into an
iterating worker: the agent edits, a `run` step judges the result, and the gate
sends the agent back until the check passes.

## The gate schema

A gate is declared on a **run step** under the `gate` key:

```yaml theme={null}
steps:
  - run: npm test
    key: test
  - run: ./flaky-integration.sh
    gate:
      success_if: "exit_code == 0"
      on_failure:
        restart_from: test
        output: "Integration check failed; restarting from tests"
```

<ParamField path="gate.success_if" type="string">
  A CEL expression that decides success. The only variable available is
  `exit_code` — the step's process exit code. Examples: `exit_code == 0`,
  `exit_code < 5`. Required unless `on_failure` is present.
</ParamField>

<ParamField path="gate.on_failure.restart_from" type="string">
  The `key` of an earlier step in the same job to restart from when the gate
  fails (set `key:` on the target step). Execution resumes at that step.
</ParamField>

<ParamField path="gate.on_failure.output" type="string">
  An optional message recorded with the restart decision, shown in the run
  timeline.
</ParamField>

<Note>
  Gates are valid on **both run steps and agent steps** — `success_if` always
  evaluates the step's `exit_code`. The most reliable loop still puts the gate on a
  `run` step that objectively verifies the agent's work (tests, a linter), with
  `restart_from` pointing back at the agent.
</Note>

## How a gate is evaluated

After a gated step finishes, Shipfox evaluates the gate and reaches one of three
outcomes:

| Outcome         | When                                       | Result                                                                    |
| --------------- | ------------------------------------------ | ------------------------------------------------------------------------- |
| **Passed**      | `success_if` evaluates true                | The step succeeds; the job continues                                      |
| **Failed**      | `success_if` is false                      | Restart from `restart_from` if the budget allows; otherwise the job fails |
| **Uncheckable** | No exit code, or the CEL expression errors | The job fails immediately — never restarts                                |

## Loop bounds

Gates create loops, so Shipfox bounds them for you — there is no way to author an
unbounded retry.

* **Per-step restart cap.** A gating step is allowed **3 attempts by default** (one
  initial run plus two restarts). When the cap is exhausted and the gate still
  fails, the step is marked failed and the **whole job stops**.
* **Job execution timeout.** Independently, the job-level
  [`execution_timeout`](/concepts/jobs-steps) bounds total wall-clock time (default
  6 hours). Whichever bound is reached first ends the loop.

Design loops to converge within a few attempts; the cap is a safety net, not a
budget to spend.

## Related pages

<CardGroup cols={2}>
  <Card title="Gate and retry guide" icon="rotate">
    A worked example that retries a step until it passes.
  </Card>

  <Card title="Jobs & Steps" icon="layer-group" href="/concepts/jobs-steps">
    Step kinds, job isolation, and `execution_timeout`.
  </Card>
</CardGroup>
