Goals
A Goal keeps Qwen Code working across turns until a stated condition is met. Set one with /goal <objective>, and the session keeps going on its own. Each turn is recorded as evidence; when the model proposes that the objective is complete or blocked, an independent verifier judges that proposal from the evidence alone. The session stops when the verifier accepts, or when the Goal is paused, cleared, or stopped by a limit.
Commands
| Command | Behavior |
|---|---|
/goal | Show the current Goal and its status. |
/goal <objective> | Create a Goal, or replace the active one. |
/goal set <objective> | Same as above, explicit form. |
/goal edit <objective> | Revise the active Goal’s wording without starting over. |
/goal pause / resume | Stop or continue the loop without losing the Goal. |
/goal clear | Remove the Goal. |
/goal-draft <intent> | Have the objective written for you before you set it (below). |
Creating, editing, or resuming a Goal requires a trusted workspace (/trust). Headless usage is covered in Headless Mode.
Once a Goal has billed a turn, the footer pill and every status card show what it has spent against the window it is allowed, as 1.2k/30.0m. The figure counts the model calls the Goal makes in its own turns; subagents and the verifier’s own checks are not included. The window is set by model.goalTokenBudget; resuming a Goal that has spent its window grants another one on top of what it has already spent, so the figure reads 30.0m/60.0m rather than starting over. A Goal with no budget shows only what it has spent. A Goal that has not billed a turn yet shows no figures at all.
Two more windows are available and off by default: model.goalMaxTurns caps how many turns a Goal may finish, including user-driven Goal turns, and model.goalMaxActiveMinutes caps wall time while it remains active in a running process, including waits and idle time between turns. Time while the Goal is paused, blocked or stopped does not count against the second one, nor does downtime across a restart; a process that is merely suspended is still charged. Whichever window runs out first, the Goal gets the same single wind-down turn to hand off and then stops until you resume it, and resuming authorizes another window on top of what has already been used. Only the window that ran out moves; the others stay where they were, and the -1 opt-out likewise only takes a ceiling off a Goal that has already spent it. The time window is read between turns rather than enforced by a timer, so a Goal can run well past it before it stops: a turn already running is never interrupted, and a window that runs out while no turn is running is not noticed until the next turn ends. Active time is counted between recorded transitions, so time in a turn a restart interrupted is not charged. Changes to either setting take effect after restart. Both windows arm only on a Goal created after the change, so bounding a Goal already on the record means replacing it with /goal set — a new Goal at revision 1, with its turn, token and active-time meters reset and its evidence window starting at the replacement, so evidence the old Goal recorded is no longer citable — or clearing it and starting again. A resume or an edit never adds a ceiling that was not armed at creation.
Each turn the session takes on its own reports what the Goal has spent so far, how many turns are behind it, and — unless the Goal runs unbounded — the window it is allowed. Every such turn except the final wind-down hand-off also carries standing instructions to re-check the workspace rather than trust earlier turns’ reports, to work toward the end state the objective asks for, to do something different when the previous turn changed nothing (from the second turn on, once there is a previous turn to judge), and to check every requirement against citable evidence before proposing that the Goal is done.
A long Goal periodically compresses the evidence it has recorded into checkpoint claims with a side model check, so later turns and the verifier still have it to cite. Each model call the check makes is bounded by model.goalCheckpointTimeoutSeconds, 180 seconds by default. A reply wrapped in a markdown fence is read without the fence. If its claims overrun the aggregate byte budget, include a claim over the per-claim character limit, number more than one checkpoint may hold, cite an id that was not in the request, or change the proof kind of a source they cite, it makes one corrective model call that names what was wrong, and both calls share that ceiling. A reply that is not a JSON object holding a non-empty claims array gets no corrective call, and neither does one with any malformed claim, even beside usable ones: an extra key at either level, an unrecognised proofKind, an empty claim, or a sourceRefs list that is empty, holds a non-string or empty id, repeats an id, or holds more than 32 ids. A check that does not finish in time is abandoned as inconclusive; it counts toward the checkpoint stall limit only when the evidence window has overflowed, while a non-overflowing check preserves the streak and retries on a later turn. A check after a stall on an overflowing window does not resend the same request: it sends the evidence in batches of 24 records, or 12 after a second stall, one model call per batch, each bounded by that ceiling on its own. A window that still has room, and the check a resumed session replays at startup, are still sent in one call, since neither can spend a stall. Each batch’s claims are carried into the next, and only the last batch’s claims are kept. A batch that fails ends the check the way a failed single call does, and the recorded failure names the batch, as in batch 2/5: …. A check after a stall can therefore take up to five times the ceiling, or nine times after a second stall, and a message you send meanwhile waits for it; the whole span counts as active time, so it is charged against model.goalMaxActiveMinutes like any other wait. Batching helps when the model could not answer for the whole window in time or with usable claims; a checkpoint that comes back with the full 32 claims and still leaves evidence behind still needs a narrower objective. The calls are streamed, so the per-request transport timeout bounds only connect and first response, and the ceiling itself stops at the stream guards’ 15-minute lifetime cap because past that the guard, not the setting, ends the check. That 15-minute limit on the setting is fixed, and raising the stream guard’s own cap does not lift it.
A failing checkpoint shows up before it stops the Goal. While an active Goal’s stall streak runs, the footer pill switches to checkpoint N/3 stalled on its own; once the Goal pauses or stops, the pill shows that status instead. The web shell’s Goal status strip shows the count whatever the status. Whenever a terminal Goal status card is rendered, for example by /goal or by a pause, resume or verifier card, it shows how many consecutive checks have stalled out of the three the Goal allows, together with the last failure while one is recorded; the web shell’s Goals dialog and headless /goal text output show the same line, while the web shell’s transcript cards for Goal events show only the stop reason, and the model sees both fields when it reads the Goal. A check that fails while the window still has room is shown too, without spending a stall, but only while the Goal is active, or when that failure is itself what stopped the Goal, as with a checkpoint request too large to send. A checkpoint stop for any other reason clears the failure and keeps the streak, and a completed Goal shows no checkpoint line. The failure is kept as a single line with control characters removed. A Goal stopped by three stalled checkpoints names what the last one ran into. A check that could not fit the window within the checkpoint claim bounds, whether a full claim list that still left evidence behind or claims over the claim count or size budget, means the objective produces more evidence than one window holds, so narrow it. An answer that could not be folded into claims means the checkpoint model is not returning the structured output it is asked for, and narrowing the objective does not fix that. A check that never answered can mean an unreachable or rate-limited provider, a check that did not finish within model.goalCheckpointTimeoutSeconds, or an error in the check itself; the recorded failure says which. Resuming after any of the three starts a fresh evidence window.
Interrupting a Goal
Cancelling a Goal turn pauses the Goal. Press Esc while the model is answering or while its tools are still running, and the turn stops, the Goal moves to paused, and the card and /goal both say why it stopped. Nothing continues until you run /goal resume.
Typing a message while a Goal is active does not pause it. Your message runs as the next Goal turn, so use it to steer the work; use /goal pause or /goal clear to stop it.
Every pause states its reason: that you interrupted it, that you ran /goal pause, that the session token limit blocked the next model request, that the turn failed, or that three turns in a row recorded nothing the verifier could judge and no proposal — Goal bookkeeping reads (get_goal, update_goal) do not count as progress. A Goal stopped by a limit keeps the reason for that limit instead.
How a Goal is judged
The verifier never runs commands or reads files on its own. It only sees what is already in the transcript:
- Visible assistant output and tool results count as evidence. The objective text, your prompts, and the model’s hidden reasoning do not.
- Printed text proves only that text was printed. A claim that tests pass, a file changed, or a remote is updated needs the corresponding tool result in the transcript.
- A claim that you confirmed, chose, or approved something needs a real message from you; the verifier rejects proposals that assume it.
- When evidence is missing the verdict is “not yet”, not “done”. A condition nobody can evidence keeps the loop running until a limit stops it.
So the objective has to make the agent produce evidence: run the named check and show the decisive output.
Writing a good objective
Put these into the objective, in this order:
| Part | What to write |
|---|---|
Outcome: | One sentence: what is true when this is done. |
Done when: | Numbered, binary checks. At least one names a command and its expected exit code or output line, and asks for that line to be pasted. |
Must not: | Files not to touch, tests or thresholds not to weaken, irreversible actions (push, delete, publish) not to take. |
Budget: | Advisory model instruction for when to give up, such as “stop as blocked after 20 turns”. To enforce one, set model.goalMaxTurns or model.goalMaxActiveMinutes in settings, not here. |
On block: | What to report when stuck, and which decision a human must make. |
Context: | Only facts the agent cannot find in the workspace: branch, environment, earlier decisions. |
Keep it to one objective. /goal set and /goal edit accept any length, but stay roughly under 1,200 characters: the objective is re-sent on every Goal turn. An objective the model proposes through propose_goal is capped at 1,500 characters. Both commands collapse newlines to spaces, so number the items rather than relying on line breaks.
Budget is an instruction to the model about when to stop and report a blocker; the model may or may not honour it. To make the runtime itself stop at a turn count or a duration, set model.goalMaxTurns or model.goalMaxActiveMinutes. Writing either into the objective does not configure those, and does not change the Goal’s token budget.
| Weak | Why it fails | Stronger |
|---|---|---|
| make checkout faster | No threshold, no check. | Outcome: checkout p95 is below 250 ms. Done when: 1) npm run bench:checkout exits 0 and prints p95 < 250 (paste the line); 2) npm test exits 0. Must not: change the benchmark or skip tests. Budget: as model guidance, stop as blocked after 20 turns. On block: report the measured p95 and what blocks it. |
| clean up the auth module | ”Clean” has no evidence. | Ask what would be observable: zero lint warnings in src/auth, a coverage threshold, a file count. |
| ship the release | Irreversible, and needs a human decision. | Narrow to a checkable pre-release state (tag exists, npm run release:dry-run exits 0) and put “do not publish” in Must not. |
| after I confirm the design | The verifier cannot see a confirmation that never happened. | Move it to On block: as the decision a human must make. |
Let /goal-draft write it
/goal-draft <what you want done> is a bundled skill that does the above for you. It reads only enough of the workspace to establish the scope and real verification commands, without running tests, building, installing dependencies, or starting services. It asks at most one round of questions when essential choices are unclear, then writes a compact objective, usually with 3–5 completion checks (fewer when enough). Explicit requirements are preserved; it does not add checks just to reach a count.
For an audit, completion means covering the agreed scenarios and reporting evidence, including reproduction steps for confirmed defects. Finding no defects is a valid result. The draft should not invent a minimum number of scenarios, evidence files, exploration rounds, or defects.
If a success criterion, command, input path, or essential decision cannot be established, the skill returns a draft marked “Needs clarification” with <TODO: …> items. It does not offer that draft for approval or print a runnable /goal set or /goal edit command. Nonessential defaults are marked [ASSUMPTION]; they do not stand in for missing success criteria.
Once the objective is ready, an interactive terminal or Web Shell session can show the propose_goal approval dialog described below. Clients without Goal proposal support, headless runs, sessions with the tool disabled, and sessions with an active Goal receive a command to run manually instead. The hand-off says that the draft has not been applied. The skill never starts the work itself, and nothing is set without your approval.
Pass an existing objective to tighten it: /goal-draft all tests pass and the lint is clean. For an active Goal, an explicit request to tighten it produces /goal edit; a replacement uses /goal set. If the intended operation is unclear, the skill includes that choice in its single round of questions.
Approve a Goal the model proposes
In an interactive terminal or a Web Shell turn with an attached client, the model has a propose_goal tool. When /goal-draft finishes, or when you ask for an outcome that spans several turns, it can propose the objective instead of printing a /goal set … line for you to copy. The proposal appears as an approval dialog showing the full objective. Approving it sets the Goal exactly as /goal set would, the moment the current turn ends (the model acknowledges and stops; the first Goal turn then starts on its own), and declining sets nothing — the model sees only that the tool call was not allowed, and its instructions tell it not to ask why and not to propose the same objective again. The approval is bound to the turn that asked for it: if that turn is cancelled or otherwise never reaches its end, the approval is dropped rather than applied under a later message or an automated turn. No permission rule or approval mode (including YOLO) skips this dialog, and the tool refuses while another Goal is active, in plan mode, and in untrusted folders; subagents are never offered it. Web Shell uses its existing Allow/Reject permission panel. A stopped Goal can be replaced only if it still matches the version shown for approval; changing it invalidates the proposal. Headless runs, Web Shell channel deliveries and automatic turns, and ACP clients without the required approval and turn-lifecycle support retain the printed /goal set hand-off.
Turn it off with goals.modelProposed: "disabled" in your user settings. Because the setting decides whether the model may ask you to start an autonomous loop, it is honored only from user and system scope; a workspace .qwen/settings.json value is ignored with a warning.
The skill is instructed to be read-only, and only its non-mutating tools are auto-approved (get_goal, read_file, glob, grep_search). ask_user_question is deliberately not auto-approved, so its question dialog is shown before the skill drafts from your answers. Like other bundled skills, a project or personal skill named goal-draft overrides it, and skills.disabled can turn it off. See Skills for how bundled skills are discovered.
Telemetry
Goal transitions are reported through two independent settings: telemetry enables OpenTelemetry events and metrics, while privacy.usageStatisticsEnabled controls usage statistics and is on by default. The OpenTelemetry event is named qwen-code.goal_state; usage statistics report the same transition data without the Goal id.
Reported transitions are set, replace, edit, pause, resume, clear, verifier rejection, completion, blocking, and usage limits. Events carry turn count, token spend, active time, and any configured budgets. A clear event identifies only the removed Goal and its revision, with no usage figures. A replace event describes the new Goal, not the removed Goal’s final spend. User pauses and automatic no-progress pauses share the same pause cause; events include the raw no-progress streak when available. Only completion, blocking, and usage limits contribute to the outcome histograms. A resumed session does not re-report the transition its Goal was recovered from. A new stop committed during recovery, including a replayed checkpoint reaching a limit, is reported normally.
The Goal event itself contains no objective text or stop reasons, only the objective’s length in code points. Other telemetry can contain that text: tool-call telemetry records the arguments of propose_goal and update_goal, including the objective or model-authored reason, even when telemetry.logPrompts is off.