第2話 防災フックが鳴らなかった話
困ったこと
この環境には「危ない命令を出したら止める警報装置」が27個も入れてありました。ところがふと調べたら、そのうち25個が、いつの間にか一度も鳴っていなかったことが分かったのです。火災報知器が27個あって、火事になっても2個しか鳴らない——しかも誰も気づかない。パソコンの画面には何も表示されないので、「動いていない」こと自体がずっと隠れていました。
どう解決したか
最初は「警報器の設定ミスだろう」とあちこち直しましたが、直りません。三日がかりで原因を追いかけ、ついに分かったのは、警報器の設置場所の書き方が原因でした。Windowsという土台から別の部屋(Linux環境)の装置を呼び出すとき、住所の書き方をすると、その土台が勝手に住所を別の言葉に書き換えてしまう仕組みがあります。おかげで呼び出された装置は「そんな場所はない」と言って、音もなく止まっていたのです。動いていた2個だけは、住所の書き方がたまたま書き換えを逃れる形式だった。つまり「設定ミス」ではなく「住所の書き方」だったのでした。
flowchart TB
A["🔔 フックが鳴らない"] --> B["計測器に目印を仕込む"]
B --> C{"目印は書かれる?"}
C -->|"書かれる"| D["計測器は健全・原因は別"]
D --> E["真因: 別の仕掛けが道標(設定の道)を書き換えていた"]
E --> F["修正後: 鳴る 🔔"]
何が変わったか
27個すべての警報器を、書き換えを逃れる正しい形式に書き直しました。そして教訓を仕組みにしました。「警報器は鳴らないと存在しないのと同じ」なので、試験用の小さな火事(実際には無害な命令)を定期的に流して、本当に鳴るか確かめるテストを入れました。また、この一件で「原因だと考えたことが違っていたら、記録に『撤回』と書いて残す」習慣もできました。間違いを隠さず消すことで、次に同じ道で迷わなくなったのです。
<details><summary>▶ 技術的にどう作ったか</summary>
何が起きていたか
- Windows側のClaude Codeから
wsl bash ~/.../guard.sh形式でhookを起動すると、MSYSのパス変換が/home/...をC:/Users/...に書き換える。WSL内にそんなパスは無いのでスクリプトが存在せず、終了コード・ログなしの静かな失敗になった - 27 hook中25本がこの形式。生存していた2本(
check-command-safety.py/path-rewrite.py)はwsl bash -c "..."形式(-c "..."の文字列内はMSYS変換を免れる)だった
誤診の連鎖と確定方法
- 誤診1「空matcherが無効」→
""を"*"に変更で効果なし。誤診2「"*"も無効」→ multi-llm-reviewで却下(実際に発火していた)。誤診3「guardスクリプトのパース不具合」→ 実在したが単独では説明不能 - 確定: スクリプト冒頭に一時的な呼び出しログ(canary)を挿入し、通常のBash呼び出しでguardが呼ばれていないことを直接観測。手動実行ではcanaryが書かれるため計測器自身は健全と切り分け、再現実験でMSYS変換によるパス書き換えを特定
- 修正: settings.jsonの25 hookを
wsl bash -c "bash ~/..."形式へ一括変換・再起動後にバナー表示/破壊的コマンドのブロック/PostToolUse記録の3点で効果確認
副次修正
guard-destructive-commands.sh の独立欠陥2件(tool_name抽出の空白非対応・exit 2でなく{"decision":"block"}返却が必要)をTDDで修正(33→38テスト)
所在情報(要約・原文リンクなし)
- 記録: SSOT
01_DECISIONS/claude-code/2026-08-22_hook不発の真因はwsl-bash-posixパス変換-matcher説を撤回.md(outcome: confirmed・誤診3件の経緯も記録) - 関連: 2026-08-30の「並行セッションstaged巻き込み-Windows版hook未登録の真因特定」で第2段対応済み
</details>