diff --git a/docs/architecture/upstream-routing.md b/docs/architecture/upstream-routing.md index 0ef2901a34..64311c023f 100644 --- a/docs/architecture/upstream-routing.md +++ b/docs/architecture/upstream-routing.md @@ -523,9 +523,9 @@ endpoint exists has three independent guards: 3. **`agent_models` default is empty.** Slice 2's `PipelineConfig.agent_models` field defaults to `{}`, and the repository-level `default_agent_model` defaults to `None`. - Without operator action, refine/plan phase spawns resolve to the - built-in `"fable"` Claude path; every other spawn resolves to - `"opus"`. Both use `upstream="anthropic"`. + Without operator action, all spawns resolve to the built-in + `"opus"` Claude path (fable is no longer the default, though still + opt-in selectable). All use `upstream="anthropic"`. Any one of the three suffices to keep the LiteLLM client cold on a given deployment. All three are independent: a misconfiguration on diff --git a/docs/guides/per-agent-models.md b/docs/guides/per-agent-models.md index c01fe6a3eb..054a9d6ab7 100644 --- a/docs/guides/per-agent-models.md +++ b/docs/guides/per-agent-models.md @@ -68,8 +68,9 @@ PipelineConfig( Default-constructed `PipelineConfig.agent_models` is `{}` — every existing pipeline continues to spawn every role on the built-in -Claude default with `upstream="anthropic"` (`"fable"` for the -refine/plan phase roles, `"opus"` for everything else). +Claude default with `upstream="anthropic"` (`"opus"` for all roles — +fable is no longer the built-in default, though it remains opt-in +selectable via `agent_models` / `default_agent_model`). ### Repository-level default — `default_agent_model` @@ -102,20 +103,11 @@ in `orchestrator/agent_model_resolution.py` walks the chain: 1. `pipeline_config.agent_models.get(role.value)` — per-pipeline, per-role override 2. `get_default_agent_model(repo)` — repository-level default -3. Built-in default — `"fable"` for the refine and plan phase roles - (producers and reviewers, `_FABLE_DEFAULT_ROLES`), `"opus"` for - everything else - -> **Minimum sandbox Claude Code version.** The `fable` / `fable[1m]` -> aliases require Claude Code ≥ 2.1.170. The sandbox's -> `CLAUDE_CODE_VERSION` build-arg in `sandbox/Dockerfile` defaults to -> `latest` (#3137 — `stable` lagged the fable launch and crash-looped -> refine/plan agents per #3136). A build-time gate in the Dockerfile -> fails the image build if the installed binary doesn't know `fable` -> or `opus`, so a stale pin surfaces at build time rather than at -> spawn. If you need to pin to an older `CLAUDE_CODE_VERSION` that -> predates the alias, also set a repo-level `default_agent_model: opus` -> AND temporarily drop `fable` from the Dockerfile gate. +3. Built-in default — `"opus"` for all roles. Fable is no longer the + built-in default (it remains opt-in selectable via `agent_models` / + `default_agent_model`); the refine/plan branch + (`_FABLE_DEFAULT_ROLES`) now resolves to opus alongside everything + else. The result is an `AgentModelDecision` dataclass with fields `(claude_code_alias: str, upstream: str, upstream_model: str | None, @@ -689,8 +681,7 @@ optional `branch` override: Per-pipeline `agent_models` entries **override** the repo-level `default_agent_model`. Both can be unset — the resolver falls back to -the built-in default (`"fable"` for refine/plan roles, `"opus"` -otherwise). +the built-in default (`"opus"` for all roles). ### 4. Run the pipeline and observe routing @@ -768,8 +759,7 @@ invariant](../architecture/upstream-routing.md#no-op-by-default-invariant)): 3. **`agent_models` default is empty.** Both `PipelineConfig.agent_models` and repo-level `default_agent_model` default to nothing — the resolver returns - the built-in Anthropic path (`"fable"` for refine/plan roles, - `"opus"` otherwise). + the built-in Anthropic path (`"opus"` for all roles). Any single guard suffices. All three are independent; a misconfiguration on one does not silently activate the LiteLLM path. diff --git a/sandbox/Dockerfile b/sandbox/Dockerfile index 9483bd84d8..eafe67272c 100644 --- a/sandbox/Dockerfile +++ b/sandbox/Dockerfile @@ -247,10 +247,11 @@ RUN if [ "$CLAUDE_AGENT_SDK_VERSION" = "latest" ]; then \ # Install Claude Code CLI (native installer) for the egg user # # Channel choice: 'latest', not 'stable'. orchestrator/agent_model_resolution.py -# defaults refine/plan roles to bare model-family aliases at launch ('fable'), -# and the stable channel lags those launches — at the time of #3136 stable -# (2.1.153) predated the fable alias entirely, so a stable-channel image -# crash-loops every refine/plan agent at spawn. +# spawns agents on bare model-family aliases ('opus' is the built-in default; +# 'fable' remains opt-in selectable per #3197), and the stable channel lags +# new family launches — at the time of #3136 stable (2.1.153) predated the +# fable alias entirely, so a stable-channel image crash-loops any agent +# spawned on an alias it doesn't know. ARG CLAUDE_CODE_VERSION=latest # Cache-bust (#3136): BuildKit re-checks this URL's content on every build, so # the install layer below re-runs exactly when the release channel moves to a @@ -285,8 +286,8 @@ RUN su - egg -c "curl -fsSL https://claude.ai/install.sh | bash -s -- $CLAUDE_CO # Save installed version for update checks (non-fatal if this fails) (/home/egg/.local/bin/claude --version 2>/dev/null | head -1 > /home/egg/.local/VERSION || true) && \ # Fail the build if the installed build predates a model family that - # orchestrator/agent_model_resolution.py uses as a built-in default - # ('fable' for refine/plan roles, 'opus' for everything else): a Claude + # orchestrator/agent_model_resolution.py can spawn agents on ('opus', the + # built-in default; 'fable', opt-in selectable per #3197): a Claude # Code build whose alias table lacks the family rejects the bare alias at # session init and the agent crash-loops at spawn (#3136). No auth is # mounted at build time, so this is a strings-level heuristic — a build @@ -295,13 +296,12 @@ RUN su - egg -c "curl -fsSL https://claude.ai/install.sh | bash -s -- $CLAUDE_CO for family in fable opus; do \ if ! grep -aq "claude-${family}-" /home/egg/.local/bin/claude; then \ echo "ERROR: installed Claude Code ($(cat /home/egg/.local/VERSION 2>/dev/null)) does not know the '${family}' model family."; \ - echo "agent_model_resolution.py uses '${family}' as a built-in default — agents would crash-loop at spawn (#3136)."; \ + echo "agent_model_resolution.py can spawn agents on '${family}' — agents would crash-loop at spawn (#3136)."; \ echo "Pass a newer build: docker build --build-arg CLAUDE_CODE_VERSION= ..."; \ echo "If pinning to an older CC build is intentional (and every"; \ echo "agent_models entry has been pinned away from '${family}'),"; \ echo "drop '${family}' from the 'for family in ...' list above to"; \ - echo "skip the gate for that family (matches the bypass guidance"; \ - echo "in docs/guides/per-agent-models.md)."; \ + echo "skip the gate for that family."; \ exit 1; \ fi; \ done && \