1
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を4か月走らせて、安全フックが止めた操作を正直に数えてみた

1
Posted at

Claude Code を、ほぼ24時間ずっと自律で走らせている。シェルにも Git にも触れる状態で、私が寝ている間もセッションが動き続ける。

こういう運用をしていると、一つ怖い問いが残る。誰も見ていない時間に、このエージェントは私のマシンに対して何をやろうとしているのか。

私は PreToolUse フックで、ブロックした危険なコマンドを全部ログに残している。今回、そのログを4か月ぶんまとめて、自分で実際に掘って数えてみた。結論を先に書く。見出しになりそうな大きい数字は、そのまま書いたら嘘になる。そして嘘を剥がした後に残ったものの方が、見出しよりずっと役に立つ。この記事は他人の事故を紹介したものではなく、自分の環境で走っているエージェントのログを自分で数え直した実測の記録だ。

生の数字は嘘になる

ブロックの記録は 99,402 件あった。「AIの安全フックが4か月で約10万件の危険操作を止めた」——書きたくなる。でも書けない。

生の件数(99,402)は、そのまま記事にしたら嘘になる。 内訳を出すと理由が分かる。

  • ユニークなコマンドは 337 件だけ。残りはほとんど同じコマンドの再試行だ。エージェントはブロックされると、同じ操作を手を変えて何度も試す。だから1つの操作が数百〜数千行に膨れる。
  • 日別で見ると、2026年3月22〜30日に約 87,000 件が集中していた。これはフック自体を開発してテスト一式を何千回も回していた時期で、実運用で危険を捕まえた記録ではない。
  • 3月のテスト期を除いた実運用期(4〜7月)に絞ると 10,351 件。同じコマンドの再試行をまとめてユニークにすると、223 件まで落ちる。しかもこの223件にも、フックのテスト用のコメント行や変数への代入がブロックに巻き込まれたものが数十件混じっている。純粋な危険操作だけを数えると、もっと少ない。

10万が223。ざっと450倍の水増しだ。自分のログを記事のネタにする時、生の件数をそのまま使うと、この450倍を読者に売りつけることになる。

「派手な破滅」は、実はほとんど自分のテストだった

223 件をさらに眺めていて、私は自分の記事の危うさに気づいた。

ログには rm -rf /Remove-Item -Recurse -Force /(WSL2のWindows側を根こそぎ消す形)が何度も並んでいる。root や home を消す形は、実運用期だけで約2,000行。数だけ見れば「無人のAIが、root ごと消そうとした痕跡が数千件」——恐怖を煽るには十分な素材に見える。

だが1件ずつ中身を自分で確かめると、手が止まった。この約2,000行は、同じ数種類の形が繰り返し記録されたものだった。しかも rm -rf / のように、空白の数だけ違う版が並んでいる。エージェントが打つはずのない不自然な形で、しかも同じ束が4月にも5月にも6月にも顔を出す。実機で確認すると、これは正規表現が取りこぼしなく拾えるかを検証する、私自身の回帰テストの痕跡だった。エージェントが本当に root を消しにいったのではない。

もしここで「私のAIは root を消そうとした痕跡が数千件ある」と書いていたら、それは作話だった。安全を売る記事で数字を作話したら、守ろうとしているものを自分で壊す。危ないところだった。

じゃあ、本物は何だったのか

自分のテストを除いて、エージェントが実運用の中で本当に繰り返し試して止められた操作は、地味なものだった。ほとんどが force 系の Git 操作だ。

  • git reset --hard origin/main(ユニークで35件)——リポジトリをoriginに合わせてから作業ブランチを切る、その前処理。
  • git checkout --force / git checkout -f <branch>——別のブランチへ強制的に切り替える。
  • git clean -fd——追跡外のファイルを一掃する。

どれも、実在するブランチ名(feat/feature-deprecation-detector-2026-05-27 のような)や日付が付いていて、エージェントが実際の仕事の流れの中でやった操作だと分かる。そして、そのほとんどはその文脈では安全だった。同期して、ブランチを切るだけなのだから。

それでもフックは、この種類をまるごと止める。なぜか。フックには「安全な同期」と「未コミットの3時間ぶんの作業を上書きする瞬間」の区別がつかないからだ。git reset --hard は、たいてい無害で、たまに全てを消す。区別できない以上、クラスごと止めるしかない。

本当に怖いのは、退屈な操作だ

ここが今回いちばん伝えたいところだ。

本当にデータを失わせるのは、rm -rf / のような派手な操作ではない。退屈な操作だ。

私のログで実運用に残っていたのも、派手なコマンドではなく force 系の Git 操作だった。そして公開されている事故の起票を見ても、データが消える形はだいたい地味だという報告が多い。サブエージェントが git checkout で強制的にブランチを切り替え、まだコミットしていなかった作業を上書きして消す。あるいは、mv が黙って失敗した後に、空になったつもりのディレクトリへ rm -rf が続けて走り、移したばかりのファイルがまだ元の場所にあったのに消える(GitHub の起票 #72625 でこの並びが報告されている)。派手さは無い。だが結果は同じで、戻せない。

どれも、99回やれば99回無事に済む操作だ。100回目、そこに未コミットの作業があった時だけ、静かに全部消える。エージェントは rm -rf / のような分かりやすい破滅は打たない。打つのは、この退屈で、たいてい安全な操作の方だ。だからこそ危ない。人間もAIも「たいてい安全」を警戒し続けるのは苦手だから。

「モデルは賢いからそんなことしない」は、対策にならない。賢いモデルでも、100回目の未コミットの一回は引く。毎回確実に止められるのは、コマンドが走る前に決定的に判定する PreToolUse の網だけだ。「たいてい気をつけている」は保証ではない。決定的な網だけが保証になる。

自分のログを取ってみるといい

これは有料の何かを買わなくても、今日始められる。危険操作をブロックしたら1行残すだけの、小さな PreToolUse フックでいい。

#!/bin/bash
# ブロックした危険コマンドを1行だけ残す最小フック。
# settings.json の PreToolUse(Bash)に登録して使う。
INPUT=$(cat)
CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty')

# ここは各自の危険判定に差し替える。まずは記録だけでも価値がある。
if printf '%s' "$CMD" | grep -qE 'reset --hard|checkout (-f|--force)|clean -[fdx]'; then
  printf '[%s] %s\n' "$(date -Iseconds)" "$CMD" >> "$HOME/.claude/watched-commands.log"
fi
exit 0   # 0のまま=記録だけ。ブロックしたい時だけ非0で返す。

これを何週間か回してから、自分のログを今回の私と同じように正直に数えてみてほしい。生の件数ではなく、テストと再試行を抜いた後の、本当に自分のエージェントが試した操作を。たぶん、派手な破滅はほとんど無くて、退屈な force 系の操作が地味に並ぶはずだ。そこが、あなたのデータが実際に消えうる場所だ。


私が実運用で積み上げた、この種の「退屈だが本当に危ない」操作を止めるフック集と、その裏にある実際の事故の記録は、事故防止のための本(¥800)にまとめている。今日から使えるフックが要る人は、そちらが早い。

1
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
1
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?