Skip to content

Goal workflows with /goal in Claude Code

The /goal command in Claude Code sets a completion condition and keeps the session working turn after turn until a separate small fast model (Haiku by default on the Claude API) judges the condition met or impossible, or an unrecoverable error clears it. It suits migrations, acceptance criteria, and other work with a checkable end state.

This page is for developers who run long Claude Code sessions and for tech leads who want those runs to end in evidence rather than in “looks done”. The situation it solves: you are two hours into a migration, and your whole job has become typing “keep going” every time a turn ends. The work is clear and the finish line is obvious, yet every step waits on you.

What you’ll walk away with from goal workflows

Section titled “What you’ll walk away with from goal workflows”
  • A three-part condition template (end state, proof command, constraints) that the evaluator can actually judge.
  • Four copy-paste goals for migrations, acceptance criteria, size budgets, and issue triage.
  • An unattended claude -p recipe with a hard spending cap and a tool allowlist.
  • A verification routine that proves the result without reading every changed line.
  • A failure table that maps each way a goal stalls or lies to its fix.

When is /goal the right way to keep Claude Code working?

Section titled “When is /goal the right way to keep Claude Code working?”

Use /goal when the end state is checkable and the path to it is unpredictable. Four mechanisms keep a session moving without you, and they differ in what starts the next turn and what stops the run.

MechanismNext turn starts whenRun stops whenReach for it when
/goalThe previous turn ends (plus automatic retries and idle check-ins in interactive sessions)The evaluator returns met or impossible, an error you must fix occurs, or you run /goal clearThe finish line is a test result, exit code, count, or empty queue
/loopA time interval elapsesYou stop it, or Claude decides the work is doneYou poll external state on a cadence, such as CI or a deploy
Stop hook in settingsThe previous turn endsYour script or prompt decidesThe check must be deterministic or apply to every session in a scope
Auto modeNever; it works inside one turnClaude judges the turn doneYou want fewer permission prompts, not more turns

Auto mode and /goal are complementary. Auto mode removes per-tool prompts; /goal removes per-turn prompts, and it hands the “am I done?” decision to a fresh model instead of the one doing the work. A goal run is an agentic workflow: the model works through the task turn after turn until a separate check says it is done. If the job is bigger than one session, such as hundreds of files split across agents, use dynamic workflows instead. If it must run on a schedule with no open terminal, use routines. If you want the loop to outlive the session, with state on disk and a fresh context each iteration, compare /goal with the Ralph loop in autonomous loops.

  1. Choose the permission mode first. A goal does not change your permission mode. In Manual mode, Claude still stops to ask before every tool call your settings do not allow, so an unattended goal stalls on the first npm test. On v2.1.283 and later (the latest release channel), interactive terminal and VS Code sessions start in auto mode on every plan, unless a settings file sets disableAutoMode or the session runs on a model auto mode does not support, such as Haiku. On the stable channel (v2.1.274 on 26 September 2026), start the session explicitly if your plan, provider and model support auto mode (see the permissions page linked below); otherwise allow the goal’s commands with explicit allow rules:

    Terminal window
    # Terminal
    claude --permission-mode auto

    Keep your explicit deny rules; auto mode still honours them. See permissions, sandboxes, and approval modes for the full matrix.

  2. Lock the checks the goal depends on. A goal that says “tests pass” rewards an agent that edits the tests. Add deny rules for the test tree to .claude/settings.json before you walk away. A leading / anchors the path at the project root:

    {
    "permissions": {
    "deny": ["Edit(/tests/**)", "Edit(**/*.test.ts)", "Edit(/vitest.config.ts)"]
    }
    }

    Deny rules cover Claude’s file tools, not shell commands that write files. Protecting the oracle adds the sandbox and hook layers that close that gap.

  3. Set the goal. Run /goal followed by the condition, up to 4,000 characters. Setting a goal starts a turn immediately with the condition as the directive, so you do not send a separate prompt. A ◎ /goal active indicator shows how long it has run. One goal is active per session; a new /goal replaces the old one.

  4. Watch the verdicts, not the diff. The transcript shows each verdict the evaluator returns; press Ctrl+O to see the reason behind it. Run /goal with no argument to see the condition, elapsed time, evaluated turns, token spend, and the most recent reason.

  5. Verify and close. When the evaluator returns met, Claude Code clears the goal and records an achieved entry. That verdict is a claim, not proof. Run the checks in How do you prove a goal’s result without reading every line? before you merge. To stop early, run /goal clear (aliases: stop, off, reset, none, cancel); /clear also removes the goal.

