Skills in available list treated as reference material instead of invocations

Resolved 💬 1 comment Opened May 10, 2026 by fwends Closed Jun 8, 2026

Summary

Claude Code repeatedly fails to invoke skills listed in the available-skills system reminder, instead treating their SKILL.md and references/*.md files as documentation to read and reimplement inline. This causes:

  1. The skill workflow being bypassed entirely
  2. Logic from the skill being duplicated as one-off project files
  3. User having to re-explain "there is already a skill for this" — repeatedly

Reproduction pattern

A user-built project skill exists with the structure:

.claude/skills/<skill-name>/
├── SKILL.md
├── references/<workflow>.md   # contains inline code snippets for each scenario
└── scripts/                    # generic reusable scripts

When a triggering message arrives (e.g. an image whose handling matches the skill description), the model:

  1. Sees the skill in the available-skills list ✓
  2. Does NOT call Skill("<skill-name>")
  3. Instead, manually Reads the relevant reference file
  4. Executes the snippets — or worse, wraps them into a per-instance script (cancel_<customer>.py, add_<customer>.py)
  5. Reports success

The end-state may be functionally correct, but:

  • The skill's entry-point behaviour (any pre/post hooks, validation, telemetry, future updates) is bypassed
  • The project accumulates per-customer one-off scripts instead of using the skill's generic scripts
  • The user's investment in building the skill is wasted on every invocation

Why I think this happens (model self-report)

When asked to introspect, the model reports treating skills in the available-skills list as reference material rather than invocable tools. The skill description reads like documentation, the SKILL.md is a markdown file, and the references/ folder contains code snippets — all of which feel like things to read, not things to call. The Skill tool itself is one of many tools, and there's no strong gradient pulling the model toward it when a Read + manual execution would also accomplish the task.

Even after correction within a session, the model tends to address the symptom (e.g. "don't make per-guest scripts") rather than the root cause ("invoke the skill as step 0"), requiring a second, sharper correction from the user.

Suggested fixes

  1. System-prompt-level guidance that when a user message matches a skill's trigger description, calling Skill(...) is the first action — before Read, Bash, or any other tool. The Skill tool description does say this ("BLOCKING REQUIREMENT: invoke the relevant Skill tool BEFORE generating any other response about the task") but the gradient in practice is weak.
  1. Stronger framing in the available-skills list — perhaps prefixing each skill with "Invoke via Skill(...) when:" rather than just listing the description, so the model sees them as callable rather than as a knowledge base.
  1. Telemetry / self-check: after a tool call sequence completes, if a skill in the available list matches the task that was just performed and Skill(...) was never called, flag it.
  1. Refusal pattern: if the model is about to write a file at scripts/<customer_name>.py or similar per-instance path while a matching skill exists, surface a self-check.

Severity

Moderate-but-persistent. Each occurrence costs the user a correction round-trip (sometimes two), and over time pollutes the project with one-off scripts that diverge from the skill. For users who have invested in building skills as their primary automation surface, this is a recurring papercut that undermines the whole skill system.

Environment

  • Claude Code CLI
  • Model: Claude Opus 4.7 (1M context)
  • Project type: user has multiple custom project-level skills under .claude/skills/
  • Reproduced multiple times across separate sessions on the same project (per user)

Not included

Project-specific names, customer details, and database IDs have been omitted intentionally — the issue is the general behaviour, not the specific incident.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