#!/usr/bin/env bash # # PreToolUse guard (Bash): block `git commit` ONLY when the rbw vault agent is locked # AND the commit would actually need the vault. The pre-commit ansible-lint hook decrypts # vault.yml via rbw — but it is scoped (`files: ^(roles|playbooks|inventories)/.*\.ya?ml$`, # always_run:false), so a docs-/config-only commit never triggers it and needs no vault. # (2026-06-17 kaizen, docs/FRICTION.md: the old guard blocked *every* locked commit, so a # docs-only commit got snagged needing a vault password it never uses.) # # Fails OPEN: blocks only on a definitive "Ansible content staged AND rbw locked" signal. # rbw missing, not a plain `git commit`, `--no-verify`, or no Ansible content staged → allow. # When unsure it errs toward blocking (asking for an unlock is cheap; a deep pre-commit # failure is not). # set -uo pipefail input=$(cat 2>/dev/null) || exit 0 cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // empty' 2>/dev/null) || exit 0 case "$cmd" in *"git commit"*) : ;; # a git commit — check further *) exit 0 ;; # not a commit — allow esac case "$cmd" in *"--no-verify"*) exit 0 ;; # hooks skipped anyway — allow esac command -v rbw >/dev/null 2>&1 || exit 0 # rbw not installed — allow rbw unlocked >/dev/null 2>&1 && exit 0 # unlocked — allow # rbw is LOCKED. Only block if this commit would run the vault-decrypting ansible-lint # hook — i.e. staged content matches its `files:` scope. Mirror that regex exactly. ANSIBLE_RE='^(roles|playbooks|inventories)/.*\.ya?ml$' cd "${CLAUDE_PROJECT_DIR:-.}" 2>/dev/null || exit 0 files=$(git diff --cached --name-only 2>/dev/null) || exit 0 # `git commit -a/--all` also sweeps in modified tracked files that aren't staged yet. # (Substring match — errs toward including them, which only ever over-blocks. Safe.) case " $cmd " in *" -a"*|*"--all"*) files="$files"$'\n'"$(git diff --name-only 2>/dev/null)" ;; esac # No Ansible content in the fileset → ansible-lint hook won't run → no vault needed → allow. printf '%s\n' "$files" | grep -Eq "$ANSIBLE_RE" || exit 0 # Ansible content staged AND rbw locked — the commit would fail deep in pre-commit. Block. cat <<'JSON' {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"rbw is locked and this commit stages Ansible content — the pre-commit ansible-lint hook needs the vault password to decrypt vault.yml. Run: rbw unlock (docs-/config-only commits are exempt and won't hit this guard.)"}} JSON exit 0