Enforce Segregation of Duties on a Valuation File
SOX-style segregation of duties doesn't disappear just because the preparer is an agent instead of a person — the same entity shouldn't prepare and approve the same output. This tracks which files a session has written to, then blocks any later tool call in that same session whose content marks one of those files "approved" or "final."
1. Save this as the hook script
#!/usr/bin/env python3
"""PreToolUse hook — blocks a session from marking a file it wrote itself
as approved/final. Wire to Write|Edit|Bash."""
import json, re, sys
from pathlib import Path
data = json.load(sys.stdin)
tool_input = data.get("tool_input") or {}
session_id = data.get("session_id", "unknown")
state_path = Path(data.get("cwd", ".")) / ".claude" / f"sod_written_{session_id}.txt"
path = tool_input.get("file_path", "")
content = (tool_input.get("content") or tool_input.get("new_string") or
tool_input.get("command") or "")
written = set(state_path.read_text().splitlines()) if state_path.exists() else set()
if path and re.search(r"(?i)\b(approved|final|signed[- ]off)\b", content) and path in written:
print(f"BLOCKED: this session already wrote {path} — the same session "
f"cannot also mark it approved/final. Needs a second reviewer.", file=sys.stderr)
sys.exit(2)
if path:
state_path.parent.mkdir(parents=True, exist_ok=True)
written.add(path)
state_path.write_text("\n".join(written))
sys.exit(0)
Save to a path inside your project, e.g. .claude/hooks/enforce-sod-valuation-file.sh, then chmod +x it.
2. Wire it into .claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit|Bash",
"hooks": [
{"type": "command", "command": "python3 .claude/hooks/enforce-sod-valuation-file.py"}
]
}
]
}
}
Before you use this
A hook runs with your local shell's permissions — review the script above before adding it, the same way you'd review any script you didn't write yourself. See our guide on hook security risks for the specific CVEs this category of feature has already produced.
Related hooks