Chapter 31

Chapter 31 — Loops and goals (`/loop`, `/goal`)

Two commands for work that needs more than one turn.

/loop is a dumb repeater: it re-sends the same message on a timer until you stop it. /goal is an objective the engine tracks — with budgets, an audit discipline, and hard limits that stop a runaway.

They are most useful together: a goal supplies the what and the stopping condition, a loop supplies the keep going. But each works alone, and /goal --auto removes the need for the loop entirely.

/loop — repeat something on a timer

❯ /loop 30s check whether the build finished and summarise any new errors
loop started (every 30s): check whether the build finished and summarise any new errors

The first token is an interval (30s, 5m, 2h, 1d); everything after it is the message that gets re-sent. Omit the interval and the whole line becomes the body, fired every 5 minutes:

❯ /loop /goal continue
loop started (self-paced (5min default)): /goal continue

Managing it:

Command What it does
/loop <interval> <body> Start. Aliases: none — the interval must come first to be read as one
/loop or /loop status (or list) Show the active loop’s body
/loop stop (or cancel / kill / off) Stop it

One loop at a time. Starting a second while one runs prints loop already running — /loop stop first rather than stacking them.

The body is sent exactly as typed, so it can be a prompt, a slash command, or anything else you would type yourself. The loop does not wait for the previous turn to finish before the next interval elapses — it is a timer, not a queue.

/goal — an objective the engine tracks

❯ /goal start "migrate every test file to the new fixture" --budget-tokens 400000

A goal carries an objective, optional budgets, a running count of tokens and iterations, and a status. It persists in the session, so it survives /load.

/goal continue is the interesting one. It doesn’t just re-prompt — it builds an audit prompt from the current state that asks the model to:

  • restate the objective as concrete deliverables,
  • build a checklist from the prompt to the artifacts,
  • inspect real evidence — files, test output, command results,
  • not accept proxy signals as completion,
  • treat uncertainty as not achieved.

That last pair is the whole point. A model asked “are you done?” tends to say yes. A model asked “check the artifacts and treat uncertainty as not achieved” goes and looks.

The commands

Command What it does
/goal start <objective> [flags] (or set / new) Begin. Quote the objective to include words that start with --
/goal or /goal status One-line state
/goal show (or info) Full state: budgets, tokens used, iterations, last audit
/goal continue (or next) Fire one audit iteration
/goal complete [reason] (or done) Mark it done yourself
/goal abandon [reason] (or stop / cancel) Give up on it

Flags on /goal start

Flag Effect
--budget-tokens N (or --tokens) Soft ceiling on tokens. At 1.0× the model is nudged to wrap up; at 1.5× the engine stops it (below)
--budget-time T (or --time) Same, for wall-clock — 30m, 2h
--auto (or --auto-continue) Keep iterating without a /loop wrapper (below)
--require <path> A file that must exist before the goal may be marked complete. Repeatable (below)

Running a goal to completion

Two ways.

With a loop — the explicit version:

❯ /goal start "get the integration suite green" --budget-time 2h
❯ /loop 2m /goal continue

Every two minutes, one audit iteration. When the goal reaches a terminal status the loop stops itself:

loop auto-stopped (goal complete)

With --auto — no loop at all:

❯ /goal start "get the integration suite green" --budget-time 2h --auto

After each turn that made tool calls, the engine queues the next /goal continue immediately — no waiting for an interval. This is usually what you want: it is faster, and it can’t double-fire.

Don’t combine them. --auto deliberately stands down while a /loop is active, precisely so the two don’t both queue an iteration.

The safety rails

This is the part worth reading before you leave a goal running unattended. There are four independent stops, and they exist because “the model decides when it’s finished” is not a safe design on its own.

1. Soft budget, then hard limit

Crossing your token or time budget at 1.0× swaps in a wrap-up prompt — the model is told it is out of budget and should converge.

