Skip to content

feat(config): add Claude 5 family (Fable 5, Sonnet 5) to the Model enum - #68

Merged
arcaputo3 merged 2 commits into
mainfrom
tjc-1550-fable-5-model
Jul 9, 2026
Merged

feat(config): add Claude 5 family (Fable 5, Sonnet 5) to the Model enum#68
arcaputo3 merged 2 commits into
mainfrom
tjc-1550-fable-5-model

Conversation

@arcaputo3

Copy link
Copy Markdown
Contributor

Summary

  • Adds Model.Fable5 (claude-fable-5) and Model.Sonnet5 (claude-sonnet-5) as first-class enum cases with fromId mappings.
  • New Model.fable convenience alias.
  • ⚠️ Repoints Model.sonnet and Model.default from Sonnet4_6 to Sonnet5 — the aliases are documented as "point to current generation", but default affects any caller that never sets a model. Flagging for explicit review; easy to revert to Sonnet4_6 if you want the default pinned.
  • SupportsEffort: adds XHigh + Max compile-time evidence for both new models (Sonnet 5 is the first Sonnet tier with xhigh). Negative cases (e.g. Sonnet4_6 + XHigh) unchanged.

Motivation

TJC-1550 moves the tjc-agents fleet default to Claude Fable 5. tjc-agents currently carries a Models.fable = Model.fromId("claude-fable-5") shim (lands as Custom); once this ships in a release, the shim collapses to Model.fable and the pin bumps.

Validation

  • ModelSpec: 32/32 on JVM and JS (id mapping, fromId round-trip, JSON codec round-trip, alias assertions).
  • AgentOptionsSpec (JS): 105/105 — typeChecks positives for Fable5/Sonnet5 × XHigh/Max, negatives still rejected.
  • __.compile 389/389; scalafmt applied.
  • Live check: claude-sonnet-5 responds 200 with the TJC org key. claude-fable-5 is currently gated org-side (400 model_not_available — data retention must be enabled ≥30d), which is a Console setting, not an SDK concern.

🤖 Generated with Claude Code

…ort evidence, sonnet/default aliases (TJC-1550)

- Model.Fable5 ("claude-fable-5") and Model.Sonnet5 ("claude-sonnet-5") enum
  cases + fromId mappings, new Claude 5 family section
- new Model.fable alias; Model.sonnet and Model.default repointed
  Sonnet4_6 → Sonnet5 per the "point to current generation" contract
  (behavior change for callers that never set a model)
- SupportsEffort: XHigh + Max evidence for Fable5 and Sonnet5 (Sonnet 5 is
  the first Sonnet tier with xhigh)
- ModelSpec (32, jvm+js) and AgentOptionsSpec (105, js) green, including
  typeChecks positives for the new model/effort pairs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@claude

claude Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review — feat(config): add Claude 5 family (Fable 5, Sonnet 5)

Clean, focused change. The additions to the Model enum, fromId mappings, SupportsEffort evidence, and matching test coverage all line up. The self-flagged default repoint in the description is exactly the right thing to surface. A few notes below, mostly minor.

🟡 Stale docs in Effort.scala (should be updated in this PR)

This PR adds XHigh + Max evidence for Fable5 and Sonnet5, but the Effort enum docstrings still only mention the Opus/Sonnet-4.6 tiers:

  • Effort.scala:21/** Deeper than high. Supported by Opus 4.8 and Opus 4.7. */ → should now also list Fable 5 and Sonnet 5.
  • Effort.scala:24/** Maximum effort. Supported by Opus 4.8, Opus 4.7, Opus 4.6, and Sonnet 4.6. */ → same.

These are the source of truth developers read for which models accept xhigh/max, so it's worth keeping them in sync with the new SupportsEffort givens.

🟡 Behavioral change: Model.default / Model.sonnetSonnet5

You already called this out (👍), just documenting the blast radius for reviewers: every caller that never sets a model, plus the ~18 examples using .withModel(Model.sonnet) (A2A, Session, Subagent, Hook, Permission, MacroTool, etc.), now resolve to claude-sonnet-5 instead of claude-sonnet-4-6. Given the live check shows claude-sonnet-5 responds 200 with the org key, this seems safe — but note claude-fable-5 is still org-gated (400 model_not_available), so leaving default = Sonnet5 (rather than Fable5) is the correct conservative choice here, even though the TJC-1550 fleet default moves to Fable 5. Worth a one-liner in the PR/commit that scalagent's default intentionally stays on Sonnet 5 until Fable 5 is generally available.

🟢 Minor: Model.opus under the "current generation" comment

