Shipfox
Understand

Feedback loops

Understand how a gate and a restart form a loop, why the check must be independent, and how to bound retries and protect writes.

A feedback loop repeats part of a job after a check rejects the result. One step does the open-ended work. A later step measures it. The job returns to the earlier step only when the check fails.

A gate is the decision point, not the loop itself. A gate can accept or reject a result without repeating anything. The restart rule creates the loop.

Gate outcomes

The check answers one question: is this result good enough to continue? The restart answers another: which earlier work gets another attempt?

Because the two are separate, a workflow can:

  • Fail the job without retrying unsafe work.
  • Restart only the part that can improve the result.
  • Give the repeated step evidence from the failed check.

This also makes the run readable. The check result explains why the job continued, failed, or created another step attempt.

Use an independent check, not the agent's opinion

An agent that judges its own work repeats the same judgment. A test, a type check, a linter, a policy script, or an independent review measures something outside that judgment.

The check doesn't need to prove every property. It must prove the property that decides whether the next action is safe. A unit test can guard behavior before a commit. A policy check can guard a deploy. A read-back can confirm an external write.

This complete workflow shows the loop. The project needs an npm test command and configured agent defaults.

.shipfox/workflows/fix-tests.yml
# yaml-language-server: $schema=https://www.shipfox.io/docs/workflow.schema.json
name: Fix failing tests
runner: shipfox

triggers:
  manual:
    source: manual

jobs:
  fix:
    steps:
      - key: fix
        prompt: >
          Fix the failing tests in this repository.
          Previous check: ${{ step.is_retry
            ? step.restart.feedback
            : "No previous check. Run the tests and inspect the failures." }}

      - run: npm test
        gate:
          success: step.exit_code == 0
          on_failure:
            restart_from: fix
            feedback: The tests still fail. Reproduce the failure and fix its cause.

The first attempt gets initial guidance. A failed test restarts fix in the same job checkout, so the agent can revise its files. A passing test lets the job continue.

The Gate fields reference defines the exact fields.

Give the next attempt useful feedback

A retry without new evidence can repeat the same mistake. Useful feedback names the failed property and points the repeated step to evidence it can inspect.

Static guidance is enough when the step can run the check again and read its output. A checking step can also publish a small result for the feedback message. Keep large logs in the run instead of copying them into every prompt.

Feedback isn't a new event. It belongs to the restart decision, and only the repeated attempt can read it. Context and templating explains when each kind of data becomes available.

Loop bounds

Every attempt costs runner time and, for an agent, model cost. Slow checks multiply that cost. A loop therefore needs a clear stopping rule and a limited number of attempts.

The loop stops in one of two states:

  • The check passes.
  • The attempt limit runs out and the job fails.

Don't weaken the check to make the loop finish. A failure after the limit is evidence that the task needs another approach or a person.

Limits lists the exact attempt limits. The run history keeps each step attempt and its feedback.

Keep external writes out of the loop

A restart repeats every step from the restart point through the gate. A deploy, a notification, a comment, a payment, or a repository push inside that range can happen more than once.

Prefer this order:

  1. Make local or reversible changes.
  2. Run the check.
  3. Perform the external write only after the gate passes.

When a write must happen inside the repeated range, make it safe to repeat. Use a stable identity, and update or read back the existing result instead of creating a new one on every attempt.

A loop can repeat a bad side effect as easily as it can improve code. Don't use it to retry a deploy or an external write that has no check in front of it.

Use Build a feedback loop for the authoring task.

Was this page helpful?
Edit this page on GitHub

On this page