# Keep CodeBuddy Working Until a Goal Is Met

> Use `/goal` to 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 `/goal` command requires `@tencent-ai/codebuddy-code` with the goal feature included (the `GoalService` module 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](#comparison-with-other-autonomous-workflows): choosing between `/goal`, `/loop`, and Stop hooks
- [Setting a goal](#setting-a-goal) and [tips for writing effective conditions](#writing-an-effective-condition)
- [Checking status](#checking-status), [clearing early](#clearing-a-goal-early), [running in non-interactive mode](#running-in-non-interactive-mode)
- [How the evaluator works](#how-the-evaluator-works)
- [Implementation notes and known limitations](#implementation-notes-and-known-limitations)


## 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 clean
```

Once 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](#how-the-evaluator-works) 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 0`` or ``\`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
/goal
```

In 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 clear
```

The 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 clear` and 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](./headless.md) and [Remote Control](./remote-control.md). 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](./hooks.md). 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 achieved` status bar.
- **No (`ok: false`)**: Injects the reason as an `isMeta=true` user message into the history (so the main model sees what to work on next), and lets CodeBuddy continue working. Also writes a `goal-progress` UI 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-title`
- `file-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

- [`/loop` for scheduled tasks](./scheduled-tasks.md#使用-loop-创建循环任务): Triggers repeatedly on a time interval, rather than until a condition is met
- [Hooks Guide](./hooks-guide.md) / [Hooks Reference](./hooks.md): Understand the underlying mechanism of prompt-based Stop hooks; write your own when you need more complex evaluation logic
- [Non-interactive (headless) mode](./headless.md): Run `/goal` in CI / scripts via `-p`
- [Remote Control](./remote-control.md): Trigger goals in Web UI / WeChat channels
- [Slash Commands Overview](./slash-commands.md): Index of all built-in slash commands
