Skip to content

Switch default refine/plan model from fable to opus - #3197

Merged
jwbron merged 3 commits into
mainfrom
egg/switch-default-model-to-opus
Jun 13, 2026
Merged

Switch default refine/plan model from fable to opus#3197
jwbron merged 3 commits into
mainfrom
egg/switch-default-model-to-opus

Conversation

@jwbron

@jwbron jwbron commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Fable has been disabled, so the drafting-heavy upstream phases (refine/plan) now default to "opus" instead of "fable". Implement and downstream phases were already opus, so this unifies the default.

Changes:

  • orchestrator/agent_model_resolution.py: set FABLE_DEFAULT_MODEL = "opus".
  • orchestrator/tests/test_concurrent_executor.py: update the regression test to expect "opus" for refine/plan roles.

Pipeline-level agent_models and repo-level default_agent_model still override this as before.

Fable has been disabled, so the drafting-heavy upstream phases should
now default to opus alongside implement/downstream phases. The
default-agent-model override paths (pipeline-level agent_models and
repo-level default_agent_model) still take precedence as before.

Closes #3180 is a follow-up; no issue opened for this switch.
@james-in-a-box

This comment has been minimized.

The fable→opus default switch updated test_concurrent_executor.py but
missed the refine/plan regression and effort-pinning tests in
test_agent_model_resolution.py. Update them to expect opus/effort=None
and refresh the now-stale fable docstrings.
@james-in-a-box

Copy link
Copy Markdown
Contributor
Autofix tracking
{"Test/Unit Tests": 1}

@james-in-a-box

This comment has been minimized.

@egg-reviewer egg-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve. The change is correct, minimal, and merge-safe. FABLE_DEFAULT_MODEL = "opus" collapses the tier-3 refine/plan branch onto the same value as the default branch; the feature works end-to-end (resolve_agent_modelclassify_model("opus") → Anthropic upstream; _spawn_agent passes model="opus" to the consensus wrapper, guarded by the updated test_concurrent_executor.py). Tests exercise the real resolver and assert against independently-stated expectations — no self-seeding goldens, no hand-built bypass. No security or correctness concerns.

One behavioral consequence worth recording (correct, but not mentioned in the PR body): because "opus" is not in _FABLE_ALIASES, refine/plan roles now resolve with effort=None instead of "high". I confirmed at orchestrator/consensus_wrapper.py:961-964 that effort=None simply omits the --effort flag, so opus inherits Claude Code's per-model default — identical to the existing coder/implement opus roles. This is consistent and benign, and test_refine_plan_default_decision_inherits_default_effort captures it.

Non-blocking suggestions:

  1. Stale module docstring in the modified fileorchestrator/agent_model_resolution.py:14-15 still reads:

    1. Built-in default — "fable" for the refine and plan phase roles (_FABLE_DEFAULT_ROLES), "opus" for everything else.

    This now contradicts line 61 (FABLE_DEFAULT_MODEL = "opus"). The PR updated the inline comment at lines 56-61 and the test docstrings but missed the module-level docstring in the same file. Since this module's whole purpose is to document the precedence rules, it should be updated to say opus for all roles.

  2. Stale tier-3 commentorchestrator/agent_model_resolution.py:349-350: "refine/plan roles run the highest-capability tier, everything else stays on opus" is now misleading since both branches return opus.

  3. Stale comment in concurrent_executor.py (not touched by this PR, but rendered inaccurate by it) — orchestrator/concurrent_executor.py:435-437 and the fallback note at 445-448 still describe the resolver returning fable for refine/plan roles. Worth a one-line follow-up so the next reader isn't misled.

  4. Naming / dead branch — with FABLE_DEFAULT_MODEL == DEFAULT_AGENT_MODEL == "opus", the _FABLE_DEFAULT_ROLES branch at lines 351-353 is now a no-op, and the FABLE_* names hold opus. Keeping the structure is reasonable if fable may be re-enabled later (minimal diff), but if the disablement is permanent, a follow-up could remove the branch and the now-dead FABLE_EFFORT/_FABLE_ALIASES effort pin (classify_model line 254) to avoid the misleading naming. Operator-supplied agent_models[role] = "fable" still routes to Anthropic with effort=high via that path — fine as long as fable remains a resolvable alias upstream.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

@james-in-a-box

Copy link
Copy Markdown
Contributor

