Coding Agent Hooks Must Cover Every File-Editing Tool
The safeguard missed the edit path
A coding agent can follow a requested change and still bypass the safeguard meant to constrain it. I asked an agent to update a Java endpoint and its tests while leaving application configuration untouched. The test passed, but my pre-edit check never ran.
Those facts describe two separate outcomes. The requested code change appears to have worked well enough for the test to pass, yet the check intended to control which files could be changed did not provide the expected protection. A passing test can say something about behavior; it cannot show that a boundary was enforced. That distinction is easy to miss when a successful result makes the whole run feel trustworthy.
I’d added a hook, a small program designed to reject edits before they reach the file system. The catch was that I attached it to write_file, while the agent made both changes with apply_diff. The hook protected one editing route, not the agent’s actual route.
Tool names matter because controls attach to behavior
For a person, “edit this file” sounds like one operation. For an agent, it may be an instruction carried out through one of several tools, each with its own mechanics. A tool that writes a complete file and a tool that applies a patch can produce the same visible result while taking different paths through the system. A safeguard attached to only one path sees only some of the activity.
That makes a hook’s scope a practical engineering question. It isn’t enough to know that a hook exists or that its code contains the right rule. You also have to know when the system invokes it, which tools invoke it, and whether an edit can reach the file system without passing through that invocation point. If those details aren’t clear, the hook’s apparent coverage may be broader than its actual coverage.
Here’s the thing: the agent didn’t need to ignore the request for the boundary to fail. It could perform the requested endpoint and test changes through a supported editing tool, while the configuration check sat on a different route. The gap came from how the safeguard was attached, not from evidence that the agent deliberately tried to defeat it.
A passing test answers a different question
Tests are useful, but their result has a defined scope. A passing test indicates that the tested conditions succeeded; it doesn’t establish that every file stayed within the requested boundary, that every pre-edit check ran, or that the editing process followed a particular control. In this case, the test passed while the check did not run. Both observations can be true at once.
That distinction matters because safeguards and tests address different failure modes. A test can catch a change that breaks expected behavior. A pre-edit hook can reject an attempted change based on a rule, such as whether a sensitive file is in scope. Neither can stand in for the other. One checks an outcome; the other is meant to constrain a step on the way there.
There’s a process lesson here, too. When an agent reports that work is complete, the final code and test result may not tell you which editing tools it used or which checks fired. If the protection depends on a particular hook, knowing that the change succeeded isn’t enough. You need some confidence that the relevant control actually ran.
Map the available edit paths
The practical response is to inspect the agent’s available editing tools before relying on a hook to protect files. That means identifying the paths that can alter files and checking which of them trigger the safeguard. If a supported tool can make a change without invoking the hook, then the rule doesn’t cover the full edit surface.
This review should follow the actual setup rather than assumptions based on tool names. A hook may be attached to a particular operation, while the agent’s instructions allow several ways to make a change. The key question is simple: can every edit that matters reach the file system only after the relevant check? If the answer is unclear, the safeguard’s effective boundary is unclear too.
And this is the part most people overlook: a control can work exactly as configured and still fail to protect the thing its user cares about. The hook did its job on write_file; the problem was that the agent used apply_diff. Correct configuration at one point in a workflow doesn’t guarantee coverage across the workflow.
Why this matters for agent workflows
As coding agents gain access to multiple tools, safety depends partly on how those tools connect to the checks around them. The same requested edit may be expressed as a whole-file write, a patch, or another supported operation. That flexibility can help an agent work with different tasks, but it also means that a control designed around one operation may leave other operations unchecked.
If you’re working in this space, treat safeguards as part of the tool path, not as a property of the prompt. Prompts communicate intent: here, update the endpoint and tests but leave application configuration alone. A deterministic check is meant to enforce a boundary regardless of whether the agent remembers or correctly interprets that instruction. For that promise to hold, the check has to meet the edit at every route it can take.
This is more significant than it looks. A narrowly attached hook can create confidence without providing broad protection, especially when a successful test makes the run seem uneventful. The sensible question after a change isn’t only whether the code works; it’s whether the control meant to limit that change actually observed the operation.
That’s the practical lesson: a prompt describes the boundary you want, but a deterministic safeguard has to cover every tool that can cross it. Check the agent’s available edit paths before relying on a hook to keep sensitive files out of scope.