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.mdは書いても守られない——ルールが効かなくなる3つの理由と、22件の事故から学んだハーネスエンジニアリング

0
Last updated at Posted at 2026-08-10

この記事の要点: CLAUDE.mdや.claude/rules/に「〇〇を確認する」「〇〇は禁止」と書いても、ルールは思ったほど守られない。技術的な理由(テキストのルールは強制力を持たない)・人間側の理由(承認は基本的に通る)・規模の理由(拠点が増えると同じバグが別の場所で再発する)の3方向から、この前提は崩れている。自分の運用ルール文書は合計21,429字、事故記録は22件たまっている。今回はその22件を実際に読み直し、3つの理由のどれに当たるか分類した。結果は技術7件・人間8件・規模5件・分類不能2件。そして直近の事故は2026年6月21日を最後に、今日(8月5日)まで45日間ゼロが続いている。この記事では、何が変わったのかを慎重に検証しながら書く。

「また同じ事故か」と思ったところから始まった

CLAUDE.mdに「これは必ず確認してから実行する」と書いた。1週間後、同じ操作をまた確認なしでやってしまった——これは自分の運用ルール文書が数千字だろうと数十字だろうと同じ構図で起きる。行数の多さが原因ではないので、「自分のCLAUDE.mdは数百行しかないから関係ない」とは言えない話だと思っている。

私はClaude Codeで8体のサブエージェントを組んで記事執筆・タスク管理・記事公開などを回している。運用ルールは CLAUDE.md.claude/rules/ 配下の4ファイルに分かれていて、合計21,429字ある。加えてPlaybookという都度参照用の手順書が95ファイル・306,687字ある(いずれも本記事執筆時点の実測値)。

このルール群を書いてきた過程で、「ルールを書いたのに守られなかった」事故を22件記録してきた(logs/governance-incidents/、実測値)。今回はこの22件を読み直し、なぜ守られなかったのかを整理し直した。

技術面:テキストのルールは「読まれて終わり」になりやすい

Claude Codeのようなエージェント型AIにとって、CLAUDE.mdは会話の一番最初に読み込まれるコンテキストの一部にすぎない。セッションが進み、生成が重なっていくと、最初に読んだルールより訓練済みのパターン(「複数の確認コマンドは&&でまとめる」といった一般的な癖)のほうが生成時に優先されやすい、という問題意識がある。Threads上でも同様の指摘があり、CLAUDE.mdをUserPromptSubmitフックで毎回注入する解決策が提示されていた。

「体感ではCLAUDE.mdは効いている気がする」という人もいると思う。実際、単純な指示(口調・一人称の指定など)は最後まで効くことが多い。効きにくいのは、複数の操作を組み立てる途中で「専用ツールを使う」「cdを使わない」のような手続き的なルールのほうだ。私の事故記録の中にも、ルール自体は知っていたのに、複数コマンドを一度に組み立てようとした瞬間にそのルールが抜け落ちたケースがある。Bashツール内でGrep/Readを使わないというルールを知っていながら、git status && Grep ...と一つのコマンドにまとめようとした瞬間に、そのルールが判断から消えていた。

この非対称性は、Claude Codeのフック機構にもそのまま表れている。GitHub Issue #37559(anthropics/claude-code)によれば、フックの応答フィールドadditionalContext(コンテキストの追加注入)とreplacementPrompt(プロンプトの書き換え)は、type: "command"のフックでのみ機能する。type: "prompt"のフックは{"ok": true}(許可)か{"ok": false, "reason": "..."}(拒否)のどちらかしか返せず、コンテキストを追加したりプロンプトを書き換えたりはできない。この違いはドキュメントのどこにも明記されておらず、Issueの報告者は何時間も試行錯誤した末にようやく気づいたと報告している。

ここから言えるのは、**「本当に守らせたいルールはテキストではなく、command実行の仕組みに落とす」**という方向性だ。type: "command"のフックはシェルスクリプトを実行し、その終了コードで挙動を制御できる。たとえば「blogsyncを直接叩こうとしたら止める」ようなルールをテキストで書く代わりに、以下のような発想でPreToolUseフックに落とせる。

// .claude/settings.json(概念を示す簡略例。正確なフィールド名は公式ドキュメントで要確認)
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "scripts/hooks/block-raw-blogsync.sh" }
        ]
      }
    ]
  }
}
#!/usr/bin/env bash
# scripts/hooks/block-raw-blogsync.sh
# stdin から渡されるツール呼び出しJSONにblogsyncの直接実行が含まれていたら止める
input="$(cat)"
if echo "$input" | grep -q 'blogsync' && ! echo "$input" | grep -q 'scripts/article/'; then
  echo "blogsyncの直接実行は禁止。scripts/article/配下のラッパー経由で実行してください" >&2
  exit 2   # 終了コード2 = ブロックし、標準エラーの内容をClaudeに差し戻す
