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.
forge run --cmd 'claude -p "work the next task in tasks.md"' # dry run
forge run --cmd '…' --max-iterations 3 --yes # executeTwo things to understand before using it
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.
A deliberate exception to the doctrine
Everywhere else, forge is a configurator, not a step-firer: 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 | the change's consecutive-failure streak has tripped |
| spend budget | a --max-tokens / --max-tool-calls ceiling is crossed |
| 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 continuingZero 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, 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 — why forge configures rather than fires
- Verify circuit-breaker — the primary stop condition
- Isolated Verification — running the loop's checks off your real tree