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.
Copy & share
On this page10 sections
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 turnYou 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
| Step | What runs | Uses model tokens? |
|---|---|---|
| 1 | The coder works on the goal, with your usual model and tools | Yes |
| 2 | Typecheck, then your test suite | No |
| 3 | project run to build and run the app, if goalLoop.runtimeProbe is on | No |
| 4 | The reviewer checks the repository against the goal and earlier verdicts | Yes |
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:
| Round | What changes |
|---|---|
| 1, 2 | Nothing. The reviewer's reason goes back to the coder |
| 3 | The reviewer switches to a stronger model, in case it is the one getting it wrong |
| 4 | The coder switches to a stronger model |
| 5 | The coder is told to stop patching and rethink its approach |
| Limit | The 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 setgoalLoop.maxIterations. - Token limit.
goalLoop.tokenCap, counted across coder and reviewer. - You stop it.
Ctrl+Xstops 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.standIn | What it does |
|---|---|
off | Never answers. Questions wait for you as usual |
conservative | Answers multiple-choice questions only (default) |
full | Also 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
| App | What you see |
|---|---|
| Desktop | A goal bar above the message box: one dot per round colored by its verdict, elapsed time, tokens used, and a cancel button |
| Terminal UI | A goal banner and a verdict line for each round in the conversation |
| Headless | Round 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
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 code | Meaning |
|---|---|
0 | PASS |
1 | Stuck, repeated failure, round limit or token limit |
2 | Timed out (--timeout) |
130 | Aborted |
SettingsLink to this section
{
"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 have | Use |
|---|---|
| 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.