PlanGate

settings wiring 契約(正本 / TASK-0080 S1)

.claude/settings.json が満たすべき PreToolUse hook wiring の正本bin/plangate doctor --check-settings がこの契約と実体を突合する。 適用は scripts/apply-claude-settings.shユーザー実行。AI は self-mod ガードで .claude/settings.json を編集できないため)。

必須 PreToolUse hooks(matcher: Edit|Write 系)

Hook command(必須トークン)
EH-1 plan-exists scripts/hooks/check-plan-exists.sh
EH-2 c3-approval scripts/hooks/check-c3-approval.sh
EH-3 plan-hash scripts/hooks/check-plan-hash.sh ${PLANGATE_HOOK_TASK:-} ${PLANGATE_HOOK_FILE:-}
EH-6 forbidden-files scripts/hooks/check-forbidden-files.sh
EH-9 delegation-commit-boundary scripts/hooks/check-delegation-commit-boundary.sh

契約ポイント

検証・適用

⚠️ 適用スクリプトの副作用(本契約の範囲外): apply-claude-settings.sh は本契約が定める PreToolUse 6 項目だけでなく、.claude/settings.example.json全 hook event(SessionStart / PostToolUse / Stop を含む)を取り込む。 そこには契約外かつ副作用の大きい hook が含まれうる(例: SessionStart の scripts/gh-pin-account.shgh auth switchマシン全体の gh CLI active account を切り替える)。また「不足を足すが削除はしない」方針の 裏返しとして、example から意図的に削除した hook は再実行のたびに復活 する(opt-out 手段は現状なし)。--all-events opt-in 化は #975 で follow-up。

不変

責務分離(V-3 MJ-2/MJ-3 反映)

検証対象 手段 役割
CI settings-drift(required) .claude/settings.example.json(契約 reference) check-settings-wiring.sh --target example 正本 reference が契約と乖離しないことを保証(example が壊れたら全員に波及するため)
bin/plangate doctor --check-settings .claude/settings.json(ユーザー実体) 構造(JSON)検証 実環境の wiring 未適用=Shadow Config を検出
既存 bin/plangate doctor hook-wiring check .claude/settings.json vs .claude/settings.example.json 既存 check(TASK-0069) settings.example.json を契約整合させた結果、未適用を通常 doctor でも FAIL(従来の false-PASS を是正)

.claude/settings.json は gitignore(ユーザーローカル)のため CI では検出 不可。実体 drift の検出は doctor(ローカル / V-1・handoff DoD)が担う。 両者は役割が異なり、どちらか一方では「Shadow Config を構造的に防ぐ」根拠に ならない(CI=reference 健全性 / doctor=実体適用)。

タスクロックの強制経路(V-3 MJ-1 反映)

V-1/handoff 完了の DoD(docs/workflows/05_verify_and_handoff.md / working-context.md)に 「doctor --check-settings PASS」を必須化。強制は次の二重で成立する:

  1. DoD 明文 + 通常 doctor の hook-wiring FAIL: settings.example.json を 契約整合させたため、未適用環境では bin/plangate doctor(通常実行)も FAIL する。doctor FAIL 状態での完了報告は Iron Law(検証証拠なしに完了 扱いしない)違反。
  2. doctor --check-settings: 構造検証で未適用箇所を決定論的に列挙。

