#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.Plan format
ThePlannerAgent, 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>:
| Syntax | Effect |
|---|---|
#E1 | Replaces with the trimmed full output |
#E1.summary | First non-empty line of the output |
#E1.head=200 | First 200 runes with … if truncated |
#E1.last=200 | Last 200 runes with leading … |
Complexity heuristic
ComplexityScore(task) → int[0,10] is calibrated to fire Plan-First only when it pays off. The score blends three signals:
| Signal | Cap | What counts |
|---|---|---|
| Action verbs | 5 | implement, add, create, fix, refactor, write, test, deploy, build, run, update, … — bilingual (en + pt-BR: implementar, adicionar, criar, corrigir, escrever, …) |
| File artefacts | 3 | Matches on \b[\w./-]+\.(go|ts|py|md|yaml|…)\b + Dockerfile|Makefile|… |
| Sequencers | 2 | then, after, finally, e depois, e em seguida, por fim, após, … |
Examples
- Trivial (score 1)
- Multi-action (score 6+)
- PT-BR (score 6+)
auto does not fire Plan-First. Orchestrator handles directly.Execution flow
runPlanFirstIfApplicable
Checks
cli.pendingPlanFirst (one-shot) or quality.ShouldPlanFirst(cfg, userQuery).Planner dispatch
agentDispatcher.Dispatch([{Agent: planner, Task: PlannerStructuredOutputDirective + userQuery}]).ParsePlan
Tolerant to markdown code fences and trailing prose. Validates: unique IDs, deps point to earlier steps,
#E<n> placeholders point to declared IDs.TopologicalOrder + Execute
Stable topological order (lex sort on ties) guarantees reproducibility. Each step: resolve placeholders → dispatcher.Dispatch → store output.
Inject report
Two synthetic messages go into
cli.history:- assistant: the plan JSON (the model sees what was attempted)
- system: deterministic
FinalReportwith task/agent/status/output per step
Fault tolerance
A failing step does not abort the run. The behavior is “continue downstream with substituted error”:HadErrors flag is set so Reflexion can decide to escalate.
/plan — manual invocation
cli.pendingPlanFirst flag is consumed and cleared on the first subsequent /agent or /coder invocation.
Environment variables
| Env var | Default | Values | Effect |
|---|---|---|---|
CHATCLI_QUALITY_PLAN_FIRST_MODE | auto | off|auto|always | Trigger mode |
CHATCLI_QUALITY_PLAN_FIRST_THRESHOLD | 6 | 0..10 | Minimum score for auto to fire |
Override via persona (CHATCLI_AGENT_PLANNER_*)
PlannerAgent respects the usual per-agent overrides:
Observability
Each run emits structured logs:When to turn off
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.