Policies
How claw-drive decides what Session B can do.
The policy object
A policy is either the string "bypass" — no gating, useful for demos, never for real work — or an object with up to four rule lists plus two scalars:
{
"auto_approve": [
{ "tool": "Read" },
{ "tool": "Bash", "bash_command_matches": "^git (status|diff|log|show|branch) " }
],
"auto_defer": [
{ "tool": "Bash", "bash_command_matches": "^sudo\\s", "name": "sudo → human" }
],
"auto_reject": [
{ "tool": "Bash", "bash_command_matches": "rm -rf |git push ", "severity": "high" }
],
"escalate_default": true,
"decision_timeout_seconds": 3600
}
Rule shape
tool— exact match against the tool name (Bash,Read,Edit,Glob,Agent, …).bash_command_matches— regex against the full Bash command. Bash-only.arg_matches— regex against identifying arg fields on non-Bash tools.Edit.file_path,Glob.pattern,Agent.subagent_type, etc.name— human-readable tag. Appears in events andpending. Strongly recommended onauto_deferrules.
Evaluation order
Rules are checked in this order:
auto_reject → auto_defer → auto_approve → escalate_default
This closes compound-command bypasses like:
git status && rm -rf /tmp
# git status matches auto_approve, rm -rf /tmp matches auto_reject → reject wins.
Templates
Two policy templates ship in templates/:
claw-drive-policy.json— conservative starter. Default when no--policyis passed. Safe for unknown projects.claw-drive-policy-permissive.json— starter plus common dev-CLI auto-approves:rg,sed,awk,jq,diff,mkdir -p,touch, non-recursivecp,mv, safegitops likefetchandpull --ff-only, path/env introspection. Destructive rules preserved verbatim.
The CLAW-GATE convention
The default policy includes a review-gate defer rule:
"auto_defer": [
{ "tool": "Bash", "bash_command_matches": "^echo 'CLAW-GATE:", "name": "review gate" }
]
In the scenario brief, tell B:
Before each risky step, run echo 'CLAW-GATE: <your question>' with the Bash tool and wait for my response.
When B runs the echo, the defer rule fires. The monitor alerts A. The human answers. A calls provide_tool_output with the answer. B reads it and proceeds. No new primitive — same mechanism as a sudo defer.
Learning from decisions: remember_as_policy
When you approve, reject, or defer a call interactively, pass remember_as_policy: true to have claw-drive derive a rule and append it to the session's live policy.
Derivation is narrow by default for non-Bash tools:
Edit /path/foo.ts→{ tool: "Edit", arg_matches: { file_path: "^/path/foo\\.ts$" } }Glob "**/*.md"→{ tool: "Glob", arg_matches: { pattern: "^\\*\\*/\\*\\.md$" } }Agentwithsubagent_type: "Explore"→{ tool: "Agent", arg_matches: { subagent_type: "^Explore$" } }
Tools without a recognised identifying arg (TodoWrite, ExitPlanMode, custom MCP tools) fall back to tool-wide matching. The derived rule's name is tagged (tool-wide fallback) so it's obvious at review time.
Bash remembering derives from a prefix of the command, same as before.
Testing a policy
Use claw-drive policy-test '<command>' to see which list fires and what the resolved decision is — no session spin-up required:
$ claw-drive policy-test 'kill -9 1'
Decision: escalate
Default action: defer
List: auto_defer
Matched rule: "kill -9 of init/everything/process-group (PID 1, -1, or 0)"
Pattern: \bkill\s+-9\b.*?\s(-1|0|1)(\s|$|[;&|])
Severity: high
Tool: Bash
Command: kill -9 1
Policy: <repo>/templates/claw-drive-policy.json (starter)
Three output formats: human (default), --explain (walks every rule with ✓/✗ marks), --json (single-line for piping to jq and CI tooling).
Bash uses positional shorthand; non-Bash tools take --tool TOOL --arg KEY=VALUE (repeatable):
claw-drive policy-test --tool Read --arg file_path=/etc/passwd
claw-drive policy-test --tool Edit --arg file_path=/tmp/foo.ts --arg old_string=secret
claw-drive policy-test --tool Grep --arg pattern='api_key'
--policy SPEC selects which policy to test against. SPEC is one of starter (default), permissive, bypass, or a <path> to a custom policy JSON. --exit-on reject|defer|approve|escalate turns the diagnostic into a CI gate — exits 1 on match, 0 otherwise.
Timeouts
The default decision_timeout_seconds is 3600 (1 hour). If an escalation sits unresolved that long, the runner fires the rule's default_action:
- Plain
escalate_default: truematch →approveon timeout. auto_rejectmatch →rejecton timeout.auto_defermatch →deferon timeout.
Override per-session via start_session's decision_timeout_seconds argument, or per-policy via the field above.