[BUG][REGRESSION] model:/effort: frontmatter override silently broken for BOTH commands/*.md and skills/SKILL.md (v2.1.220, worked on v2.1.197)

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 4 comments · opened Jul 26, 2026

This is not a duplicate — please don't auto-close it as one

I know this looks similar to several older reports, and I want to head that off explicitly: this is a fresh, broader regression, confirmed today on the current version (v2.1.220), and it's more severe than any single prior report because it now affects both mechanisms at once (commands/*.md and skills/SKILL.md), for the simplest possible case (direct, user-typed slash invocation — no Agent/Task tool involved). The stale-bot auto-closed every prior related report without a fix (see "Related reports" below) — please don't repeat that here without someone actually looking at it. If maintainers confirm it's truly the same root cause as one of those, I'm happy to have this merged/redirected there instead of closed outright — I'd just like a human to make that call, since the stale-bot closures previously buried this without resolution for months.

Summary

Setting model: and/or effort: in the frontmatter of a custom slash command (~/.claude/commands/<name>.md) or a skill (~/.claude/skills/<name>/SKILL.md) no longer switches the model/effort for that invocation. The override is silently ignored — no error, no warning — and the turn silently runs on the session's default model/effort instead. This removes a basic, previously-reliable control surface: there is currently no working way to pin a specific model/effort to a specific slash command.

Severity / impact

This is a sharp regression, not a missing feature: the exact same override mechanism worked correctly in the same long-lived session on v2.1.197 (2026-07-02) and fails on v2.1.220 (2026-07-26). Anyone who built tooling around per-command model/effort selection (multi-model workflows, cost-tiering cheap/expensive models per task, reasoning-effort tiering) has lost that control with no warning that anything changed.

Context

Found this while building cc-arch-hands — an open-source installer that generates per-model/per-effort slash commands and sub-agents for Claude Code (e.g. /oh, /o1h, /sx in the repro below are its generated commands). The whole tool's value proposition depends on this override actually working, which is how the regression surfaced so clearly.

Repro — commands/*.md

~/.claude/commands/o1h.md:

---
model: claude-opus-4-8
effort: high
---

$ARGUMENTS

Invoke /o1h test in a session whose default model is claude-sonnet-5 (no /model switch in between). Repeated for a few variants in the same sitting:

| Command | frontmatter model: | frontmatter effort: | actual model used | actual effort used |
|---|---|---|---|---|
| /o1h | claude-opus-4-8 | high | claude-sonnet-5 | — |
| /oh | claude-opus-5 | high | claude-sonnet-5 | — |
| /sx | claude-sonnet-5 | xhigh | claude-sonnet-5 | high |

Repro — skills/SKILL.md

~/.claude/skills/cah-model-switch-test/SKILL.md:

---
name: cah-model-switch-test
description: diagnostic skill
model: claude-opus-4-8
effort: high
---

Reply with exactly this text and nothing else: "skill override test ok".

Invoked directly by the user typing /cah-model-switch-test (not via the assistant calling the Skill tool programmatically — i.e. the invocation path some other reports say should work). Same failure.

Evidence it's a real execution mismatch, not just a display bug

The session transcript (~/.claude/projects/<proj>/<session>.jsonl) records an attachment entry of type command_permissions at invocation time, capturing the requested override, e.g.:

{"type":"attachment","attachment":{"type":"command_permissions","allowedTools":[],"model":"claude-opus-4-8"},"timestamp":"2026-07-26T07:03:21.540Z","version":"2.1.220"}

But every subsequent assistant entry in that same turn (thinking, tool_use, text) — starting from the very first one — reports "message.model":"claude-sonnet-5". The API call itself ran on the session default, not the requested override. This isn't the status bar mislabeling something: the field that identifies which model actually produced the tokens says session-default, for the entire turn, both for commands/*.md and for skills/SKILL.md.

The same holds for effort:, tracked separately via the statusLine envelope's effort.level field: requested xhigh, observed high (the session default) for the full turn.

Before/after (this exact mechanism used to work)

In the same long-lived session, on v2.1.197 (2026-07-02), the identical command_permissions → subsequent-assistant-entries pattern correctly switched to claude-opus-4-8 for an entire multi-tool-call turn — several consecutive assistant entries all showed "model":"claude-opus-4-8" right after the override attachment. So this isn't "frontmatter override was never reliable" — it demonstrably worked, on this exact machine/session, 24 days ago.

Related reports (older, narrower, auto-closed by the stale-bot — not fixed)

  • #63599 — effort: ignored in commands/*.md (claimed skills honor it; that's no longer true either, see above)
  • #56819 — model: frontmatter override inconsistent for slash commands (closed as dup of #45191)
  • #45191 — Skill frontmatter model: field ignored at runtime
  • #79664 / #79661 / #79654 — Skill frontmatter model: override ignored specifically when invoked via the Skill tool (works when user types /skill-name) — this report shows it now fails even for the user-typed path
  • #81313 — Skill frontmatter effort: applied on slash-command invocation but ignored via the Skill tool

None of these were resolved — they were auto-closed for inactivity by the stale-bot, most are now locked, so I couldn't comment on them directly. I left detailed reproduction data (raw transcript excerpts) as comments on #63599 before opening this — linking here for anyone who wants the fuller trace: https://github.com/anthropics/claude-code/issues/63599#issuecomment-5082484411 and https://github.com/anthropics/claude-code/issues/63599#issuecomment-5082495834

Environment

  • Claude Code v2.1.220 (regression window: v2.1.197 → v2.1.220)
  • Windows 11
  • Reproduced across claude-opus-4-8, claude-opus-5, claude-sonnet-5 as the target override

Ask

If you've hit this too — a 👍 on this issue (rather than a duplicate comment) helps signal how many people currently have zero working way to pin model/effort per command. Happy to attach fuller transcript excerpts or a minimal repro repo if useful.

View original on GitHub ↗

3 Comments

PHPCraftdream · 1 month ago

Pinging folks from related reports in case this is useful context (or in case any of you are still hitting this) — several of your issues got locked after auto-close, so tagging you here since you can no longer comment on your own threads:

  • @FrankC01 (#56819 — model: override inconsistent for slash commands, locked)
  • @workpodsjun (#45191 — Skill frontmatter model: ignored at runtime, locked)
  • @ivan01march (#60804 — status bar shows wrong effort vs. skill frontmatter, locked)

And from the still-open/commentable related reports:

  • @HakanCOSKUN (#63599 — effort: ignored in commands/*.md)
  • @klkr-dev (#79664 / #79661 / #79654 — model: override ignored via the Skill tool)
  • @nakkulla (#81313 — effort: ignored via the Skill tool)
  • @tfvchow (#34296 — 429 from skill model: frontmatter)

If any of you have a still-reproducing case (especially on a recent version), a comment with your Claude Code version + a transcript excerpt would help build the picture of how far this regression window actually extends. And a 👍 on this issue is a low-effort way to signal "still affected" even without new repro details.

MichaelRDionne · 28 days ago

Independent corroboration on v2.1.220, plus a discriminator I haven't seen posted anywhere: the override works in print mode (claude -p) and fails in the interactive TUI, on the same machine, same account, same binary, minutes apart.

That narrows this from "the override is broken" to "the override is not applied on the interactive code path," which may be useful for whoever picks this up.

Probes (all v2.1.220, same session settings, "model": "claude-opus-5[1m]" set in settings.json)

Scratch commands, each a bare frontmatter pin over a trivial prompt:

| Invocation | frontmatter model: | model that served the turn | pin |
|---|---|---|---|
| claude -p "/pin-fable" | fable | claude-fable-5 | ✅ fired |
| claude -p "/pin-sonnet" | claude-sonnet-5 | claude-sonnet-5 | ✅ fired |
| claude -p "<plain prompt>" | — | claude-opus-5 | n/a (correct default) |
| claude -p --resume <opus-session> "/pin-fable" | fable | claude-fable-5 | ✅ fired, mid-session |
| interactive TUI, user-typed /captain | fable | claude-opus-5 | ❌ recorded, not applied |

The resume case matters: it rules out "the override only binds at session creation." It flipped the model on an already-running Opus session — in print mode.

Scale of the interactive failure

Censused every assistant entry on the main chain (.isSidechain != true) across 422 local transcripts in one project:

| Command | frontmatter model: | invocations | pin demonstrably fired |
|---|---|---|---|
| /captain | fable (alias) | 121 marked sessions | 1 |
| /handoff | claude-sonnet-5 (full ID) | 220 | 0 |

Combined ≈ 1 in 290 (~0.3%). Two details from that census:

  • The single fire and a failure six minutes later the same morning are indistinguishable in the transcripts — same binary, same command file, same session shape. Whatever gates it isn't visible from the client side.
  • Alias vs fully-qualified ID makes no difference. The full-ID form (claude-sonnet-5) has the worse record of the two. The command_permissions attachment shows the alias resolving correctly — "model":"claude-fable-5" — so resolution happens and is then discarded.

Ruled out on this machine

  • Model availability / quota / entitlement. The same account served claude-fable-5 headless on demand the same day, and the Sonnet pin fails identically at 0/220 — so it isn't about one model tier being gated.
  • The settings.json model pin fighting the override. "model": "claude-opus-5[1m]" was in force for all four passing headless probes.
  • Frontmatter key support. It parses; the request reaches command_permissions intact.

Untested, offered as leads

Two hypotheses fit everything above and I can't separate them from client-side evidence: the model may be bound at prompt-submit before command frontmatter is parsed (a TUI-only ordering difference from the print path), or interactive session-model state may take precedence over a per-turn override in a way the print path doesn't implement.

On the regression framing

One wrinkle worth flagging rather than asserting: my own interactive failures extend back to 2026-07-20 through 07-24 on claude-opus-4-8, before the v2.1.220 window. That doesn't contradict the before/after evidence in the original report — it may simply mean an interactive-path flakiness predates the regression and v2.1.220 made it total. Recording it in case the bisect comes back cleaner than expected.

Practical note for anyone hitting this

There's no in-band signal — the model's own system prompt names the pinned model whether or not the pin fired, so an agent asked to "act as X" will believe it is X. The only reliable check is the transcript:

jq -r 'select(.type=="assistant" and (.isSidechain!=true)) | .message.model' \
  ~/.claude/projects/<proj>/<session-id>.jsonl | sort | uniq -c

Setting the model at session level with /model works and survives across turns; it's the only workaround I've found that holds.

MichaelRDionne · 28 days ago

Follow-up to my earlier comment (the headless/interactive discriminator): we ran the controlled interactive negatives on v2.1.220. Same setup — a user-level command whose frontmatter pins model: to a model different from the session's, macOS, Max plan. Three runs, three different conditions, zero fires:

  1. Account-default selection. Session explicitly on another model via the /model picker, then selected "Default (recommended)" (stdout: Set model to Opus 5 (1M context) (default)), then invoked the pinned command. First turn after the invocation and every main-chain turn thereafter served on claude-opus-5; zero pinned-model turns. One caveat for anyone reproducing: selecting "Default" still sets a session model per the stdout, so the TUI may have no true no-selection state to test.
  1. Fresh session, pinned command as the very first prompt. No /model activity at all, no prior assistant turn. All 12 main-chain turns on claude-opus-5, zero pinned-model turns.
  1. Immediately after /clear. /clear, one ordinary turn, then the pinned command. (This mirrors the single interactive fire in our 422-transcript census, which occurred minutes after a /clear — we wondered if cleared context was the trigger. It isn't, or at least isn't sufficient.) All post-invocation turns on claude-opus-5.

In every run the transcript records the request faithfully — the command_permissions attachment carries the pinned model ID — and the API turn is served on the session model anyway. Headless -p on the same binary/machine/account applies the override correctly, including on --resume.

Takeaway for the bisect: this argues against session-model-selection precedence as the mechanism — the override fails even under the account default and even on a session's first prompt, where no explicit selection exists to outrank it. It is consistent with the model being bound at prompt-submit before command frontmatter is applied on the interactive path. Sample sizes are small (one run per condition, against our measured interactive fire rate of ~1 in 290), so offered as narrowing evidence, not proof.

Showing cached comments. Read the full discussion on GitHub ↗