Copy-paste goals that hold up over many turns

Section titled “Copy-paste goals that hold up over many turns”

Each goal below names one end state, the command that proves it, the constraints that must survive, and a stop clause. Paste them at the Claude Code prompt and change the paths.

The triage goal needs a tool that reads and writes issues. Add the GitHub MCP server once per project, following the GitHub MCP setup guide:

Terminal window
# Terminal. Single quotes keep ${GITHUB_PAT} unexpanded in .mcp.json.
claude mcp add -s project --transport http github https://api.githubcopilot.com/mcp/ \
-H 'Authorization: Bearer ${GITHUB_PAT}'

Because this goal closes issues unattended, make GITHUB_PAT a fine-grained personal access token limited to Issues: read and write on this one repository.

How do you write a condition the evaluator can judge?

Section titled “How do you write a condition the evaluator can judge?”

The evaluator reads the conversation only. It does not run commands or open files, so a condition is only as good as the evidence Claude prints. Build every condition from three parts, then add a stop clause.

PartWeakStrong
End state“the migration is done”“npm run db:status reports no pending migrations”
Proof shown in the transcript“tests pass”“npm test exits 0 and its summary line shows 0 failed and 0 skipped”
Constraintsnone“do not edit package.json, lockfiles, or tests/”
Stop clausenone“stop after 25 turns and list what remains”

Three rules follow from the evaluator’s design:

  • Name the proof command in the condition. If Claude edits files but never runs the check, the evaluator has nothing to read and returns not yet met, turn after turn.
  • Translate taste into a measurement. “The code is more readable” cannot be judged from a transcript. “Every file in src/utils/ is under 200 lines” can.
  • Treat the stop clause as soft. “Stop after 25 turns” is judged by the evaluator from the conversation, not enforced by a counter. Your hard limits are /goal clear in a session and --max-budget-usd in a claude -p run.

How does Claude Code decide a goal is done?

Section titled “How does Claude Code decide a goal is done?”

/goal is a session-scoped, prompt-based Stop hook. After each turn, Claude Code sends the condition and the conversation so far to the small fast model. On the Claude API that model is Haiku; on Bedrock, Google Cloud, or Foundry, the provider’s default applies. The evaluator returns one of three verdicts, each with a reason:

  • Not yet met. Claude starts another turn and uses the reason as guidance.
  • Met. Claude Code clears the goal and records an achieved entry.
  • Impossible. Claude Code clears the goal and records a failed entry with the reason.

Four behaviours change how a long goal runs:

  • Stall detection. If Claude keeps answering the evaluator without using tools for several turns in a row, Claude Code stops the loop, prints a warning, and returns control with the goal still set. Evaluation resumes after your next prompt.
  • Errors you have to fix clear the goal. An authentication failure (when Claude Code manages its own credentials), an exhausted credit balance, a context overflow that auto-compaction could not clear, or an unavailable model prints Goal cleared after an unrecoverable error. Fix the cause, then set the goal again.
  • Other errors retry or pause (v2.1.269+, interactive). Transient failures such as an overloaded server retry automatically, behind a Goal still active notice; after three automatic retries the goal pauses instead. Rate limits, usage limits and a hook that ended the turn print Goal paused.
  • Background work defers evaluation. While a subagent or background shell command runs, evaluation waits. After 30 minutes of waiting, a check-in asks Claude to read the running tasks’ output and fix or stop stuck ones. Later check-ins back off to 1 hour, then every 2 hours, and an interactive session starts at most three idle check-ins per goal between your prompts. CLAUDE_CODE_GOAL_CHECKIN_MINUTES changes the first interval; 0 turns off check-ins and automatic retries.

To evaluate on a different model, set ANTHROPIC_DEFAULT_HAIKU_MODEL. That variable also changes the haiku alias and background tasks such as conversation summarisation, so set it deliberately. Evaluation tokens bill on the small fast model; see the models hub for current prices.

How do you prove a goal’s result without reading every line?

Section titled “How do you prove a goal’s result without reading every line?”