既知の限界(V2 候補): 完全な PreToolUse-hook レベルの機械 block解消済 (PR #347).codex/hooks.json + .codex/hooks/eh-bridge.sh で Codex CLI 側にも EH-1/2/3/6/9 が物理 PreToolUse block として配線済。 ❌ 未解消(#1078 実測 2026-08-13): .codex/hooks.json は parse 拒否され Codex 側の hook 登録は 0 件。EH-1/2/3/6/9 は Codex セッションで一度も発火していない。Claude Code 側は従来通り .claude/settings.json で配線。詳細は本ファイル後段の §Codex CLI parity 参照。

Codex CLI parity (#336 / Gap 4) — 達成済 部分達成(5 / 11 wiring)・強制力は未検証 強制力 0 / 11(Codex 側 hook は 1 件も登録されていない)

是正記録 2(2026-08-13 / #1078 / 本節で 2 度目の是正): 直下の「是正記録 1」で 「部分達成(5 / 11 wiring)・強制力は未検証」 へ書き換えたが、 これもまだ実態より甘かったcodex app-server の JSON-RPC hooks/list(モデル呼び出しを 伴わない=課金ゼロ)で実測したところ、.codex/hooks.json は JSON 全体が parse 拒否されており、 PlanGate の hook は 1 件も登録されていない。すなわち EH-1 / EH-2 / EH-3 / EH-6 / EH-9 は Codex セッションで一度も発火していない(「未検証」ではなく 0 件で確定)。 「5 / 11」は 設定ファイルに記述されている件数であって、登録数でも強制力でもない。 3 軸を分けた現況は下表「parity の 3 軸」を参照。過去の記述は削除せず残す。

本節では「達成済」も「部分達成」も強制力については使わない。 Codex 側の強制力は 0 / 11 であり、これは実測(hooks/listwarnings / 登録 0 件)に基づく確定値である。


是正記録 1(2026-08-13 / #1078: 本節の見出しは 2026-05-25 の PR #347 以来 「達成済」 と記載していたが、 #1078 の実測で .claude/settings.example.json 側 11 wiring のうち Codex 側に あるのは 5 件(EH-1 / EH-2 / EH-3 / EH-6 / EH-9)で、6 件が欠落している ことが判明した。さらに 配線済み 5 件についても「実際に発火し block している」 実走証跡が無い(後述「未検証事項」)。過去の記述は削除せず、実測に基づく 現況を以下に追記する。「達成済」は EH-1/2/3/6/9 の 設定ファイル上の配線 に限った記述として読むこと。強制力の等価は本節時点では主張しない。

model tier の parity: Claude Code は .claude/agents/*.md frontmatter の model:(inherit/sonnet)、Codex は .codex/agents/*.tomlmodel_reasoning_effort(low/medium)で同一の 2 tier を表現する。対応表の 正本は model-profiles.md §11。

parity の 3 軸(混同禁止 / #1078 実測)

「何件配線したか」と「何件効いているか」は別の数である。本節では以下 3 軸を分けて数える。 過去 2 回の誤りは、いずれも 軸 A の数を軸 C の主張に流用したことで起きた。

定義 数え方 Claude Code Codex CLI
A. 記述(declared) 設定ファイルに hook として書かれている件数 .claude/settings.example.json / .codex/hooks.json を静的にパース 11 5
B. 登録(registered) ランタイムが実際に読み込んで登録した件数 Codex は hooks/list。Claude は同等の問い合わせ経路を本 PBI では使っていない 未測定 0
C. 強制力(enforced) 実際に発火して block した件数(上限は軸 B 軸 B が上限を与える。下限は実走ログで確認(未取得) 未測定 0(軸 B = 0 より上限 0)

⚠️ Claude 側の B / C を「11」と読み替えないこと。 本 PBI は Claude 側について ランタイム登録状態を問い合わせていない.claude/settings.json は gitignore で リポジトリに存在せず、bin/plangate doctor --check-settings構造・契約の検証で あって「harness が hook を登録したか」の問い合わせではない)。 「設定の存在は動作の証拠ではない」という本節の教訓は Claude 側にも等しく効く。 Codex で起きたことが Claude で起きないと結論づける根拠は、本 PBI の範囲では無い。

設定ファイル全体が parse 拒否されている(#1078 実測・根本原因)

.codex/hooks.jsontop-level に仕様外キーが 2 つある:

キー 扱い
2 $schema_note 仕様外(JSON にコメント構文が無いため注記として置かれたもの)
3 $note 仕様外(同上)

Codex CLI の hooks config パーサは top-level に descriptionhooks の 2 キーしか許容しないdeny_unknown_fields)。1 キーの違反でファイル全体が捨てられる(部分適用ではない)。

hooks/list(cwd = 本リポジトリ)の実応答 warning(verbatim):

failed to parse hooks config <repo>/.codex/hooks.json: unknown field `$schema_note`, expected `description` or `hooks` at line 2 column 16

このとき登録されたのは river-review plugin の PostToolUse 1 件のみで、 PlanGate の PreToolUse 5 件は 0 件。サンドボックスで双方向に再現済み (注記キーを足すと hooks[] が空・外すと登録される=決定論的)。

なお project trust は本件の原因ではない(本リポジトリは trusted 済み)。原因は parse 拒否である。

🚨 注記キーの単独除去は禁止(「登録された」が「効いている」に化けるため)

この 2 行を消すだけの PR を作ってはならない。 「top-level の $schema_note / $note を消せば直る」は 誤りである。

2 行を消すと hooks/list 上は PlanGate hook 5 件が登録され、trustStatus も付く。 外形上は「parity 回復」に見える。しかし eh-bridge.sh の欠陥により hook は 空入力で PASS し続け、強制力は 0 のままである。

これは「Codex が使えなくなる」より危険である。 本節が潰そうとしている 「登録 ≠ 強制力」という誤りを、是正行為そのものが再生産する経路になる。 「登録 5 件・warnings 空」という緑のシグナルだけが増え、実態は 0 のまま据え置かれる。

除去は eh-bridge.sh の I/O 契約修正と同一 PR でなければならない。 順序は stdin 転送の実装を先行必須とし、実際に block される証跡が取れて初めて 注記キーを除去する受入基準に「hooks/list に登録された」を使ってはならない (登録は前提条件であって成果ではない)。受入基準は deny が実際に返ることに置く。

実測: bridge は deny ではなく allow に倒れる(#1078 / 2 度目の是正で判明)

当初この節には「除去すると全操作が deny され Codex が使用不能になる」と書いていたが、 実測で否定されたeh-bridge.sh に PreToolUse payload を直接投入した結果:

対象ファイル hook rc decision
bin/plangate check-plan-exists / c3-approval / forbidden-files / plan-hash 0 allow
docs/working/TASK-1078/plan.md 同上 4 本 0 allow
.claude/settings.json(HO パス) 同上 4 本 0 allow

12 / 12 が allow。Hardening Override パスですら通る。

理由は 2 つで、いずれも「deny 側」ではなく「allow 側」に倒れる:

  1. stdin 非転送eh-bridge.sh L33 で INPUT=$(cat) により吸い切り、L69 の hook 起動へ渡していない)。hook は PLANGATE_HOOK_FILE / PLANGATE_HOOK_TASK の env のみという縮退した文脈で走り、判定材料が無いため rc=0(PASS)を返す
  2. 未知 exit code を allow に落とす(L86-89 の *) 分岐)。deny になるのは rc が 1 または 2 のときだけ

「パス解決の not-found → deny」は配線済み 5 本には効かない。 配線されている 5 本は実体が scripts/hooks/実在するため not-found 分岐に入らない。 not-found deny が発火するのは 未配線の hook(EH-12 / EH-13 — 実体が scripts/ 直下)を名前だけ足したときであり、その場合もそもそも現状は呼ばれない。

eh-bridge.sh の構造欠陥 一覧(本表を唯一の正本とする

他ファイルはこの表を参照すること(個数・内容を各所で書き下すと不一致が生じる)。

# 欠陥 位置 倒れる向き
B-1 stdin を吸い切り hook へ渡さない(Codex の入力経路は stdin のみ) L33 / L69 allow(判定材料が無く rc=0)
B-2 未知 exit code を allow に落とす(deny は rc 1 / 2 のみ) L86-89 allow(fail-open)
B-3 hook パスを scripts/hooks/ 固定で解決 → not-found で deny L26 / L28-29 deny(ただし未配線 hook を足したときのみ
B-4 matcher に Codex に存在しないツール名(Edit / Write)を含む .codex/hooks.json 死に文字列(実質 apply_patch のみ一致)

現況で支配的なのは B-1 / B-2= allow 側。B-3 は将来 EH-12 / EH-13 を配線する ときに初めて効く。「bridge は deny 側に厳しい」という読みは誤りである。

「設定ファイルの存在は動作の証拠ではない」(構造原因)

本件の本質は 配線の記述を動作の証拠として扱っていたことにある。

一般則: 「設定ファイルが存在する / 正しく書けている」は ランタイムがそれを受理した ことを意味しない。強制力を主張するには ランタイム側の登録状態を問い合わせた証跡が要る。 これは Shadow Config(本ファイル後段「Wiring Integrity Enforcement(#500)」)の Codex 版であり、同じ検出原理(実体への問い合わせ)で塞ぐ。

後続の必須スライス: hooks/list による機械検出

課金ゼロで登録状態を機械検出できるため、これを doctor / CI に組み込むことを 必須スライスとする。

「達成済」主張の残存箇所 一覧(#1078 / 全数照合 2026-08-13)

git grep による全数照合の結果。本 PR で是正した箇所と、未是正のまま残る箇所を 明示的に区別する(未是正箇所を書き残さないと網羅性が担保できないため)。

本 PR で是正済み

ファイル 箇所
docs/ai/settings-wiring-contract.md 本節見出し / L79 既知の限界 / 三層表 / 等価強制マトリクス前文 / bridge 動作 / U-3
docs/plangate.md §Codex CLI parity 見出し・本文・強制マトリクス / v8.10 年表行
docs/ai/harness-improvement-roadmap.md v8.10.0 Codex CLI parity 行 / 完了確認の注記
docs/ai/hook-enforcement.md 物理配線 11/12 の注記・配線状態表の Codex 列
.agents/skills/ai-dev-exec/SKILL.md 「物理 hook 等価達成」ブロック
.agents/skills/local-exec-handoff/SKILL.md 「EH-1/2/3/6/9 が自動発火する」
plugin/plangate/skills/{ai-dev-exec,local-exec-handoff}/SKILL.md 上記の同期先(scripts/sync-plugin-plangate.sh により生成)

未是正(Human 適用待ち / 別 PBI

ファイル:行 残存主張 理由
CLAUDE.md:34 「EH-1/2/3/6/9 を Codex session 中 … 物理発火」 HO パス。patch を docs/working/TASK-1078/patches/CLAUDE.md.codex-parity.patch に用意(適用は Human)
AGENTS.md:18 / AGENTS.md:48 「物理 hook bridge を有効化」「Codex 側でも発火」 HO パス。patch を docs/working/TASK-1078/patches/AGENTS.md.codex-parity.patch に用意(適用は Human)。Codex セッションが読む側の正本のため実害が最大
README.md:450 「完全対応(物理 hook parity 達成済)」 本 PR スコープ外(別 PBI で是正)
README.md:90 「物理配線は 11/12」 同上。hook-enforcement.md と同じ数え方に依存
docs/ai-driven-development.md:312 「等価な物理 hook 強制が有効化されている (#347)」 同上
.codex/README.md:43 「.codex/hooks 物理 hook で強制」 .codex/ は本 PBI で変更しない(bridge 修正 PBI で扱う)
.codex/hooks.json:3 $note 内の「等価な強制力を Codex セッションで実現する」 同上(この行の除去自体が上記「単独除去は禁止」の対象
.codex/skills/{ai-dev-exec,local-exec-handoff}/SKILL.md .agents/skills と同文 .codex/ は変更しないscripts/install-plangate-skills-to-codex.sh による同期で追従させる
tests/extras/README.md:14 「Codex CLI 物理 hook parity 検証 (#347)」 テスト説明。別 PBI
CHANGELOG.md v8.10.0 節 「Codex CLI parity 完成(100% 達成)」 履歴のため不改変(当時の記録)。誤読防止の注記追加は別 PBI

Human 適用待ちの patch: docs/working/TASK-1078/patches/ 配下。 HO パスのため AI は適用できないCLAUDE.md / AGENTS.md)。 適用コマンド: git apply docs/working/TASK-1078/patches/<name>.patch 追跡は docs/working/TASK-1078/status.md の BLOCKED 項目。 本節の実測根拠: docs/working/TASK-1078/evidence/hooks-list-reverify.md

判明事項 (2026-05-25 PR #347): OpenAI Codex CLI は PreToolUse / PostToolUse hook API を公式提供しており、Claude Code の hook 仕様と直接互換 (matcher / stdin JSON / exit 2 で deny / hookSpecificOutput.permissionDecision)。公式仕様: https://developers.openai.com/codex/hooks

三層の強制機構

機構 カバー範囲
1. Session 前 scripts/codex-guarded.sh (PR #343) validate / doctor / EH-8 privacy / plan.md hash snapshot
2. Session 中 (物理 pre-Write block) .codex/hooks.json + .codex/hooks/eh-bridge.sh (PR #347) 機能していない EH-1 / EH-2 / EH-3 / EH-6 / EH-9 を Codex 側でも発火 登録 0 件・発火 0 件(#1078 実測)
3. Session 後 scripts/codex-guarded.sh post-flight plan.md hash drift 検知 + validate 再実行

層 2 の但し書き(#1078 / 2 度目の是正で強化): 当初「Codex 側でも発火」と書き、 1 度目の是正で「配線されていることを指す(実走証跡は無い)」に緩めたが、 実測では層 2 はまったく機能していない.codex/hooks.json は parse 拒否され hook が 1 件も登録されていない(上記「設定ファイル全体が parse 拒否されている」)。 層 2 は現時点で存在しないものとして扱うこと。

層 1 / 層 3 の但し書き: scripts/codex-guarded.sh の pre/post-flight は本件と 独立に機能するが、それは正規入口(scripts/codex-guarded.sh)を経由した場合に 限る正規入口の使用を強制する機械ゲートは存在しない.github/workflows/ 配下に codex-guarded の参照は 0 件=規範層の運用に依存)。素の codex / codex exec を直接起動した場合、層 1 / 層 2 / 層 3 のいずれも働かない

等価強制マトリクス(全 wiring / #1078 で全数化)

比較対象は .claude/settings.example.json.claude/settings.json は gitignore でリポジトリに存在しないため)と .codex/hooks.json の全数差分。

⚠️ 本表は「軸 A(記述)」の表である。 12 wiring 中 Codex 側に記述があるのは 5 件・ 欠落 7 件(#1267 が settings.example.json へ EH-3b = Bash matcher の check-plan-hash.sh を追加したため、#1078 時点の 11 → 12。実測: settings.example.json の hook コマンド総数 12)。ただし記述のある 5 件も含め、Codex 側の登録数は 0・強制力は 0 (上記「parity の 3 軸」)。本表の ✅ は「効いている」ではなく「書かれている」を意味する。

# 強制 event / matcher Claude Code (settings.example.json) Codex CLI (.codex/hooks.json)
1 EH-1 plan-exists PreToolUse Edit\|Write ✅ (matcher apply_patch\|Edit\|Write)
2 EH-2 c3-approval PreToolUse Edit\|Write ✅ 同上
3 EH-3 plan_hash PreToolUse Edit\|Write ✅(引数 ${PLANGATE_HOOK_TASK:-} ${PLANGATE_HOOK_FILE:-} を渡す) ⚠️ 配線あり・引数を渡さず env のみ(引数 / env / stdin の 3 系統が hook ごとに不統一)
4 EH-6 forbidden_files PreToolUse Edit\|Write ✅ 同上
5 EH-9 delegation-commit-boundary PreToolUse Bash
6 EH-13 approval-token-write(Edit/Write 系) PreToolUse Edit\|Write scripts/check-approval-token-write.sh ❌ 未配線
7 EH-13 approval-token-write(Bash 系) PreToolUse Bash ✅ 同上 ❌ 未配線
8 EH-12 git-destructive guard PreToolUse Bash scripts/check-git-destructive.sh ❌ 未配線
9 gh-pin-account SessionStart scripts/gh-pin-account.sh ❌ 未配線(Codex 側に SessionStart 配線が無い)
10 check-post-edit-diff PostToolUse Edit\|Write\|MultiEdit scripts/hooks/check-post-edit-diff.sh ❌ 未配線(Codex 側に PostToolUse 配線が無い)
11 check-stop-diff-status Stop scripts/hooks/check-stop-diff-status.sh ❌ 未配線(Codex 側に Stop 配線が無い)
12 EH-3b plan_hash(Bash レーン / #1104) PreToolUse Bash ⚠️ 配線あり・現状 no-op(下記注) ❌ 未配線

#12(EH-3b)の実測(2026-08-28 / origin/main = 3f0cadd: check-plan-hash.sh の対象パス抽出は tool_input.file_path のみを見る。Bash の PreToolUse payload が持つのは tool_input.command なので target_file は常に空になり、HO 判定も write-intent 判定も行われない。配線されただけの状態では (a) no-task セッションで全 Bash が exit 2、(b) PLANGATE_SKIP_REASON で回避すると skip-decision-log.jsonl へ未追認エントリが積まれ check-skip-acknowledged.sh が FAIL、という 摩擦だけが残る。是正 patch と残存脅威モデル: docs/working/_reports/1104-bash-lane-noop-patch-applicable.md。 回帰テスト: tests/extras/ta-79-eh3-bash-lane.sh#1104 は open(Bash コマンド文字列からの 書き込み先抽出は未実装)。

欠落 6 件を「単に名前を足せば直る」と読んではならない。#6〜#9 の hook 実体は scripts/ 直下にあり、eh-bridge.shscripts/hooks/<NAME>ハードコード で解決するため、名前だけ足すと not-found 分岐で無条件 deny になる(実測)。 是正には bridge の I/O 契約変更(パス解決 / stdin 転送 / payload 正規化)が要り、 実装は本節の範囲外(#1078 の別スライス)。

matcher の死に文字列(#1078 実測)

.codex/hooks.json の matcher は apply_patch|Edit|Write だが、Codex CLI が送る tool_nameapply_patch / Bash のみで、Edit / Write / MultiEdit は Codex に存在しない(0.144.1 バイナリ埋め込みの JSON Schema と 公式仕様 の一致で確認)。 したがって matcher 中の Edit / Write は Codex 側では一致し得ない死に文字列であり、 実質 apply_patch のみが一致する。また apply_patchfile_path を持たない ため、bridge は *** Update/Add/Delete File: を正規表現で抽出している。

未検証事項(「検証済み」と書いてはならない項目

以下のうち U-3 は #1078 の hooks/list 実測で決着した(ただし 想定と別の原因で。 下表参照)。U-1 / U-2 は引き続き未検証であり、現時点では「Codex 側の強制力が 働いている」ことの根拠にならない。文言・状態表は実走証跡が得られるまで 「未検証」のまま扱う。

U-1 / U-2 のステータスは「観測あり・因果未確定」(「未観測」ではない)。 別走で bare allow は無害(弾かれず実害が出ていない)、空 reason の deny は fail-open が再現との観測が得られているが、permission_mode=bypassPermissions の 交絡があり、観測された挙動が hook 応答の解釈によるものか permission_mode による ものか切り分けられていない。したがって 因果は未確定であり、確定するまで 本節に結論を書き込んではならない。

なお U-1 / U-2 がどちらに転んでも軸 C(強制力 0 / 11)は変わらない — 登録 0 件が上位の制約だからである。

ID 未検証事項 分かっていること(実測) 分かっていないこと
U-1 bridge の allow 応答が受理されるか CLI バイナリに PreToolUse hook returned unsupported permissionDecision:allow という文字列が実在する。eh-bridge.sh は正常系で bare permissionDecision: "allow" を返している 実行時に実際に allow が unsupported として弾かれるか。弾かれた場合の Codex 側の既定挙動(allow 継続 / エラー停止)
U-2 reason 空文字の deny が deny として通るか 同じく deny without a non-empty permissionDecisionReason という文字列が実在する。PlanGate hook が無出力で終了した場合、bridge の reason は空文字になりうる 空 reason の deny が無視される(= fail-open)か否か
U-3 解決済 hook trust により既存 5 hook がそもそも発火しているか hooks/list 実測で決着。発火していない。ただし原因は trust ではなく .codex/hooks.json の parse 拒否(上記「設定ファイル全体が parse 拒否されている」)。EH-1/2/3/6/9 は登録 0 件=一度も発火していない — (「trust が原因」という当初の仮説は否定された。trust は本件の原因ではないが、parse 拒否を直した後には別途 trusted_hash 運用が必要になる)

U-1 / U-2 の実走はモデル API 呼び出しを伴うため、実施可否は Human 判断(#1078)。 一方 登録状態の確認は hooks/list で課金ゼロに行える(上記「後続の必須スライス」)。 Codex セッションの安全性を .codex/hooks.json の存在に依拠して評価しないこと — 存在していても 現に登録されていないというのが本件の実測結果である。 Session 前後の scripts/codex-guarded.sh(層 1 / 層 3)は本件と独立に機能する。

なぜ「達成済」のまま気づかれなかったか(構造原因)

等価強制マトリクスは EH-1/2/3/6/9 の 5 行のみで、行単位では正しいが 集合として不完全だった。新しい hook(EH-12 / EH-13 / SessionStart / PostToolUse / Stop)を .claude/settings*.json に追加した際に 本マトリクスへ追記することを求める運用ルールも、両者の集合差を検出する 機械チェックも存在しない。結果、追加のたびに parity のギャップが静かに広がり、 見出しの「達成済」だけが残った。機械検出の追加は #1078 の後続スライス候補。

さらに深い原因(2 度目の是正で判明): 上の説明は 軸 A(記述)の集合差しか 見ていない。軸 A を全数化しても、記述された 5 件が実は 0 件しか登録されていない ことは検出できなかった。集合差の機械チェックを足すだけでは不十分であり、 ランタイムの登録状態(hooks/list)を問い合わせる検査が要る。 「マトリクスを全数化する」ことと「強制力を確認する」ことは別作業である。

Codex bridge の動作(設計上の意図。現在この経路は動いていない)

⚠️ 以下 1〜6 は PR #347 時点の設計意図であり、現況の記述ではない。 手順 1 の時点で .codex/hooks.json が parse 拒否されるため、2 以降は 1 度も実行されていない

  1. Codex CLI が apply_patch / Edit / Write / Bash 呼び出し前に .codex/hooks.json を参照
  2. 該当 matcher の command (= .codex/hooks/eh-bridge.sh <HOOK_NAME>) が起動
  3. eh-bridge.sh が stdin JSON から file path (Edit/Write.file_path or apply_patch.command の *** Update/Add/Delete File:) を抽出
  4. PLANGATE_HOOK_FILE / PLANGATE_HOOK_TASK を設定し scripts/hooks/<HOOK_NAME> を起動
  5. exit code を Codex の hookSpecificOutput.permissionDecision (allow / deny) に翻訳
  6. Codex CLI が deny 時は write を物理 block

手順 3〜4 の実測補足(#1078): bridge は stdin を INPUT=$(cat)吸い切り、 手順 4 の hook 起動時にその stdin を hook へ渡していない(渡すのは PLANGATE_HOOK_FILE / PLANGATE_HOOK_TASK の env のみ)。Codex の hook 入力経路は stdin のみCODEX_HOOK_* 系 env はバイナリ内に 0 件)であるため、 stdin で判定する hook(例: v8.19.0 で stdin 常時独立評価・fail-closed 化された EH-13)は この bridge 経由では正しく判定できない。手順 6 の「物理 block」も 上記「未検証事項」U-1〜U-3 の成立が前提であり、実走証跡は未取得。

責務分界 (継続)

Wiring Integrity Enforcement(#500 / 配線整合性の強制・Specification)

Status: Specification(方針確定。実装は HO パス絡みのため受け入れ条件ごとに段階 PBI へ分解) 出自: river 3 相レビュー(2026-06-08、Gemini + コードベース調査で代替実行 / external-reviewer-interface §10 unavailable 準拠)で検出した「規範↔実装」乖離 (Shadow Config)への是正方針。

強制モード方針(example=warning / 本番=strict)

doctor によるモード別必須 strict 配線検証

EH-13: 承認トークン書込みガードの採番・配線

採番改訂(TASK-1023 G-6 / Human 裁定 2026-08-10): 本節は当初 EH-10 として 採番していたが、hook-enforcement.md 側で EH-10 / EH-11 は #760 / #762 用に予約済みEH-12 は protected branch 破壊的 git 操作ブロック (check-git-destructive.sh)に採番済みであり衝突していた(R-033)。予約体系を 尊重し、衝突しない最小の空き番号 EH-13 へ改番する(G-6=(b))。

検証ロジックの対称化・テスト空白の解消(実装方針)

受け入れ条件 実装方針 主パス(HO)
EH-2(c3_status)strict 化 EH-3 と同じ python3 strict JSON 解析へ置換(permissive な grep/sed 抽出を廃止し EH-3 と対称化) scripts/hooks/check-c3-approval.sh
EH-1/EH-2 stdin fallback env 未注入時に stdin tool_input.file_path から解決し SKIP(allow) を解消 scripts/hooks/*.sh
maintenance verdict テスト VALID / CONSUMED / OUT_OF_SCOPE / HARDENING_OVERRIDE の fixture + assert を追加 tests/hooks/
ta-06 ログ握りつぶし解消 >/dev/null 2>&1 圧縮をやめ、どの EH が落ちたかをログに残す tests/.../ta-06-hooks.sh

段階 PBI 分解(HO 実適用は Human)

本仕様の HO 実装は以下の順で段階 PBI に分解する(各々 plan → C-3 承認 👤 → exec、 HO 適用は Human):

  1. EH-2 strict 化 + EH-1/EH-2 stdin fallback(hooks 堅牢化・最小単位・回帰リスク低)
  2. EH-13 採番・配線 + check-approval-token-write 統合(#420 と協調。旧記載 EH-10 は TASK-1023 G-6 裁定で EH-13 へ改番)。配線時は既存の契約検証スクリプト scripts/check-settings-wiring.shchecks リストにも EH-13(check-approval-token-write.sh)を追加し、CI / ローカルの契約ドリフト検知(--target example)に組み込んで配線漏れを防ぐ
  3. doctor Wiring Integrity Enforcement(Governance Contract 定義 + exit 1)
  4. hooks 回帰テスト拡充(maintenance verdict fixture + ta-06 ログ解消)

各段階は承認境界(HO)の変更を含むため mode-classification.md により最低 high・ Standard C-3 同期固定(autonomous APPROVE 無効)とする。

CLI 配線(EH-4/5/7)— TASK-0143

Status: Implemented(scripts/apply-task-0143-eh457-wiring.sh --apply 適用後に有効)

PreToolUse hook ではなく bin/plangate CLI サブコマンド経由で発火する配線。

Hook CLI 配線先 発火タイミング
EH-4 (check-test-cases.sh) bin/plangate verify <TASK> V-1 実行(strict=1、test-cases.md なしで block)
EH-5 (check-verification-evidence.sh) bin/plangate verify <TASK> V-1 通過(warn のみ、evidence なしで WARNING)
EH-7 (check-merge-approvals.sh) 手動呼び出し推奨 merge 前: sh scripts/hooks/check-merge-approvals.sh <TASK>

doctor 可視化

bin/plangate doctor=== CLI Hook Wiring (EH-4/5/7) === セクションが:

  1. EH-4/5/7 スクリプトの存在・実行権限を PASS/WARN/FAIL で報告
  2. bin/plangate verify への配線状態(grep 確認)を PASS/WARN で報告

適用方法(Human-owned)

# 差分確認(必須)
sh scripts/apply-task-0143-eh457-wiring.sh --dry-run
# 適用
sh scripts/apply-task-0143-eh457-wiring.sh --apply

適用後: bin/plangate doctor の出力で [PASS] EH-4 wired / [PASS] EH-5 wired を確認する。

C-3 Approval Mode 設定(EH-3 conversation 経路)— TASK-0144

Status: Implemented(scripts/apply-task-0144-c3-mode.sh --apply 適用後に有効)

.plangate.yml プロジェクト設定で C-3 承認モードを選択できる経路。

モード定義

モード 動作 c3.jsonsource フィールド
cli(デフォルト) bin/plangate approve <TASK> で対話的に承認・c3.json 生成 "cli"
conversation 会話内で人間が APPROVE 発話 → AI が exec 前に c3.json を生成 "conversation"

設計の核心

EH-3 conversation 経路(新規)

scripts/hooks/check-plan-hash.sh に追加された判定:

  1. target_filedocs/working/TASK-*/approvals/c3.json にマッチ
  2. .plangate.yml を読んで c3_approval.mode を取得
  3. conversation の場合 → EH-3_C3_CONVERSATION_SKIP をログに記録して exit 0(Write を許可)
  4. cli の場合 → 既存の maintenance / SKIP_REASON 判定に進む

追加ファイル

ファイル 種別 説明
.plangate.yml 設定ファイル プロジェクト設定(c3_approval.mode: cli\|conversation
schemas/plangate-config.schema.json Schema(HO) .plangate.yml の JSON Schema 検証定義
schemas/c3-approval.schema.json Schema(HO 変更) source フィールドを追加(optional)
scripts/hooks/check-plan-hash.sh Hook(HO 変更) conversation SKIP 経路を追加
bin/plangate CLI(HO 変更) _read_plangate_config() 追加 / source: "cli" / doctor セクション追加

適用方法(Human-owned)

# 差分確認(必須)
sh scripts/apply-task-0144-c3-mode.sh
# 適用
sh scripts/apply-task-0144-c3-mode.sh --apply

適用後: bin/plangate doctor の出力で === C-3 Approval Mode === セクションを確認する。