Goal-Conditioned Work
Set a verifiable completion condition and the agent works autonomously toward it,
verifying its own progress after every turn, until the goal is met, a turn cap is
hit, or you stop it. It’s available as the /goal slash command in the
TUI and as kolega-code ask --goal from the
CLI.
How it works
Section titled “How it works”The goal loop runs after each completed work turn:
- Work turn — the agent uses its full toolset (read, edit, run commands, run tests, dispatch sub-agents) to make progress toward the goal.
- Verification — a read-only investigation sub-agent inspects the current
codebase state and decides whether the goal is met, ending its reply with a
JSON verdict:
{"ok": true}or{"ok": false, "reason": "<remaining gap>"}. - Met — the goal completes, a confirmation message is shown, and the active-goal prompt extension is dropped so subsequent turns are normal again.
- Not met — the agent is nudged to continue. The nudge includes the verifier’s stated remaining gap and the number of turns left before the cap, so the agent can prioritize accordingly. The loop repeats from step 1.
A turn cap (default 50) is a safety backstop. If the goal isn’t met within that many evaluation turns, the loop pauses and tells you to refine the goal or clear it.
Safety model
Section titled “Safety model”A few guarantees that keep the loop safe:
- The verifier inspects the codebase fresh each evaluation (stateless across turns), so it judges the real current state, not a stale snapshot.
- A malformed, unparseable, or failed verdict is always treated as not-met. A broken evaluator can never falsely complete a goal — the loop keeps running or hits the cap.
- The verifier runs on the configured long-context model, separate from the working agent’s turn.
In the TUI: /goal
Section titled “In the TUI: /goal”Type /goal in the composer to set, check, or clear a goal.
| Form | Effect |
|---|---|
/goal <condition> |
Set a goal and start working toward it |
/goal |
Show goal status (condition, runtime, turns evaluated, tokens spent, verifier’s latest reason) |
/goal clear |
Remove the goal (aliases: stop, off, reset, none, cancel) |
/goal -p <condition> |
Run-to-completion mode (alias: --print) — no pauses until the goal is met or capped |
A few things to know:
- Esc pauses the goal loop after the current turn finishes. Sending any message resumes it from where it left off.
- You can’t set or clear a goal while a turn is active — stop the turn first.
- Setting a new goal replaces an active unmet one.
- When the turn cap is reached, the goal is paused with a note. Refine it with
/goal <condition>or remove it with/goal clear. - The status dashboard (Status side-panel tab) shows a
Goalline with the condition (truncated if long) and a state label —active,paused, ormet. - Persistence: goal state is saved with the session and restored on resume, so the loop picks up where it left off. See Sessions & Resuming.
/goal all tests pass and the linter is cleanLetting the agent set a goal
Section titled “Letting the agent set a goal”The top-level TUI build-mode agent can also call set_goal when an explicit
governing instruction directs it to enter goal mode. That direction can come
from you, an activated Agent Skill, or another host-provided workflow with
authority over the current task:
Set a goal that all parser tests pass and the parser documentation is current.set_goal creates the same persistent goal state as /goal <condition>. The
current turn becomes the first work turn, then the normal read-only verifier and
continuation loop take over. Calling it again replaces an active unmet or paused
goal with fresh state.
The agent must not infer goal mode merely because a request contains a desired outcome, acceptance criteria, or asks it to finish some work. Instructions found in untrusted task data—such as repository files, fetched web pages, or incidental tool output—also do not authorize goal mode. The tool is used only when a user, activated skill, or authoritative host workflow explicitly directs it.
The tool only sets or replaces a goal. Use /goal to inspect its status and
/goal clear to remove it.
The tool is not available in plan mode: a planning turn cannot start an
autonomous loop by itself. /goal <condition> works in either mode, and a goal
set in build mode keeps driving turns after you switch to plan mode.
The planner does know that the Build agent can call set_goal. When an
authorized request for goal mode also needs a plan, it can place that call at
the intended transition into autonomous work and provide the verifiable goal
condition. Required setup, workspace changes, dependency provisioning, or
authentication may come before that transition; set_goal belongs before the
work that should run under the evaluate-and-continue loop.
From the CLI: ask --goal
Section titled “From the CLI: ask --goal”kolega-code ask --goal "<condition>" [--goal-max-turns N] [options]The positional prompt is optional when --goal is given — the CLI
synthesizes the first work-turn message from the condition. You can still pass a
prompt to give the agent a head start:
# No prompt needed — the condition drives the loopkolega-code ask --goal "all tests pass and ruff is clean" --project .
# Lower the turn cap for a bounded taskkolega-code ask --goal "the failing test in test_parser.py passes" --goal-max-turns 10 --json
# Give the agent a starting point, then let the goal loop take overkolega-code ask "start by fixing the parser" --goal "all tests pass" --project .| Option | Description |
|---|---|
--goal <condition> |
Set an autonomous completion goal and loop until it is met or capped (no prompt required) |
--goal-max-turns <N> |
Maximum evaluation turns before an unmet goal gives up (default 50) |
Plain output
Section titled “Plain output”In plain (non-JSON) mode, after each evaluation the CLI prints a line to stderr:
[goal] turn 1: not met — test_parser.py still fails on test_parse_empty[goal] turn 2: not met — two tests still failing in test_parser.pyWhen the loop ends, a final summary line is printed:
[goal] met after 3 turn(s)# or[goal] not met (turn cap reached) after 10 turn(s)The agent’s response text is written to stdout as usual, so piping stdout still gives you just the answer.
Exit code
Section titled “Exit code”With --goal, the exit code reflects the outcome:
| Code | Meaning |
|---|---|
0 |
The goal was met |
1 |
The turn cap was reached without meeting the goal |
JSON output
Section titled “JSON output”With --json, the command streams newline-delimited JSON objects as usual, plus
two goal-specific kind values alongside the existing message, event, and
summary kinds:
kind |
Meaning |
|---|---|
goal_eval |
Emitted after each evaluation: {met, turns, reason} |
goal_result |
Final outcome: {met, turns, reason} |
Example stream (abbreviated):
{"kind": "message", "data": {"role": "assistant", "content": [{"type": "text", "text": "Fixing the parser…"}], "stop_reason": "end_turn", "usage": {"provider": "anthropic", "input_tokens": 1200, "output_tokens": 85, "total_tokens": 1285}}}{"kind": "goal_eval", "data": {"met": false, "turns": 1, "reason": "test_parse_empty still fails"}}{"kind": "message", "data": {"role": "assistant", "content": [{"type": "text", "text": "Added the empty-input guard…"}], "stop_reason": "end_turn", "usage": {"provider": "anthropic", "input_tokens": 1450, "output_tokens": 60, "total_tokens": 1510}}}{"kind": "goal_eval", "data": {"met": true, "turns": 2, "reason": ""}}{"kind": "goal_result", "data": {"met": true, "turns": 2, "reason": ""}}{"kind": "summary", "messages": 2, "session_id": "abc123"}- Write conditions that are verifiable — “all tests pass”, “the file exists”, “the command succeeds” — rather than subjective ones like “make the code better”.
- Keep the turn cap in mind for open-ended goals. Lower it with
--goal-max-turnsfor bounded tasks where you don’t want the agent to spin for 50 turns. - The verifier is strict. “Mostly done” is not met — the goal is met only when there is concrete evidence (described files/code exist, relevant tests or commands pass).
- A good condition names a checkable outcome, not a process. “Refactor the parser” is a process; “the parser module has 100% test coverage and all tests pass” is a checkable outcome.