Skip to main content
A gate block on a step lets you define a success condition and a retry strategy. When the condition fails, Shipfox restarts execution from an earlier keyed step in the same job — creating a loop that continues until the condition is met or the job times out. Pair it with an agent step and the loop does real work: the agent edits code, a shell step verifies it, and the gate loops back — no scripting.

Gate schema

A gate block sits directly on a step and supports two fields:
gate:
  success_if: exit_code == 0   # CEL expression evaluated against the step outcome
  on_failure:
    restart_from: install      # must reference an earlier step that has a `key:` field
success_if and on_failure are both optional individually, but at least one must be present. A gate with only success_if marks the step as failed without retrying. A gate with only on_failure always retries. Use both together for a conditional retry loop.

Basic retry loop

The canonical pattern installs dependencies, runs an agent to fix failing tests, then verifies:
jobs:
  fix:
    steps:
      - key: install
        run: npm ci
      - model: claude-opus-4-8
        prompt: Fix the failing tests in the repository.
      - run: npm test
        gate:
          success_if: exit_code == 0
          on_failure:
            restart_from: install
On each iteration, the agent reads the updated codebase and attempts another fix. The loop continues until npm test exits 0. The loop is bounded automatically: a gating step gets 3 attempts by default before its restart budget is exhausted and the job stops. The job-level execution_timeout adds a second, outer bound — see Loop bounds.

success_if expressions

The success_if field accepts a CEL (Common Expression Language) expression that is evaluated after the step completes. The only variable available today is exit_code, which holds the integer exit code of the step’s shell command. Common patterns:
ExpressionMeaning
exit_code == 0Step succeeded (standard Unix convention)
exit_code != 1Any exit code except a hard failure
More CEL variables (stdout content, step duration, etc.) are planned for future releases.

Rules for restart_from

The step referenced by restart_from must appear before the gate step in the same job. Forward references are not allowed.
  • The target step must have a key: field — restart_from matches keys, not display names. (name: is a separate, display-only label.)
  • The reference is matched by step key, not by position — reordering steps changes which step is targeted.
  • restart_from must reference a step within the same job. Cross-job retries are not supported; use needs to express job dependencies instead.

Job execution timeout

A gating step already stops after a bounded number of restarts (3 by default). On top of that, execution_timeout sets an outer wall-clock bound on the whole job — useful when steps are slow and even a few attempts could run long:
jobs:
  fix:
    execution_timeout: "30m"
    steps:
      - key: install
        run: npm ci
      - model: claude-opus-4-8
        prompt: Fix the failing tests in the repository.
      - run: npm test
        gate:
          success_if: exit_code == 0
          on_failure:
            restart_from: install
The value is a duration string. When the timeout is reached, the job is cancelled and marked as failed regardless of where execution is in the loop.

Next steps

  • Jobs & Steps — understand job isolation, needs, and parallel execution.
  • Agent Steps — configure the model, thinking level, and provider for agent steps.