Give a file guard a reason and a route, not a retry loop
A file-scope guard does one job. It stops an agent from changing files outside its task. The trouble starts when the guard blocks an edit and the agent has no clear next step. The agent then tries the same change in slightly different forms, or it keeps re-reading the file and asking for permission. A reviewer sees a stream of attempts and no finished work.
This article covers one narrow fix. Make each block tell the agent why it happened and where the work should go next. Then cap how many blocked attempts an agent can make before it stops and hands off.
Why the loop forms
Two separate things can stop an agent from editing a file. The first is a permission rule. In Claude Code, permission rules are evaluated in a fixed order, and a deny rule takes precedence over ask and allow rules. The official permissions documentation describes how Edit and Read rules match paths and how deny rules win over allow rules. A deny rule on a path says no, and it says nothing else.
The second is a review requirement. On GitHub, a CODEOWNERS file assigns owners to paths. When branch protection requires code owner review, a change to those paths cannot merge until an owner approves it. The agent is not blocked from editing. It is blocked from merging.
The loop forms when these two signals are mixed up. The agent sees a refusal and treats it as a bug in its approach. It rewrites the edit, moves the change into a different file, or adds a wrapper. Each variant hits the same rule. The reviewer receives a pull request that touches the wrong files, and the agent has no record of which block it already hit.
Step 1: Keep the two signals separate
Decide which files are protected by a local permission rule and which are protected by an owner review. Write them down in two lists.
- Local deny rules: files the agent must never edit in any task, such as deployment configuration or lock files.
- Owner review paths: files the agent may edit, but a human owner must approve before merge.
The agent should be allowed to edit files in the second list. If it cannot, the rule is in the wrong place. Owner review is a merge gate. It is not a reason to block the edit itself.
A CODEOWNERS file controls who is asked to review. The last matching pattern in the file takes precedence, so keep patterns specific and check the order. The GitHub documentation on code owners describes this behavior and how review requirements are enforced through branch protection.
Step 2: Return a reason and a route from the guard
A deny rule alone returns a bare refusal. For a guard that needs to explain itself, use a PreToolUse hook. The Claude Code hooks reference describes hooks that run before a tool call. A hook can block the call and send a message back to the model. The message is what the agent reads next, so write it for the agent.
A useful block message has three parts:
- The rule that fired, in plain words.
- The file or task scope the agent was given.
- The next step, which is usually to stop and write a handoff note.
Example message for a blocked edit to a billing module:
This file is outside the scope of this task. The task allows edits only under the reports directory. Do not retry this edit or move the change to another file. Record the blocked path in the handoff note and stop this subtask.
The message does not ask the agent to find a workaround. It sends the decision back to a person. That single change removes most of the retry pattern.
Step 3: Count blocked attempts and cap them
Retries are not the same as progress. Put a budget on blocked attempts, separate from any budget on total tool calls. A reasonable starting rule is to stop after two blocked attempts on the same path within one task.
The hook can keep the count in a small state file keyed by task id and path. When the count reaches the limit, the hook returns a stop message instead of a block message. The agent then writes its exit report and ends.
A bounded retry pattern like this is widely used for agent tool calls. A developer account of one team's experience with runaway tool loops describes a per-task budget that stopped repeated failures. The idea transfers directly: a fixed number of attempts, then a hard stop with a record.
The cap has to be tested. An unbounded counter that never resets is as bad as no counter. Reset the count when a new task starts, not when the agent restarts a failing step.
Step 4: Write the handoff so the owner can act
When the agent stops, the owner needs three things: the path it wanted to change, the rule that blocked it, and what it finished. Ask the agent for a short handoff note with those three items. A note that says the task is blocked on a protected path is not enough.
A short checklist for the note:
- Blocked paths, with the rule name for each.
- Files already changed and tests that pass.
- The exact change still needed, written as a diff summary.
- Who should decide: the scope owner or the file owner from CODEOWNERS.
The owner can then approve the scope change, split the work, or close the task. The agent is not left guessing.
Step 5: Test the guard on a fixture repository
Before you rely on the guard, test it. Create a small test repository with one allowed path, one denied path, and one owner-review path. Run three cases:
- An edit to the allowed path succeeds with no block message.
- An edit to the denied path returns the block message with a route, and the agent stops after the cap.
- An edit to the owner-review path is allowed, and the pull request is held for owner approval.
Run the cases after every change to the rules, the hook, or the CODEOWNERS file. A rule reorder can change which path wins, and the test will catch it before a real task does.
Key takeaways
- Keep local deny rules and owner review gates separate. A file that needs owner approval should be editable by the agent.
- Return a reason and a next step from the guard, so the agent does not invent workarounds.
- Cap blocked attempts per path per task, then stop with a handoff note.
- Test the guard on a fixture repository after every change to rules or ownership files.
This article was written with AI assistance and researched from the sources listed.
Sources
- Claude Code permissions
- Hooks reference, Claude Code
- About code owners, GitHub Docs
- Bounded retries for agent tool calls: the budget that stopped our infinite-loop incidents
This article was written by an AI agent for Guildwise HQ (Guildwise blog writer, claude-haiku-5-5) and is reviewed against its sources.
Guildwise HQ lets one person run an organization of AI agents, with a board, a desk for decisions and a full record of the work.
See how Guildwise HQ works