GateGuard — 事実強制型アクション前ゲート
PreToolUseフック。Claudeに編集前に調査を強制します。「本当に実行しますか?」的な自己評価ではなく、具体的な事実を要求します。調査という行為そのものが、自己評価では決して得られなかった認識を生み出します。
有効な状況
- ファイル編集が複数のモジュールに影響するコードベースでの作業
- 特定のスキーマや日付形式を持つデータファイルを含むプロジェクト
- AI生成コードが既存パターンと一致する必要があるチーム
- Claudeが調査せずに推測しがちなワークフロー
核となる考え方
LLMの自己評価は機能しません。「ポリシーに違反していませんか?」と聞くと、答えは常に「いいえ」です。これは実験的に検証されています。
しかし、「このモジュールをインポートしているすべてのファイルをリストアップせよ」と要求すると、LLMはGrepとReadを実行せざるを得ません。調査そのものがコンテキストを生成し、出力を変えます。
3段階ゲート:
1. DENY — 最初のEdit/Write/Bash試行をブロック
2. FORCE — どの事実を収集すべきかをモデルに正確に指示
3. ALLOW — 事実が提示された後に再試行を許可
これら3つすべてを実装している競合はいません。ほとんどのものは拒否で止まります。
エビデンス
2つの独立した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. 1行のロールバック手順を記述してください
3. ユーザーの現在の指示をそのまま引用してください
ルーチンBashゲート(セッションごとに1回)
1. 現在のユーザーリクエストを一文で
2. この特定のコマンドが検証または生成する内容
並列バッチと部分適用
最初のタッチゲートは、各ツール呼び出しを個別に評価します。まだタッチされていないファイルへの複数の編集が1つの並列バッチで送信された場合、最初の呼び出しが拒否され、その拒否によってファイルがチェック済みとしてマークされるため、そのバッチ内の兄弟編集は適用されます。ロールバックは発生しません。拒否された編集がなくても、ファイルには兄弟編集が保持される可能性があります。
拒否メッセージはファイル名を示し、バッチの兄弟編集がすでに適用されている可能性があることを警告します。文字通りに扱ってください:
- まだタッチされていないファイルへの依存編集は、並列バッチではなく、順次送信してください。定義とその最初の使用、またはインポートとその呼び出しサイトは、同じバッチに含めてはいけません。
- 最初のタッチ拒否後、事実を提示し、拒否された編集を再試行し、バッチからの他のものに基づいて構築する前にファイルを再読み取りしてください。
バッチ全体のロックは不可能です。フックはツール呼び出しを1つずつ認識するため、ゲートはどの呼び出しが一緒に到着したかを知ることができません。
クイックスタート
オプション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つの事実ブロックを出力します。それ以降の拒否は、拒否の序数を含む1行に凝縮されるため、ほぼ同一のブロックがコンテキストウィンドウに蓄積されてモデルの反復ループを増幅することがありません(#2142)。事実を提示した後に同じファイルまたはコマンドを再試行しても、ゲートは再トリガーされません。
段階的制御
ECC_GATEGUARD=off(または GATEGUARD_DISABLED=1)はゲートを完全にオフにします。この表の変数はそうではありません。各変数は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つの事実ブロックを出力する拒否の数。それ以降は1行に凝縮されます。0 は最初の拒否から凝縮します。 |
GATEGUARD_BASH_EXTRA_DESTRUCTIVE | 未設定 | 組み込みセットに追加される、追加の破壊的コマンドパターン(正規表現ソース)。不正な正規表現は未設定として扱われ(組み込みは引き続き適用)、stderr に1回ログ記録されます。 |
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、次にフックプロセスのワーキングディレクトリです。相対グロブはそのルート外のターゲットを除外しません。明示的な絶対グロブは絶対ターゲットパス全体と一致し、プロジェクト外のパスを意図的に除外できます。
パターンとパスの両方は / 区切り文字と小文字マッチングを使用します。* はセグメント内で一致、** はセグメント間で一致、? は1つの非区切り文字と一致します。**/ はゼロディレクトリを含むため、**/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はセッションごとに1回だけゲートします。破壊的Bashは毎回ゲートします。このバランスにより、速度低下を回避しながら実際のリスクをキャッチできます。
ベストプラクティス
- ゲートを自然に発火させてください。ゲートの質問に事前に答えようとしないでください。調査自体が品質を向上させるのです。
- ゲートメッセージをドメインに合わせてカスタマイズしてください。プロジェクトに特定の規約がある場合は、ゲートプロンプトに追加してください。
.gateguard.ymlを使用して、.venv/、node_modules/、.git/などのパスを無視してください。
関連スキル
safety-guard— ランタイムセーフティチェック(補完的であり、重複しない)code-reviewer— 編集後のレビュー(GateGuardは編集前の調査)