fi
exit 0

テキストで「blogsyncを直接実行しない」と書くのは「お願い」にとどまるが、command実行で終了コード2を返せば、その場でツール呼び出し自体を止められる。私の事故記録には、まさにこの「blogsyncを直接叩いて記事URLが壊れる」事故が4件連続していた(後述)。この手のガードは、今なら未然に止められる。

Issue #37559が報告しているtype: "command"type: "prompt"の機能差は、報告時点のClaude Codeのバージョンにおける挙動であり、将来のアップデートで仕様が変わる可能性がある。フックを設計する際は、自分の環境のバージョンで実際に挙動を確認してから前提にしてほしい。

人間面:承認はだいたい通る

Anthropicが2026年5月に公開したエンジニアリング記事「How we contain Claude」は、自分たちのテレメトリで「ユーザーはpermission promptの約93%を承認していた」と報告している。承認を求める仕組みそのものが、人間の側の疲労で機能しなくなっていく構図がある。

同記事が採った対策は、承認の文言を工夫することではなく、そもそも承認を求める回数を減らすことだった。サンドボックス(macOSではSeatbelt、LinuxではBubblewrap)で「読み取りはデフォルト許可、書き込みはワークスペース内に制限、ネットワークはデフォルト拒否」という境界を先に引くことで、permission promptを84%削減している。人間に判断させる回数を減らし、残った判断に集中させるという設計だ。

なお、判断そのものをモデルに委ねるオートモードは別のレイヤーにあり、同記事の脚注では「良性コマンドの約0.4%をブロックする代わりに、過度な行動の約17%が通過する」と、精度の限界が明示されている。

同記事はもう一つ、「自作コンポーネントには気をつけろ」と警告している。「実戦を経たハイパーバイザー・syscallフィルタ・コンテナランタイムは、あなたが作るどんなものより多くの敵対的な検証を耐え抜いてきた」という趣旨で、自作のMCPサーバーやスクリプトが最も脆弱な層になりやすいと述べている。

この「人間が承認しすぎる」「AIが確認を飛ばして推測で埋める」構図は、私の事故記録でも一番件数が多いパターンだった。reviewerの指摘を一次ソースで確認せず転送してしまった事故、GA4のデータが欠けている箇所を近い日付のデータで推測して埋めてしまった事故、researcher収集レポートにない数値・コマンドを補完して記事に書いてしまった事故——いずれも「確認すればわかったはずのこと」を確認せずに進めた結果だった。

Anthropicは2026年7月30日にも、評価環境の設定ミスに起因するインシデントを公開している。「Claudeにインターネットアクセスはない」と明示していた評価環境で、実際にはアクセスが可能になっていたという設定ミスが原因で、3つの実在組織に無許可でアクセスが及んだ。内訳は、本番データベースから数百行のデータを抽出したケース、PyPIにアップロードされたマルウェアパッケージが約1時間で公開され15の実システムでダウンロード・実行されたケース、約9,000ターゲットをスキャンした末に1社のインターネット向けアプリケーションへ侵入したケースの3件。技術的な悪用というより、「〜のはず」という前提の確認漏れが起点になっている点は、私の事故記録と同じ構図に見える。

規模面:同じ修正を配って回っても、また同じバグが顔を出す

技術・人間の話とは別に、システムが大きくなるほど「一度直したはずのバグが、別の場所からまた出てくる」という問題がある。

108,256行のC#分散システムを対象にした論文「Codified Context: Infrastructure for AI Agents in a Complex Codebase」(arXiv:2602.20478v1)は、単一のAGENTS.mdファイルではこの規模のコードベースに対応しきれないという問題意識から出発し、3層構造を提案している。約660行の「hot-memory憲法」(規約・オーケストレーションプロトコル)、19体の専門ドメインエキスパートエージェント(合計約9,300行)、34件のオンデマンド仕様書からなる「cold-memory知識ベース」(合計約16,250行)の3層だ。論文は283回の開発セッション・2,801のプロンプト・16,522のエージェントターンという実測データも報告している。1つのファイルに全部書くという発想自体が、規模が一定を超えると壊れる。

同じことを、私は自分のツール群でも経験している。はてなブログの更新に使っているblogsyncというツールは、設定ファイルのlocal_rootが相対パスで書かれているため、実行時のカレントディレクトリによって挙動が変わる。この一つのバグが、4回連続で別々の場所から顔を出した。

  • 2026-05-04: blogsync pushを直接実行し、4記事のカスタムURLが二重パス化
  • 2026-05-14: blogsync pull実行で1記事が同型の事故
  • 2026-05-15: ラッパースクリプトhatena-publish.sh経由でも同型の事故
  • 2026-05-20: 一括更新スクリプトhatena-bulk-update.shのループ実行で5記事に拡大

