Keep CodeBuddy Working Until a Goal Is Met β
Use
/goalto set a completion condition. CodeBuddy will keep working across multiple turns until the condition is satisfied before handing control back to you.
Version requirement: The
/goalcommand requires@tencent-ai/codebuddy-codewith the goal feature included (theGoalServicemodule in agent-cli).
/goal sets a completion condition that CodeBuddy continuously works toward without you prompting it step by step. At the end of each turn, a small-fast model evaluator checks whether the condition holds β if not, CodeBuddy automatically starts the next turn rather than returning control to you. Once the condition is met, the goal is automatically cleared.
/goal is suitable for tracking substantial work that has a verifiable end state:
- Migrating a module to a new API until all call sites compile and tests pass
- Implementing a design doc until all acceptance criteria are met
- Splitting a large file into focused modules until each one is within the size budget
- Processing a list of issues with a certain label until the queue is empty
This document covers:
- Comparison with other autonomous workflows: choosing between
/goal,/loop, and Stop hooks - Setting a goal and tips for writing effective conditions
- Checking status, clearing early, running in non-interactive mode
- How the evaluator works
- Implementation notes and known limitations
Comparison with Other Autonomous Workflows β
The following three approaches all allow a session to keep running between multiple prompts. Choose based on "who triggers the next turn":
| Approach | When the next turn starts | When it stops |
|---|---|---|
/goal | Immediately after the previous turn ends | Evaluator confirms the condition is met |
/loop | Triggered by a time interval | You stop it manually, or the model decides work is done |
| Stop hook | Immediately after the previous turn ends | Your script or prompt decides |
Both /goal and Stop hooks trigger after each turn. /goal is a session-level shortcut β you type a condition, and it only applies to the current session. Stop hooks are defined in settings, apply to all sessions within their scope, and can run either deterministic scripts or model-evaluated prompts.
Tip: All of the above keep "the current session" running. If you need work that runs independently of the current session (e.g., nightly test runs or morning issue triage), see Scheduled Tasks.
Using /goal β
Each session can only have one active goal at a time. The same command takes on the role of "set / view / clear" depending on the arguments.
Setting a Goal β
Follow /goal with the condition you want to satisfy. If there is already an active goal, the new one replaces it (the old goal's hook is automatically unregistered).
text
/goal all tests in test/auth pass and the lint step is cleanOnce set, CodeBuddy immediately starts a turn, passing "the condition itself" as the instruction to the main agent β you don't need to send an additional prompt. A β /goal active (Xs) indicator also appears at the bottom-right of the input box, refreshing every second with the elapsed time, so you always know when goal mode is active.
After each turn, the evaluator returns a short reason explaining "why the condition is / is not yet met." This reason is injected into the conversation history as an isMeta=true internal message, allowing the model to see the evaluator's perspective on the next turn and precisely address what is missing β this is the key mechanism for the model to "know what steps remain."
Session-level behavior: The goal keeps running until the condition is met or you run
/goal clear. Run/goal(no arguments) to see stats like turns and tokens.
Writing an Effective Condition β
The evaluator judges the condition based solely on what CodeBuddy has already expressed in the conversation β it does not run commands or read files itself. So the condition should be phrased in a form that "CodeBuddy's own output can prove." "All tests in test/auth pass" works because CodeBuddy will run the tests itself, and the results will be in the transcript for the evaluator to read.
A condition that robustly supports multi-turn work typically includes:
- A measurable end state: test results, build exit codes, file counts, an empty queueβ¦
- A provable method: e.g.,
\`npm test\` exits 0or\`git status\` is clean - Inviolable constraints: things that must not be changed along the way, e.g., "no other test file is modified"
The condition length limit is 4000 characters.
If you want to set a fallback upper bound for the goal, add a turn/time clause to the condition, e.g., or stop after 20 turns. CodeBuddy will check progress against this clause each turn, and the evaluator can also read it from the conversation.
Checking Status β
Run /goal without arguments:
text
/goalIn the TUI this opens a goal recap panel; in Web UI / ACP clients it opens an equivalent panel via ACP broadcast; in headless / SDK environments without a UI it falls back to plain text output.
Panel contents include:
- The condition
- Elapsed time
- Number of evaluated turns
- Token consumption (incremental during goal)
- The most recent reason from the evaluator
If there is no active goal but a goal was previously achieved in this session, the panel shows that goal's condition, duration, turn count, and token count.
Clearing a Goal Early β
text
/goal clearThe following tokens are all treated as synonyms for clear: stop, off, reset, none, cancel. These are only recognized as clear commands on exact single-token match β /goal stop using deprecated API is still treated as "set a new condition" and won't be consumed.
Running /clear to restart the session also removes the active goal (hook unregistered + meta cleaned up).
Resuming a Session with a Goal β
When resuming a session via --resume / --continue, an unfinished goal is restored (both condition and scope are preserved).
Current limitation: On resume, the original goal's createdAt / turnCount / token starting point are carried over β the timer and counters are not reset. If you want to "restart the clock," first run
/goal clearand then/goal <condition>again. Goals that were already achieved or cleared will not be restored (meta has been deleted).
Running in Non-Interactive Mode β
/goal works in non-interactive (headless) mode and Remote Control. In -p mode, setting a goal causes the evaluator loop to run until completion:
bash
codebuddy -p "/goal CHANGELOG.md has an entry for every PR merged this week"To terminate early before the condition is met, press Ctrl+C.
How the Evaluator Works β
/goal is a wrapper around a session-level prompt-based Stop hook. Whenever CodeBuddy's main agent finishes a turn, the current condition + the current conversation are sent together to the configured small-model evaluator. The evaluator returns a three-state result (yes / no / unreachable) with a short reason:
- Yes (
ok: true): Clears the goal, logs an "achieved" event, and the UI shows aβ Goal achievedstatus bar. - No (
ok: false): Injects the reason as anisMeta=trueuser message into the history (so the main model sees what to work on next), and lets CodeBuddy continue working. Also writes agoal-progressUI status bar:⯠Goal not yet met⦠continuing. - Unreachable (
ok: false, impossible: true): Used when the evaluator determines that "this goal simply cannot be completed in the current session" (the condition is self-contradictory, required capabilities/resources are unavailable, or the model has exhausted reasonable attempts). The goal is immediately cleared, and the UI showsβ Goal could not be achieved, preventing the loop from getting stuck.
The evaluator uses the small model bound to the lite slot in the product configuration (mapped to gpt-5.1-codex-mini / gemini-2.5-flash / DeepSeek deepseek-v4-flash etc. depending on the model provider). Evaluation only reads the existing transcript without invoking tools, so using a small model is both fast and cheap.
Billing: Tokens consumed by the evaluator are billed to the small model account and are typically negligible compared to the main turn.
Evaluation Window Constraint β
To prevent "achieved immediately after setting" β where a previously achieved goal in the same session leaves a success response in the transcript β we inject the current goal's createdAt (ISO 8601) into the evaluator's user prompt with an explicit instruction:
Evaluate ONLY the conversation that happened AFTER this timestamp. Earlier messages MUST NOT be used as evidence.
If no qualifying activity has occurred since the goal was set, the evaluator must return {"ok": false, "reason": "Goal was just set; no work has been done yet against the new condition."}.
History Sanitization for the Evaluator β
Before feeding history to the evaluator, we filter out the following extended item types (they are project-specific and unrecognized by the SDK):
goal-result/goal-progress(goal's own UI status items)summary/topic/ai-title/custom-titlefile-history-snapshot
These items are neither user input nor assistant responses, have no value for the evaluator's judgment, and would trigger Unknown item type warnings from the SDK.
Implementation Notes and Known Limitations β
The following table summarizes key behaviors and known limitations of the current implementation for debugging reference:
| Behavior | Status | Notes |
|---|---|---|
| Set / replace / kick-off | β | Immediately starts a turn after setting; auto-replaces when there's already an active goal |
/goal clear aliases | β | Supports five single-token synonyms: stop / off / reset / none / cancel |
/clear also clears active goal | β | Unregisters goal hook and cleans meta on session restart |
| Condition limit | β | 4000 characters |
| Reason feedback into history | β | Injection format: Stop hook feedback: [<condition>]: <reason> |
| Three-state semantics (ok / not-yet / impossible) | β | Immediately clears goal on unreachable, preventing infinite loops |
| Evaluator uses small model | β | Uses the model bound to lite slot (mapped per provider) |
/goal no-arg β status view | β | TUI / Web UI panel + headless text fallback |
Persistent running indicator β /goal active (Xs) | β | Always visible at bottom-right of input box, 1Hz refresh |
--resume turn / timer / token reset | β Pending | Currently carries over original createdAt / turnCount; use /goal clear + re-set to restart |
See Also β
/loopfor scheduled tasks: Triggers repeatedly on a time interval, rather than until a condition is met- Hooks Guide / Hooks Reference: Understand the underlying mechanism of prompt-based Stop hooks; write your own when you need more complex evaluation logic
- Non-interactive (headless) mode: Run
/goalin CI / scripts via-p - Remote Control: Trigger goals in Web UI / WeChat channels
- Slash Commands Overview: Index of all built-in slash commands