You shipped an agent skill last week and it worked. This week you tightened the description, added a step, and fixed a typo in the body. It still works — until an output comes back wrong, and you realize you have no idea which of those three edits did it, and no clean way back to the version that was fine.
That's the trap with skills specifically. A skill is a prompt in a folder, not code. There's no compiler to reject a bad change, no failed test to block the merge. The agent runs the new instructions exactly as happily as the old ones and simply behaves differently. So the safety net that code gets for free — version control plus a test that fails loudly — is something you have to add on purpose. Here's the whole discipline, and it's lighter than it sounds.
The 30-second version: Put every skill under git. Version distributed skills with Semantic Versioning starting at1.0.0. Treat any edit to thedescriptionline as a major bump, because the description is the trigger. Keep a rollback ready three ways —git reverta project skill, reinstall the pinned version of a plugin skill, ordisable-model-invocation: trueas the emergency circuit breaker. Gate description changes behind a trigger eval before you push.
If you haven't built one yet, start with the SKILL.md build guide — this piece assumes you already have a skill and now need to change it without fear.
Why a skill breaks differently than code#
When you break code, the failure is loud and immediate: a red build, a stack trace, a test that goes from green to red. When you break a skill, nothing happens. The YAML still parses, the folder still loads, /skill-name still runs. The only symptom is that the agent now does the wrong thing — and it does it silently, on some fraction of prompts, discovered later from a customer complaint or a weird transcript.
There are two ways to break a skill, and they map to the two halves of what a skill is:
- The body — the instructions Claude follows once the skill loads. Break these and the skill runs but produces worse output.
- The description — the one line Claude reads to decide whether to load the skill at all. Break this and the skill fires on the wrong prompts, or stops firing on the right ones. This is the sneakier failure, because the skill you were editing looks untouched — it just never shows up.
Version control fixes the first problem (get back to good). A trigger eval fixes the second (prove the trigger didn't move). You need both.
Step 1 — Put the skill under git, even the personal ones#
A project skill already lives in a repo — .claude/skills/<name>/SKILL.md — so it's versioned the moment you commit it. That's the easy case, and it's why committed project skills are the ones you can trust: their history is right there in git log.
The gap is your personal skills in ~/.claude/skills/. Those are the ones you edit most casually and version least — and an unversioned edit there has no undo. Give them a home:
cd ~/.claude/skills
git init
git add .
git commit -m "skills baseline"
Now every personal skill has a history and a git revert. Two minutes, and the casual edits stop being one-way doors.
One boundary worth internalizing: cloud sessions and scheduled routines read a repository's committed .claude/skills/, not your laptop's ~/.claude/skills/. So your personal git repo protects you; the skills that run unattended in cloud sessions are protected only by the project repo they're committed to. The skills that most need discipline are the automated ones — version those hardest.
Step 2 — Adopt Semantic Versioning, and be honest about "major"#
The moment a skill leaves your own machine — shared with a team, published as a plugin — it needs a version number other people can pin to. A plugin declares it in .claude-plugin/plugin.json:
{
"name": "release-notes",
"version": "1.4.2",
"description": "Draft release notes from the commit range since the last tag.",
"author": { "name": "your-team" },
"keywords": ["release", "changelog", "git"]
}
Use Semantic Versioning and start at 1.0.0. Resist 0.1.0 — if the skill works, it's 1.0.0. The only judgment call is what counts as each kind of bump, and for skills the answer is not the same as for a library:
- Patch (
1.0.0 → 1.0.1) — you fixed wording or a typo in the body. No behavior change. - Minor (
1.0.x → 1.1.0) — you added a step, a bundled script, or anallowed-toolsentry. New capability, nothing removed. - Major (
1.x → 2.0.0) — you edited the description, renamed the skill, or removed something the body promised.
That last rule surprises people: editing one sentence is a breaking change? Yes — because the description is the trigger. Change it and you've changed when the skill fires, which is exactly the kind of silent, downstream-affecting change SemVer's major bump exists to flag. Keep a short CHANGELOG.md next to the skill and, on every major, write the one line that says what moved and how to adapt. The compare table above is the cheat sheet.
Step 3 — The safe-change workflow#
Editing a skill in place, on main, is how the "which of three edits broke it" story starts. Do this instead:
git switch -c skill/release-notes-tighten-trigger
# edit .claude/skills/release-notes/SKILL.md
/reload-plugins # pick up the change in your current session
Live change detection covers SKILL.md text automatically, but if the skill folder is also a plugin, changes to its hooks/, agents/, or .mcp.json need /reload-plugins to take effect — so make it a reflex. Then, before you merge, run the eval. If you touched the description, that means a full trigger eval; the companion how-to is a 30-prompt harness you can run in a minute. If the eval is green, merge and tag:
git tag skill-release-notes-v2.0.0
git push --tags
The tag is what makes rollback a one-liner instead of an archaeology project.
Step 4 — Roll back in under a minute#
When a skill misbehaves in production, you want the fastest path to a known-good state. There are three, and which one you reach for depends on where the skill lives.
Project skill — git revert. It's a file in your repo. Revert the commit, push, and every cloud session and routine picks it up on the next pull:
git revert <bad-commit-sha>
git push
Plugin skill — reinstall the pinned version. Because the version is explicit in plugin.json, point the marketplace entry back at the last good tag (or git checkout skill-release-notes-v1.4.2) and /reload-plugins. Everyone who reinstalls lands on the identical known-good build — that determinism is the entire reason to graduate a proven skill out of a loose folder and into a versioned plugin.
Any skill, right now — the circuit breaker. If a skill is actively misfiring and you haven't found the bad commit yet, don't debug under fire. Disable auto-loading:
---
name: release-notes
description: Draft release notes from the commit range since the last tag.
disable-model-invocation: true
---
Now Claude won't load it on its own judgment — it only runs if you type /release-notes deliberately. The misfires stop instantly, and you fix the description on your own schedule. Reach for this before you bisect, not after.
The trap: rolling back the body but not the trigger#
The failure that survives a naive rollback is the trigger regression. You revert the body to last week's good version, the outputs look right again in your tests — but you left the edited description in place, so the skill is still firing on prompts it shouldn't, or missing ones it should. Your body tests never catch it because they only run after the skill has already triggered.
That's why the discipline is two-part: version control gets the body back; only an eval proves the trigger is where you think it is. A description change without a trigger eval is the single most common way a "safe" skill rollback leaves a live regression in place. The next piece is that eval — how to write trigger evals for an agent skill — and together they're the whole story: git for state, evals for behavior, and a circuit breaker for the bad minute in between.