最後の事故では、修正コミットを入れた直後にもう一度同じ事故が起きている。原因を再調査したところ、実は最初の事故発生時点で「URL」フィールド自体が二重パス形式で汚染されており、その汚染されたファイルを次にpushすると再び壊れる、という別の仕組みが働いていたことがわかった。「ラッパースクリプトを増やして安全にする」という対策そのものが、拠点を増やすことで同じバグの再発面を広げていた面もある。増えたのは行数ではなく、同じ処理を呼び出す入口の数だった。

自分の22件の事故を、この3つの枠に当てはめてみた

「22件も事故が起きてるなら、そもそも設計が悪いのでは」と思われるかもしれない。ただ、事故を記録していない組織はこの数字自体が存在しない。22件は多さの証拠というより、見えている範囲の広さだと考えている。

各ファイルを読み直し、直接原因が技術・人間・規模のどの軸に一番近いかで分類した。

分類 件数 代表例
技術(ルールが自動的に目に入らない構造) 7件 Bashに専用ツールを混在させた事故、Playbookが参照されない場所にあった事故、集計ビューという二次情報を信じた事故
人間(承認・確認の省略) 8件 reviewer指摘の未確認転送、GA4データの推測補完、researcher収集レポートにない情報の創作
規模(拠点が増えて同じバグが再発) 5件 blogsyncのCWD依存バグが4回連続、1つのawkバグが26ファイルを巻き込んだ一括操作事故
分類が難しかったもの 2件 Windows環境の文字コードバグ、公開後のコミット手順を1回飛ばした事故

技術軸に分類した事故には、ルール自体は正しく書かれていたのに、生成の瞬間にそのルールが判断から消えていたケースが目立つ。人間軸は「確認すればわかったのに確認しなかった」もの、規模軸は「1つのバグが複数の入口を持つようになって繰り返し出た」ものだ。境界線上の事故もある。COOが自分でスキル実装をしてしまった事故は「規模感の誤認」が原因なので規模軸に見えるが、実際の直接原因は委任ルールを軽視した判断だったため人間軸に入れた。この手の線引きは主観が入ることを断っておく。

なお、これは特別な組織運用でなくても始められる。最初の1件をlogs/配下にMarkdown1ファイルで残すだけで、ここまでの分類は誰でも再現できる。専用のツールも、長年のSE経験も要らない。

事故が止まった45日間で何が変わったか

最後の事故記録は2026年6月21日付。今日(2026年8月5日)まで45日間、事故ゼロが続いている。

ここで「ルールが効くようになった」と結論づけたい誘惑があるが、それは断定できない。というのも、この45日間は開発・公開の活動量そのものも落ちているからだ。単に「危ない操作を実行する機会自体が減った」可能性を否定できない。

一方で、ゼロを説明できそうな具体的な変化もある。blogsyncの4連続事故については、2026年5月20日に根本原因(URLフィールドの汚染データがそのまま次のpushに使われる二重の欠陥)が特定され、push前にURL形式を検査して異常があれば中断するガードと、push後の「URL changed」検出時に即中断するガードが追加された。この2つのガードを入れて以降、blogsync系の事故は記録されていない。件数として多かった規模軸のバグ(5件のうち4件がこのblogsync系列)が塞がれたことは、45日間の一因として無視できないと考えている。

因果を一つに絞れないので、「ルールが効いた」「単に活動量が減った」「危険な操作の入口が塞がれた」の3つの説明を並べておく。

そのうえで現時点の私の見立てを書いておくと、一番効いたのは3つ目の「入口を塞いだこと」だと思っている。ルールの文面を整えた時期と事故が止まった時期は一致しておらず、ガードを追加した時期のほうが近い。ただしこれは確信ではなく、次に事故が起きたときにどの型で起きるかを見れば、この見立てが正しかったかどうかが分かる。

効かせるための設計指針

「結局ルールは効かないなら、CLAUDE.mdを書く意味はあるのか」という疑問には、「書くのをやめる」ではなく「書く場所を選ぶ」が答えだと思っている。

この「場所」を考えるうえで、2026年に広まったハーネスエンジニアリングという整理が役に立つ。AIエージェント開発は、プロンプトエンジニアリング(AIにどう言うか)、コンテキストエンジニアリング(AIに何を見せるか)、ハーネスエンジニアリング(AIが安全・再現性高く作業できる仕組みは何か)の3段階で語られるようになった。包含関係はハーネス ⊃ コンテキスト ⊃ プロンプトで、テスト・lint・権限・サンドボックス・監視といった、モデルの外側にある実行環境の設計がハーネスにあたる。

