# Stop Rules

Course: [Loop Engineering](https://www.devtutorials.ai/learn/loop-engineering) · Section: First Loop
Source: https://www.devtutorials.ai/learn/loop-engineering/first-loop/stop-rules

> Goal conditions, iteration caps and no-progress detection. Watch a runaway loop burn its budget on a flaky test, then write the three rules that make every loop end.

## What Stop Rules Do

A stop rule decides, after every turn, if the loop goes again or stops (see **From Prompting to Loops**).

In this lesson, you will write the three stop rules every loop needs.

A loop that never stops is not a loop. It is a bill.

Each rule catches a different problem:

- **A goal a program can check** ends the run when the job is done.
- **An iteration cap** ends the run when anything else goes wrong.
- **A same-failure rule** spots one ticket that is stuck.

**Simple lesson:** a loop is only safe to leave alone when it knows how to end.

### A Real-World Example

Think about the lock screen on a phone.

You type your PIN. If it is right, the phone opens. A machine checked it, so there is no doubt.

If you are wrong, you may try again. But after a few tries, the phone makes you wait.

And if someone types the same wrong PIN again and again, they are not getting closer.

Now map this to a loop:

- **The phone opens** = the goal condition.
- **Only a few tries** = the iteration cap.
- **The same wrong PIN again** = no progress.

Click the button. You saw a runaway in **From Prompting to Loops**. This time, look at what it costs.

- **The checker:** it never calls #6 done. Good.
- **The loop:** it never stops trying #6. Bad.
- **The scorecard:** most of the tokens went on one ticket that could never pass.

**Simple lesson:** a checker stops a false "done". Only a stop rule stops the loop.

## What You Will Learn

In this lesson, you will learn:

1. Why a goal must be something a program can check.
2. How to pick an iteration cap.
3. How to spot "no progress" with a failure line.
4. When to stop the loop and when to escalate a ticket.
5. How to read a run and tell which rule needs fixing.

## Rule 1: A Goal a Program Can Check

### Goal Condition

**Goal condition** = a check that tells the loop the job is done. A program must be able to answer it with yes or no.

Goals a program can check:

- `pnpm test` passes for every ticket
- `src/lib/export.ts` exists and its test passes

Goals that are a feeling:

- make it work
- make the board feel faster

### What a Feeling Does to a Loop

Click the button. Every ticket gets fixed. Then the loop keeps going, because nothing can say "it works now".

Here is the same idea, step by step:

- **Frame 1:** the work is done, but every turn after that finds nothing to do.
- **Frame 2:** a cap ends the run. But a cap is not a goal. You still cannot tell if the job is done.

### Goals in Each Tool

Real tools have goal loops too.

| Tool | Goal loop |
|---|---|
| Claude Code | `/goal <condition>`. A small, fast model checks the condition after each turn. It has no turn cap of its own, so add one: "or stop after 20 turns". |
| Codex | `/goal <objective>`, with `edit`, `pause`, `resume` and `clear`. |
| Cursor | `/goal` in the Cursor CLI, still rolling out. Until you have it: a cloud agent with a checklist and a checker subagent. |

**Simple lesson:** if a program cannot check your goal, your loop cannot finish.

## Rule 2: An Iteration Cap

### What a Cap Is

**Iteration cap** = the most turns one run may take. In the loop file it is `max_iterations`.

When the loop reaches the cap, it stops. No matter what.

The cap is your backup. A good run should end on its goal, long before the cap.

### Picking a Cap

Too low, and good work is cut off. Too high, and a runaway costs a lot before it stops.

A simple way to pick one:

1. Give each ticket one turn.
2. Give each ticket one more turn, for a broken first fix.
3. Add the `same_failure` turns, for one stuck ticket.

Example:

- 6 tickets: 6 turns
- one retry each: 6 more turns
- `same_failure: 3`: 3 more turns
- cap: **15**

In short: **cap = 2 × tickets + same_failure.**

**A cap alone does not fix a flaky test.**

- Without rule 3, the stuck ticket uses up every spare turn.
- The loop hits the cap and leaves other tickets undone.
- The cap stops the loop. It does not make it smart.

**Simple lesson:** set the cap from the work: about twice the tickets, plus room for one stuck ticket.

## Rule 3: No-Progress Detection

### Failure Lines

A loop makes **no progress** when it tries again and gets the same result. A flaky test is the classic case.

To spot it, the loop compares failure lines. A failure line is the short message that says what failed:

```text
FAIL src/lib/sort.test.ts > sorts by due date (timeout after 5000 ms)
```

**same_failure** = how many times in a row one ticket may fail with the same line. With `same_failure: 3`, the third same failure means "stop trying this ticket".

### Stop, or Escalate?

When no progress is found, the loop has two choices:

- **Stop** the whole loop. Safe, but the other tickets wait.
- **Escalate** the stuck ticket. Send it to a person with its evidence, then move on.

**Escalate** = hand a ticket to a person instead of trying again. In the loop file, `escalate: triage-inbox` names where it goes.

Watch both choices on the same work:

Step through all three frames.

- **Frame 1:** no escalate target. One stuck ticket stops everything.
- **Frame 2:** #6 goes to the inbox. The rest are fixed, and the goal is met.
- **Frame 3:** what the person finds: why it was sent, and the failure line.

**Simple lesson:** escalate a stuck ticket, so one problem does not block the whole run.

## All Three Rules Together

### Same Work, Different Rules

Each rule catches a different problem. Watch the same four tickets with three different stop settings:

- **Frame 1:** all three rules. The goal ends the run. The cap is never needed.
- **Frame 2:** no `same_failure`. #6 eats the turns, and the cap ends the run with work undone.
- **Frame 3:** a cap of 4. Good work is cut off.

### In the Loop File

Put together, the three rules look like this:

```yaml
trigger:
  goal: "pnpm test passes for every ticket in this run"
stop:
  max_iterations: 15
  same_failure: 3
escalate: triage-inbox
```

**Read the Stop rules column in the Timeline.**

- After every turn, it shows each rule's check, such as `goal: not yet · iterations 4/15`.
- When a rule fires, the column says which one.

**Simple lesson:** the goal ends a good run. The cap ends a bad one. `same_failure` stops one stuck ticket from ruining both.

## Write the Stop Rules

### Lab Exercise: A Goal, a Cap and a Same-Failure Rule

This loop has a goal that is a feeling, and no stop rule.

Click the button to open the loop file in the lab.

1. Replace `make it work` with a goal a program can check. Use your test command.
2. Add a `stop:` block with `max_iterations:` and `same_failure:`.
3. Press **Check my loop**.

Your loop passes when, on all 3 versions:

- the run ends because the goal is met
- flaky ticket #6 lands in the inbox

**Why does an escalated ticket count for the goal?**

- The loop did its part. It tried, saw no progress, and handed #6 to a person.
- So #6 is handled for this run, even though it is not fixed.

**Simple lesson:** a safe loop has all three rules, and an inbox for what it cannot fix.

## TL;DR

1. **Stop rules** = the checks after every turn: go again, or stop.
2. **Goal condition** = a yes-or-no check a program can run, such as `pnpm test` passing.
3. **Iteration cap** = `max_iterations`, the most turns one run may take.
4. **Picking a cap** = 2 × tickets + `same_failure`.
5. **No progress** = the same failure line again and again.
6. **same_failure** = how many same failures in a row before the loop gives up on a ticket.
7. **Escalate** = send the stuck ticket to a person with its evidence, then move on.
8. **Reading a run** = find the rule that fired, then fix the rule that was missing.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| A goal that is a feeling | "Make it work" is clear enough. | Name a command or a file a program can check. | The loop never knows it is done. |
| No cap | "It will stop when it is done." | Always add `max_iterations`. | A stuck loop runs all night. |
| A cap that is too small | "A small cap saves money." | Cap = 2 × tickets + `same_failure`. | Good work is cut off. |
| A cap with no `same_failure` | "The cap will handle flaky tests." | Spot the same failure and escalate. | One ticket eats every spare turn. |
| Stopping instead of escalating | "Stop everything if one ticket is stuck." | Escalate the stuck ticket and keep going. | The rest of the work waits for no reason. |

## Check Your Understanding

## Exercises

Press **Check** when you are ready. Use **Show Hint** if you get stuck.

## What to Remember

- Every loop needs three stop rules: a goal, a cap, and a same-failure rule.
- A goal must be something a program can check. "Make it work" never ends.
- Set the cap from the work: 2 × tickets + `same_failure`.
- `same_failure: 3` spots a stuck ticket, such as a flaky test.
- Escalate a stuck ticket to a person, and keep going with the rest.

## Further Reading

- [Loop Engineering and Autonomous Agent Systems, by Rick Hightower](https://rickhigh.substack.com/p/loop-engineering-and-autonomous-agent): "no stop condition means a runaway".
- [Loop Engineering, by Addy Osmani](https://addyosmani.com/blog/loop-engineering): `/goal` and how its condition is checked.
- [Loop engineering: designing loops you can walk away from, by CodeRabbit](https://coderabbit.ai/blog/loop-engineering): designing loops that end safely.

Next: **Triggers**. A goal loop runs once and misses tomorrow's failures, so you turn it into a daily loop.
