# Feedback loops (https://www.shipfox.io/docs/understand/feedback-loops)

Description: 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 [#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 [#use-an-independent-check-not-the-agents-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.

```yaml title=".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](https://www.shipfox.io/docs/reference/workflow-schema#gate-fields) defines the exact fields.

## Give the next attempt useful feedback [#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](https://www.shipfox.io/docs/understand/data-and-templating) explains when each kind of data
becomes available.

## Loop bounds [#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](https://www.shipfox.io/docs/reference/limits) lists the exact attempt limits. The [run
history](https://www.shipfox.io/docs/understand/workflows-and-runs) keeps each step attempt and its
feedback.

## Keep external writes out of the loop [#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](https://www.shipfox.io/docs/how-to/author-workflows/build-feedback-loop) for the authoring task.