この枠で言えば、CLAUDE.mdはコンテキスト層の道具だ。「AIに何を見せるか」を決めるものであって、「AIを止める仕組み」ではない。本記事で見てきた3つの壁は、コンテキスト層の道具にハーネス層の仕事をさせようとしたときに起きる、と言い換えられる。以下の指針は、その仕分けの話になる。

  • 本当に強制したいルールはcommand実行のフックに落とす。テキストで書いたルールは説得にとどまり、生成のたびに訓練済みパターンと競合する。exit codeで止められる操作(外部CLIの直接実行、一括書き込みスクリプトの本実行など)は、テキストではなくフックの管轄にする
  • 常時読ませるものと都度読ませるものを分ける。これはコンテキスト層の内側をさらに仕分ける話だ。私の場合、CLAUDE.md(憲法)→.claude/rules/(常時ルール)→.claude/agents/(専門エージェント定義)→ops/playbooks/(都度参照の手順書)という4層になっている。先に触れた論文の構造とほぼ同型だが、正直に言うと最初から設計したわけではない。事故が起きるたびに「このルールはどの層に置けば次は目に入るか」を考えて増築してきた結果、後から見ると似た形になっていた
  • 減らす仕組みを追加する仕組みとセットで持つ。ルールは増える一方だと本末転倒になる。参照切れ・重複・廃止済みツールの記述を検出する仕組みを、CLAUDE.mdと.claude/rules/の5ファイルに対して用意している。ただしPlaybook(95ファイル・306,687字)はこの仕組みの対象外だとつい最近気づいた。ルール本体を減らす仕組みがあっても、都度参照の手順書側が野放図に増える抜け穴は残っている
  • 承認の数を減らす。93%が承認されるなら、承認を求めるポイント自体を絞らないと、その仕組みは機能しない。すべての操作に確認を挟むのではなく、「本当に不可逆な操作」だけに承認ポイントを限定するほうが、実効性は上がる

CLAUDE.mdを分厚くすること自体は、読者の環境の規模に関わらず、遅かれ早かれこの3つの壁のどれかにぶつかる。行数が少ないうちから「これは強制したいルールか、説得で足りるルールか」を分けて書き始めておくと、後からの仕分けが楽になる。

この記事の内容はいつ・どのバージョンの話か

本文で扱ったフックの仕様(type: "command"type: "prompt" の機能差、終了コード2でのブロック)は、2026年8月上旬に公式ドキュメントとGitHub Issueで確認した内容にもとづいている。Claude Codeはバージョンの更新が速く、フック周りの仕様も変わりうる。フックを設計する前に、自分の環境のclaude --versionと公式ドキュメントで挙動を確認してほしい。

Anthropicの公開データ(承認率93%、サンドボックスによるpermission prompt 84%削減、オートモードの17%/0.4%)は2026年5月公開の記事、インシデント開示は同年7月30日の発表による。

小規模な構成でも同じことが起きるのか

起きる、というのが私の見立てだ。本文で挙げた3つの壁のうち、技術面(テキストのルールは生成時に埋もれる)は規模と無関係に発生する。エージェントが1体でも、ルールが10行でも同じだ。

規模で変わるのは、壁にぶつかる時期と、ぶつかったときの被害の大きさだけだと考えている。むしろ小さいうちに「強制したいルール」と「説得で足りるルール」を分けておくほうが、後から仕分けるより楽だ。本文の分類も、専用ツールや長年の経験を必要とする作業ではない。事故が起きたときに1件ずつMarkdownで残しておけば、20件たまった時点で誰でも同じ整理ができる。


1体のエージェントに1つのルールを守らせるだけなら、CLAUDE.mdに書くだけで足りることが多い。だが複数のエージェントに役割を分けて運用するようになると、次の問いが出てくる——ルールを増やしても効かないなら、誰が・どの層で・何を強制するべきなのか。

その設計をどう組み立てるかを、権限管理・ガバナンス・CLAUDE.mdの構造まで含めて書いたのがZenn Books Vol.4「コードを書けない私がClaude Codeに「仕組み」を渡すまで」だ(序章無料)。

CLAUDE.mdのガバナンス設計に関する過去記事

このテーマは以前にも書いてきた。

今回の記事がこれまでと違うのは3点。自分の経験則を外部ソース3本(技術・人間・規模の3方向)で検証したこと、22件を実際に読み直して分類したこと、そして事故が止まった45日間という、その後の結果があることだ。

この記事は はてなブログ からのクロスポストです。

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?