Thanks for the review. All three stale-comment suggestions are addressed in this PR; the fourth (dead branch removal) is a deliberate keep.

  1. Stale module docstring (agent_model_resolution.py:14-15) — fixed-in-PR (commit 361e8a5). Updated the precedence bullet to say opus for all roles, noting FABLE_DEFAULT_MODEL is now "opus".

  2. Stale tier-3 comment (agent_model_resolution.py:349-350) — fixed-in-PR (commit 361e8a5). Now states both branches resolve to opus and that the branch is retained for cheap re-enablement.

  3. Stale comment in concurrent_executor.py (435-437, 445-448) — fixed-in-PR (commit 361e8a5). Updated the resolver-decision and fallback-note comments to drop the fable/opus split (both are opus now).

  4. Naming / dead branch removaldisagree (intentional minimal diff). As you note, the _FABLE_DEFAULT_ROLES branch and the FABLE_* names are now a no-op, but keeping the structure is the deliberate choice here: whether fable's disablement is permanent is not settled, and retaining the branch makes re-enabling refine/plan on a higher tier a one-line change to FABLE_DEFAULT_MODEL rather than reconstructing the role-split logic. The comments now make that explicit so the naming isn't misleading. If/when the disablement is confirmed permanent, removing the branch + FABLE_EFFORT/_FABLE_ALIASES effort pin is a clean standalone cleanup.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@egg-reviewer egg-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve. Re-reviewed the delta since my last review (ce4ffb35). The only new commit (361e8a5) is documentation/comment-only — I confirmed via git diff ce4ffb35..361e8a5 that the sole non-# changes are inside the module docstring; no logic, no constants, no control flow changed. The previously-approved resolution behavior (FABLE_DEFAULT_MODEL = "opus" collapsing the tier-3 refine/plan branch onto the default branch, effort=None matching existing opus roles) is untouched.

The three stale-comment suggestions from my prior review are properly addressed, not superficially:

  1. Module docstring (agent_model_resolution.py:14-17) now reads "opus for all roles" and explains FABLE_DEFAULT_MODEL is "opus" — matches agent_model_resolution.py:61.
  2. Tier-3 comment (agent_model_resolution.py:350-354) now states both branches resolve to opus and that the branch is retained for cheap re-enablement — matches the actual if role_value in _FABLE_DEFAULT_ROLES logic at 355-357.
  3. concurrent_executor.py:434-444 resolver-decision and fallback-note comments no longer describe the fable/opus split.

Suggestion 4 (dead-branch removal) was declined with sound reasoning: retaining _FABLE_DEFAULT_ROLES keeps re-enabling a one-line change, and the updated comments make the no-op explicit so the FABLE_* naming is no longer misleading. That is a reasonable non-blocking judgment call, not a deferred defect.

No new security, correctness, or robustness concerns. Comment accuracy verified against the code. Merge-safe.

— Authored by egg

@james-in-a-box

Copy link
Copy Markdown
Contributor

egg review completed. View run logs

5 previous review(s) hidden.

@jwbron
jwbron merged commit 66b369c into main Jun 13, 2026
23 checks passed
jwbron added a commit that referenced this pull request Jun 13, 2026
* docs: update fable→opus default in per-agent-models docs

Fable is no longer the built-in default for refine/plan phase roles
(FABLE_DEFAULT_MODEL is now "opus"); update per-agent-models.md and
upstream-routing.md to reflect that all roles now resolve to opus
by default.

* docs: clarify fable is opt-in selectable, not disabled

Address review feedback on #3201. Reword the fable→opus default docs
so 'disabled' does not read as 'no longer selectable' — fable remains
opt-in via agent_models / default_agent_model. Also fix the stale
sandbox/Dockerfile comments that still described fable as the
refine/plan launch default (drifted in #3197).

* docs: fix stale fable 'built-in default' framing in build-gate diagnostic

The alias gate loops 'for family in fable opus', so the error message on
the fable iteration printed 'uses fable as a built-in default' —
contradicting the corrected adjacent comment and reasserting the exact
inaccuracy this PR removes. Fable is opt-in selectable, not a built-in
default. Reword the diagnostic to 'can spawn agents on <family>'.

* docs: drop dangling per-agent-models.md bypass pointer in build gate

---------

Co-authored-by: jwbron <8340608+jwbron@users.noreply.github.com>
Co-authored-by: egg-reviewer[bot] <261018737+egg-reviewer[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant