Can the agent get past it
What was tested, what happened, and what is still true afterwards.
Can the agent get past it
The first question every engineer asks, and it deserves a measurement rather than an argument.
Reproduce all of it with tests/enforcement_probe.sh, which takes about a minute.
What was tested
A throwaway PreToolUse hook in a throwaway directory. It logs what it saw, then
returns a decision for Bash. Claude Code is asked to run echo <marker>, and the
transcript is checked for the marker to see whether the command actually ran.
Environment. Claude Code 2.1.228, August 2026.
What happened
| Permission mode | Hook decision | Hook fired | Command ran |
|---|---|---|---|
default | deny, exit 2 | yes | no |
bypassPermissions (--dangerously-skip-permissions) | deny, exit 2 | yes | no |
default | ask | yes | no |
bypassPermissions | ask | yes | no |
The hook also reported the mode it was running under.
hook fired tool=Bash permission_mode=bypassPermissionsWhat that means
A permission mode does not turn the gate off. --dangerously-skip-permissions
skips Claude Code's own permission prompts. A PreToolUse hook runs before that
prompt, so skipping the prompt does not skip the hook.
The documentation says the same thing in two places. Hooks run before the permission prompt, for every tool, and a hook that exits 2 stops the tool call before permission rules are evaluated.
This is worth stating plainly because the opposite is widely repeated, including by people who ship hooks of their own. On the version tested here, the flag does not bypass the hook. The evidence is version-specific by nature, which is why the probe ships in the repository. Run it on the version you actually deploy, and run it again as part of every runtime upgrade, because a measurement from last quarter is not a control.
An escalation that cannot reach anyone does not become a pass. In a non-interactive session there is nobody to answer a prompt, and the call stopped rather than proceeding. Coco does not rely on that. In a mode that cannot ask, the gate converts ESCALATE to BLOCK itself, so the receipt says what actually happened rather than recording an escalation nobody could have answered.
What still gets past it
Said before a customer finds them.
Editing the settings file. The hook is registered in a settings file, and anyone who
can write that file can remove the gate. On a managed fleet the answer is managed
settings, which a user cannot override, together with allowManagedHooksOnly, which
blocks user and project hooks so only the organisation's hooks load.
disableBypassPermissionsMode and disableAutoMode close the modes as well, though
on this evidence they are not the hole.
A different client. The gate governs sessions on a machine where it is installed. An agent run somewhere else is not governed by it, which is an argument for deploying it as policy rather than as a developer's choice.
Everything that is not a tool call. The gate sees the actions an agent takes. It does not read what the model says, and it does not contain what a permitted action does once it is running. A contract that permits a database write cannot then supervise the write.
A wrong contract. The gate enforces what is written. A rule that is too loose passes things it should not, and no amount of enforcement fixes a rule nobody checked. This is why observe mode exists and why the catch report is the artefact that decides whether to enforce.
Next
- Managed fleet for closing the first row
- What it does not stop for the whole threat model