Zenn記事5本が消えた日
Claude Codeに「Zennの記事にフッターを追加して」と頼んだ。
エージェントはZenn APIに PUT を送った。フッターのテキストだけを。RESTの PUT はリソース全体を置き換える。本文がフッター1行に上書きされた。
教訓を書いた。「PUTの前に必ずGETせよ」と。
翌日。同じエージェントが同じミスをやった。今度は5記事全部。読者のコメントで気づいた。
教訓は書いてあった。読まれなかった。
教訓を「書く」だけでは意味がない
AIエージェントは忘れる。正確には、前のセッションの教訓を参照しない。CLAUDE.mdに書いても、長くなれば埋もれる。
必要なのは「教訓を書く仕組み」ではなく「教訓を守らせる仕組み」。
PreToolUseフックとは
:::details 初心者向け: フック(hook)とは
プログラムの「ある動作の前後に、自分のコードを差し込む仕組み」です。身近な例えで言うと、玄関のセンサーライトのようなもの。ドアが開く(=ツールが実行される)前にセンサーが反応して、ライトが点く(=チェックが走る)。PreToolUseフックは「ツール実行の前に差し込むチェック」で、危険な操作を事前にブロックできます。
:::
Claude Codeには hooks という仕組みがある。ツール実行の前後にコマンドを挟める。
-
PreToolUse— ツール実行前に発火 -
PostToolUse— ツール実行後に発火
PreToolUse フックがゼロ以外の終了コードを返すと、ツール実行がブロックされる。
ここに「過去の教訓との照合」を挟む。それがbrain guardの発想。
brain guardの仕組み
Shared BrainというCLIツールを作った。中核は brain guard コマンド。
処理の流れはこう。
コマンド文字列を受け取る
↓
全教訓ファイル(YAML)を読み込む
↓
各教訓の trigger_patterns と正規表現マッチ
↓
マッチあり → 警告表示 + チェックリスト提示 + 監査ログ記録
マッチなし → そのまま通過(監査ログには記録)
:::details 初心者向け: 正規表現(regex)とは
文字列のパターンを表現する記法です。たとえば curl.*PUT は「curlという文字の後ろのどこかにPUTがある文字列」にマッチします。.* は「何でもいい文字が0個以上」という意味。スマホの検索で * をワイルドカードとして使うのに似ています。brain guardでは、この正規表現でコマンドが危険かどうかを判定しています。
:::
実行例。
$ brain guard "curl -X PUT https://api.zenn.dev/articles/abc123"
============================================================
⚠️ CRITICAL LESSON: api-put-safety
(violated 2x, last: 2026-02-09)
============================================================
PUT replaces the ENTIRE resource. Fields not included in the
request body will be overwritten with empty/default values.
Checklist:
[ ] GET the current resource state
[ ] PUT body contains ALL required fields
[ ] Test on 1 item before batch operation
[ ] Verify result after update
Source: Zenn 5-article deletion (2026-02-09)
Proceed? [y/N]
2回事故を起こした教訓が、3回目の前に割り込む。
設定方法
1. インストール
pip install shared-brain
または直接クローン。
git clone https://github.com/yurukusa/shared-brain.git
cd shared-brain
pip install -e .
2. Claude Codeへのフック登録
ワンコマンドで完了。
brain hook install
内部では ~/.claude/settings.json にこう書き込む。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/path/to/brain guard \"$TOOL_INPUT\""
}
]
}
]
}
}
$TOOL_INPUT にはClaude Codeが実行しようとしているコマンド文字列が入る。brain guardはそれを受け取り、教訓データベースと照合する。
:::details 初心者向け: settings.jsonとは
Claude Codeの設定ファイルです。~/.claude/settings.json に置かれていて、ここにフックの定義を書くとClaude Codeが自動的に読み込みます。上のJSONの "matcher": "Bash" は「Bashコマンドを実行するときだけこのフックを発火させる」という意味です。
:::
既存の settings.json がある場合はマージ。既にインストール済みなら二重登録しない。冪等性あり。
# 確認
brain hook status
# 🟢 Installed
# 削除
brain hook uninstall
3. 教訓ファイルの書き方
教訓はYAML形式。~/.brain/lessons/ に置く。
# ~/.brain/lessons/api-put-safety.yaml
id: api-put-safety
severity: critical
created: "2026-02-08"
violated_count: 2
last_violated: "2026-02-09"
trigger_patterns:
- "PUT /api/"
- "requests\\.put"
- "curl.*-X PUT"
- "fetch.*method.*PUT"
- "\\.put\\("
- "PUT https?://"
lesson: |
REST PUT replaces the ENTIRE resource. Fields not included in the
request body will be overwritten with empty/default values.
ALWAYS:
1. GET the current resource state first
2. Modify only the fields you need in the response data
3. Send ALL fields in the PUT body
4. Test on 1 item before batch operations
5. Verify the result after the PUT
checklist:
- "GET the current resource state"
- "PUT body contains ALL required fields"
- "Test on 1 item before batch operation"
- "Verify result after update"
source:
incident: "Zenn 5-article deletion (2026-02-09)"
url: "https://zenn.dev/yurukusa/books/6076c23b1cb18b/viewer/2-safety-guards"
tags: [api, destructive, data-loss, rest]
各フィールドの役割。
| フィールド | 役割 |
|---|---|
id |
教訓の一意識別子 |
severity |
critical / warning / info |
trigger_patterns |
正規表現のリスト。コマンド文字列と照合 |
lesson |
教訓の本文。エージェントへの警告メッセージ |
checklist |
実行前に確認すべき項目 |
violated_count |
過去の違反回数。重みづけの根拠 |
source |
教訓の出典。なぜこの教訓が存在するか |
4. 教訓の追加
ファイルから。
brain write -f my-lesson.yaml
対話式で。
brain write
# ID、severity、パターン、チェックリストを順に入力
もう一つ実例。git push --force を防ぐ教訓。
# ~/.brain/lessons/git-force-push.yaml
id: git-force-push
severity: critical
trigger_patterns:
- "git push.*--force"
- "git push.*-f "
- "git reset --hard"
- "git clean -fd"
- "rm -rf"
lesson: |
Force push, hard reset, and recursive delete are destructive
operations that cannot be easily undone.
Before any destructive git operation:
1. Create a backup branch
2. Verify you're on the correct branch
3. Confirm the operation with the user
checklist:
- "Created backup branch (git checkout -b backup/...)"
- "Verified current branch is correct"
- "User explicitly requested this destructive operation"
tags: [git, destructive, irreversible]
監査ログ
全てのguardチェックはJSONL形式で記録される。
{"timestamp": "2026-02-09T10:30:00+00:00", "agent": "cc-main", "action": "PUT /api/articles/abc", "lessons_matched": ["api-put-safety"], "checked": true, "followed": true, "note": "user_confirmed"}
{"timestamp": "2026-02-09T10:31:00+00:00", "agent": "cc-sub", "action": "curl -X PUT", "lessons_matched": ["api-put-safety"], "checked": true, "followed": false, "note": "user_aborted"}
brain audit で集計レポートが見える。
$ brain audit
📊 Audit Report
==================================================
Total checks: 47
Followed: 45
Blocked: 2
Compliance: 96%
Per-lesson breakdown:
[api-put-safety] checks=12, followed=12, blocked=0
[git-force-push] checks=5, followed=3, blocked=2
Last 10 entries:
✅ 2026-02-09T10:30 [cc-main] PUT /api/articles/abc (user_confirmed)
❌ 2026-02-09T10:31 [cc-sub] curl -X PUT (user_aborted)
...
「教訓を読んだか」「守ったか」が数値で出る。これが「教訓を書いただけ」との決定的な違い。
guard engineの実装
コアのパターンマッチ部分。約30行。
def guard(command: str, agent: str = "unknown", auto_confirm: bool = False) -> bool:
"""コマンドを全教訓と照合。安全ならTrue。"""
lessons = load_all_lessons()
matches = []
for lesson in lessons:
patterns = lesson.get("trigger_patterns", [])
if not patterns:
continue
for pattern in patterns:
try:
if re.search(pattern, command, re.IGNORECASE):
matches.append(lesson)
break
except re.error:
# 正規表現が壊れていたら部分文字列マッチにフォールバック
if pattern.lower() in command.lower():
matches.append(lesson)
break
if not matches:
log_audit(agent, command, None, checked=True, followed=True, note="no_match")
return True
# 警告表示 + チェックリスト提示
for lesson in matches:
display_warning(lesson) # severity別の色分け表示
# 監査ログ記録
lesson_ids = [m.get("id", "unknown") for m in matches]
log_audit(agent, command, lesson_ids, checked=True, followed=None, note="guard_triggered")
return True # 非インタラクティブ時は警告のみで通過
ポイント。
-
正規表現マッチ。
re.IGNORECASEで大文字小文字を無視 - フォールバック。正規表現が不正でもクラッシュしない。部分文字列マッチに落ちる
-
全件記録。マッチしなくても
no_matchとして記録。「チェックが走ったこと自体」の証跡
PyYAMLなしでも動く
依存ライブラリはゼロ。PyYAMLがなくても自前の簡易YAMLパーサーで動作する。
try:
import yaml
except ImportError:
yaml = None # フォールバックパーサーを使用
pip install を挟まずに即使える。CI環境やDocker内でも軽量に動く。
テスト
170テスト。全パス。0.98秒で完了。
$ python3 -m pytest tests/ -q
170 passed in 0.98s
カバー範囲。
- YAMLパーサーのエッジケース(Unicode、空ファイル、壊れたYAML)
- guardのパターンマッチ(複数マッチ、正規表現特殊文字、大文字小文字)
- 監査ログ(1万件ロード、壊れたJSONL行のスキップ、同時書き込み)
- hookのinstall/uninstall(冪等性、既存設定との共存)
- Unicode全パイプライン(日本語教訓ID → guard発火 → 監査ログ → レポート表示)
組み込み教訓21種
最初から使える教訓を21個同梱している。一部を紹介。
| 教訓ID | severity | 何を防ぐか |
|---|---|---|
api-put-safety |
critical | GETなしPUTによるデータ上書き |
git-force-push |
critical | force push、hard reset、rm -rf |
no-secrets-in-code |
critical | コード内のAPIキー・パスワード |
dont-delete-without-confirm |
warning | 確認なしの削除操作 |
verify-before-claim |
warning | 成功を確認せず「完了」と報告 |
test-before-deploy |
warning | テストなしデプロイ |
backup-before-migration |
warning | バックアップなしのDB変更 |
自分のプロジェクト固有の教訓をYAMLで追加すれば、チーム全体で共有できる。
限界と注意点
過検知はある。 verify-before-claim の教訓は "successfully" や "completed" をトリガーにしている。普通のログ出力にも反応する。severity を info に下げるか、パターンを絞ることで対処する。
Claude Code固有。 PreToolUse フックはClaude Codeの機能。他のAIコーディングツールには直接使えない。ただし brain guard 自体はスタンドアロンのCLIなので、シェルエイリアスやgit hookとして他の環境でも使える。
# シェルエイリアスとして
alias curl='brain guard curl'
# git pre-commit hookとして
#!/bin/bash
brain guard "git commit" || exit 1
パターンの精度は使い手次第。 正規表現が甘いと誤検知する。厳しすぎると漏れる。実運用しながら調整するしかない。
教訓
AIエージェントに「気をつけて」と言っても無駄。仕組みで止める。
やったこと。
- 実際の事故(Zenn 5記事消失)から教訓をYAMLに構造化
-
brain guardでコマンド実行前に自動照合 - Claude Codeの
PreToolUseフックとして登録 - 監査ログで「読んだか・守ったか」を記録
教訓は書くものではなく、実行するものだ。
🛡️ npx cc-safe-setup — 650+個のhookをワンコマンドで導入
🔍 npx cc-health-check — 8カテゴリ・40+項目で環境をスコアリング
📘 Claude Codeを本番品質にする — 安全hookの設計から自律運用まで体系的に解説(¥800・Ch.2まで無料公開)
📖 AIに仕事を任せてみた — PreToolUseを含む658個のhookを作るまでの800時間の全記録(¥800・第2章まで無料)
リポジトリ: github.com/yurukusa/shared-brain
関連記事
- Anthropic公式「Skills完全ガイド」 — CLAUDE.mdとSkillsの設計パターン
- Claude Codeを108時間無人で走らせて起きた全事故と、そこから作った安全装置 — hookの全体像
- hookのifフィールドで不要なプロセス起動をゼロにする
設定が正しいか不安な方へ
無料で診断: npx cc-health-check で安全スコアを即座に確認できます。プロによる詳細レビュー($50〜)も受付中。
自分のトークン消費パターンを確認したい方へ
Token Checkupで5つの質問に答えるだけでトークン消費の診断ができる。Hook Selectorで最適なhookセットも分かる。
📖 トークン消費に困っているなら → Claude Codeのトークン消費を半分にする——800時間の運用データから見つけた実践テクニック(¥2,500・はじめに+第1章 無料)
📖 非エンジニアがClaude Codeで事業を回した全記録(¥800) — $800のAIコストで¥6,000を稼ぐまでの失敗と改善。第2章まで無料
⚠️ CVE-2026-21852(2026年4月公開): プロジェクト内.claude/settings.json経由でAPIキー窃盗。対策: npx cc-safe-setup(ユーザーレベル設定で免疫)→ 詳細
⚠️ Opus 4.7緊急情報(2026年4月17日)
Opus 4.7のauto mode安全分類器がOpus 4.6にハードコードされている問題が発覚。3日間で23件以上のデータ損失。さらにv2.1.100以降、APIコールごとに約20,000トークンが見えない場所で追加課金されている問題も判明(#46917、GitHub上196件のリアクション)(50GB永久消失含む)。4倍のトークン消費も報告されている。対策: npx cc-safe-setup --opus47(Survival Guide / Safety Scanner)