Claude Code Advisor¶
Advisor pairs a cheap main model (Sonnet/Haiku) doing headless work with a
stronger reviewer model (Opus) that gets consulted automatically at decision
points: before committing to an approach, after a repeated error, and before
the agent declares a task done. It rides on the --advisor <model> CLI flag
(or the advisorModel settings key), requires Claude Code v2.1.98+, and only
works on Anthropic-API auth. Rolled out 2026-07-09.
Design intent: Advisor runs ONLY on unattended cheap-model surfaces, headless
claude -p work with nobody watching in real time. Interactive sessions and
any run already on Opus/Fable get nothing, there's no stronger reviewer to
pair an already-top-tier model with (and pairing Opus with Opus just doubles
cost for no benefit).
The central knob¶
One environment variable, FORGE_CLAUDE_ADVISOR, is the single source of
truth. It names the reviewer model (default opus). Set it empty to disable
Advisor everywhere that reads it; unset it to fall back to the default.
Two thin accessors read that variable so no call site hand-rolls the enable/skip logic:
- Python:
scripts/forge_advisor_config.py—advisor.advisor_args(model)returns["--advisor", "opus"]or[]. - Bash:
scripts/forge_advisor_config.sh—sourceit, thenforge_advisor_flags "$MODEL"prints--advisor opusor nothing (useread -r -a ARR <<< "$(forge_advisor_flags "$MODEL")"to get an array-safe arg list).
Both skip automatically when model contains opus or fable (case
insensitive), so callers never need their own model-tier branch, they can
unconditionally splice the accessor's output into the claude -p argv.
To disable Advisor at the Claude Code engine level regardless of any
--advisor flag already baked into a call site (e.g. mid-incident kill
switch), export CLAUDE_CODE_DISABLE_ADVISOR_TOOL=1 in the process
environment. That's the CLI's own escape hatch, separate from the forge-level
knob above.
Wired surfaces (2026-07-09)¶
| Surface | File | Model(s) | Notes |
|---|---|---|---|
| Dispatcher workers | scripts/forge_dispatcher.sh |
sonnet (default) / haiku / opus | resolve_model() picks the id; advisor args computed once in spawn_worker(). No-op when task requests opus. |
| Autoagent shared runner | autoagent/core/_claude_runner.py |
sonnet (specialist default) / opus (manager) | Single shared runner used by every tick script (manager, specialist). Manager's MANAGER_MODEL=claude-opus-4-8 auto-skips. |
| Followup jobs | scripts/forge_followup.py |
haiku / sonnet (default) / opus per job | /followup skill schedules these; fired by forge-followup-dispatcher.timer. |
/spawn, warm pool, coordinator spawn tool |
scripts/forge_spawn_session.sh |
sonnet (default) / haiku / opus / fable | Canonical implementation shared by the /spawn skill, forge_spawn_pool.sh, and tool_spawn_remote_session in the coordinator bot. Opus/fable spawns unaffected. |
| Spawn-headless-agent skill | .claude/skills/spawn-headless-agent/SKILL.md |
sonnet (default) / haiku / opus | Template snippet now sources forge_advisor_config.sh before the nohup claude -p line. |
Telegram remote bridge /ask |
scripts/forge_telegram_remote_bridge.py (run_claude_ask) |
user-selected (default sonnet) | Escape-hatch one-shot chat command; skipped when user picks opus/fable. |
| Telegram remote bridge routing brain | scripts/forge_telegram_remote_bridge.py (_call_brain_sonnet) |
claude-sonnet-5 (fixed) |
Classifies free-text Telegram messages into actions; always qualifies. |
Restarted after edit: forge-dispatcher.service, forge-remote-bridge.service
(both are long-running processes that hold the old code in memory).
forge_followup.py, forge_spawn_session.sh, and _claude_runner.py are
invoked fresh per call (timer-fired oneshots or ad hoc subprocesses), no
service restart needed, next invocation reads the edited file.
Tested¶
End-to-end: dropped a real trivial haiku task into tasks/pending/, watched
the restarted dispatcher pick it up. Confirmed via ps that the live worker
process argv included --model claude-haiku-4-5-20251001 --advisor opus
--dangerously-skip-permissions --no-session-persistence, and the task
completed normally with the expected result file. Cleaned up after
confirming.
Boundary: raw Anthropic API callers get nothing¶
Advisor is a Claude Code CLI feature. Anything that talks to the Anthropic
Messages API directly instead of shelling out to claude -p cannot use it,
there's no CLI process to attach the flag to. This includes
scripts/forge_brain_server.py (the warm in-process brain server that most
Telegram bot brains prefer, falling back to cold claude -p subprocess only
on failure) and any other direct-SDK integration. Do not attempt to hack
Advisor onto the raw-API path, the intended failover here is the cold
claude -p subprocess call, which DOES get Advisor once/if that fallback
path is wired (see "Not yet wired" below).
Not yet wired (found via grep, left for a follow-up pass)¶
These are all secondary cold-claude -p-subprocess fallback paths behind a
primary raw-API brain-server call (see boundary above), always Sonnet, always
unattended. Wiring them is a 3-line change each (import
forge_advisor_config, splice advisor.advisor_args(model) into the
existing subprocess argv) once there's budget to test them individually:
scripts/forge_telegram_brain.py(call_brain, subprocess fallback branch)scripts/forge_telegram_inbox_brain.py(_call_sonnet, subprocess fallback branch)scripts/forge_food_lookup.py(lookup_sonnet)
Explicitly NOT touched: scripts/forge_session_launcher_api.py¶
Out of scope for this pass, it carries Justin's uncommitted WIP. It has three
headless claude -p call sites that would benefit:
_curate_prompt()(~line 1005): Sonnet, headless "curate a worker briefing" pass, a clean decision-point candidate._name_from_prompt()(~line 1583): Haiku, trivial slug generation, low value for Advisor (matches the CLI's own "skip on short reactive tasks" guidance) but harmless to wire.chat_turn()/api/chat(~line 1971): model is caller-selected (MODEL_IDS[req.model]), backs a live chat UI (chat.justinsforge.com) where a human is reading the reply turn by turn, judgment call whether that counts as "unattended."
Exact edit needed (once the WIP lands and this file is unblocked): add import
forge_advisor_config as advisor near the top (module already sits in
scripts/, plain import works), then splice *advisor.advisor_args(model)
into each of the three cmd/argv lists shown above, mirroring the pattern
used in this same file's sibling scripts (forge_followup.py,
forge_telegram_remote_bridge.py).
Disabling¶
- One surface: prepend
FORGE_CLAUDE_ADVISOR=(empty) to that process's environment (systemd unitEnvironment=line, or the shell that launches it). - Everywhere:
export FORGE_CLAUDE_ADVISOR=in the environment every wired systemd unit and the dispatcher shares, or setCLAUDE_CODE_DISABLE_ADVISOR_TOOL=1for the engine-level kill switch.