# Bounded Autonomous Run

`forge run` executes an agent loop repeatedly and **stops it the moment an
enforced gate opens**, escalating to a human instead of retrying forever.

```sh
forge run --cmd 'claude -p "work the next task in tasks.md"'          # dry run
forge run --cmd '…' --max-iterations 3 --yes                          # execute
```

## Two things to understand before using it

::: danger Forge has no model
Forge cannot write code. It has no LLM, no inference, no agent of its own. The
loop **orchestrates an executor you supply** via `--cmd` — an agent CLI
invocation — and enforces the harness gates between its iterations. Forge is
the harness here, not the agent.
:::

::: warning A deliberate exception to the doctrine
Everywhere else, forge is a [configurator, not a step-firer](/harness/overview):
it prepares gates and evidence, and the agent host drives the loop. `forge run`
is the one command that drives the loop itself. That exception is opt-in and
bounded on purpose — read the safety posture below before using it.
:::

## Safety posture

| Property | Behavior |
|---|---|
| Default | **Dry run.** Without `--yes` it prints the plan and executes nothing |
| Iteration cap | `--max-iterations`, default 5, **hard ceiling 50** |
| Already-open gate | Refuses to start — **zero** executor invocations |
| Gate opens mid-run | Stops immediately, names the gate, exits non-zero |
| Gate check *errors* | Stops. An unreadable gate is never assumed closed |
| Iterations exhausted | Stops and **escalates** — finishing the cap is not success |
| Executor fails | Stops and escalates |

Only one outcome is a clean exit: the executor signalling it is done. Every
other ending prints `escalate: a human should review before continuing` and
returns a non-zero exit code.

## The plan output

```
$ forge run --change add-billing --cmd 'my-agent --once' --max-iterations 3
Plan: up to 3 iteration(s) on change "add-billing"
  executor: sh -c "my-agent --once"
  gates:    verify circuit-breaker, spend budget, secscan
  stop:     any gate opening halts the loop and escalates to a human

dry run: nothing executed (pass --yes to run)
```

## The gates

Each is checked once before the first iteration and again after every one. All
three are **read-only here** — the loop records no gate state of its own.

| Gate | Opens when |
|---|---|
| [verify circuit-breaker](/harness/circuit-breaker) | the change's consecutive-failure streak has tripped |
| [spend budget](/harness/budget) | a `--max-tokens` / `--max-tool-calls` ceiling is crossed |
| [secscan](/harness/secscan) | the change has unwaived high-severity findings |

The budget gate short-circuits closed when no ceiling is configured, so it can
never open by accident.

```
$ forge run --change add-billing --cmd '…' --yes
run: STOPPED after 0 iteration(s) — gate-open
  gate:   verify circuit-breaker
  detail: circuit breaker open after 3 consecutive verify failures
escalate: a human should review before continuing
```

Zero iterations. Starting an autonomous loop on top of an already-failing
change is exactly the situation the breaker exists to prevent.

## What it records

Every iteration appends an event to the change's
[session timeline](/harness/timeline), so a completed run is auditable after the
fact: how many iterations ran, what each did, and which gate ended it.

## Cost note

The secscan gate walks the working tree on the pre-flight check and after each
iteration, so an N-iteration run pays N+1 scans. On a large repository, prefer a
low `--max-iterations`.

## See also

- [Enforcement Doctrine](/harness/overview) — why forge configures rather than fires
- [Verify circuit-breaker](/harness/circuit-breaker) — the primary stop condition
- [Isolated Verification](/harness/isolation) — running the loop's checks off your real tree
