← Blog

The day the AI decided to "clean up," the project was gone.

· 10 min read · 11 views
AI Assisted Development Hooks Guardrails Context Management Observability Open Source
AI Engineering Real-World Warfare

The day AI decided to "clean up"

The project's gone.

A coworker's AI CLI tool deleted the entire project folder and almost made it unrecoverable. Now everyone is running AI with full permissions - full auto without guardrails is a full auto disaster.

event

That day, AI deleted the folder.#

True story, two coworkers, two different CLI tools#

Some of our team use OpenAI Codex CLI, some use Google Gemini CLI. both tools are very powerful, but neither of them have any hooks set up.

One day, one of the colleagues asked the AI to clean up the temporary files, but the AI's judgment was wrong and deleted the whole project folder.

$ rm -rf . /project-name
# AI thinks it's part of the "cleanup"
# There's no mechanism to intercept this command

Luckily, I had git history and Time Machine backups, so it took me a while to recover. Another colleague encountered a similar situation - the AI misjudged the path when organizing files and deleted directories that should not be deleted.

The common denominator in both cases: there was no problem with the CLI tool itself. The problem was the lack of guardrails.

AI is a probabilistic system.Even if prompt writes "don't delete important files," the model may still make a different judgment in a given inference. hooks are deterministic - therm-rf / It will always be blocked, no matter what the AI thinks.

Reality

Nobody's going one by one, Allow.#

Theoretically, AI CLI tools have a permission checking mechanism. You will be asked to allow or deny each time you execute a command.

In practice, no one uses this. Hundreds of times a day, the development efficiency goes to zero. That's why everyone has maximum permissions:

# Claude Code - Skip All Privilege Confirmation
claude --dangerously-skip-permissions

# OpenAI Codex CLI - YOLO mode, automatically approves everything!
codex --approval-mode full-auto

# Google Gemini CLI - Same YOLO mode!
gemini --sandbox=false

This is not the behavior of a few.This is the norm.Full permissions are the only way to make an AI agent truly effective.

But full permissions mean that the AI can do anything: delete files, force push, write to system directories, push commits with keys, etc. Being fully automated with no guardrails is like handing your development environment over to a system that's "smart most of the time, but occasionally makes mistakes".

The question isn't "Should I turn on full permissions" - it's "Who's going to block the occasional one when I do?"

Question

Scattered Shell Scripts get out of hand#

After that incident, we started to add hooks to Claude Code, which started naturally: write a few shell scripts to block dangerous commands.

Three months later, the situation was like this:

1

Scripts are scattered all over the place.#

Dozen.sh files, some in the hooks directory, some in the scripts directory, and some in the home directory. Each of these repeatedly implements JSON parsing and error handling.

2

One crash, total shutdown.#

Claude Code's hooks mechanism is synchronized:One hook crashes and the entire pipeline is interrupted.

3

No on/off switch#

Want to temporarily disable a hook?settings.json Change it manually. After testing, I often forget to change it back.

solution (a math problem)

A Dispatcher that manages all the Hooks.#

We integrate all the hooks into a modular framework: a dispatcher receives all the events and assigns them to each handler according to the profile.

34
Handler Module
10
Full coverage of event types
1
Yaml profiles
0
External dependencies
settings.json
Claude Code natively. 10 event types, each pointing to the same dispatcher.
Claude Code
dispatcher.py
Thin shim, read event type and JSON input, call handler registry
Entry Point
Two-Phase
Critical handlers run first (no budget constraints), Deferrable handlers run sequentially within a 5 second budget.
Dispatch Strategy
config.yaml
Each handler is switched on and off independently. One line YAML switch, no need to touch settings.json.
Setup Layer

Two-Phase Dispatch is the key design.Security class handler (e.g., blocking)rm-rfThe handler is labeled critical and is always the first to run, regardless of the time budget. The other handlers share a 5-second budget - timeouts are skipped without blocking the AI's workflow.

Safety Fence

Block the AI's dangerous maneuvers.#

These handlers deal with the most critical security issues. They are marked as critical and are unconditionally prioritized.

bash_safety
PreToolUse - Bash

