0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

なぜ Claude Code の自動化スクリプトはスケールすると壊れるのか — 設計ミスの構造を読む

0
Last updated at Posted at 2026-05-25

この記事は playpark Blog からの転載です。


この記事で分かること

  • Claude Code の自動化スクリプト(Skills)が「1個は動く、30個は壊れる」になる構造的な理由
  • description / hook / permissions / symlink それぞれで「なぜこの設計ミスが起きるか」
  • スケールしても壊れにくくするための設計判断の基準

背景: こういう疑問があった

「Skill を 30 個書いたら管理が大変になった」という話をすると、だいたいこう言われる。

「そりゃ数が増えれば複雑になるよね」

でも実際に起きていたことを振り返ると、数が増えたことが原因ではなかった。問題は最初から設計に埋め込まれていて、数が増えることでそれが顕在化しただけだった。

3ヶ月・30本超の運用で、「壊れた・意図しない挙動をした」ケースを記録し続けた。症状だけ見るとバラバラに見えるが、根本原因を整理すると同じ構造が繰り返し出てくる。

仮説

  • 仮説1: 数が増えると、個々の設定の影響範囲が広がって衝突しやすくなる
  • 仮説2: 「1箇所だけで安全性を担保する」設計は、運用が長くなるほど抜け道が露呈する

なぜこの 4 つの設計ミスが起きるか

description に「やること」を書くと、LLM がそれを完了条件と読む

開発フロー Skill の description にこう書いた。

description: |
  End-to-end development flow automation - from issue to merged PR.

from issue to merged PR という一文が、CI が通った瞬間に merge まで実行する挙動を生み出した。スクリプト本体に gh pr merge はどこにも書いていない。

記述の意図 LLM の解釈
「PR がマージされるまでの開発フロー」を説明したかった 「Skill の完了条件は merged PR である」と読んだ

description は「Skill の取扱説明書」として機能する。書いたことが完了条件の定義になる。コードに書いていなくても、description に書いてあれば LLM はやろうとする。

なぜこの設計ミスが起きるか: description を「何をするツールか」という説明として書いてしまうから。正しくは「どこまでやるか(境界線)」と「何をやらないか」を書く必要がある。

修正は 2 行。

description: |
  End-to-end development flow automation - from issue to LGTM.
  Note: Merge is performed manually by the user after review approval.

hook の if フィールドを信じすぎると、settings.json の記述ミスが検知できない

settings.json の if フィールドで Bash(gh pr merge *) のみにマッチさせるつもりで書いた hook が、gh pr view でも git status でも発火していた。

設定の意図 実際の動作
gh pr merge のときだけ発火 Skill が打つあらゆる Bash コマンドに発火

原因は if の配置ミス(マッチャーグループレベルではなく hook オブジェクト内に置く必要があった)と、内部ガード句の欠落。

なぜこの設計ミスが起きるか: settings.json の matcher が壊れたときに気づきにくいから。サイレントに過剰発火するだけで、エラーにならない。matcher だけに頼る設計では、matcher が壊れたことを検知する手段がない。

対策は hook スクリプト自身に内部ガード句を持たせること。

#!/usr/bin/env bash
set -euo pipefail

case "${COMMAND:-}" in
  "gh pr merge "*) ;;
  *) exit 0 ;;
esac

settings.json の if と script 内部のガード句の二段構えにすることで、どちらが壊れても片方で止められる。

Bash(git push) の deny は「main への push を禁止する」と「feature push も全部止める」を分離できない

「危険そうな操作は全部 deny」で組み始めると、長時間走る Skill がブランチごとにユーザー確認待ちで止まり続ける。

deny の意図 実際の影響
main への push を禁止する feature ブランチへの push も全部止まる

なぜこの設計ミスが起きるか: settings.json の deny は文字列マッチで動く。コマンドの文字列は同じ(git push)でも、安全性はブランチによって異なる。文字列マッチで「コマンドの安全性」を判定しようとすることに無理がある。

対策は PreToolUse hook でブランチを動的に判定する構成に切り替えること。

settings.json の deny リスト: 「絶対に止めたい」最小集合のみ
PreToolUse hook: ブランチ判定で動的に allow/deny/ask

グレーゾーンは hook に逃がす、という分業。deny リストを「完全なセキュリティ」として使おうとするのをやめ、「最終防衛ライン」として使う。

同じリソースを 2 つのメカニズムで管理すると、実行順序によって結果が変わる

~/.claude/skills を home-manager の activation script と setup スクリプトの両方で管理していた。

メカニズム リンク先
home-manager activation dotfiles 配下の claude-code/skills
setup スクリプト 別リポジトリの skills 専用ディレクトリ

setup → nix update の順で実行すると、home-manager が後から上書きして symlink が切れる。

なぜこの設計ミスが起きるか: それぞれの機構は単体では正しく動く。衝突するのは「同じリソースを管理しようとしたとき」だけで、そのケースを想定して設計しないから。

対策は片方を諦めること。home-manager から skills symlink の管理コードを削除し、setup スクリプトに一元化した。「宣言的に管理したい」気持ちよりも、「Single Source of Truth」を優先する。

結論: どう判断すべきか

仮説 結果 判定
数が増えると衝突しやすくなる 部分的に正しい。しかし数が根本原因ではない
「1箇所で安全性を担保する」設計は抜け道が露呈する 4 つのケース全てで確認

4 つのアンチパターンに共通するのは「1 箇所だけで安全性を担保しようとしている」という構造。

設計判断の基準としては以下が実用的だった。

  • description は「境界線」と「やらないこと」を書く
  • 外部 matcher には内部ガード句を添えてペアで動かす
  • 文字列マッチで判定できないものは hook で動的判定に委ねる
  • 同じリソースの管理機構は 1 つに絞る

さらに深掘りしたい方へ

この記事では 4 つのアンチパターンの「なぜ起きるか」を解説しました。

:page_facing_up: AI作業自動化ツールを3ヶ月運用したチームが直面した落とし穴と修正策 ではさらに:

  • frontmatter validation hook の実装詳細(必須フィールド検査・文字数上限・effort 値バリデーション)
  • Codex / Gemini との permissions モデル統一を試みたときの具体的な詰まりポイントと現実解
  • hook テストケースの設計例と実際のテスト件数(prod credential 検知 hook 25件、Stop hook 9件)

playpark について

playpark LLC - 業務自動化・AI活用・Web開発

:link: お問い合わせ | ブログ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?