作業ディレクトリを消した後も、危険な操作だけを止めるフックにした

  • #開発環境
  • #Git
  • #フック
  • #安全設計

今回やったこと

作業用ディレクトリを片付けた後、同じターミナルで別のコマンドまで実行できなくなる問題を、フックの探索経路と失敗時の扱いの両面から整理した。作業場所がなくなった状態でも通常のコマンドは続けられ、マージのような注意が必要な操作は安全側に止まる構成にした。

対象は、コマンド実行前にプロジェクトのスクリプトを呼ぶフックである。フック本体が現在の作業ディレクトリの中にだけあると、そのディレクトリを削除した後に参照先が消える。今回の記録では、作業ディレクトリを削除した後、$CLAUDE_PROJECT_DIR が削除済みの場所を指し続け、フックがファイルを開けず終了コード2を返した。結果として、そのセッションでは後続の Bash コマンドが拒否された。

参照先を複数の場所から探す

最初の対策は、フック本体の置き場所を一つに決め打ちしないことだ。プロジェクトディレクトリを最初に調べ、そこに見つからない場合は作業ディレクトリの親や Git の共通ディレクトリを手掛かりに探索する。作業用ディレクトリを閉じても、同じリポジトリの本体側にスクリプトが残っていれば、フックは引き続き読み込める。

ここで大切なのは、探索を増やすこと自体ではなく、作業場所とフック本体の寿命を切り離すことだ。使い捨ての作業領域はタスク終了時に片付けられる。一方、フックの実装はその後も次の作業で必要になる。寿命が異なるものを同じディレクトリだけに置くと、片方の整理がもう片方を壊す。

通常時: 作業場所 → フック本体を読む → コマンドを判定
削除後: 作業場所は消えている → 親・共通領域を探す → 本体があれば判定

この探索順は .claude/settings.json に設定されている。探索先を順番に調べ、スクリプトが見つかった場所で判定を実行する。Git の共通ディレクトリを使うことで、作業用ディレクトリが別の場所でも、リポジトリ共通の位置を手掛かりにできる。

本体が見つからない時の失敗を分ける

探索しても本体が見つからない場合、すべてのコマンドを拒否する方法は単純だが、復旧作業まで止めてしまう。反対に、常に通す方法では、止めるべき操作も通ってしまう。そこで、失敗時はコマンドの性質で扱いを分ける。

フックの設定では、本体が見つからない時に入力されたコマンドを確認し、gh と merge を含むマージ操作はエラーとして止める。それ以外は続行できる。危険な操作だけを fail-closed にし、無関係な作業はフックの故障に巻き込まない考え方だ。

状況フックの扱いねらい
フック本体が見つかり、判定できる通常の判定へ進む既存の安全確認を使う
本体が見つからず、マージ操作を含む停止するCI確認などを飛ばした統合を防ぐ
本体が見つからず、マージ操作ではない続行する復旧や通常作業まで塞がない

この区別は、エラーの有無だけでなく、止める仕組みが壊れた時に何が起きるかを考えて決める。マージは結果が共有ブランチへ及ぶため、判定不能なら停止する。一方、ファイル確認や修正まで一律に止めると、本体を復旧するための操作もできなくなる。

変更をどこで確かめるか

記録上の修正は .claude/settings.json にフック本体のフォールバック探索と、見つからない場合のコマンド判定を置いている。また、scripts/test_ci_gate.py が関連する CI マージゲートの挙動を検査する。運用上は、作業ディレクトリを閉じた後に通常コマンドが使えることと、マージ操作が止まることをそれぞれ確認する必要がある。片方だけでは、「何でも止まる」か「何でも通る」のどちらかを見落とす。

ただし、探索先が間違っている場合や、親側にも古いスクリプトしかない場合までは、この分岐だけで保証できない。フック本体の配置と更新を一元化し、フォールバック先が正しいリポジトリを指すことも合わせて保守する必要がある。

作業用ディレクトリを消した後にフックが壊れる問題は、後片付けの順番だけで避けるより、削除後も復旧可能な構造にする方が扱いやすい。参照先は複数段で探し、判定不能時は操作の影響に応じて扱いを分ける。この二つを組み合わせれば、重要な安全確認を維持しながら、無関係な作業まで止める故障を減らせる。

更新履歴

  • 2026-10-04: 初稿作成。参照: .claude/settings.json、.claude/GITHUB.md、scripts/test_ci_gate.py。

訂正履歴

  • なし。