Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions docs/design/2026-08-25-goal-draft-skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# goal-draft: a bundled skill that writes verifier-judgeable Goals

## Problem

`/goal <objective>` accepts any non-empty string. The objective is judged by an independent verifier that only sees transcript evidence (`goalJudge.ts`, `goal-verifier.ts`): printed text cannot prove that tests passed or files changed, claims about user actions need a real user message, and an objective nobody can evidence keeps the loop running until a limit stops it. Nothing in the product tells users this. The only guidance was the web-shell placeholder `all tests pass and the lint is clean`, which itself has no check attached.

## Survey

The closest existing analogues both converge on the same shape: OpenAI's curated `define-goal` skill for Codex `/goal` (one objective string; five questions — what will be true, what evidence proves it, what threshold, what scope, when to stop and ask; at most one clarifying question) and the community `agent-goal-skill` (a fixed Goal / Context / Constraints / Done when / On block block, binary shell-checkable criteria, `<TODO>` for unknowns). Claude Code's `/goal` documentation asks for "one measurable end state, a stated check, constraints that matter" and a turn/time clause; Claude Code has no goal-writing skill — its `ProposeGoal` tool prompt carries the rules instead (≤500 characters, verifiable from the conversation alone, never widen scope). Ralph-style loops push the stop condition into the harness and rank guardrails; Anthropic's long-running-agent harness note flags "editing tests to pass" as the failure to forbid. Spec/PRD skills (superpowers, ai-dev-tasks, spec-kit, Kiro EARS, BMAD) contribute the interview discipline: batch questions, offer options, mark assumptions instead of blocking, and never start implementing.

What differs for an autonomous loop versus a PRD: the stop condition must be machine-checkable, there must be a negative stop (budget) and a block protocol, the same agent grades itself so cheap exits must be forbidden, and the whole thing must fit one session's evidence budget.

## Design

A bundled skill at `packages/core/src/skills/bundled/goal-draft/SKILL.md`, registered like every bundled skill as `/goal-draft` (model-invocable too). It is instructed to be read-only and auto-approves only the non-mutating tools (`get_goal`, `read_file`, `glob`, `grep_search`); `allowedTools` is an additive grant, so the read-only discipline is enforced by the skill's prose. `ask_user_question` is deliberately not granted: an allow rule for it would override the tool's `'ask'` default session-wide and run it without the question dialog, fabricating a declined-answer result. It never runs the checks, never edits, and cannot call `/goal` (built-in commands are not model-invocable by design).

Steps:

0. Decide whether the request is a Goal at all; one-shot tasks and judgement calls are not.
1. `get_goal`; if a Goal is active, offer `edit` versus `set`, never a second concurrent goal.
2. Ground in the workspace: verify named files and find the real test/lint/build commands; never invent paths, mark unknowns `<TODO>`.
3. At most one round of 1–3 multiple-choice questions, only when the answer changes the check, scope, or budget; if no verification path can be found the skill must ask rather than invent one; headless takes the recommended default and marks `[ASSUMPTION]`.
4. Draft in a fixed contract, one paragraph, English labels: `Outcome:` / `Done when:` (numbered, binary, at least one tool-observable with "paste that line") / `Must not:` / `Budget:` / `On block:` / `Context:`.
5. Self-check nine rules (binary items, a named check, no subjective adjectives, no "after the user confirms", budget or block clause, one outcome, verified context, length, irreversible actions in Must not) and print the objective plus a one-line `/goal set …` the user can run as-is. Stop.

The labels map onto verifier rules: `Done when` items produce `external_fact` evidence; `Must not` closes the exits the verifier cannot see; `On block` routes decisions to `blockerKind: authority` instead of a "user confirms" completion condition; `Budget` avoids indefinite "insufficient evidence" loops.

The objective is handed over on one line because `parseGoalCommand` splits on whitespace and re-joins with single spaces, so newlines would be flattened anyway.

## Scope of this change

- The skill and its test (`SKILL.test.ts` pins the allowed tools, the step order, the contract labels, the question rules, and the "do not run /goal, do not begin the task" stop).
- `docs/users/features/goals.md` (commands, how a Goal is judged, writing a good objective, `/goal-draft`), rows in `commands.md`, a pointer from `headless.md`.
- The web-shell Goals dialog placeholder now shows an objective with a check, a guardrail, and a budget in both locales.

## Later phases (not in this change)

- A `propose_goal` core tool with an approval dialog, mirroring Claude Code's `ProposeGoal` + `modelProposedGoals` (read from user/policy settings only), so the skill can offer "Set this goal" instead of a line to paste. `parseGoalCommand` keeps newlines for `set`/`edit`.
- A deterministic lint on `/goal set` (rules 1–6 above) that warns and points at `/goal-draft`, and a "refine" entry in the web-shell Goals dialog.

## Verification

