agents

The goal loop

State a goal, and Empryo runs coder and reviewer rounds until a fresh judge returns PASS: escalation ladder, stuck detection, stand-in, and CI exit codes.

Copy & share

Loading sections…

Connect MCP or install the Empryo skill

Section exports contain only that heading’s content. Markdown and text links fetch the selected content directly, without the rest of the page.

A round: the coder works, typecheck and tests run for free, a fresh reviewer returns the verdict. A stalled loop escalates a different variable each round.
A round: the coder works, typecheck and tests run for free, a fresh reviewer returns the verdict. A stalled loop escalates a different variable each round.

/review judges work you already did. The goal loop judges work it is still doing, and keeps going until the judge agrees.

You state the goal once. The coder works it. A reviewer with clean context checks the repo against that goal, returns PASS, FAIL or PARTIAL, and the loop either stops or feeds the reason back for another round.

/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        # alias
/goal clear                                    # drop the loop, keep this turn

Attach images to a goal the same way you attach them to a prompt: they ride along to the coder.

A round

StepWhat runsCost
1The coder turn — a real turn in your lane, with your toolsModel
2Typecheck, then the test suiteNo tokens
3project run when goalLoop.runtimeProbe is onNo tokens
4The reviewer reads the repo against the goal and the verdict historyModel

Steps 2 and 3 run before any model sees the round. A typecheck or test failure is an automatic FAIL, so the loop never spends a reviewer call to learn the build is broken.

The reviewer gets fresh context every round, its own model slot, and its own prompt-cache lane. It judges the current state of the repo, not the diff narrative, and it re-checks that nothing it passed earlier has regressed.

When it stalls, it changes something

Feeding the same failure to the same model is how loops burn tokens. Each round changes one variable instead:

RoundWhat changes
1–2Nothing. The reviewer's reason goes back to the coder as the next prompt
3The reviewer escalates — a weak sensor reads like a stubborn bug
4The coder escalates
5Re-plan. The coder is told to stop patching and re-derive its approach
CapStuck. The loop stops and reports why

Escalation resolves against your task router and fallback chains, so the ladder spends your configured models rather than picking new ones.

When it stops

  • PASS — the reviewer verified the goal. The loop closes with its summary.
  • Oscillation — the same failure reason twice in a row. Two identical verdicts mean the coder is not moving, and a third round would not move it either.
  • Round cap — 5 by default, --max=N per run, goalLoop.maxIterations as your default.
  • Token capgoalLoop.tokenCap, counted across coder and reviewer rounds.
  • Ctrl+X — kills the loop in any phase, mid-code or mid-review.

Only a stuck loop asks you a question. It is never a routine gate between rounds.

Steering a running loop

Type while it runs. Your line folds into the next round's coder prompt instead of interrupting the turn, and prior verdicts stay in the prompt so the coder can see what it already tried. /goal clear removes the loop without killing the turn in flight.

The stand-in

A loop that blocks on ask_user at 3am is a loop that achieved nothing by morning. When you are away, the stand-in answers in your place from a fresh context, logs the decision with its rationale, and falls back to the safe path when it is not confident.

goalLoop.standInBehavior
offNever decides. The normal blocking prompts run
conservativeMultiple-choice questions only (default)
fullAlso decides web-search and fetch-page approvals

Override a stand-in decision later and the correction is remembered, so it converges toward your judgment across sessions.

Headless runs treat you as away by definition: there is no keypress to detect, so the dial alone gates it.

Where you watch it

SurfaceWhat you see
DesktopThe goal strip above the composer: one pip per round tinted by its verdict, elapsed time, tokens spent, and a cancel button
TUIThe goal banner and per-round verdict rows in the transcript
HeadlessRound progress on stderr, verdicts in the --events stream

An unbounded loop shows an ∞ marker instead of a row of empty pips.

In CI

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 — oscillation, round cap, or token cap
2Timeout (--timeout)
130Aborted

Configuration

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

Every round is a real coder turn plus a real reviewer run, priced into the tab's totals like any other work (cost tracking). A loop that runs its cap is five judges and four coder turns, so state a goal specific enough to converge on: "rejects expired tokens, proven by a test" converges, "improve auth" does not.

Goal loop or review-until-pass

You haveUse
A goal, and no code yet/goal
Code that exists, and no goal but "hold up under a fresh reading"`/review pass`

/goal escalates models when it stalls; /review pass keeps judging the same way until the findings are gone.

Next

Documentation