Skip to main content
Plan-and-Solve synthesizes a structured plan before the orchestrator starts dispatching. ReWOO (Reasoning Without Observation) extends it: the plan uses #E1, #E2 placeholders resolved at runtime with the outputs of previous steps. Combined, they avoid the expensive pattern of “dispatch an agent, wait, think again, dispatch another”.
When it fires: /plan <task> forces it; CHATCLI_QUALITY_PLAN_FIRST_MODE=always always; auto (default) triggers when ComplexityScore(task) >= 6. To review the plan before executing, use /plan preview <task> (dry-run).
By default, an auto/always trigger now routes to the verified Task Graph, not the in-loop runner described here. The legacy Plan-and-Solve runner has no validation gates and no independent reviewer; the task graph has both. This page documents the planner, the complexity heuristic (still the trigger) and the plan-solve opt-out. See Routing to the task graph.

Plan format

The PlannerAgent, when it receives the PlannerStructuredOutputDirective marker at the start of the task, emits strict JSON:

ReWOO placeholders

Any step can inject the output of earlier steps via #E<n>:
Use .head=N for large tasks (e.g. “given this log #E1.head=500, diagnose”) to bound the context the next step sees.

Complexity heuristic

ComplexityScore(task) → int[0,10] is calibrated to fire Plan-First only when it pays off. The score blends three signals:

Examples

auto does not fire Plan-First. Orchestrator handles directly.

Execution flow

1

User fires /agent or /coder

The task goes into AgentMode.Run().
2

runPlanFirstIfApplicable

Checks cli.pendingPlanFirst (one-shot) or quality.ShouldPlanFirst(cfg, userQuery).
3

Planner dispatch

agentDispatcher.Dispatch([{Agent: planner, Task: PlannerStructuredOutputDirective + userQuery}]).
4

ParsePlan

Tolerant to markdown code fences and trailing prose. Validates: unique IDs, deps point to earlier steps, #E<n> placeholders point to declared IDs.
5

TopologicalOrder + Execute

Stable topological order (lex sort on ties) guarantees reproducibility. Each step: resolve placeholders → dispatcher.Dispatch → store output.
6

Inject report

Two synthetic messages go into cli.history:
  • assistant: the plan JSON (the model sees what was attempted)
  • user: deterministic FinalReport with task/agent/status/output per step + handoff
The second message is role=user (not system) since the April 2026 fix. Models like Claude Sonnet 4.6 refuse completion when the conversation ends on assistant (“This model does not support assistant message prefill”). The synthetic user turn closes the conversation correctly and gives the orchestrator an explicit anchor to finalize.
7

ReAct loop continues (or is skipped, if dry-run)

With the report in history, the orchestrator finalizes without re-executing completed steps. In dry-run mode (/plan preview), the loop is skipped via planDryRunHandled — no orchestrator call is made.

Fault tolerance

A failing step does not abort the run. The behavior is “continue downstream with substituted error”:
This mirrors how the orchestrator already reacts to per-agent errors today (continues, summarizes, lets the model decide). The HadErrors flag is set so Reflexion can decide to escalate.

/plan — manual invocation

/plan accepts six forms. Autocomplete (Tab) offers the subcommands.

Behavior matrix

The cli.pendingPlanFirst flag is consumed and cleared on the first subsequent /agent or /coder invocation. The cli.pendingPlanDryRun flag (exclusive to preview/dry modes) is cleared at the same point and makes AgentMode.Run return before the ReAct loop via planDryRunHandled.
Recommended flow for large changes: /plan preview <task> first → review the JSON → if approved, run /plan coder <same task>.

Routing to the task graph

The complexity heuristic is still the deterministic trigger — but what it triggers changed. By default (CHATCLI_QUALITY_PLAN_FIRST_STRATEGY=taskgraph) a firing auto/always trigger steers the orchestrator to the verified Task Graph instead of running the legacy in-loop runner:
  • A one-line orchestration hint is folded into the user turn, pointing the model at @taskgraph: decompose the work into a DAG with per-task validation contracts, and let the engine verify each task with an independent reviewer before it counts as done.
  • The steer is non-coercive: the task-graph skill still tells the model to skip the graph for genuinely serial work or fewer than ~5 independent tasks, so a deep single-file change is not shoehorned into a DAG.
Set plan-solve to keep the pre-routing behavior (the in-loop runner on this page); explicit /plan always uses the planner regardless of strategy.

Environment variables

Override via persona (CHATCLI_AGENT_PLANNER_*)

PlannerAgent respects the usual per-agent overrides:

Observability

Each run emits structured logs:
And prints a friendly one-liner to the terminal:

Spinner during Plan-First

The “planning structured steps…” spinner shows only during the pure PlannerAgent call (step 1). During PlanRunner execution (step 2), the spinner is intentionally off and a static status line is emitted:
This is critical for safety: steps can trigger interactive approvals (ShellAgent exec, CoderAgent write) via the policy engine. If the spinner stayed active during execution, its \r\033[K repaint would overwrite the approval prompt and block the response. Ctrl+C still cancels normally — the cancelled context propagates through dispatcher and PlanRunner between steps.

Dry-run: what gets rendered

/plan preview <task> renders:
If ParsePlan fails (planner returned malformed JSON or prose), the raw output is printed with a warning — never silent.

When to turn off

Plan-First adds +1 LLM call (the planner) per fired turn. In budget-tight environments, off or threshold=8 save money.

See also

#3 Reflexion

Reflexion consumes HadErrors from the plan runner to generate lessons when steps fail.

Multi-Agent Orchestration

The dispatcher that PlanRunner reuses is the same as the standard orchestrator.

PlannerAgent (pre-PR)

The agent existed before the pipeline — this pattern formalizes how to invoke it deterministically.

Full configuration

All env vars and slashes in one place.