Skip to content

The goal loop

State a goal and Empryo keeps working in rounds, with a fresh reviewer checking each one, until the goal passes, the loop gets stuck, or it hits a limit.

How it works5 minutes to read
Copy & share
On this page10 sections
One round: the agent works, typecheck and tests run, then a fresh reviewer gives a verdict. When progress stalls, each round changes one thing.
One round: the agent works, typecheck and tests run, then a fresh reviewer gives a verdict. When progress stalls, each round changes one thing.

With /goal, you describe the result you want once, and Empryo keeps working until an independent reviewer agrees it's done. You don't have to check each attempt and say "try again".

Each round, the agent (the coder) works on the goal. Then a reviewer, a separate agent that starts with a clean context, checks the repository against your goal. It answers PASS, FAIL or PARTIAL. On anything but PASS, its reason becomes the coder's next instruction.

/goal the auth middleware must reject expired tokens, with a test that proves it
/goal fix the flaky upload test --max=8
/loop migrate the config loader to zod        # same as /goal
/goal clear                                    # stop the loop, keep the current turn

You can attach images to a goal the same way you attach them to a normal prompt. The coder receives them.

What happens in a roundLink to this section

StepWhat runsUses model tokens?
1The coder works on the goal, with your usual model and toolsYes
2Typecheck, then your test suiteNo
3project run to build and run the app, if goalLoop.runtimeProbe is onNo
4The reviewer checks the repository against the goal and earlier verdictsYes

Steps 2 and 3 run first. If the typecheck or tests fail, the round fails right away, and no reviewer is needed to tell you the build is broken.

The reviewer starts fresh every round and can use its own model. It judges the current state of the code, not the coder's description of it, and re-checks that anything it passed earlier still works.

When progress stallsLink to this section

Sending the same failure back to the same model rarely helps. So after a couple of tries, each round changes one thing:

RoundWhat changes
1, 2Nothing. The reviewer's reason goes back to the coder
3The reviewer switches to a stronger model, in case it is the one getting it wrong
4The coder switches to a stronger model
5The coder is told to stop patching and rethink its approach
LimitThe loop stops and reports why it's stuck

The stronger models come from your task router and fallback chains. The loop only uses models you've configured.

When it stopsLink to this section

  • PASS. The reviewer confirmed the goal. The loop ends with a summary.
  • Repeated failure. The same failure reason twice in a row means the coder isn't making progress, so the loop stops.
  • Round limit. 5 by default. Change it per run with --max=N, or set goalLoop.maxIterations.
  • Token limit. goalLoop.tokenCap, counted across coder and reviewer.
  • You stop it. Ctrl+X stops the loop at any point, whether the coder or the reviewer is running.

The loop only asks you a question when it's stuck. It doesn't pause for approval between rounds.

Redirecting a running loopLink to this section

Type while it runs. Your message is added to the next round's instructions instead of interrupting the current one. Earlier verdicts stay in the instructions, so the coder can see what it already tried. /goal clear ends the loop without stopping the turn that's running.

When you're away: the stand-inLink to this section

A loop that stops overnight to ask a question gets nothing done by morning. When you're away, a stand-in agent can answer the coder's questions for you. It decides from a fresh context, logs its choice and reasoning, and picks the safe option when it isn't confident.

goalLoop.standInWhat it does
offNever answers. Questions wait for you as usual
conservativeAnswers multiple-choice questions only (default)
fullAlso approves web searches and page fetches

If you later override one of its decisions, the correction is saved to memory, so it learns your preferences over time.

In headless runs you count as away, because there's no one at the keyboard. The standIn setting alone decides.

Where to watch itLink to this section

AppWhat you see
DesktopA goal bar above the message box: one dot per round colored by its verdict, elapsed time, tokens used, and a cancel button
Terminal UIA goal banner and a verdict line for each round in the conversation
HeadlessRound progress on stderr, verdicts in the --events stream

A loop with no round limit shows ∞ instead of a row of empty dots.

In CILink to this section

bash
empryo --headless --loop "the migration must be reversible and covered by a test"
empryo --headless --loop "fix the failing suite" --max-iterations 8 --json
Exit codeMeaning
0PASS
1Stuck, repeated failure, round limit or token limit
2Timed out (--timeout)
130Aborted

SettingsLink to this section

json
{
  "goalLoop": {
    "maxIterations": 5,
    "tokenCap": 2000000,
    "standIn": "conservative",
    "runtimeProbe": false
  }
}

Each round is a real coder turn plus a real review, and both count toward the tab's costs. A loop that uses all 5 rounds runs five reviews and several coder turns. Make the goal specific enough to be checked: "rejects expired tokens, proven by a test" works well, "improve auth" does not.

/goal or /review pass?Link to this section

You haveUse
A goal, and the code isn't written yet/goal
Code that already exists, and you want it to hold up to a fresh review`/review pass`

/goal switches to stronger models when it stalls. /review pass keeps reviewing the same way until the reviewer finds nothing left.

NextLink to this section