Facepalmed after reading this https://techcrunch.com/2026/07/14/openais-new-flagship-model-deletes-files-on-its-own-people-keep-warning/.

It's an article uncovering the shortcomings of Sol 5.6, Open Ai's new frontier model, specifically about an issue that existed since the beginning of the ai era: agents deleting stuff that's actually important.

Below, I break down exactly how I solve this in my own setup (look near the bottom of the article for a provider-agnostic solution).

The Delete Command My AI Is Not Allowed to Run

My Claude agents run with a lot of autonomy. They edit files, apply database migrations, deploy to production, send real emails. Thousands of shell commands a week, most of which I never see.

Exactly one command is banned outright: rm.

Not discouraged in a claude.md. Not "be careful with" in a skill instruction. Banned, deterministically, through a hook so that neither I nor the agent can forget to enforce it.

You still want to automate clean up?:

Every delete in my system goes through mv into the Trash with a date suffix instead. At the end of every build session, I just go to that one folder, review the mv decisions Claude made, and manually delete what I agree with (5 min tops). This post is about why that one rule is load-bearing, and about the mechanism behind it, because the mechanism is the actually useful part.

Speed changes the math on mistakes

A human with a terminal makes deletion mistakes occasionally, because a human types slowly and hesitates before scary commands. An agent doesn't hesitate. It executes its plan at full speed, and if the plan contains a wrong assumption, the damage arrives at full speed too.

The failure mode isn't malice and it isn't even stupidity. It's claude cleaning up what it believes are temp files, three seconds after misreading which directory it's in. rm makes that mistake permanent. mv to the Trash makes the same mistake a mild inconvenience. Same agent, same wrong belief, completely different blast radius.

Before I set this up, I thought I wouldn't need a safeguard, that all the horror stories on X of ai deleting their data would "never happen to me". However, during a routine cleanup removing duplicates, claude deleted an entire folder filled with context files and an ed-tech platform I'd been working on for months. I felt like I had just dropped my phone in the sewer. Luckily, I was able to recover it but those few hours of panic are something I never wanted to go through again. So how can you set this up before having to learn it the hard way.

Why use a hook instead of a prompt rule?

Here's the part that generalizes beyond deletion (helpful when thinking of other rules your agents should follow).

My first version of this rule was a line in the instructions: "never use rm, always move files to Trash." It worked, mostly. But instructions compete with everything else in the context window. A long session, a compacted conversation, a subagent that got a summarized version of the rules: eventually some path leads to an agent that never saw the line, and the rule quietly stops existing.

The fix is a hook. Claude Code lets you register small programs that intercept the agent's tool calls before they execute. Mine pattern-matches every shell command about to run; anything containing rm gets blocked before execution, and the agent gets back a message telling it to use mv ~/.Trash/ with a date suffix instead.

That distinction, instructions in markdown vs a hook, is vital when running agents with real permissions. Hooks = trust. Trust = autonomy. Autonomy =

How do you set up a Claude Code hook to block a command?

A Claude Code hook is a small program that Claude Code runs at fixed points in the agent loop, and a PreToolUse hook is the kind that fires before a tool call executes. The hook receives the exact tool input the agent is about to run, in this case the full shell command, and returns a decision: allow it, block it, or block it with a message the agent reads. Blocking with a message is the useful move, because the agent treats that message as feedback and immediately routes around the wall. My delete guard is one PreToolUse hook on the Bash tool: it pattern-matches every command for rm, blocks any match, and tells the agent to move the target into the Trash with a date suffix instead. Registration lives in the Claude Code settings file, so the guard applies to every session on the machine.

The setup is one hook that fires before every Bash command. Pseudocode, close to the real thing:

on PreToolUse(Bash):
  if command matches rm-pattern:
    block()
    tell_agent("rm is disabled. Move to ~/.Trash/ with a date suffix instead.")

And the agent-facing behavior it produces:

agent: rm -rf ./build-cache
hook:  blocked. rm is disabled here.
agent: mv ./build-cache ~/.Trash/build-cache-2026-07-08

A few practical notes from running this for months. Match generously: rm hides inside chained commands, &&, pipes, shell functions. Block the whole command, not just the fragment, and make the error message tell the agent what to do instead, because a bare "no" produces retries while a redirect produces compliance. Lastly, make sure to add a date-suffix to file moves to Trash; it becomes easier to track and make decisions on when doing your manual pass.

What if you aren't using Claude Code?

If you are building your own custom agents from scratch with Sol 5.6 or Fable, you don't have a handy settings file to lean on for native hooks.

To make this work for any provider, you have to pull the safety logic out of the model's prompt instructions and into your orchestration layer.

Instead of hoping the LLM behaves, you build a middleware loop in your application code. This loop intercepts every tool call after the model requests it but before your system actually executes it. Here is what that looks like in Python:

def execute_agent_loop(prompt, active_model):
    # 1. The model decides it wants to run a tool
    tool_request = active_model.generate_response(prompt)
    
    # 2. THE HOOK: Intercept before execution
    if tool_request.name == "Bash":
        command = tool_request.arguments.get("command", "")
        
        # Match generously to catch chained commands
        if "rm " in command:
            # 3. Block execution and redirect the agent
            return "Error: rm is disabled globally. Move to ~/.Trash/ with a date suffix instead."
            
    # 4. If safe, execute the actual tool
    return actual_bash_environment.run(command)

Writing your hook at the orchestration level gives you the exact same benefits as the Claude Code PreToolUse hook. It's centralized so you write the rule once in your code, and it applies immediately to whichever new API you swap in tomorrow.

Most importantly, it's deterministic. You rely on hardcoded pattern matching to catch a dangerous string rather than asking a probabilistic AI to evaluate its own safety.

The allowlist mindset

Once the delete guard existed, the pattern started spreading, because the shape is reusable: find an action where a mistake is irreversible, and demote it from "possible" to "impossible without a human."

Deletion was first. The same thinking now gates production database changes (agents write migrations, but applying them requires my explicit confirmation) and anything that publishes to the outside world. The question I ask about any new capability is the one I'd ask about a new employee's badge access: not "will it probably behave," but "what happens on its worst day."

Autonomy isn't the opposite of safety. Autonomy is what you get to grant once the irreversible mistakes are structurally off the table. My agents get more freedom than most people give theirs, and the delete guard is a big part of why I can afford to.