fable/sonnet now point at the 5 family while opus remains Opus4_8. That's correct (there's no Opus 5), but the shared // Convenience aliases (point to current generation) comment reads a touch inconsistently. Optional: a short note that opus tracks the 4.8 tier as the current Opus.

✅ Strengths

  • fromId round-trip, JSON codec round-trip, and alias assertions all extended for both new models — the default is Sonnet5 test was correctly updated rather than left asserting the old value.
  • SupportsEffort positives added in AgentOptionsSpec typeChecks and negatives left intact — good that the compile-time-evidence contract is exercised for the new tiers.
  • No behavioral surprises in fromId: unknown IDs still fall through to Custom.

Nothing blocking. Recommend folding the Effort.scala docstring update into this PR since it's the same conceptual change; the rest are optional polish.

…; document default-stays-Sonnet5 rationale (review follow-up)

- Effort.XHigh/Max docstrings now list Fable 5 + Sonnet 5 alongside the
  Opus/Sonnet 4.x tiers, matching the SupportsEffort givens
- Model.default doc states why it stays Sonnet 5 (Fable 5 is org-gated on
  data retention; opt-in via Model.fable)
- alias comment clarifies opus tracks the 4.8 family (no Opus-tier Claude 5)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arcaputo3

Copy link
Copy Markdown
Contributor Author

Review follow-ups addressed in d-latest:

  • 🟡 Effort.scala docstringsXHigh and Max now list Fable 5 + Sonnet 5 alongside the 4.x tiers, in sync with the SupportsEffort givens.
  • 🟡 Default rationale — documented directly on Model.default: it intentionally stays Sonnet 5 because Fable 5 requires org-level data retention (still gated for us); Fable is opt-in via Model.fable. Durable in code rather than only in the PR body.
  • 🟢 Alias comment — now reads "track each tier's current generation" with an explicit note that opus stays on the 4.8 family since there is no Opus-tier Claude 5.

🤖 Generated with Claude Code

@claude

claude Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review — Claude 5 family (Fable 5, Sonnet 5) 🤖

Reviewed the diff plus surrounding code. Tight, well-scoped change with solid internal consistency. Approving in spirit; one behavior change deserves an explicit owner sign-off.

What's good

  • Consistency across the three source files. The SupportsEffort givens line up exactly with the updated Effort docstrings — XHigh: Fable5 / Sonnet5 / Opus4_8 / Opus4_7; Max: those plus Opus4_6 / Sonnet4_6. Good that the doc was updated in the follow-up commit rather than left to drift.
  • fromId round-trips. Both new IDs are mapped and the round-trip test list was extended so the property holds.
  • Model IDs match the published identifiers (claude-fable-5, claude-sonnet-5).
  • Good judgment on the default. Keeping Model.default = Sonnet5 (not Fable5) because Fable is org-gated on data retention is the right call, and documenting why directly on the val is exactly where a future reader will look.
  • No exhaustive match on Model exists elsewhere in src/, so the new enum cases can't silently break a downstream case. fromId correctly still routes unknowns to Custom.

Worth a second look

  • The default/sonnet repoint Sonnet4_6 → Sonnet5 is a runtime behavior change, not just an additive enum bump. Every caller that never sets a model — and every example in examples/ using Model.sonnet (18 call sites: A2AExample, MacroToolExample, PermissionExample, etc.) — now resolves to claude-sonnet-5 instead of claude-sonnet-4-6. Consistent with the documented "aliases track the current generation" contract, and you flagged it in the PR body, so this is just asking for an explicit sign-off from whoever owns the default rather than a change request. The pin is trivially revertable if Sonnet 5 GA/pricing status isn't settled for all consumers.

Minor / optional

  • Test redundancy in ModelSpec. The new "Model enum has correct IDs for Claude 5 family" + "fromId parses known Claude 5 models" tests already assert exactly what the later "Fable 5 maps to claude-fable-5" / "Sonnet 5 maps to claude-sonnet-5" tests assert. Harmless, and it mirrors the existing per-model style for Opus 4.8/4.7, so keeping it for symmetry is defensible — just noting the duplication.
  • No new negative type-check case. Existing negatives (e.g. Sonnet4_6 + XHigh) are unchanged, which is fine. A typeChecks-should-fail assertion for something like Model.Haiku4_5 + Effort.XHigh would lock in that the new givens did not accidentally widen the surface — but the givens are per-.type, so this is genuinely low risk.

Nothing blocking. Validation numbers in the description (ModelSpec 32/32, AgentOptionsSpec 105/105, compile 389/389) cover the surface area well.

@arcaputo3
arcaputo3 merged commit 2de6412 into main Jul 9, 2026
3 checks passed
@arcaputo3
arcaputo3 deleted the tjc-1550-fable-5-model branch July 9, 2026 15:55
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