Skip to content
claude-codeagentspractitionerai-codingproductivity

Don't Prompt Every Step. Give Claude a Finish Line.

Claude Code's /goal command lets you state a done condition and walk away. Claude keeps working, turn after turn, until a separate model confirms the finish line is crossed. Here's what it is, when it earns its keep, how to write a condition that actually works, and where it bites you.

Fabian Mösli Fabian Mösli
· 11 min read · 2026-07-01

Key Takeaways

  • A goal is a finish line, not a task. You tell Claude the condition that means 'done' and it keeps starting new turns on its own until that condition holds — you stop babysitting each step.
  • A second, cheaper model is the referee. After every turn it reads the conversation and rules yes or no on your condition. It can't run your code or open your files, so your condition has to be something Claude's own output proves in the chat.
  • The whole thing lives or dies on how you word the finish line. Vague goals either never stop or get gamed; a good one names one measurable end state, how to prove it, and what must not change on the way there.
In this guide

There’s a specific kind of tired that comes from using Claude Code well. You give it a task, it does a chunk, it stops and hands control back to you, you glance at the result, you say “yes, keep going” or “no, the tests are still failing, try again.” Then it does the next chunk and stops again. And again. You’re not doing the work anymore, but you’re still standing over it, tapping it forward every few minutes like a slow checkout machine.

The /goal command is Anthropic’s answer to exactly that. You state the finish line once. Claude keeps working toward it across turn after turn, on its own, and only comes back to you when a separate model confirms the line is crossed. You set it, then you go make coffee.

I want to be honest up front about who this is for. /goal shines brightest on work with a clean, checkable end state, and a lot of that work is developer work: get the tests green, get the build passing, clear the issue queue. If you’re a non-developer maintaining a website or running content through Claude Code, it still applies, but you have to think a little harder about what “done” even means for your kind of task. I’ll get to that.

What a goal actually is

Normally Claude Code runs turn by turn. It works, it stops, control returns to you, you prompt the next step. That back-and-forth is the default rhythm.

A goal changes what starts the next turn. Instead of you deciding it’s time for another round, a completion condition does. You run /goal followed by a plain-English description of what “finished” looks like, and from that moment Claude keeps launching new turns by itself until the condition holds. No condition met yet? It starts another turn. Condition met? The goal clears and you get control back.

The mental model I keep coming back to is hiring a contractor to paint a room. The task-by-task version is you standing in the doorway after every wall: “okay, next wall.” The goal version is you saying “the room is done when every wall has two coats, there’s no tape left on the trim, and the drop cloths are folded up” — and then you leave. The contractor knows exactly when they’re finished, and so do you, because you both agreed on the finish line before anyone picked up a brush.

That analogy also tells you where it goes wrong, which we’ll come back to. A finish line that’s too fuzzy (“make the room look nice”) means the painter stops whenever they feel like it. Hold that thought.

How it works under the hood

The part that surprised me is that Claude isn’t judging its own homework. There are two models involved.

The main model — Sonnet, Opus, whatever you’re running — does the actual work each turn. Then, every time it finishes a turn, the condition you wrote plus the whole conversation so far gets handed to a second, smaller, faster model (Haiku by default) that acts as a referee. The referee reads everything and returns a plain yes or no, with a one-line reason. “No” tells Claude to keep going, and that reason becomes a hint for the next turn. “Yes” clears the goal and marks it achieved.

Two separate models matters. The one grinding on the problem is often the worst judge of whether it’s actually done. It’s invested, it’s been staring at the same code for ten turns, it wants to be finished. A fresh referee that didn’t do the work is harder to fool. It’s the difference between marking your own exam and having someone else mark it.

Here’s the constraint that governs everything else: the referee only sees the conversation. It can’t run your tests, open your files, or check your database. It judges purely on what Claude has surfaced in the chat. So “all tests in the auth folder pass” works fine — but only because Claude actually runs those tests and the passing output lands in the transcript where the referee can read it. If the proof never shows up in the conversation, the referee has nothing to rule on.

One more mechanical detail worth knowing: setting a goal kicks off a turn immediately, with your condition as the instruction. You don’t type the goal and then send a separate prompt. The condition is the prompt. While it’s running you get a little ◎ /goal active marker showing how long it’s been going.

When it makes sense to use it

Reach for a goal when the work is substantial, mostly mechanical, and has an end state you can actually check. The official examples are a good tell for the shape of it:

  • Migrating a module to a new API until every call site compiles and the tests pass
  • Implementing a design doc until all its acceptance criteria hold
  • Splitting one bloated file into smaller modules until each is under a size budget
  • Working through a backlog of labeled issues until the queue is empty

Notice what those have in common. Each one is a slog of many similar steps, and each one has a moment where you can point at something and say “there, that’s done” — a green test suite, an empty queue, a file under 300 lines. That combination, tedious plus checkable, is the sweet spot.

For my own work on this site, the version that clicks is the content-style pass. I periodically go through the guides here and scrub the AI-writing tells — the stiff phrasing, the “not X, it’s Y” flips, the overused em dashes. That’s dozens of near-identical edits across two dozen files, and it has a checkable end: every guide reads clean against the style checklist. That’s a goal. Something like: every guide in the guides folder has been read against protocols/content-style.md and its tells fixed, without changing any links, facts, or headings — report each file as you finish it. Tedious, repetitive, and with a finish line I can describe. Exactly the job I don’t want to tap forward one file at a time.

When does it not make sense? When the work is short (just ask normally), when it’s genuinely creative or subjective (writing that needs one consistent human voice doesn’t have a finish line a referee can call), or when you can’t describe “done” in a way that shows up in the conversation. If you can’t finish the sentence “this is done when ______” with something concrete, a goal is the wrong tool.

Writing a condition that actually works

This is the whole game. A goal is only as good as its finish line, and most of the failures I’ve seen come from a lazily worded condition.

A condition that survives many turns usually has three parts:

One measurable end state. A test result, a build exit code, a file count, an empty queue. Something with a definite value, not a vibe. “The code is better” has no end state. “Every file in the components folder is under 200 lines” does.

A stated check, how Claude should prove it. Remember, the referee only reads the transcript. So tell Claude how to demonstrate success in a way that lands in the chat: “run the test suite and show it exits 0,” “run git status and show it’s clean.” You’re not just naming the goal, you’re naming the evidence.

The constraints that matter, what must not change. This is the part people forget, and it’s the most important. State anything that has to stay true on the way to the finish line: “don’t modify any other test file,” “don’t touch the public API,” “don’t delete anything to make the check pass.”

That last one isn’t paranoia. It’s the single biggest gotcha with goals, and it deserves its own section.

The downsides, honestly

The monkey’s paw problem. You said “no failing tests.” A literal-minded worker under pressure to satisfy that condition has an obvious cheat: delete the failing tests. Now there are no failing tests. Condition met, referee says yes, goal clears, and your code is more broken than when you started. This is a real failure mode, and it comes straight from the fact that Claude is optimizing to satisfy the words you wrote, not the intent behind them. The fix is guardrails in the condition itself: “all tests pass without removing or skipping any test.” Whenever you write a goal, spend a second thinking like a rules-lawyer trying to satisfy it the cheap way, and close that door.

It can run forever. If the condition can never be proven — because it’s vague, or genuinely impossible, or the proof never makes it into the chat — Claude will keep starting new turns indefinitely, burning tokens the whole time. The defense is to bound it: add a clause like “or stop after 20 turns” right in the condition. Claude reports progress against that clause each turn, and the referee will call it done when the limit hits.

It costs real tokens. Every turn is a full turn of the main model working unattended. A goal that runs for thirty turns is thirty turns of spend, and if you’re on a plan with weekly limits, an ambitious overnight goal can eat a real chunk of your budget. (The referee itself is cheap — it’s the little Haiku model — so that part is negligible. It’s the main-model turns that add up.)

Unattended means unattended. For a goal to run without you, Claude also needs to be able to take its actions without stopping to ask permission for each tool call. That’s what auto mode is for, and the two pair together: auto mode removes the per-tool prompts, /goal removes the per-turn prompts. Turn a capable model loose on your repo with both enabled and no guardrails and you can do real damage. Run it on a branch, not on main.

The referee is judging a story, not reality. Because it only sees the conversation, a confident-but-wrong Claude can, in principle, narrate success that didn’t happen and get a “yes.” Two models make this much harder than self-judgment, but it’s not airtight. The mitigation is the same as always: make the condition demand real evidence in the transcript, not a summary. “Tests pass” backed by actual test output beats “I’ve fixed everything.”

How to try it, concretely

You’ll need Claude Code v2.1.139 or later, and you have to have accepted the trust dialog for the workspace (the goal machinery is built on the hooks system, so it won’t run in an untrusted folder).

Start tiny, on a throwaway branch, so you can watch the loop without risking anything:

/goal every file in the src/utils folder is under 100 lines,
achieved by splitting longer ones into focused files, without
changing any function's behavior — run the tests after each split
and show they still pass. Stop after 15 turns.

That one condition has all the pieces: a measurable end state (files under 100 lines), a stated check (tests still pass, shown in the chat), a constraint (don’t change behavior), and a bound (stop after 15 turns). Setting it kicks off the first turn right away. Then just watch. After each turn you’ll see the referee’s one-line reason for why it’s not done yet, which is genuinely interesting: it’s a live readout of what Claude thinks it still has to do.

A few commands to know while it runs:

  • /goal on its own (no text) shows the status: the condition, how long it’s been running, how many turns, tokens spent, and the referee’s latest reason.
  • /goal clear stops it before the condition is met. (stop, off, reset, cancel, and none all work too, and starting a fresh conversation with /clear also drops it.)
  • If you close the session with a goal still active and later resume it with --resume or --continue, the goal comes back — though the timer and turn count reset.

It also works headless, which is where it gets genuinely useful for scheduled work. This runs the whole loop to completion in one shot from your terminal or a script:

claude -p "/goal CHANGELOG.md has an entry for every PR merged this week"

Ctrl+C stops a headless goal if you need to bail.

Goal, loop, or hook?

One last thing, because it’s easy to mix these up. Claude Code has three ways to keep a session running between your prompts, and they trigger on different things.

/goal starts the next turn when the previous one finishes, and stops when the referee confirms your condition. Use it when you have a checkable finish line.

/loop starts the next turn when a time interval passes — every ten minutes, say — and stops when you stop it or Claude decides the work is done. Use it for “keep chipping away / keep watching” work that’s paced by the clock rather than by a condition.

A Stop hook is the do-it-yourself version. It lives in your settings file, applies to every session in its scope, and runs your own script or prompt to decide when to keep going. /goal is actually a friendly wrapper around exactly this — a Stop hook scoped to just the current session. Reach for a real Stop hook when you want the same check to apply automatically across every session, or you need custom logic a one-line condition can’t express.

For most people, most of the time, the answer is /goal. Give Claude a finish line, make it a good one, add the guardrail that stops it cheating, and let it run.


If you’re new to Claude Code as an agent rather than a chatbot, start with Claude Code: When AI Stops Talking and Starts Doing. And once you’re comfortable running one goal, the next unlock is having Claude split work across subagents that each go handle a piece in parallel.

Published: 2026-07-01

Last updated: 2026-07-01

Stay in the loop

Don't miss what's next

I'm curating the best AI tools for professionals. Join the list and I'll reach out when I have something worth sharing.