An achieved verdict means a small model read a transcript and believed it. Treat it as the agent’s claim and verify it with evidence that does not depend on the transcript.

  1. Re-run the decisive check outside the session. Push the branch and let CI run the same tests, type check, and lint. A green CI run on a clean checkout is the proof; the transcript is not.

  2. Confirm the constraints held. Run git diff --stat main...HEAD -- tests/ package.json (adjust the paths to your constraints). Empty output means the goal did not reach what it promised not to touch.

  3. Review by risk, not by line. Run /code-review on the branch, then read only the files the review flags and anything that touches authentication, payments, or data deletion.

  4. Attach the evidence to the pull request. Paste the final /goal status (condition, turns, token spend) and the CI link into the description. The evidence bundle defines the full contract.

Sign-off stays with the developer who set the goal: they own the condition, so they own the judgement that it described the right outcome. A tech lead standardising goals across a team should review the condition templates, not every diff they produce.

/goal works in non-interactive mode, in the desktop app, and through Remote Control. With -p, the loop runs to completion in one invocation. A claude -p session starts in Manual permission mode, so allow exactly the tools the condition needs and cap the spend:

Terminal window
# Terminal or a trusted CI job on your own branch
claude -p "/goal CHANGELOG.md has an entry for every PR merged since the v4.2.0 tag, \
as listed by 'git log v4.2.0..HEAD --merges --oneline'. Do not edit any other file. \
Stop after 15 turns." \
--allowedTools "Bash(git log *)" "Read" "Edit(/CHANGELOG.md)" \
--max-budget-usd 5 \
--output-format stream-json --verbose
  • --max-budget-usd is the hard ceiling that the turn clause is not; it works only with --print.
  • With the default text output, nothing prints until the run ends, so a long goal looks stuck. --output-format stream-json --verbose emits each message as it happens.
  • Ctrl+C stops the run before the goal resolves.
  • In a non-interactive session, check-ins arrive only when a turn ends; there are no idle check-ins.

Never run a goal against an untrusted ref, such as a fork’s pull request, while secrets are in the environment: project hooks and MCP servers run with the job’s credentials. CI/CD integration shows the safe checkout pattern.

A goal still active when a session ends comes back on every resume route: --continue, --resume with a session ID, name, or transcript path, and the session picker. The condition carries over; the turn count, timer, and token baseline reset. Achieved or cleared goals do not return.

/goal arrived in Claude Code v2.1.139. Automatic retries need v2.1.269 or later, and idle check-ins v2.1.236 or later. Because the evaluator is part of the hooks system, /goal follows the same workspace trust rule as hooks in settings files. It is unavailable when disableAllHooks is true after settings precedence applies, or when allowManagedHooksOnly is set in managed settings. In each case the command says why instead of failing silently.

SymptomCauseRecovery
The goal never completes although the work looks doneClaude never printed the proof the condition namesPut the proof command in the condition: “npm test exits 0 and its summary line is shown”
The evaluator returns met after one green runThe condition accepts partial evidenceRequire totals: “0 failed and 0 skipped across the whole suite”
Tests pass because tests changedNothing stopped the agent editing the oracleAdd deny rules for the test tree and a “do not edit tests/” constraint; check with git diff --stat
The goal is cleared as impossibleThe condition contradicts a constraint, or the proof command cannot succeed in this environmentRead the recorded reason, run the proof command yourself, relax the conflicting constraint, and set the goal again
Goal cleared after an unrecoverable errorAuthentication, credit balance, context overflow, or model availabilityFix the named cause, then run /goal with the same condition
Goal pausedA rate limit, a usage limit, a hook that ended the turn, or three failed automatic retriesWait for the limit, or send a message to resume immediately
The goal sits idle while a subagent or background command runsEvaluation is deferred until the background work finishesCheck /tasks, stop the stuck job, or lower CLAUDE_CODE_GOAL_CHECKIN_MINUTES; after three idle check-ins, Claude Code pauses them until your next prompt
The loop stops with a warning and the goal still setStall detection: several turns with no tool useSend a prompt that names the next concrete step, or rewrite the condition
Token spend keeps climbingNo stop clause, or an unreachable end stateRun /goal to read the spend, then /goal clear; in -p runs, always pass --max-budget-usd
/goal refuses to runUntrusted workspace, disableAllHooks, or allowManagedHooksOnlyAccept the trust dialog, or ask whoever owns managed settings to allow hooks