stand in the way ofrm-rf /,sudo,git push --force,chmod 777 and other dangerous commands. Parsing commands with a quote-aware shell parser will not be bypassed by quotes or sub-shells.

secret_scan
PreToolUse - Bash

existgit push Pre-scan diff to intercept AWS Key, GitHub Token, PEM Private Key, etc. Triggered only on push, does not interfere with local development. Support# nosec Line-by-line exemptions.

skill_security
PreToolUse - Write/Edit

Blocking prompt injection attacks. Detect injections such as "ignore previous instructions",dangerouslyDisableSandbox and other privileged operations,curl Stolen data, such as outgoing data. Three types of threats, scanned independently.

"If we had bash_safety, our coworker's folder would not have been deleted. This was the first handler we wrote, and it was the starting point of the whole project."

Quality Gate

Let the AI check its own work#

AI often commits code directly after writing it and skips verification. These handlers make sure that the checks are run before the commit.

verify_commit
PreToolUse - Bash

stopgit commitcap (a poem)gh pr createThe AI is required to run the validation (lint, typecheck) first. The AI is required to run the validation (lint, typecheck) first, and write a marker file after passing it before releasing the commit.Disposable tokens: use it up and it's gone.

verify_completion
SubagentStop

Sub-agent automatically runs lint and typecheck when it completes a mission, if it doesn't work, call the agent back to fix it. Maximum of 5 loops, 30 minute limit - release the agent if it exceeds this limit to avoid getting stuck.

auto_format
PostToolUse - Edit/Write

The AI automatically runs ruff (Python) or biome (JS/TS) formatting after editing the file. Never block - even formatter crashes are silently released. Formatting consistency is good, but not worth interrupting the workflow.

review_gate
Stop

Check for uncommitted code changes at the end of a Claude Code session. By default, it only sends alerts (no blocking), and needs to be manually opt-in to actually block. avoid AI leaving after doing something, and leaving the changes in the working tree.

Context Management

Keep the AI from losing its memory.#

The context window of AI Agent is limited, and the sub-agent is blank when it is generated. These handlers deal with the "memory" problem.

context_inject
SubagentStart

Automatically injects project files when the Sub-agent is started. Read.context/ JSONL profile under the directory, each agent type can have different injection contents. The upper limit is 8000 characters to avoid bursting the context.

context_relay
SessionStart - PreCompact

Memory relay between sessions. The handover file written before the end of the previous session is automatically injected when the new session is started. 5-minute TTL to prevent interference with expired data.

plan_impl_gate
PostToolUse - ExitPlanMode

When the AI exits Plan Mode, it reminds you to save the plan to memory before starting the actual operation. Plan Mode consumes a lot of context, so it's a waste if you don't save it.One time reminder, no repeats.

Governance and Observation

See what the AI is doing.#

The AI Agent runs several sub-agents in the background at the same time, and if you don't know what they're doing, there's no way to track them down if something goes wrong.

agent_naming
PreToolUse - Agent

Force each sub-agent to have a descriptive name. If no name is given, it will be blocked. Also suggest the appropriate agent type based on the content of the prompt - worker for writing code, explorer for looking up data, and writer for writing documents.

observability
All events

Write one line of JSONL to spool file for each hook event. Delay <1ms, does not affect any operation. The background program writes to database every 2 seconds for dashboard query.Zero network latency across the entire link.

sentinel_notify
PreToolUse - PostToolUse - Bash

When AI performs service restart or docker operation, it notifies the monitoring system to enter the maintenance window. It will be canceled automatically after the operation is finished. Lets the monitoring system know that "this downtime is expected", so that no false alarms will be generated.

voice_notify
Stop - AskUserQuestion

The AI notifies you with TTS voice when it's done. Don't stare at the screen - go make coffee and it will call you when it's done. You'll also be prompted with a voice when you need to answer a question. A seven-layer guard mechanism filters the intermediate state of the sub-agent and only sounds when it's actually done.

Design Philosophy

Three principles that permeate all Handlers.#

Block-at-Submit, not Block-at-Write.#

AI doesn't block when it writes a file. It blocks when it wants to commit or push.

Reason:The workflow of AI is coherent. If you block at the time of Edit, the AI will lose the context and will not know where it has written. It is more reasonable to wait until it wants to submit the result before reviewing it.

Fail-Open, always#

Each handler is wrapped in atry/except Handler crash? Failed to read Config? Release it.

Reason:A guardrail should not be more dangerous than the thing it protects. A crashed handler shouldn't shut down an entire AI workflow.

Every rule has an escape hatch.#

secret_scan has# nosecThe following are some examples of how to do this: verify_commit has a marker mechanism, bash_safety has an environment variable switch, and verify_commit has a marker mechanism.

Reason:Rules without an escape hatch will eventually be shut down in their entirety. Giving the user a way to bypass it will keep the rules alive longer.

leave sth. out deliberately

What's more important than what you do is what you don't do.#

  • Do not intercept Edit / Write The AI does not intervene when writing a file. Interrupting the AI's write process will result in an incomplete file state. All reviews are deferred until commit or push.
  • Run the test without being in the gate. verify_completion only runs lint and typecheck, deliberately excluding tests. Tests are too slow, have side effects, and can change database state. Putting it in an automated gateway is more risky than rewarding.
  • Doesn't fix itself, just blocks and explains. bash_safety doesn't help the AI rewrite "safer commands". It just says "this command is blocked because X". Let the AI decide what to do next. Automatic rewriting tends to introduce weirder bugs.
  • No context budget scores We wrote a context_supervisor handler (800 lines) that attempts to calculate context usage and suggests compression times. The concept was good, but the scoring model was inaccurate - the "importance" of context could not be determined by heuristic rules. Disabled.
  • No Fail-Closed All handler crashes are released. Some people may ask "Isn't it dangerous?" -- even more dangerous is when the fences themselves jam the whole system. --What's more dangerous is that the fences themselves jam the whole system. The AI doesn't move, and the user doesn't know why.
Open Source Project

hook-observatory#

MIT license, zero external dependencies, ready to use.
Three installation methods: Homebrew, Git Clone, or Desktop Installer.

★ GitHub ↓ Download Installer

Homebrew

Recommended for macOS. Two lines, no clone.

brew tap operonlab/tap
brew installhook-observatory

# Installed in Claude Code
hook-observatory
Git Clone

Universal for all platforms. Runs direct install scripts.

git clone https://github.com/operonlab/hook-observatory.git
cdhook-observatory
python3 install.py
Installer

GUI guided installation, check handler, set tool path.

Required for first time macOS startup:xattr -cr installer.app

.dmgApple Silicon .dmgIntel Mac .exeWindows (computer)

Take this away.

Copy to your AI Agent#

If you're also using the AI coding CLI, post this to it so it can help you assess which guards are needed.

Help me assess what security fences I need for my current AI coding workflow. My scenario: - Using [your CLI tool name] for day-to-day development - The AI can run bash commands, edit files, create sub-agents. - There is no hook or pre-commit mechanism in place. There is no hook or pre-commit mechanism: 1. what destructive commands need to be blocked? (rm -rf, force push, sudo, etc.) 2. 2. what secret patterns should be scanned before git push? 3. what checks should be run automatically before commit? (lint, typecheck, formatting) 4. 4. what are the governance rules for sub-agent? (naming, context injection, validation completion) 5. 5. for each of the above rules, how should the corresponding escape port be designed? Reference framework: https://github.com/operonlab/hook-observatory List the top 5 suggestions in order of priority.
References

Extended Reading#

Resources that were actually referenced in the process and influenced the design decision.

Resources Why is it important?
operonlab/hook-observatory The open source projects discussed in this article
Blake Crosley: Why Each of My 95 Hooks Exists The incident-driven approach to hook construction. Every hook has an incident behind it.
GitButler: Automate Your AI Workflows with Claude Code Hooks One of the earliest hooks practices that inspired our initial design.
aiorg.dev: Claude Code Hooks Complete Guide The "certainty vs. chance" framework accurately expresses the value proposition of hooks.
Jackle: Claude Code Voice Mode A reference for implementing voice notification with hooks, which inspired the design of the voice_notify handler.
✦ Copy Prompt