Skip to content

feat: git hook gates の配線 — hooksPath shim + core.hooksPath include (Closes #196) - #197

Merged
kosako merged 4 commits into
mainfrom
feat/196-git-hook-gates-wiring
Jul 14, 2026
Merged

feat: git hook gates の配線 — hooksPath shim + core.hooksPath include (Closes #196)#197
kosako merged 4 commits into
mainfrom
feat/196-git-hook-gates-wiring

Conversation

@kosako

@kosako kosako commented Jul 14, 2026

Copy link
Copy Markdown
Owner

概要

commit 境界の git hook gates(agent-tools#202 で実体 merge・両 home 配備済み)の dotfiles 側配線(Closes #196)。実体 = agent-tools・配線 = dotfiles の既存分界に従う。契約の正本は agent-tools の docs/git-hook-gates.md

変更内容

  • 新 capability enableGitHookGates(personal=true / work=false)+ 新 module git-hook-gates。dispatcher は gate 欠損時 fail-closed(exit 2)で commit を止めるため、agent-tools 未配備のマシン(work)には配らない(issue の「配布条件に注意」への対応)。
  • shim 2 本 ~/.config/git-hook-gates/hooks/{pre-commit,commit-msg}: 薄い exec のみ(ロジックなし)。$HOME/.claude 側 deploy を指す(dotfiles 裁定。両 home 同一 byte 配備のため機能差なし・$HOME は sh が実行時展開)。
  • core.hooksPath: dot_gitconfig(意図的 plain・全 profile 共有)に無条件 [include] を追加し、実体 hooks.gitconfig(capability-gated)が core.hooksPath = ~/.config/git-hook-gates/hooks を設定。signing.gitconfig と同型(missing include は git が無視)。
  • template 自己 gate(requires 不使用): false は空 render → chezmoi が既存 target も削除。requires だと fail-closed な shim が残置され commit が止まり続ける(codex-settings: user 層 hooks.json で personal-safe-gh-hook を Codex 活性化 (#181) #184 教訓)。
  • doctor: 配線 chain(shim / hooksPath / dispatcher)を report-only 監視。「配線済みなのに dispatcher 不在 = commit 停止中」を最も強く warn。honest-label(--no-verify / repo-local hooksPath bypass)。
  • preflight: apply 前に dispatcher 不在(= apply すると commit が止まる)と既存 core.hooksPath を warn。
  • docs: docs/git-hook-gates.md(配線側の正本: 構成 / fail-closed 配布条件 / 置き場所の理由 / 強度ラベル / 既知の限界 = husky 等の repo-local hooksPath・--no-verify・別 client)+ config-ownership 表 1 行 + README 行。

設計判断(レビュー観点)

  1. 置き場を ~/.config/git/hooks にしなかった: .config/git は git-signing module の宣言 path で、enableGitSigning=false の profile では chezmoiignore が dir subtree ごと管理から外す(throwaway render で実測)。gate をその下に置くと signing off だけで commit gate が黙って消える(安全機構の silent fail-open)→ 独立 dir で交差を断ち、test で placement を pin。
  2. ~ は git が展開(core.hooksPath は pathname 型・実測済み)。render に実 home path を焼かない。
  3. work の .gitconfig も変わる(plain file 共有のため include 行が増える)が、include 先が work では管理されず missing → git が無視 = 挙動不変。会社 Mac は次回 pull + apply で無害に追従。

検証

  • 全テストローカル PASS(validate 系 + render 系 + 新規 test-git-hook-gates)
  • test-git-hook-gates.sh(CI 配線済み): render exact pin(shim 2 本 + hooks.gitconfig の byte 一致・実行 bit・sh -n)/ end-to-end(rendered ~/.gitconfig + fake dispatcher で実 commit → 両 stage 到達・fail-closed で block・--no-verify bypass)/ cap-off が既存配線を真に削除 / enableGitSigning=false で gate が消えないこと
  • shellcheck -S warning / bash -n / zsh -n / validate-policy --all PASS
  • 実機 doctor / preflight(apply 前): shim 未配置 warn + dispatcher 配備済み ok を正しく報告
  • 実機 smoke(clean commit 通過 / token 形 block / trailer 検証 / 既存 hook chain / --no-verify)は merge → apply 後に実施し git hook gates の配線: hooksPath shim (pre-commit / commit-msg) + global core.hooksPath #196 に記録(issue の最終チェックボックス)

残リスク

  • 実機 apply までは配線されない(doctor が「not wired」を報告し続けるのは正しい挙動)
  • CLI 更新で env marker(CLAUDECODE 等)が消えると trailer gate は人間 commit 扱いに fail-open(実体側 #202 の既知事項・smoke で生存確認)

🤖 Generated with Claude Code

https://claude.ai/code/session_016qw8BkUDu5WdwhYfiFFX5n

…efs #196)

commit 境界の git hook gates (agent-tools#202 で実体 merge 済み) の dotfiles 側
配線。実体 = agent-tools・配線 = dotfiles の既存分界に従う。

- 新 capability enableGitHookGates (personal=true / work=false) + 新 module
  git-hook-gates。dispatcher は gate 欠損時 fail-closed (exit 2) なので、
  agent-tools 未配備のマシンに配ると commit が止まる → work は false。
- shim 2 本 (~/.config/git-hook-gates/hooks/{pre-commit,commit-msg}) は薄い
  exec のみ。~/.claude 側 deploy を指す (dotfiles 裁定・両 home 同一 byte)。
- core.hooksPath は dot_gitconfig の無条件 [include] → managed
  hooks.gitconfig で設定 (signing.gitconfig と同型: missing なら git が無視)。
- gate は template 自己 gate (false は空 render → prune)。requires だと
  fail-closed な shim が残置される (#184 教訓)。
- 置き場は ~/.config/git 配下にしない: git-signing off の profile で
  chezmoiignore が subtree ごと落とし gate が黙って消えるため (実測)。
- doctor: 配線 chain (shim/hooksPath/dispatcher) を report-only 監視。
  「配線済み + dispatcher 不在 = commit 停止」を最も強く warn。
- preflight: apply 前に dispatcher 不在と既存 core.hooksPath を warn。
- test-git-hook-gates.sh: render exact pin + rendered ~/.gitconfig で実 commit
  を通す end-to-end (両 stage 到達 / fail-closed / --no-verify bypass) +
  cap-off の真の削除 + git-signing 独立性。CI 配線済み。
- docs: git-hook-gates.md (配線側の正本・強度ラベル・既知の限界) +
  config-ownership 表 + README 行。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qw8BkUDu5WdwhYfiFFX5n
@kosako

kosako commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

🔍 レビュー依頼(→ Codex)

  • 観点: commit 境界の安全配線なので、(1) fail-closed 配布条件の穴(agent-tools 未配備環境で commit が止まる経路が残っていないか)、(2) capability gating の完全性(cap off で配線が真に外れるか・requires 残置の再発)、(3) chezmoiignore / module path の相互作用(git-signing との交差ほか)、(4) doctor / preflight の honest-labeling(偽の安心を作らないか)、(5) テストの検証力(exact pin / end-to-end が契約を実際に固定しているか)を重点的に。production レール(review lens: ai-antipattern / existing-system-respect 観点)。
  • ランク: 🔴 must(merge 前必須)/ 🟡 should(推奨・対応は依頼元判断)/ ⚪ nit(任意)
  • 結果はこの PR にコメントで返します

…197 Codex must 対応)

- must-1: 配線 render を 2 段 gate に — enableGitHookGates (intent) AND
  destination に agent-tools 配備 3 本が完全存在 (readiness、
  .chezmoitemplates/git-hook-gates-armed が destDir を probe)。新規マシンは
  apply しても武装せず brick しない。sync 後の再 apply で武装、配備消失は
  次の apply で可視に解除。
- must-2: doctor / preflight の readiness 判定を dispatcher 1 本から 3 本
  (dispatcher + public-safety-gate + ai-trailer-gate) に拡張。partial deploy
  が武装しないことを test で pin。
- should-1: doctor の cap=false 分岐に残置配線 (shim / hooksPath) の検出を追加。
- should-2: preflight の core.hooksPath warn を「managed 値と一致なら ok・
  別値のみ warn」に変更 (ノイズ抑制)。
- test: bare / partial deploy の非武装 pin + full deploy fixture 化。
  docs / modules / profiles コメントを 2 段 gate に追従。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qw8BkUDu5WdwhYfiFFX5n
@kosako

kosako commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

📋 レビュー結果(by Codex)+ 対応(8dd7f9a)

初回判定: ⛔ 要対応(must 2 件) → 全件対応し push 済み。再レビューを依頼します。

🔴 must(2 件・対応済み)

  • .chezmoidata/profiles.yaml:77 — 新規 personal マシン(agent-tools 未配備)で apply すると fail-closed 配線が全 commit を止める。採用 → 配線 render を 2 段 gate 化: enableGitHookGates(intent)AND destination に配備 3 本が完全存在(readiness、新設 .chezmoitemplates/git-hook-gates-armed.chezmoi.destDir を stat probe)。未配備マシンは apply しても武装せず(bare destination の非武装をテストで pin)、sync 後の再 apply で武装、配備消失は次の apply で可視に解除。「render/apply 自体を拒否」案は onboarding の apply ごと壊すため不採用、非武装素通りを選択。
  • scripts/preflight.sh:137 — readiness 判定が dispatcher 1 本のみで partial deploy が緑になる。採用 → doctor / preflight とも 3 本(dispatcher + public-safety-gate + ai-trailer-gate)の executable presence を判定。arming probe も同じ 3 本条件。partial deploy(dispatcher のみ)が武装しないことをテストで pin。

🟡 should(2 件・対応済み)

  • doctor の cap=false 分岐 — 採用 → 残置配線(shim 存在 or core.hooksPath が managed dir を指す)を検出して warn(apply で prune を案内)。
  • preflight の core.hooksPath warn — 採用 → managed 値と一致なら ok、別値のみ warn。

検証

  • 全テストローカル PASS(static + validate 系 + render 系 + 新 test-git-hook-gates: bare 非武装 / partial 非武装 / full deploy exact pin / end-to-end 実 commit / cap-off prune / signing 独立)
  • 実機 preflight: 3 本 deployed → 「apply arms the commit gates」を正しく報告

- must (GHG-READY-EXEC): probe が presence のみで doctor/preflight の -x 判定と
  不整合 — 0644 dispatcher で「preflight は非武装と報告するのに apply が武装」
  し commit が止まる。probe を「regular file かつ owner-exec bit」(stat の
  type/perm、owner-exec = perm/64 mod 2) に強化し、非実行 dispatcher fixture の
  非武装をテストで pin。
- should (GHG-DOCTOR-PARTIAL): deploy+wiring とも不完全な分岐の断定を避け
  「stage-dependent or blocked」に (partial apply の診断精度)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qw8BkUDu5WdwhYfiFFX5n
@kosako

kosako commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

📋 再レビュー結果(by Codex)+ 対応(17d11a8)

再レビュー判定: ⛔ 要対応(must 1 件・新規) → 対応し push 済み。must 対応の確認再レビューを依頼します。

🔴 must(1 件・対応済み)

  • GHG-READY-EXEC .chezmoitemplates/git-hook-gates-armed — arming probe が presence のみで、doctor / preflight の -x readiness と不整合(0644 の dispatcher で「preflight は非武装と報告するのに apply は武装」→ commit が止まる)。採用 → probe を「regular file かつ owner-exec bit」要求に強化(stattype / perm、owner-exec = perm/64 mod 2。template に bitwise が無いため div/mod で判定・実測済み)。非実行 dispatcher fixture が非武装に留まることをテストで pin。

🟡 should(1 件・対応済み)

  • GHG-DOCTOR-PARTIAL doctor の deploy+wiring 両不完全分岐 — 「commits are not gated yet」の断定を避け「commit behavior may be stage-dependent or blocked until sync and apply both complete」に変更。採用

検証

  • test-git-hook-gates 全 pass(非実行 dispatcher 非武装ケース追加)+ static(bash -n / shellcheck)+ test-render / test-preflight / test-doctor PASS

@kosako

kosako commented Jul 14, 2026

Copy link
Copy Markdown
Owner Author

📋 増分再レビュー結果(by Codex)

判定: ✅ merge 可(must 0 件) — GHG-READY-EXEC(probe の -x 強化)・GHG-DOCTOR-PARTIAL とも解消確認済み。Codex は div/mod 式の 0755→真 / 0644→偽 も独立確認。

🟡 should 1 件(docs/git-hook-gates.md の readiness 表現を「3 本すべてが regular file かつ実行可能」に)→ 採用・対応済み(最新 commit)。

CI green を確認して merge します。merge 後に実機 apply + smoke(#196 のチェックリスト)を実施して issue に記録します。

@kosako
kosako merged commit 89c6ffd into main Jul 14, 2026
2 checks passed
@kosako
kosako deleted the feat/196-git-hook-gates-wiring branch July 14, 2026 02:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

git hook gates の配線: hooksPath shim (pre-commit / commit-msg) + global core.hooksPath

1 participant