Status: v5(Implementation: 10/10 hooks Done — #169 完走 — #157 で 3 hook、#169 セッション A で 2 / B で 3 / C で 2 = 計 10 hook、3 mode 設計) Hook 数の現状(v8.7.0 以降): 本書は v8.5.0 時点の 10/10 hooks スナップショット。 v8.6.0 で EH-8(
check-metrics-privacy.sh、metrics privacy 強制)、 v8.7.0 で EH-9(check-delegation-commit-boundary.sh、TASK-0073 F2)を追加し、 現状は EH-1〜EH-9 + EHS-1〜EHS-3 = 12/12。本書本文の表は v8.5.0 構成のまま 維持し、追加分の詳細はそれぞれの実装 PR / CHANGELOG /bin/plangate doctor出力を参照。EH-12(追加・配線は Human apply 待ち): protected branch 上の破壊的 git 操作 block(
check-git-destructive.sh)。hook 本体(非 HO のscripts/直下)は 実装・テスト済みだが、PreToolUse 配線はscripts/apply-eh-git-destructive-guard.sh --apply(Human-owned)の実行後に 有効化される。番号は EH-10 / EH-11 が既に予約済み(#760 PostToolUse 軽量品質 チェック / #762 Stop 軽量 verify の「候補」名として.claude/settings.example.jsonのコメントで使用、 かつ EH-10 はdocs/rfc/ai-self-set-gate-hook-enforcement.mdの RFC Draft が保持)のため、衝突しない最小の空き番号として EH-12 を採番した。
実装と物理配線の区別(2026-06-10 棚卸し / 2026-06-27 更新): 12/12 は 「スクリプト実装 + 単体テスト済み」を指す。発火経路(settings.json / .codex/hooks.json / CI / bin/plangate)への物理配線は 11/12(PreToolUse/CI 配線 6
bin/plangateCLI 配線 5 = EH-4 / EH-5 / EHS-1 / EHS-2 / EHS-3。EH-7 のみ doctor 可視化 + 手動推奨)。残る配線の完全化は #500 Wiring Integrity Enforcement (仕様策定済み)の実装範囲。発火条件の供給(EPIC #527 follow-up・配線済み): EHS-1/2/3 の発火条件
PLANGATE_VALIDATION_BIAS=strictは、bin/plangate verify/handoff --verifyが--profile <key>を受理しmodel-profiles.yamlのvalidation_biasを解決して内部 export する(TASK-0147 / #644 で配線・適用済み)。env で明示注入済みなら尊重し、 normal/lenient profile・無指定では非発火(既存挙動不変)。未知 key / yaml 欠落時は normal fallback + stderr 警告(scripts/_resolve_validation_bias.py)。
配線状態 Hook 発火経路 ✅ 配線済み(6) EH-1 / EH-2 / EH-3 / EH-6 / EH-9 Claude PreToolUse + Codex hooks.json EH-8 CI(metrics-privacy.yml)+ doctor + codex-guarded ✅ CLI 配線(5、apply 後) EH-4 / EH-5 bin/plangate verify—EH-4: V-1 前 strict / EH-5: V-1 後 warn(TASK-0143)EHS-1 bin/plangate verify—V-3 不合格時にvalidation_bias=strictで block(TASK-0145 増分1)EHS-3 bin/plangate verify—V-1 FAIL 時に fix-loop increment +validation_bias=strictで上限超過 block(TASK-0146 増分2)EHS-2 bin/plangate handoff --verify—handoff.md 6 要素不足をvalidation_bias=strictで block(TASK-0146 増分3)⏳ doctor 可視化のみ(1) EH-7 bin/plangate doctorCLI Hook Wiring セクション + 手動推奨(#500 後続)
関連:
responsibility-boundary.md/tool-policy.md/model-profiles.md実装:scripts/hooks/check-plan-exists.sh/check-c3-approval.sh/check-plan-hash.sh/check-test-cases.sh/check-verification-evidence.sh/check-forbidden-files.sh/check-merge-approvals.sh/check-v3-review.sh/check-handoff-elements.sh/check-fix-loop.sh設定例:.claude/settings.example.json/ 単体テスト:tests/hooks/run-tests.sh
各強制は「いつ発火するか」が経路ごとに異なる。とくに CLI(
bin/plangate)を 通さない運用では、CLI 層の強制(EH-4 / EH-5 / EHS-1/2/3)は一度も発火しない。「ローカル強制」の前提に注意: 層 A(Claude PreToolUse)/ 層 E(Codex hooks)は Claude Code / Codex などの AI ツールを介して編集・コミットしたときのみローカル発火する。 エディタで直接ファイルを編集して
gitで直接コミットする完全手動運用では層 A/E も 発火しない(その場合の最終防壁は層 B の CI = PR/push トリガー)。以下の「手動 / AI 任せ」は 主に「AI ツールは使うがbin/plangateCLI は回さない」運用を指す。
| 層 | 強制 | 発火契機 | CLI を使わない運用での実態 |
|---|---|---|---|
| A. Claude PreToolUse(自動・bypass 不能) | EH-1 / EH-2 / EH-3 / EH-6 / EH-9 + 承認トークン直書き block(TASK-0123)+ EH-12(apply 後) | Edit / Write / Bash のたび | ✅ 常時発火 |
| B. CI(自動・bypass 不能) | EH-8(metrics privacy)/ settings drift / schema-validate / skip-ack / pr-issue-link | PR / push | ✅ 常時発火 |
C. CLI(bin/plangate 実行時のみ) |
EH-4 / EH-5 / EHS-1 / EHS-2 / EHS-3 | verify / handoff --verify を実行したときだけ |
🔴 休眠(CLI 未実行なら不発) |
| D. 外部設定 | EH-7(マージ 2 段階レビュー) | main へのマージ | 🔶 GitHub branch protection 設定に依存(Human-owned admin) |
| E. Codex hooks | EH-3 / check-script-basename | Codex セッション中の apply_patch / Bash 等 | (Claude Code 運用では非該当) |
bin/plangate verify / handoff --verify を回さなければ実効しない。休眠ゲートを CLI 非依存で常時強制したい場合の選択肢(PR トリガーの CI 移植など)は 別途設計判断(新規 PBI / EPIC #527 の後続)とする。本節は現状把握であり方針は未確定。
PlanGate の Iron Law のうち runtime 強制可能な不変条件(現状 #1〜#7 相当)を、プロンプトに頼らず runtime で決定論的にブロック する。プロンプト薄型化(PBI-116-01 で達成)と両立して、強制力を維持する。なお Iron Law #8(出典照合)は決定論的 hook 化が困難なため、プロンプト + diff-audit(旧 self-review、ソフト面)で担保し runtime hook の対象外とする。
responsibility-boundary.md § 5 と整合。最低 6 件:
docs/working/TASK-XXXX/plan.md が存在しない状態で、production code(CLAUDE.md / AGENTS.md / docs/ai/ / .claude/ / bin/ / schemas/ / plugin/ 等)を編集しようとしたapprovals/c3.json の c3_status: APPROVED がない、または存在しない状態で exec フェーズに進もうとしたapprovals/c3.json 発行後、plan.md が変更されたが c3.json の plan_hash が更新されていない
#282 / TASK-0105 ハードニング: c3.json の plan_hash 抽出を寛容
sedから strict JSON 解析(scripts/plan_hash_util.recorded_plan_hashと意味一致)へ変更。不正 JSON / 非 object / prefix 不一致の c3.json は 承認記録として信用せず空=SKIP(旧 sed は不正でも plan_hash を抽出し 比較続行=不正記録を承認境界の根拠にしていた)。承認境界はより厳格化 =安全側。正常系(PASS)・改竄検知(BLOCK)の挙動は不変(回帰なし)。
test-cases.md が存在しないevidence/verification.md または同等のログがないまま子 PR を作成しようとしたforbidden_files に該当するファイルを編集delegation_commit_boundary: no-commit を宣言
(env PLANGATE_DELEGATION_NOCOMMIT=1)した文脈で git commit / git push 試行git -c/-C/env 前置/command git/gh pr merge/sh -c 等の回避形を網羅。信頼境界=stdin JSON 正本main / master の状態で
git reset --hard / git push --force(-f / --force-with-lease /
--force-if-includes を含む)を実行しようとしたPLANGATE_BYPASS_HOOK=1 で常時 passscripts/check-git-destructive.sh
(scripts/ ルート = HO 外。配線は
scripts/apply-eh-git-destructive-guard.sh)scripts/hooks/ へ複製しない。.claude/settings*.json から
scripts/check-git-destructive.sh を直接参照する。scripts/hooks/ は tracked
(17 ファイル)なので複製すると同一内容の tracked ファイルが 2 つ並び、両者の
drift を検出する CI も存在しない(#956 の commit 済み drift と同一構造)。
同方式の先例: scripts/check-approval-token-write.sh
/ scripts/gh-pin-account.sh(いずれも scripts/ 直下から settings が直参照)。
副次効果として apply が触る HO は settings.json / settings.example.json の
2 ファイルだけになるmatcher: "Bash"(apply 後)。信頼境界は EH-9 と同じく
stdin JSON tool_input.command が正本、env PLANGATE_HOOK_CMD は CLI テスト専用head -1 を挟んではならない(jq -r は JSON の \n を実改行へ
展開するため、2 行目以降=破壊的操作そのものが捨てられ allow に化ける)。
実改行 / CR / tab に加え、jq 非搭載時の grep fallback で残る literal な
\n も空白へ平坦化してから検査する。これにより ; && 改行 \ 行継続
・行頭インデント・コメント行・heredoc 本文・CRLF が同一に扱われる。
jq あり / なしの両経路を必ずテストすること(改行バグは jq 経路特有だった)docs/working/_audit/hook-events.log に class + sha256 hash のみ記録
(command 全文は記録しない。EH-9 と同方式)git checkout -q <b> 2>/dev/null || git checkout -q -b <b> origin/<b> の
両側が失敗(同名ブランチ既存で -b が fatal: already exists)したにも
かかわらず || 連結ゆえ set -e が発火せず、次行の git reset --hard が
main 上で実行され他セッションの未コミット変更を破棄した
(git fsck --lost-found の dangling blob から復旧)。同型の学びは
AGENT_LEARNINGS.md に 2026-07-12 から存在したが防げなかったため、
規範層(responsibility-classes.md
「Bash 連結コマンド時の error guard」)を技術層で補強するreset --hard は捕捉できない。
EH-12 はその隙間を埋める(Defense in Depth の技術層を 1 段追加)git reset --hard / git push --force|--force-with-lease|
--force-if-includes|-f / git push <remote> +<refspec>(先頭 + の強制更新)git -C <other-repo> は
cwd の branch で判定する(安全側=過剰 block に倒れる)。最初の git 以降を
一括で見るため git status; echo "reset --hard" のような文字列も
main 上でのみ過剰 block しうる(安全側・EH-9 と同じ緩さ)。
worktree を壊す git checkout -f / git clean -fd は本 hook の対象外tests/extras/ta-58-git-destructive-guard.sh
(サンドボックス複製 + git symbolic-ref で branch を制御し、実 docs/working/_audit を汚染しない)設計ステータス: 配線・適用済み(TASK-0145 / 0146 / 0147)。スクリプト実装に 加え、発火条件
validation_bias: strictもbin/plangateに配線済み。発火条件の供給は--profile <key>→model-profiles.yamlのvalidation_bias解決 →PLANGATE_VALIDATION_BIAS内部 export(TASK-0147 / #644)。 CLI 依存度の注意: これらは CLI 層(§0 の層 C)であり、bin/plangate verify/handoff --verifyを実行したときのみ発火する(手動 / AI 任せ運用では休眠。CI 移植は TASK-0148 で検討)。
Model Profile の validation_bias: strict プロファイル(gpt-5_5_pro 等)では、
上記 EH-1〜EH-7 に加えて以下 3 件を追加で強制:
standard 以上の mode で V-3 外部 AI レビュー (review-external.md) なしに PR 作成scripts/hooks/check-v3-review.shvalidation_bias: strict かつ mode ∈ {standard, high-risk, critical} かつ review-external.md が存在しないbin/plangate verify(V-3 不合格時に strict で block。TASK-0145)。CLI 層(§0 層 C)handoff.md が必須 6 要素(要件適合 / 既知課題 / V2 候補 / 妥協点 / 引き継ぎ文書 / テスト結果)を欠く状態での WF-05 完了宣言scripts/hooks/check-handoff-elements.shvalidation_bias: strict かつ handoff.md に 6 セクション未充足bin/plangate handoff --verify(6 要素不足を strict で block。TASK-0146)。CLI 層(§0 層 C)scripts/hooks/check-fix-loop.shvalidation_bias: strict かつ fix-loop カウントが閾値超過bin/plangate verify(V-1 FAIL 時に fix-loop increment + strict で上限超過 block。TASK-0146)。CLI 層(§0 層 C・CI 移植対象外)validation_bias: strict は docs/ai/model-profiles.yaml で定義され、実行時の供給は
配線済み(TASK-0147 / #644): bin/plangate verify / handoff --verify が --profile=<key>
(等号形式)を受理し、model-profiles.yaml の validation_bias を
scripts/_resolve_validation_bias.py で解決して
PLANGATE_VALIDATION_BIAS を内部 export する。env で明示注入済みなら尊重し、
normal/lenient・無指定では非発火(既存挙動不変)。未知 key / yaml 欠落時は normal
fallback + stderr 警告。
残課題: 上記は CLI 層(§0 層 C)のため、CLI を回さない運用では休眠する。PR トリガーの CI へ EHS-1 / EHS-2 を移植して CLI 非依存で常時強制する案は TASK-0148 で検討(EHS-3 は CLI プロセス計数のため CLI 維持)。
| Hook | 種別 | 実装 | 由来 |
|---|---|---|---|
| EH-1(plan.md なし production code 編集 block) | PreToolUse hook | scripts/hooks/check-plan-exists.sh |
#169 セッション A / TASK-0056 |
| EH-2(C-3 未承認 exec block) | PreToolUse hook | scripts/hooks/check-c3-approval.sh |
#157 / TASK-0048 |
| EH-3(plan_hash 改竄検知) | PreToolUse hook + CLI | scripts/hooks/check-plan-hash.sh |
#169 セッション A / TASK-0056 |
| EH-4(test-cases.md なし V-1 block) | CLI(V-1 前で呼び出し) | scripts/hooks/check-test-cases.sh |
#169 セッション B / TASK-0057 |
| EH-5(検証ログなし PR 作成 block) | CLI(PR 作成前で呼び出し) | scripts/hooks/check-verification-evidence.sh |
#169 セッション B / TASK-0057 |
| EH-6(scope 外ファイル編集検知) | PreToolUse hook + CLI | scripts/hooks/check-forbidden-files.sh |
#169 セッション B / TASK-0057 |
| EH-7(2 段階レビューなしマージ block) | CLI(マージ前で呼び出し) | scripts/hooks/check-merge-approvals.sh |
#169 セッション C / TASK-0058 |
| EHS-1(V-3 外部レビュー必須化) | CLI(mode 連携) | scripts/hooks/check-v3-review.sh |
#169 セッション C / TASK-0058 |
| EHS-2(handoff 必須 6 要素) | CLI(手動 / WF-05 で呼び出し) | scripts/hooks/check-handoff-elements.sh |
#157 / TASK-0048 |
| EHS-3(fix loop 上限超過) | CLI(V-1 fix loop 内で increment / check) | scripts/hooks/check-fix-loop.sh |
#157 / TASK-0048 |
| EH-9(委譲 commit/push 境界検知) | PreToolUse hook(Bash 前) | scripts/hooks/check-delegation-commit-boundary.sh |
#239 問題2 / TASK-0073 |
| auth-preflight(exec 前 認証三点検証) | CLI(exec 前で呼び出し) | scripts/hooks/check-auth-preflight.sh |
#239 問題3 / TASK-0073 |
| EH-12(protected branch 上の破壊的 git 操作 block) | PreToolUse hook(Bash 前・Human apply 後に有効) | scripts/check-git-destructive.sh(HO 外・単一ソース)+ 配線 scripts/apply-eh-git-destructive-guard.sh |
2026-08-02 main 上 reset --hard 実害 |
残未実装: なし(10/10 完了)。EH-7 の GitHub branch protection 自動連携は別 PBI 候補。
| モード | 環境変数 | 挙動 |
|---|---|---|
| default(推奨初期値) | なし | 違反検出時は warning のみ、continue:true(block しない)。誤検出時の作業妨害を最小化 |
| strict | PLANGATE_HOOK_STRICT=1 |
違反検出時に block / exit 1。本番運用 / CI 等で有効化 |
| bypass | PLANGATE_BYPASS_HOOK=1 |
常時 pass。緊急対応 / 既知の例外時のみ使用、監査 log に必ず記録 |
すべての判定は docs/working/_audit/hook-events.log に append-only で記録される(タブ区切り):
<ISO8601 UTC>\t<level>\t<hook-name>\t<task-id>\t<message>
level: PASS / VIOLATION / BYPASS / SKIP / INCREMENT
.claude/settings.example.json を .claude/settings.json にコピーすると PreToolUse hook(EH-1 + EH-2 + EH-3 + EH-6)+ SessionStart(gh-pin-account)が有効化される。
CLI 配線(TASK-0143 / apply-script 適用後): EH-4 は plangate verify V-1 前(strict=1)、EH-5 は V-1 後(warn)で発火。EH-7 / EHS-1 / EHS-2 / EHS-3 は引き続き手動呼び出し。
sh tests/hooks/run-tests.sh → 42 件 PASS(#157 で 12 + #169 セッション A で +9 + B で +12 + C で +9)sh tests/run-tests.sh の TA-06 で hook 子テストを呼び出しIssue #169 完了。10/10 hooks 実装済(CLI / PreToolUse 構成)。残課題は GitHub branch protection 自動連携(EH-7 の上位拡張、外部 GitHub API 操作を伴うため別 PBI)。
.claude/settings.json hooks との関係本ファイルが定義する不変条件は、既存 hooks(もしあれば)と:
plugin/plangate/rules/*-gate.md)との関係: Plugin 配布版の追加ガードレールとして共存(responsibility-boundary.md § 6 参照)Hook が block する際の通知文言:
[Hook EH-1] plan.md がないため production code を編集できません。
docs/working/TASK-XXXX/plan.md を作成してください。
形式:
[Hook EH-N] プレフィックスで該当条件を識別docs/working/PBI-116/parent-plan.mdresponsibility-boundary.mdtool-policy.mdmodel-profiles.mdcore-contract.md § 4core-contract.md