exit 2を返すPreToolUseフックはすでにあります。onFailureはまだ必要ですか?
検討する価値がある。exit 2が効くのは、スクリプトが実際に動き、意図的に2を返したときだけだ。パスの打ち間違い、実行権限の欠如、タイムアウト、スクリプトが途中で落ちて1で終わる場合、ガードは何も「判決」を下しておらず、既定は通過になる。onFailure: "block"はまさに「門番が出勤していない」ケースを補うので、両者は補い合う関係だ。
自分のフックが本当にfail-closedになっているか、どう確かめますか?
使い捨てのディレクトリでclaude -pを使い、小さく試す。3つを別々に行う。スクリプトのパスをわざと誤る、スクリプトにsleepを入れてタイムアウトを超えさせる、スクリプトをそのままexit 1させる。それぞれで、本来ブロックされるはずの無害なコマンドをClaudeに実行させ、ブロックされることを確認する。その後設定を戻して、安全なコマンドが通ることも確認する。Developers Digestの検証もこの考え方で、フックだけを変数にしている。
promptフックやagentフックでもonFailureは使えますか?
Developers Digestの整理によれば、現時点では対象外だ。onFailureはcommandとHTTPのフックにだけ適用される。確実に止めたいルールは、commandフックを門番にして前述のようにfail-closedにするのがよい。promptフックとagentフックは、意味的な判断が必要なゆるい検査に向いている。v2.1.294も、指示文として書かれたこの2種類が以前は止めるべきものを通していたと述べており、唯一の防衛線には向かないことがわかる。
エンジニアではなく、チームのルールを見ているだけです。これは私に何の関係がありますか?
チームがフックでルールを強制しているなら(例:ファイル削除の禁止、mainへの直接プッシュの禁止)、エンジニアにシンプルな質問ができる。「ガードのスクリプトが壊れたりタイムアウトしたらどうなるか」。既定の答えは通過で、画面には見落としやすいエラーが1行出るだけだ。重要なルールにonFailure: "block"が設定されているか、そしてこのゲートがまだ機能していることを誰かが定期的に検証しているかを確認する。
多くの人がフックを「必ず止める」安全装置だと考えているが、公式のhooksドキュメントははっきり書いている。終了コードだけでアクションを止められるのは2だけだ。それ以外の非ゼロ終了はブロックせず、トランスクリプトにhook errorの行が出るだけ。つまり、ガード用スクリプトが壊れたとき、既定の結果は通過になる。2026年10月8日のv2.1.295は、まさにこの穴を塞ぐonFailure: "block"を追加した。
公式のhooksドキュメントによると、次のいずれもPreToolUseをブロックしない。
リリースノートの説明では、commandとHTTPのフックについて、フックが起動できない、タイムアウトする、予期しない終了コードで終わる場合に、通過させずアクションをブロックするよう設定できる。Developers Digestが10月9日に公開した検証は、v2.1.295のclaude -pで6つの設定を試し、PreToolUseフックだけを変えた。何も設定しない場合は、スクリプト不在とタイムアウトのどちらでもコマンドが実行され、"onFailure": "block"にすると両方ブロックされ、正常に動くガードは安全なコマンドを通し続けた。
同記事は適用範囲も述べている。意味があるのはPreToolUse、UserPromptSubmit、Stopのようにブロック可能なイベントだけで、prompt、agent、MCPツールのフックは対象外だ。以下の設定例は同記事のものだ(私が読めた公式hooksドキュメントのページにはこのキーが出てこなかったので、使う前に公式ドキュメントかclaude Plugin validateで確認してほしい)。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/guard.sh",
"timeout": 5,
"onFailure": "block"
}
]
}
]
}
}
onFailureだけでは足りない。スクリプトが最後まで走り、内部のコマンド失敗にもかかわらず0で終了すれば、Claude Codeから見ると成功した許可だ。Developers Digestの著者は、エラー処理のないガードがjqのないマシンで0終了し、rm -rfを通してしまう例を見つけた。より堅実なのは、予期しないエラーをすべて終了コード2にする書き方だ。この原則に沿った例を示す(照合ルールは流れを示すためのごく粗いもの)。
#!/usr/bin/env bash
set -euo pipefail
trap 'echo "guard crashed" >&2; exit 2' ERR
cmd=$(jq -r '.tool_input.command')
if printf '%s' "$cmd" | grep -Eq 'rm +-rf +/'; then
echo "blocked: destructive rm" >&2
exit 2
fi
ここでtrap ... ERRは、jqがないといった失敗をそのままexit 2に変える。一方、if条件内のgrepが一致しなくて1を返してもtrapは発動しない。
fail-closedにはコストがある。ガードが頼るHTTPエンドポイントが落ちれば、すべてのBash呼び出しが止まる。フォーマッターや通知のような利便性のフックが壊れたくらいで、セッション全体を止める必要はない。以下は私の推奨で、公式の方針ではなく推論だ。
onFailure: "block"は、取り消せない、または悪用されうる操作(削除、プッシュ、認証情報の持ち出し)にだけ設定する。v2.1.294(10月8日)は、指示文として書かれたpromptフックとagentフック(例:「Block commands that…」)が、止めるべきものを通してしまう問題を修正し、StopとSubagentStopのpromptフックの判定も改善して、Claudeが早く止まりにくくなった。それ以前のv2.1.288のノートの要約にも、照合の失敗でPreToolUseフックがスキップされるケースを、今はその呼び出しをブロックするようにした旨がある(詳細は原文を参照)。1か月のうちに「ゲートが守れていなかった」という修正が続いているので、自分のフックを改めて検証する価値がある。