Crossing 1.5× is the engine’s business, not the model’s. The goal is forced to blocked, the loop aborts, and you get told why:

/goal continue — hard limit: token budget overrun (612000 used ≥ 1.5× 400000 budget).
Auto-blocking goal + stopping loop.

The 1.5× grace exists so a model that is genuinely one step from done isn’t cut off mid-sentence, while a model that has stopped converging can’t keep spending.

2. The iteration cap

100 /goal continue firings, whether or not you set a budget. A real audit converges well under this; the cap only catches runaways. It is the reason a goal with no budgets at all is still bounded.

3. The empty-turn guard

If a turn inside a loop produces zero tool calls — the model monologued instead of doing anything — the next firing is skipped once:

(/goal continue suppressed: prior turn made no tool calls — model just
 monologued. Will retry next /loop firing.)

One firing, not permanently: the guard resets itself so a single thoughtful turn doesn’t kill the loop.

4. Terminal status stops everything

complete, abandoned and blocked are terminal. Reaching any of them aborts an active loop and refuses further /goal continue. A blocked goal is paused, not dead — resolve the blocker and run /goal continue manually, or /goal abandon it.

--require — a gate the model cannot talk its way past

The audit prompt asks the model to verify artifacts. --require makes the engine check instead:

❯ /goal start "write the migration guide" \
    --require docs/migration.md \
    --require docs/migration-th.md \
    --auto

MarkGoalComplete is now rejected while either file is missing, with the missing paths named. The goal stays active, so an --auto loop keeps working rather than declaring victory.

This turns a prompt convention into a hard fact. A model can be convinced it wrote a file; it cannot make the file exist. Use it whenever the goal’s output is a file you can name up front — it is the single most effective thing in this chapter.

Paths are relative to the working directory, and the flag repeats.

What the model can do to a goal

Three tools, deliberately split so that “checkpoint” and “declare victory” are different acts:

Tool Effect
RecordGoalProgress Mid-loop checkpoint. Status stays active, iterations continue. The summary is carried into later iterations as a prior_audit hint, so they don’t re-audit from scratch
MarkGoalComplete Terminal complete. Requires an audit summary of what was checked and the evidence. Refused if any --require path is missing
MarkGoalBlocked Terminal blocked. Requires a reason — a missing key, an ambiguous spec, a decision only you can make

The split matters. Before it, a single tool did all three, and the cheapest path for an uncertain model was to declare completion. Now the uncertain move is RecordGoalProgress, which keeps the loop running — the tool descriptions say so explicitly: “ending the loop on insufficient evidence is the worst failure mode.”

When not to use this

  • A single well-specified task. Just ask. A goal adds bookkeeping and audit turns you don’t need.
  • Work that fans out over many files. That is Workflows — deterministic, parallel, resumable. A goal is one line of work that keeps going, not many lines at once.
  • Anything on a schedule rather than a duration. /loop dies with the session. For “every weekday at 08:30”, use Scheduling, which survives restarts and can run from a daemon.

Troubleshooting

Symptom Cause Fix
loop already running One loop per session /loop stop first
Loop fires but nothing happens Body is being suppressed by the empty-turn guard Look for the suppression notice; the next firing retries
Goal went blocked on its own Hard limit — 1.5× a budget, or 100 iterations /goal show names the reason. Raise the budget on a fresh goal, or narrow the objective
Model keeps saying it’s done, but it isn’t Nothing forces it to prove anything Restart the goal with --require <path> for each artifact
--auto isn’t firing A /loop is active, or the last turn made no tool calls /loop stop and let --auto drive
Goal survived a restart you didn’t want Goals persist in the session /goal abandon, or start a new session

See also

  • Chapter 18 — plan mode, for when you want ordered steps you approve rather than an open objective. Plan mode has its own driver and its own retry budget.
  • Chapter 25 — workflows, for bulk fan-out.
  • Chapter 19 — schedules, for repetition that outlives the session.