Fixing a Skill That Triggers Itself Unexpectedly
A team wrote this skill to streamline their deploy process:
---
name: deploy
description: Deploy the application to production.
---
Deploy the application:
1. Run the test suite
2. Build the application
3. Push to the deployment target
A few days later, an engineer reports that Claude ran the deploy
workflow in the middle of an unrelated conversation about fixing a bug,
without anyone typing /deploy.
- Explain the mechanism that caused this — why was Claude able to run the skill without explicit invocation?
- Propose the specific frontmatter fix, and explain what it changes about both invocation and how the skill loads into context.
- A teammate suggests the real fix is to make the
descriptionfield vaguer so Claude is "less likely to think it's relevant." Evaluate this suggestion.
1. The mechanism: skills are auto-invocable by default.
By default, both the user and Claude can invoke any skill — Claude
loads the skill's description into context on every turn, and
decides on its own whether the current conversation is a good match
for that skill, exactly the same way it decides whether to use any
other tool. Nothing in this skill's frontmatter restricts that. If the
ongoing bug-fix conversation touched on something Claude judged as
"the code looks ready" or "this relates to deployment," the model was
free to invoke the skill on its own initiative — this isn't a bug in
Claude Code, it's the documented default behavior for a skill with no
invocation restrictions.
2. The fix: disable-model-invocation: true.
---
name: deploy
description: Deploy the application to production.
disable-model-invocation: true
---
This restricts the skill to invocation by the user only — Claude can
never trigger it automatically, no matter how relevant the
conversation seems. It also changes context loading: with this field
set, the skill's description is no longer kept in context at all
(there's no reason for Claude to keep evaluating whether to invoke
something it's forbidden from invoking), and the full skill body loads
only at the moment the user explicitly types /deploy. Without the
field, the description is always in context and the full skill loads
whenever invoked by either party.
3. The vague-description suggestion is the wrong fix.
Weakening the description doesn't eliminate the risk, it just makes it
less predictable — Claude could still, on some conversational input,
judge a vaguely-described skill as relevant and trigger it, and now
there's also a worse failure mode: a legitimate /deploy invocation
by the user relies on the description being accurate for Claude to
understand what it's being asked to do once invoked. Weakening the
description trades a solvable problem (uncontrolled auto-invocation)
for an unsolved one (auto-invocation is still theoretically possible,
it's just rarer) while degrading the skill's usefulness on legitimate
invocation. The frontmatter field exists specifically because
"hope the description doesn't match" is not a reliable access-control
mechanism — disable-model-invocation is a hard restriction Claude
Code enforces structurally, not one it merely encourages the model to
respect.
Share this question