A PreToolUse hook on the Bash tool (sprint 4.116, work item 1; requirement 1.1 as re-recorded after Gate 1.5 rounds 1–2, 2026-09-16).
It reads the hook payload on stdin and refuses, with exit 2 and a reason naming the rule, the operations the operator's rules forbid everywhere:
| rule id | refused | passes |
|---|---|---|
git-add-all | git add -A, --all, -u, --update | git add <path> |
git-reset-hard | git reset --hard | git reset <path>, git reset --soft |
git-checkout-force | git checkout -f, --force | git checkout -- <file>, git checkout <branch> |
git-stash | any git stash but list and show | git stash list, git stash show |
drop-database | DROP DATABASE x, dropdb x unless x ends _fixture or _copy | DROP DATABASE x_fixture |
psql-live-write | INSERT, UPDATE or DELETE through psql to a database not ending _fixture or _copy, or whose target the command does not state | psql -d x_fixture -c "UPDATE …", a SELECT on live |
The rule is global: no repository, path or cwd is exempt. A protected operation reached through a wrapper or indirection (sh -c, eval, env, an absolute path, git -C, a subshell) is refused rather than guessed at — a guard that parses shell semantics loses to the next quoting trick.
What it does read is quoting (first day live, 2026-09-16: a grep for a refused pattern and a SELECT-only here-document were both refused). A quoted word is executed only when an interpreter runs it (sh -c, eval, env, timeout, sudo, xargs…) or when psql reads it as SQL; under grep, echo, printf or git commit -m it is data and passes. For psql the SQL the guard judges is the -c text, a here-document body (it is in the command text) and an echo/printf piped in; a file (-f, <) or a pipe from anything else is opaque and refused on a live target with reason=input-opaque — -c or a here-document is the read form.
The decision is pure (decide), so every route is an oracle test. On a refusal the binary also records an observation under the pair rule apply (tools-corpus corpus observation rule-apply), synchronously and best effort: a recorder failure never turns a refusal into an allow, and a refused call runs no tool and leaves no other event. 4.119's governing-refusal predicate reads that row.
Registered in .claude/settings.json under PreToolUse / Bash, beside archive-guard.