The /goal Command: Persistent Completion Conditions
- Set a persistent completion condition with /goal and explain how the Haiku evaluator checks it after each turn
- Write effective goal conditions with measurable end states, stated checks, and explicit constraints
- Run /goal in non-interactive mode for automated pipelines that need multi-turn execution to completion
The Per-Turn Prompt Problem
Imagine Claude is migrating every API call in a module to use a new library. The task has twenty files. After each file, Claude finishes its turn and returns control to you. You scan the output, decide it looks good, and type something like "continue" or "next file". Claude works on the next file, returns control, you type again. Twenty files, twenty manual prompts.
Auto mode removes the permission prompts within each turn. But it doesn't remove the turn boundary itself. Claude still stops after each turn and waits. The /goal command removes that boundary by adding a second evaluator that runs after every turn and decides whether to keep going.
When you set a goal, Claude doesn't return control to you after each turn. Instead, the evaluator checks your condition. If it's not met, Claude starts another turn. If it is met, the goal clears and Claude stops. You come back to a completed task, not a task-in-progress waiting for your prompt.
Setting a Goal
Use /goal followed by the completion condition you want satisfied:
/goal all tests in test/auth pass and the lint step is clean
Setting a goal starts a turn immediately, with the condition itself as the directive. You don't need to send a separate message. While the goal is active, a ◎ /goal active indicator shows how long it's been running.
Only one goal can be active per session. Setting a second goal replaces the first. You can check the status of an active goal at any time:
/goal
The status shows the condition, how long it's been running, how many turns have been evaluated, the current token spend, and the evaluator's most recent reason for continuing or stopping.
To cancel before the condition is met:
/goal clear
The aliases stop, off, reset, none, and cancel all work in place of clear.
How the Evaluator Works
/goal is implemented as a session-scoped Stop hook. After each of Claude's turns, the condition and the full conversation so far are sent to the small fast model (Haiku by default). The evaluator reads the transcript, returns a yes-or-no decision, and includes a short reason explaining its judgment.
A "no" decision sends the reason back to Claude as context for the next turn — Claude sees why the evaluator said the condition isn't met, which guides what to do next. A "yes" decision clears the goal and records an achieved entry in the transcript.
The evaluator does not run tools or read files independently. It can only judge what Claude has surfaced in the conversation. This is the key constraint for writing effective conditions: the condition must be demonstrable from output in the transcript, not from state that exists only on disk.
Writing Conditions That Work
A condition that holds up across many turns usually has three elements:
One measurable end state. A test result, a build exit code, a file count, an empty queue. Vague conditions like "the code is clean" give the evaluator nothing concrete to verify. Specific conditions like "npm test exits 0 and git status is clean" give it observable evidence.
A stated check. Tell Claude how to prove the condition — "run pytest test/auth and show the output". The evaluator reads the output Claude shows, not a file it goes and reads itself.
Constraints that matter. Anything that must not change on the way to done. "All tests in test/auth pass and no other test file is modified" prevents Claude from satisfying the goal by deleting inconvenient tests.
Conditions can be up to 4,000 characters. To bound a long-running goal, add a turn or time clause: "or stop after 20 turns." Claude reports progress against that clause each turn, and the evaluator judges it from the conversation.
Example of a well-formed condition:
/goal run npx tsc --noEmit, fix every error it reports, re-run to confirm exit 0, and do not modify any .test.ts file or stop after 15 turns
/goal vs /loop vs Stop Hook
All three mechanisms keep the current session running between prompts, but they trigger differently:
/goal/loopUse /goal when you have a verifiable end state and want Claude to work toward it without your involvement. Use /loop when you need time-based polling or monitoring. Use a Stop hook (via settings) when you want custom evaluation logic that applies to every session in a given scope rather than a one-off session goal.
Non-Interactive Use
/goal works in non-interactive mode with the -p flag. When you include the goal in the prompt string, Claude runs the loop to completion in a single invocation and exits when the condition is met:
claude -p "/goal all tests in test/auth pass and lint is clean"
This is the pattern for CI scripts that need Claude to perform multi-turn repair work autonomously. The script blocks until the goal is met, then exits with the final turn's output. Combine with --permission-mode acceptEdits or auto mode and the appropriate --allowedTools flags to control what Claude can do during the loop.
Resume behavior works too: a goal still active when a session ends is restored when you resume with claude --resume or claude --continue. The condition carries over but the turn count, timer, and token spend reset.
- The /goal evaluator runs after every turn, not during it — Haiku reads the transcript to check whether the condition holds, which means conditions must produce observable transcript evidence Claude can point to
- Effective conditions have three parts: a measurable end state, a stated check method, and explicit constraints — vague goals like "clean up the code" give the evaluator nothing to verify
- /goal clear cancels the goal without resetting the session — conversation history stays intact for follow-up work, and the achieved or cleared entry is recorded in the transcript
- Auto mode and /goal are complementary, not competing — auto mode removes per-tool prompts within each turn while /goal removes the per-turn prompt between turns, making the two together the standard setup for unattended work
- claude -p "/goal ..." runs the loop to completion non-interactively — ideal for CI scripts that need Claude to keep working through a repair or migration until a measurable state is reached