[BUG] Skill frontmatter model: override applied on user-typed invocation but silently ignored when invoked via the Skill tool (registers in both cases)

Status Closed — not planned
Reported on v2.1.153
Maintainer reply None cached
Activity 2 comments · opened Jul 21, 2026 · closed Jul 21, 2026

Summary

The SKILL.md frontmatter model: override is path-dependent: it works when the user types /skill-name, but is silently not applied when Claude invokes the same skill via the Skill tool mid-turn — even though the harness demonstrably registers the override on both paths (the Skill tool result and a command_permissions attachment both carry the resolved model ID).

This narrows #45191 (closed as not planned, reported as "model: has no effect at runtime"): the field is parsed and resolved correctly everywhere, and applied on the user-typed path — only the mid-turn application is missing. Since model-triggered invocation is the default path for model-invocable skills, cost-control uses of model: (route mechanics to a cheaper model, cap an expensive session) silently fail most of the time.

Reproduced identically on 2.1.153 (CLI, Windows 11) and 2.1.215 (desktop app).

Repro

Probe skill at ~/.claude/skills/which-model/SKILL.md (no context: fork):

---
name: which-model
description: Diagnostic probe — report which model is actually sampling right now.
model: opus
---
  1. Set the session to a non-Opus model (tested with claude-fable-5).
  2. Ask Claude in prose to invoke the skill → it fires Skill(which-model) mid-turn.
  3. In a later turn, type /which-model yourself.
  4. Read ground truth from message.model on assistant entries in the session transcript (~/.claude/projects/<proj>/<session>.jsonl). This field comes from the API response, and it correctly flips per-message in other scenarios (see fork evidence below), so it is trustworthy.

Evidence

Both paths in one session (2.1.215, desktop app) — transcript excerpt, annotated

L25 assistant model=claude-fable-5   <-- Claude invokes Skill(which-model); skill content loads
L29 assistant model=claude-fable-5       (all post-load sampling stays on session model)
L31 assistant model=claude-fable-5
L34 assistant model=claude-fable-5
L37 assistant model=claude-fable-5       "override did NOT take effect"
L41 user                             <-- user types /which-model
L46 assistant model=claude-opus-4-8      (override applied immediately)
L48 assistant model=claude-opus-4-8
L49 assistant model=claude-opus-4-8      "override TOOK EFFECT this time"
L53 user                             <-- next user prompt
L54 assistant model=claude-fable-5       (session model resumes, as documented)

The override registers even on the failing path (2.1.153) — raw transcript lines

Skill invoked via Skill tool; the harness resolves model: opusclaude-opus-4-8 in two places:

"toolUseResult":{"success":true,"commandName":"land-branch","model":"claude-opus-4-8"}
"attachment":{"type":"command_permissions","allowedTools":[],"model":"claude-opus-4-8"}

…yet every subsequent assistant entry in the same turn keeps the session model:

L184 assistant model=claude-fable-5  (03:32:41 — after the skill + override registered at 03:32:32)
L185 assistant model=claude-fable-5
L186 assistant model=claude-fable-5
L188 assistant model=claude-fable-5
L189 assistant model=claude-fable-5

Control: context: fork + model: works on both paths

A skill with context: fork + model: haiku, invoked via the Skill tool from an Opus session, produced a subagent transcript whose assistant entries are all claude-haiku-4-5-20251001 (5/5). So frontmatter parsing, model resolution, and per-message message.model accuracy are all fine — the gap is specifically the inline mid-turn application of an already-registered override.

Expected

Per the frontmatter reference: "Model to use when this skill is active. The override applies for the rest of the current turn" — regardless of whether the skill was invoked by the user or by Claude via the Skill tool.

Actual

  • User-typed /skill-name: override applied for the rest of the turn, reverts on next prompt ✅ (matches docs)
  • Claude invokes via Skill tool: override registered (toolUseResult.model, command_permissions.model) but sampling continues on the session model ❌

Adjacent observation

The system prompt's "You are powered by the model named …" line is frozen at session start: after /model switches or a successful skill override it keeps claiming the original model (observed claiming Fable while Opus served, and Opus while Fable served). Harmless for sampling, but it makes model self-report useless for verifying overrides; transcript message.model is the only reliable signal. Happy to split this into its own issue if preferred.

Environment

  • Claude Code 2.1.153 (CLI, Windows 11, VS Code extension entrypoint) and 2.1.215 (desktop app) — identical behavior
  • Session models involved: claude-fable-5[1m], claude-opus-4-8[1m]
  • Related: #45191 (locked; this narrows it), #17283 (Skill tool + context: fork — works on the versions above)

Template fields (added post-hoc — issue was filed via gh CLI, apologies for bypassing the form)

  • Is this a regression?: I don't know — first time testing model: frontmatter on these installs; #45191 suggests it never applied on this path.
  • Claude Code Version: 2.1.153 (CLI) and 2.1.215 (desktop app), identical behavior
  • Claude Model: session on claude-fable-5[1m] / claude-opus-4-8[1m]; skill override target opus
  • Platform: Anthropic API
  • Operating System: Windows 11
  • Terminal/Shell: VS Code integrated terminal (CLI case); Claude Code desktop app (second case)
  • Preflight: searched existing issues — closest is #45191 (closed as not planned, locked), which this narrows; single bug (the frozen system-prompt line at the end is an adjacent observation, happy to split it out).

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