- `packages/core`: `bundled-skills.integration.test.ts` parses the new SKILL.md; `goal-draft/SKILL.test.ts` (8 tests); `tsc --noEmit` clean; eslint clean.
- Built CLI run headless in a throwaway project with a `test` and `coverage` script: `/goal-draft make the auth tests pass and raise coverage` produced a six-part objective citing the real `node --test test/` and `c8 …` scripts, two `[ASSUMPTION]` notes, and a `/goal set …` line; the project tree was untouched. `/goal-draft clean up the auth module` turned "clean" into an explicit, assumption-tagged observable definition (tests pass, exports referenced, diff confined to `src/auth`) and invited the user to redefine it.
1 change: 1 addition & 0 deletions docs/users/features/_meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ export default {
hooks: 'Hooks',
'status-line': 'Status Line',
'scheduled-tasks': 'Scheduled Tasks',
goals: 'Goals',
tips: 'Contextual Tips',
};
3 changes: 2 additions & 1 deletion docs/users/features/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Commands for managing AI tools and models.
| `/permissions` | Manage permission rules | `/permissions` |
| `/agents` | Manage subagents | `/agents manage`, `/agents create` |
| `/arena` | Manage Arena sessions | `/arena start`, `/arena stop`, `/arena status`, `/arena select` (alias `choose`) |
| `/goal` | Set a goal — keep working until condition met | `/goal <condition>`, `/goal clear` |
| `/goal` | Set a goal — keep working until condition met (see [Goals](./goals.md)) | `/goal <condition>`, `/goal clear` |
| `/tasks` | List background tasks | `/tasks` |
| `/workflows` | Inspect workflow runs; cooperatively pause/resume a background run | `/workflows`, `/workflows <runId>`, `/workflows p <runId>` |
| `/lsp` | Show LSP server status | `/lsp` |
Expand All @@ -144,6 +144,7 @@ These commands invoke bundled skills that provide specialized workflows.
| `/review` | Multi-agent code review (12 parallel agents at high effort) | `/review`, `/review 123`, `/review 123 --comment`, `/review --effort low` |
| `/coordinate` | Coordinate read-only workers and one optional worktree writer | `/coordinate investigate and fix the authentication regression` |
| `/loop` | Run a prompt on a recurring schedule | `/loop 5m check the build` |
| `/goal-draft` | Turn a fuzzy intention into a verifiable `/goal` objective | `/goal-draft make the auth tests pass` |
| `/simplify` | Review recent changes and apply safe cleanup edits directly | `/simplify`, `/simplify focus on duplication` |
| `/qc-helper` | Answer questions about Qwen Code usage and configuration | `/qc-helper how do I configure MCP?` |

Expand Down
58 changes: 58 additions & 0 deletions docs/users/features/goals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Goals

A Goal keeps Qwen Code working across turns until a stated condition is met. Set one with `/goal <objective>`; after each turn an independent verifier checks the transcript, and the session keeps going until the objective is verified complete, verified blocked, paused, or cleared.

## 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](./headless.md#run-a-persistent-goal).

## 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:` | When to give up: "stop as blocked after 20 turns" or a time limit. |
| `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 and roughly under 1,200 characters. `/goal set` and `/goal edit` collapse newlines to spaces, so number the items rather than relying on line breaks.

| 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: stop as blocked after 20 turns.` |
| 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 checks whether the request is a Goal at all, reads the workspace for the real test and lint commands instead of guessing, asks at most one round of multiple-choice questions when the answer changes the check or the scope, drafts the objective in the format above, runs the self-check, and prints a `/goal set …` line you can run as-is. It never starts the work itself and never sets the Goal on your behalf.

Pass an existing objective to tighten it: `/goal-draft all tests pass and the lint is clean`.

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](./skills.md) for how bundled skills are discovered.
2 changes: 2 additions & 0 deletions docs/users/features/headless.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ Use the same `qwen --continue -p "<control>"` pattern for the other operations:
| `/goal resume` | Resume an eligible Goal and start headless Goal work. |
| `/goal clear` | Clear the Goal without confirmation or a model call. |

A Goal is only as good as its completion condition. See [Goals](./goals.md) for what the verifier can and cannot judge, and use `qwen -p "/goal-draft <intent>"` to have the objective drafted before you set it.

Runtime-scheduled Goal continuation segments do not count against `--max-session-turns`, but real user prompts still do. Explicit `--max-wall-time` and `--max-tool-calls` budgets continue to apply; exceeding either pauses active Goal work before the run exits with the budget-specific error.

With `--output-format stream-json`, each Goal status change emits a `stream_event` whose `event.type` is `goal_state`. This canonical state event is emitted even without `--include-partial-messages`. When partial messages are enabled, the older `active_goal` event follows as a compatibility projection; automation should treat `goal_state` as authoritative.
Expand Down
Loading
Loading