GateGuard — ファクト強制アクション前ゲート
PreToolUseフックであり、Claudeに編集前に調査を強制します。「本当に大丈夫ですか?」といった自己評価ではなく、具体的な事実を要求します。調査という行為自体が、自己評価では決して生まれなかった認識を生み出します。
有効な場面
- ファイル編集が複数のモジュールに影響を与えるコードベースで作業する場合
- 特定のスキーマや日付形式を持つデータファイルを含むプロジェクト
- AI生成コードが既存のパターンと一致する必要があるチーム
- Claudeが調査せずに推測する傾向があるワークフロー
基本コンセプト
LLMの自己評価は機能しません。「ポリシーに違反していませんか?」と尋ねると、常に「いいえ」と答えます。これは実験的に確認されています。
しかし、「このモジュールをインポートしているファイルをすべて列挙せよ」と要求すると、LLMはGrepやReadを実行せざるを得ません。調査自体がコンテキストを生成し、出力を変えます。
三段階ゲート:
1. 拒否 — 最初のEdit/Write/Bash試行をブロック
2. 強制 — モデルに収集すべき事実を正確に指示
3. 許可 — 事実が提示された後に再試行を許可
この3つをすべて実装している競合はありません。ほとんどのものは拒否で止まります。
エビデンス
二つの独立したA/Bテスト、同一エージェント、同一タスク:
| タスク | ゲートあり | ゲートなし | 差 |
|---|---|---|---|
| アナリティクスモジュール | 8.0/10 | 6.5/10 | +1.5 |
| Webhookバリデーター | 10.0/10 | 7.0/10 | +3.0 |
| 平均 | 9.0 | 6.75 | +2.25 |
両方のエージェントが実行可能でテストに合格するコードを生成します。違いは設計の深さです。
ゲートの種類
Edit / MultiEdit ゲート(ファイルごとの最初の編集)
MultiEditも同様に処理されます — バッチ内の各ファイルは個別にゲートされます。
{file_path}を編集する前に、以下の事実を提示してください:
1. このファイルをインポート/requireしているすべてのファイルを列挙してください(ツリーを検索 — Glob/Grep、またはBash経由のfind/grep)
2. この変更の影響を受けるパブリック関数/クラスを列挙してください
3. このファイルがデータファイルを読み書きする場合、フィールド名、構造、および日付形式を示してください(生の本番データではなく、編集済みまたは合成値を使用)
4. ユーザーの現在の指示をそのまま引用してください
Write ゲート(最初の新規ファイル作成)
{file_path}を作成する前に、以下の事実を提示してください:
1. この新しいファイルを呼び出すファイルと行を指定してください
2. 同じ目的を果たす既存のファイルがないことを確認してください(ツリーを検索 — Glob/Grep、またはBash経由のfind/grep)
3. このファイルがデータファイルを読み書きする場合、フィールド名、構造、および日付形式を示してください(生の本番データではなく、編集済みまたは合成値を使用)
4. ユーザーの現在の指示をそのまま引用してください
破壊的Bashゲート(すべての破壊的なコマンド)
rm -rf、git reset --hard、git push --force、drop tableなどでトリガーされます。
1. このコマンドが変更または削除するすべてのファイル/データを列挙してください
2. 一行のロールバック手順を記述してください
3. ユーザーの現在の指示をそのまま引用してください
ルーチンBashゲート(セッションごとに一度)
1. 現在のユーザーリクエストを一文で
2. この特定のコマンドが検証または生成するもの
並列バッチと部分適用
ファーストタッチゲートは各ツール呼び出しを独立して評価します。まだ触れられていないファイルに対する複数の編集が一つの並列バッチで送信された場合、最初の呼び出しが拒否され、その拒否によってファイルがチェック済みとしてマークされるため、そのバッチ内の兄弟編集は適用されます。何もロールバックされません:ファイルは、拒否された編集を含まない兄弟編集を保持した状態になる可能性があります。
拒否メッセージはファイルを指定し、バッチの兄弟が既に適用されている可能性があることを警告します。文字通りに扱ってください:
- 依存する編集は、並列バッチではなく、まだ触れられていないファイルに対して逐次的に送信してください。定義とその最初の使用、またはインポートとその呼び出し元は、同じバッチに含めてはいけません。
- ファーストタッチ拒否の後、事実を提示し、拒否された編集を再試行し、バッチの他のものに基づいて構築する前にファイルを再読み取りしてください。
バッチ全体のロックは不可能です:フックはツール呼び出しを一度に一つだけ認識するため、ゲートはどの呼び出しが一緒に到着したかを知ることができません。
クイックスタート
オプションA: ECCフックを使用(ゼロインストール)
scripts/hooks/gateguard-fact-force.js フックがこのプラグインに含まれています。hooks.json で有効にしてください。
GateGuardがセットアップや修復作業をブロックする場合は、ECC_GATEGUARD=off でセッションを開始してください。フックレベルの制御には、引き続き ECC_DISABLED_HOOKS にGateGuardフックIDを指定してください。
長いセッションでは、最初の GATEGUARD_FACT_FORCE_FULL_DENIALS 個(デフォルト3)のファクトフォース拒否のみが完全な4つの事実ブロックを出力し、以降の拒否は拒否順序番号を伴う単一行に凝縮されるため、ほぼ同一のブロックがコンテキストウィンドウに蓄積されてモデルの繰り返しループを増幅することを防ぎます(#2142)。事実を提示した後に同じファイルやコマンドを再試行しても、ゲートは再度トリガーされません。
段階的制御
ECC_GATEGUARD=off(または GATEGUARD_DISABLED=1)はゲートを完全にオフにします。以下の表の変数はそうではありません — それぞれが一つの動作を狭め、負荷の高い破壊的Bashチェックは実行し続けます:
| 変数 | デフォルト | 効果 |
|---|---|---|
GATEGUARD_BASH_ROUTINE_DISABLED | 未設定(ゲートオン) | ルーチンBashゲートのみを無効にします。破壊的Bashゲート(rm -rf、git reset --hard、drop table、dd if=など)は影響を受けません。 |
GATEGUARD_EXEMPT_GLOBS | 未設定(除外なし) | カンマ区切りのグロブ。一致するEdit/Write/MultiEditターゲットはファーストタッチファクトフォースをスキップします。インポート価値が低いツリー(テスト、生成されたアーティファクト、スクラッチディレクトリ)で、「これを誰がインポートしているか/このスキーマは何か」に意味がない場合を想定しています。 |
GATEGUARD_FACT_FORCE_FULL_DENIALS | 3 | 完全な4つの事実ブロックを出力する拒否の数。それ以降は単一行に凝縮されます。0 は最初の拒否から凝縮します。 |
GATEGUARD_BASH_EXTRA_DESTRUCTIVE | 未設定 | 組み込みセットに追加する追加の破壊的コマンドパターン(正規表現ソース)。不正な正規表現は未設定として扱われ(組み込みは引き続き適用)、stderrに一度ログ出力されます。 |
GATEGUARD_STATE_DIR | ~/.gateguard | セッションごとのゲート状態を保持する場所。状態を永続化できない場合、ゲートはループを避けるために操作を許可し、警告でこの変数を指定します。 |
GATEGUARD_BASH_ROUTINE_DISABLED は 1、true、on、enabled、enable、yes を受け入れます(大文字小文字および空白を区別しません)。その他の値はゲートをオンのままにします。
ゲートを完全にオフにする
| 変数 | 効果 |
|---|---|
ECC_GATEGUARD=off | セッションのGateGuardを無効にします。0、false、off、disabled、disable を受け入れます。 |
GATEGUARD_DISABLED=1 | 同じ効果。1 のみ認識します — 上記のスペルはここでは適用されません。 |
フックレベルの制御には、引き続き ECC_DISABLED_HOOKS にGateGuardフックIDを指定してください。
GATEGUARD_EXEMPT_GLOBS のグロブセマンティクス
パターンはプロジェクト相対のターゲットパス全体と一致します。プロジェクトルートは CLAUDE_PROJECT_DIR、フォールバックとしてフックペイロードの cwd、次にフックプロセスのワーキングディレクトリです。相対グロブはそのルート外のターゲットを除外しません。明示的な絶対グロブは絶対ターゲットパス全体と一致し、意図的にプロジェクト外のパスを除外する場合があります。
パターンとパスは両方とも / セパレータを使用し、小文字で一致します。* はセグメント内で一致、** はセグメント間で一致、? は一つの非セパレータ文字と一致します。**/ はゼロディレクトリを含むため、**/tests/** は tests/foo.js にも一致します。不正なパターンは除外を許可せずにドロップされます。
2.2.1以降、services/** はプロジェクトのルートservicesツリーのみをカバーし、*.md はルートのMarkdownファイルのみをカバーします。プロジェクト内のすべてのMarkdownファイルには **/*.md を使用してください。既存の非アンカー除外は調整が必要な場合があります:
{
"env": {
"GATEGUARD_BASH_ROUTINE_DISABLED": "1",
"GATEGUARD_EXEMPT_GLOBS": "**/tests/**,tests/**,**/*.test.*,**/docs/**,**/dist/**"
}
}
オプションB: 設定付きフルパッケージ
pip install gateguard-ai
gateguard init
これにより、プロジェクトごとの設定(カスタムメッセージ、除外パス、ゲートトグル)用の .gateguard.yml が追加されます。
アンチパターン
- 代わりに自己評価を使用しないでください。「本当に大丈夫ですか?」は常に「はい」と答えます。これは実験的に確認されています。
- **データスキーマチェックをスキップしないでください。**両方のA/Bテストエージェントが、実際のデータが
%Y/%m/%d %H:%Mを使用しているときにISO-8601日付を想定しました。データ構造(編集済み値を使用)をチェックすることで、この種のバグ全体を防ぐことができます。 - **すべてのBashコマンドをゲートしないでください。**ルーチンBashゲートはセッションごとに一度のみです。破壊的Bashゲートは毎回実行されます。このバランスにより、速度低下を避けながら実際のリスクを確実に捕捉します。
ベストプラクティス
- ゲートを自然に発火させてください。ゲートの質問に先回りして答えようとしないでください — 調査自体が品質を向上させるのです。
- ゲートメッセージをドメインに合わせてカスタマイズしてください。プロジェクトに特定の規則がある場合は、ゲートプロンプトに追加してください。
.gateguard.ymlを使用して、.venv/、node_modules/、.git/などのパスを除外してください。
関連スキル
safety-guard— ランタイムセーフティチェック(補完的、重複なし)code-reviewer— 編集後のレビュー(GateGuardは編集前の調査)