50 本を超えたあたりから、SKILL.md の書き方は品質にほぼ関係なくなります。ここから先は書き方ガイドの外側の話です。効くのは「書き方」ではなく「育て方」のほうです。
書き方は調べれば出てきます。育て方はあまり言語化されていません。リネームを押し切った日、3 つに分けたものを 1 つに戻した日、description を 5 回書き直したあと、そのたびに何を判断したのか。本記事は、私たちが Skill 群を運用してきた中での命名・統合・description チューニング・フォールバック設計・lint 化までの判断を、5 つの観点で振り返ります。
設計思想や書き方そのものについては、入門編の Claude Code Skills 入門ガイド、上級編の Skills 設計パターン上級編、そして SKILL.md の書き方ガイド にまとまっています。本記事はその先、運用フェーズの「判断の履歴」を扱います。
この記事で扱うこと
- 命名は「今の用途」ではなく「1 年後の広がり」で決める
- 粒度は分割側ではなく統合側に倒す
- description は仕様書ではなく「発火確率の調整値」
- フォールバックは後付けで必ず必要になる
- 規約違反は人ではなく lint で見つける
1. 命名は「今の用途」ではなく「1 年後の広がり」で決める
Skill 名を決めるとき、「今やりたいこと」をそのまま名前にすると、半年後に必ず破綻します。私たちが一番痛い目を見たのは、video-announce という Skill のリネームでした。
元の名前は外部サービス名を冠していた
video-announce の前身は、Instagram への動画投稿キャプションと SEO ハッシュタグを生成するためだけの Skill でした。Instagram 専用のロジックがあちこちに散らばり、Skill 名にも外部サービス名のプレフィックスが入っていました。
機能としては動いていました。問題は、半年後に「YouTube Shorts と TikTok にも同じ動画を投稿したい」という要望が出たときです。動画の素材は同じ、ハッシュタグの戦略は変える、トーンも微妙に変える。本来であれば「動画告知」という機能の中の一バリエーションです。ところが Skill 名が外部サービス名を引きずっているせいで、新しいプラットフォームに対応する関数を書いた瞬間、命名と中身が完全に矛盾しました。
(Skill 名):外部サービス専用を示唆
(中の関数):複数プラットフォーム対応のロジック
(呼ぶ側の認知):「これって結局何用なの?」
3 プラットフォームを並列で扱うようになった瞬間、Skill 名のプレフィックスが嘘になります。プレフィックスを残したまま機能だけ拡張すると、半年後に新しく入ったメンバーが「なんでこの名前で他プラットフォームの処理が入ってるんですか」と確実に聞いてきます。返答に詰まった瞬間が改名判断のサインでした。
改名のコストは早期ほど低い
リネームに踏み切る判断のとき、一番引っかかるのは「呼び出し箇所が複数ある」点です。CI、ドキュメント、他 Skill からのチェイン呼び出し、cron。grep で全部洗ってから一斉置換する地味な作業が要ります。
ただ、これを「半年後にやる」と「1 年後にやる」で比べると、半年後にやった方が圧倒的に楽です。Skill 数が増えるほど、依存箇所も増える。リネーム経験を 3 回くらい踏んだ上で辿り着いた結論が、迷ったら早く改名するです。
「今の用途」ではなく「1 年後の広がり」で命名する
外部サービス名を冠した Skill 名は、そのサービスと運命を共にする覚悟が必要です。サービス側に上位互換が出たとき、サービス名が変わったとき、別サービスへの拡張が必要になったとき — すべて命名の時点で背負うリスクです。
私たちが今採用している命名ルールは、ざっくりこの 3 つです。
| 良い命名 | 避ける命名 | 理由 |
|---|---|---|
| 機能ベース(動詞 + 汎用対象) | 外部サービス名プレフィックス(ig-*, slack-*) | サービス側の変化に強い |
| 短くて発火意図が明確 | 内部実装詳細(json-merge-runner) | 呼び出し側が用途を推測しやすい |
| 単数形・短い | 複数形・長い | description で発火させる前提なので短い方が衝突しにくい |
「外部サービス名は使うな」ではありません。サービス名を冠するなら、そのサービスが消えても Skill が生き残る道筋を最初に決めておく、です。
2. 粒度は分割側ではなく統合側に倒す
Skill を 1 本書くとき、どこまで 1 つにまとめるか / どこから分けるかの判断が一番難しいです。経験的に効いているのは、迷ったら統合側に倒す、です。
3 つに分けたものを 1 つに戻した話
過去に SNS 投稿系の Skill を 3 つに分けた時期がありました。投稿用、スケジュール用、外部サービスとの同期用。それぞれ責務が違うので、設計上は綺麗に見えます。
実運用で起きたのは、こういう光景でした。
呼び出すたびに、投稿のほう・スケジュールのほう・同期のほうのどれだったかで一瞬詰まる。3 つの Skill が内部でお互いを呼び出すので、どこが起点なのかも曖昧。説明書を書くたびに「まず A → 次に B → 最後に C」という 3 段フローが前提になり、この時点で Skill 化した意味、ほぼ無い。
統合した後はサブコマンド構成に変えました。1 つの Skill に対して post, schedule, sync のサブコマンドを切る。発火させる側は Skill 名 1 つを覚えれば良く、サブコマンドは --help で見える。
選択肢が減って認知負荷が下がりました。3 つの Skill を覚えていた時期より、サブコマンド付き 1 つの Skill のほうが圧倒的に使いやすい。
Skill 分割のコストは「ユーザー側の選択肢」に乗る
ここが肝。Skill を分けると、設計上は綺麗に見えます。ただ、分けたコストは設計者ではなく使う側が払う。「投稿したいときどっちの Skill を呼ぶんだっけ」を毎回判断する負荷は、設計の整理整頓のしわ寄せです。
分けるべきタイミングは、明確に違う発火条件を持つときだけ。「投稿」と「分析」のように、呼ぶシーンも入力も出力も違うものは分ける。「投稿」と「投稿のスケジュール」のように、同じ目的の中で時間軸だけが違うものはサブコマンドで吸収する。
統合 + サブコマンドにすれば、発火の単一性と機能の多様性が両立します。これは設計上の妥協ではなく、運用上の最適解だと今は思っています。
Progressive Disclosure:本体は薄く、references/ は深く
Skill を統合側に倒すと、SKILL.md の中身が膨らみます。実例として、ある Skill の SKILL.md が 312 行まで肥大化したことがありました(AI が毎回 context に読み込む側の気持ちを考えてほしいサイズ)。中身は仕様、ドメイン知識、エッジケースの説明、サンプル、トラブルシュートが全部 1 ファイルに同居。読むのに時間がかかるし、AI エージェントが毎回フルで読み込むのもコスト的に無駄です。
そこで references/ ディレクトリにドメイン知識を分離しました。SKILL.md 本体は 147 行(53% 削減)に圧縮。詳細ファイルは「必要なときだけ」AI が Read で読み込みます。
SKILL.md(薄い)
├─ 必須要素のみ
├─ サブコマンド一覧
└─ References へのポインタ
references/
├─ workflow-detail.md(深い)
├─ edge-cases.md(深い)
└─ troubleshooting.md(深い)
この Progressive Disclosure 構造は、統合側に倒した Skill ほど効きます。目安は 200〜300 行、超えたら分離を検討するのが経験的なライン。本体を薄く保つと、別の Skill から呼び出されたときの読み込みコストも下がるので、Skill 間連携でも軽量に動きます。
3. description は仕様でなく「発火確率の調整値」
SKILL.md の description フィールドは、仕様書ではなく発火確率の調整値です。最初からそう思って書けた人は、たぶんいません(私たちも最初の 10 本くらいは仕様書として書いていました)。
bug-hunt の発火しなかった話
bug-hunt という Skill を最初に書いたとき、description は英語キーワード中心の簡潔な仕様文でした。「Investigates complex bugs through hypothesis-driven multi-agent analysis」みたいな書き方です。Accepts args の記述は無し。
ところが、日本語で「このバグ調べて」と頼んだときに発火しません。「バグハント実行」と明示的に呼んでも、別の Skill が先に反応してしまうことがあります(名指ししてるのに他のやつが出てくる、あの悲しみ)。AI から見れば「英語の bug investigation を期待する Skill」に見えるので、日本語の「バグ調べて」と意味的にマッチしないのです。
改善した description のフォーマット
書き直したあとの description は、こういう構造になりました。
description: |
Collaborative multi-agent bug investigation using Agent Team.
Generates hypotheses, investigates in parallel, dynamically redirects based on findings.
Use when: (1) complex bugs with unclear root cause, (2) intermittent/hard-to-reproduce issues,
(3) multi-component bugs crossing module boundaries,
(4) keywords: bug hunt, root cause, investigate, debug, intermittent failure, flaky test,
原因調査, なぜ落ちる, 再現しない, 時々失敗
Accepts args: <issue-or-description> [--max-hypotheses N] [--max-turns N] [--repo-path <path>]
ポイントは 3 つ。
- 英語キーワードと日本語キーワードを両方並べる:AI のトリガー判定は意味マッチなので、両言語で並べると発火確率が安定します
- 「Use when」を箇条書きで明示:いつ呼ぶかを 3〜4 パターン書くと、似た用途の他 Skill との競合が減ります
- Accepts args を書く:引数付きで呼ばれた時に Skill 側が「自分が呼ばれている」と確信を持ちやすい
書き直した後、体感で 9 割は一発で発火するようになりました。
description は一度で決まらない
正直に書きます。description は最初から完璧には書けません。私たちは平均 3〜5 回書き直しています。発火しなかったログを見て、別 Skill に取られたケースを見て、ユーザー(人間)の言い回しを観察して、少しずつチューニング。
「使われ方を観察してから description を直す」を運用フローに組み込むのが、結局一番効きます。AI のトリガー判定はモデル更新でも挙動が変わるので、半年に 1 度くらいは見直す前提でいると気が楽です。
4. フォールバックは後付けで必ず要る
成功パスだけ書いて出した Skill は、運用に入った瞬間に「ちょっと待って、ここで失敗したらどうするの」が露呈します。フォールバック設計は最初から組み込むのがベストですが、最初に組み込めなかったら後から必ず付けることになる、と覚悟しておいた方が現実的です。
pr-review のフォールバック追加
pr-review Skill は、初期実装では gh pr review --approve でレビューを承認投稿する設計でした。クリーンで、API 経由なので冪等性もあり、エラーも構造化されている — 設計上は完璧です。
実運用で何が起きたか。一番分かりやすかったのは own PR のケースです。GitHub は自分が作った PR を自分で approve することを許可しないので、gh pr review --approve がそのまま exit 1 で落ちます。古い gh CLI やネットワーク不安定のケースも重なり、「approve が通る前提」で書いた Skill は実運用で普通に失敗していました。
Graceful Degradation:approve → comment にフォールバック
修正したのは、承認が失敗したらコメント投稿に落とすフォールバックを組み込むことでした。
承認できない文脈(own PR など)でも、コメント投稿に落としてレビュー投稿は最低でも形を残す。これが入った瞬間、Skill の安心感がまったく違うものになりました。そのケースでも approve を先に試すのは、許可が下りる環境ではちゃんと承認として記録されるのを優先しているからです。
失敗ログの方が学びが多い
pr-review の話はここで一旦閉じて、別の Skill で踏んだ罠に触れます。フォールバックに加えて、もう一つセットで効くのが失敗ログ設計です。ここは retrospective 系 Skill で踏んだ罠が学びになりました。PostToolUseFailure hook が payload schema を誤って読んでいたせいで、30 日間 failure ログがゼロ件だった、というバグです(ダッシュボードが綺麗すぎて、逆に気持ち悪くて気づきました)。
技術的には 2 段構えの不運でした。
- ツール失敗を捕捉する hook が、payload を間違ったキーで読んでいた。実際の失敗情報は
.errorに入っているのに、存在しない.tool_response / .tool_resultから取ろうとしていた。読めるわけがない。 - 失敗情報を整形する bash スクリプトが、静かに落ちていた。
pipefail + errexit環境で動いていたため、空入力をgrep | head | cutに渡すと、エラーログも吐かずに abort する。
結果、hook は空手で戻り、スクリプトは黙って死に、失敗パスは闇に消えていました。
成功パスは記録され、失敗パスは闇に消える。これだと運用で一番改善したい「なぜ失敗したか」のデータが集まりません。あとから「過去 1 ヶ月で失敗した Skill ランキングが見たい」と思っても、データが無いから出せない。
成功ログは「動いた証拠」、失敗ログは「直すべき証拠」。後者の方が運用上の価値が高いことが多いです。Skill のログ設計は、最初に失敗時のログ書き込みを書くくらいで丁度いい、と今は思っています。
外部依存がある Skill は、特に Graceful Degradation を前提にしてください。「外部 API は落ちる」「権限は剥奪される」「CLI のバージョンは古い」のすべてが現実に起きます。
5. 規約違反は人ではなく lint で見つける
Skill が 10 個を超えたら、人のレビューでは規約違反を拾えなくなります。50 個を超えると、誰がいつ作ったかすら追えません。
Subagent Dispatch Rules 未遵守が複数発覚
私たちのプロジェクトでは、Subagent から呼び出される Skill には特定の必須要素を SKILL.md に明記するルールがあります。Skill 数が 50 を超えたあたりで、ふと別件のレビュー中に規約の穴に気づき、全部見直したら必須要素が抜けている Skill が複数発掘されました。自分たちで決めた規約を自分たちで破っていた、という構図です。
抜けていることに気づかない理由は単純で、抜けていても 動いてしまうからです。挙動的にはおおむね期待通り、ただし subagent 経由で呼ばれたときに引数が正しくパースされないとか、エラー時の挙動が定義されていないとか、運用に入って初めて気づくレベルの綻びでした。
lint スクリプト化して機械に守らせる
修正方針は、規約を機械的に検証する lint スクリプトを書くことでした。全 Skill を走査して、必須要素の有無、命名規則、description の最低文字数、Accepts args の記述などをチェック。違反があれば exit 1。
# 例:scripts/lint-skills.sh を CI で走らせる
$ ./scripts/lint-skills.sh
[OK] skill-a
[OK] skill-b
[NG] skill-c: Missing "Accepts args" in description
[OK] skill-d
[NG] skill-e: SKILL.md exceeds 300 lines (consider Progressive Disclosure)
2 violations found.
これを CI に組み込んでおけば、規約違反は merge 前に必ず止まります。新しいメンバーが Skill を書いたときも、lint が「足りない要素」を先に指摘してくれるので、レビュアー側が同じ指摘を 3 回書く苦行から解放されます。
数が 10 を超えたら lint 化を検討する
経験的に、Skill 数が 10 を超えたあたりで人のレビューが追いつかなくなります。20 を超えると確実に漏れが出る。50 を超えたら lint なしでは無理です。
書き手のリテラシーや属人的な品質管理に頼らず、規約は機械が守らせるのが一番楽です。SKILL.md に必須要素を明示的に書く設計は、自動検証の前提にもなります。明示性は、AI に対する分かりやすさだけでなく、機械的な検証の入り口でもある、と思って書くと自然と整います。
まとめ:当たり前に見える判断には、痛みの履歴がある
ここまで 5 つの観点を振り返りました。命名・粒度・description・フォールバック・lint。あらためて並べてみると、どれも「そりゃそうでしょ」と言われそうな当たり前の話です。
並べ直すと、こんな具合です。命名で 3 回リネームし直し、粒度の分割を 1 回戻し、description を 5 回書き直し、API 失敗でレビュー投稿を 1 回落とし、30 日間 failure ログがゼロ件だったバグを 1 回踏み、規約違反を複数個発掘した。5 観点の中身は、それだけです。
Skill 運用は、書いて終わりではなく、育てて長く使うフェーズが本番です。本記事の判断のうち 1 つでも「次の Skill を書くときに思い出してもらえる」ものがあれば、書いた甲斐があります。



