You hardened a skill in the repo, shipped the PR, and the agent kept loading the old one. Or you fixed the live skill by hand, and the versioned copy quietly stayed behind. Same root cause: the copy you run and the copy you version are two different files.
~/.claude/skills/) — that folder stops being a scratch drawer and becomes a mirror of a versioned source. Now two things can silently disagree: the source can move ahead of the deployed copy (you forgot to redeploy), or the deployed copy can be hand-edited ahead of the source (you edited the mirror). Catch both with two cheap, non-blocking checks: a drift guard (does the deployed copy match its source?) and an orphan guard (is a deployed skill backed by any source at all?). Rule of thumb: edit the source, deploy to the mirror, never the other way round.A companion piece — Where Claude Code Skills Live — makes the first move: a skill worth keeping gets committed into the repo whose domain it serves, instead of living only in your personal folder. This article is about what happens after you do that at scale, when a deploy step pushes those repo-owned skills back into the folder the tool loads on every machine. That convenience introduces a classic distributed-copy problem, and it’s worth naming before it bites. (A skill is just a folder — a SKILL.md and optional scripts/ and references/, per Anatomy of a Claude Code Skill and the official skills documentation — so none of this is exotic; it’s two copies of some files.)
Edit the source, deploy to the mirror — never the other way round.
Table of Contents
Source, deploy, mirror
Three roles, and the bugs all come from confusing them:
- Source — the skill’s folder inside a repository, under version control. The only place you should edit.
- Deployer — a step (a hook, a script, a CI job) that copies each repo’s skills into the per-user skills folder so the tool finds them everywhere.
- Mirror — the per-user folder the tool actually loads from (
~/.claude/skills/<name>/). A copy. Not authoritative.
When there’s no deployer, the per-user folder is just scratch and the earlier advice is enough: commit the keepers. But the instant a deployer exists, the per-user folder is downstream of the source — and a copy that nothing reconciles will drift from its original the first time either side changes alone.
Repo folder, version-controlled. The only place you edit.
Hook / script / CI. Copies each repo's skills → mirror.
~/.claude/skills — what the tool loads. A copy, not authoritative.
Drift guard: does the mirror still match its source? Orphan guard: is a deployed skill backed by any source at all?
A copy that nothing reconciles drifts from its original the first time either side changes alone.
The two ways a deployed skill drifts
There are exactly two failure directions, and they need different fixes:
- Source ahead of mirror. You improved the skill in the repo, merged it, and forgot to redeploy. The tool keeps loading the stale deployed copy — your fix is live in git and invisible in practice. Fix: redeploy.
- Mirror ahead of source. You edited the deployed copy directly (easy to do — that’s the one the tool is loading while you work), and the versioned source never got the change. The next deploy silently overwrites your edit, and in the meantime the reviewed copy disagrees with the running one. Fix: port the hand-edit back into the source, then redeploy.
Both are the same disease — two copies, nothing watching — and neither announces itself. The deploy “succeeded”; the skill “works.” The only symptom is that the copy under review and the copy in use have quietly diverged.
Guard one: drift (deployed copy vs its source)
For every skill a repo sources, compare the versioned files against the deployed copy, content-only (normalise line endings and trailing whitespace so a Windows checkout doesn’t cry wolf). Report — don’t block — when the deployed copy is stale (differs) or missing (never deployed). Don’t assume a direction: “these two disagree” could be either failure above, so the message says reconcile, and names both fixes (redeploy, or port-back-then-redeploy), rather than overwriting blindly.
Wire it into the same pre-push nudge group as your other drift checks (shared tools, generated workflows). It’s a reminder, never a gate: a guard that blocks a push because a deploy is one commit behind would make being slightly out of sync mean being unable to work.
Guard two: orphans (deployed copy with no source)
The drift guard has a blind spot by construction: it only looks at skills a repo sources. A skill created straight in the mirror — the easiest mistake, because that’s where it runs — has no source anywhere. It’s untracked, unreviewed, unbacked-up, and the drift guard can’t see it because there’s nothing to compare against. It will be lost on the next clean deploy or a machine reinstall, exactly like a personal-folder skill, except now it’s hiding in a folder you thought was managed.
So add the missing-direction check: walk the mirror, and for each deployed skill, confirm some repo sources it. Flag any that none do. To decide “no source,” enumerate the repos you actually have (siblings in your workspace, plus any extra roots you configure) and collect the skills each one sources; a deployed skill in none of those sets is an orphan. Print the fix — give it a home in a repo and commit it — and make it opt-out per skill for the rare legitimately-external case. Non-blocking again; it’s a standing nudge until the orphan gets a source.
A skill created straight in the mirror has no source anywhere — untracked, and the drift guard can’t even see it.
When we first ran this, it immediately surfaced a real one: a genuinely useful skill that had only ever existed in the mirror. Nothing was lost — but one reinstall and it would have been, and no existing check had been watching that direction. (Tracking is a separate question from whether a skill even fires when it should — for that, see Make a Claude Code Skill Trigger Reliably.)
Why both, and why non-blocking
Drift and orphan are duals. Drift watches known skills (sourced somewhere) for a copy that disagrees. Orphan watches the mirror for a copy with no source to agree with. Together they close the loop: every deployed skill is either reconciled with a source or flagged for lacking one.
They’re reminders, not gates, on purpose. A blocking check here would punish normal workflow (you will push before you redeploy) and train people to bypass it. A push-time nudge that fails open — stays silent when it can’t reach a repo, never changes the exit code — gets read and acted on, which a false wall never does.
Correction (2026-10-07): an earlier version of this article called drift a hygiene problem, “not a correctness emergency”, because a skill one commit out of sync still works. That holds for cosmetic changes. It fails for skills that encode rules: a session once ran a whole day on a judging skill that was missing a fix merged hours earlier, and the push-time reminder fired only after the work was done. Keep these guards for hand-edits and missed redeploys, but stop a stale copy from being used at the start of the session.
The short version
Deploying skills from repos into the per-user folder the tool loads turns that folder into a mirror, and a mirror drifts from its source the moment either side changes alone. Edit the source, deploy to the mirror, never the reverse. Run two non-blocking checks: a drift guard (deployed copy vs its source — stale or missing, reconcile in whichever direction) and an orphan guard (a deployed skill no repo sources — give it a home). Both fail open and nudge at push time. That’s the whole system: one source of truth, a deploy step, and two cheap watchers that keep the copy you run honest with the copy you version.
Frequently Asked Questions
Isn’t the personal skills folder just scratch? Why guard it?
It is scratch — until you add a deployer that copies repo-owned skills into it. After that, the same folder holds deployed copies that are supposed to match a source. Scratch and deployed now live side by side, and only the guards tell them apart.
Why not just block the push until everything is in sync?
Because being one commit out of sync is normal, not broken — you routinely push before you redeploy. A blocking check would fire constantly and get bypassed. A non-blocking, fail-open nudge is read and acted on; a wall is routed around.
How does the orphan check know a skill has “no source”?
It enumerates the repositories you have (typically siblings in your workspace, plus any extra roots you configure), collects every skill each repo sources, and flags any deployed skill that appears in none of them. A source in a repo it can’t see is a false positive — cheap to silence with a per-skill allow entry or an extra configured root.
I edited the deployed copy and it works — am I done?
No. That edit lives only in the mirror; the versioned source is behind, and the next deploy will overwrite your change. Port the edit back into the source, commit it, then redeploy. “It works right now” and “it’s kept” are different states.
Where should an orphaned skill be homed?
In the repo whose domain it serves — a pipeline skill with its pipeline, a content skill with the content repo, a cross-cutting one in a shared-tooling repo. Homing by domain keeps the tool next to the code it’s about and puts it under that repo’s review and history.