You’re Not Running an AI Agent. You’re Running a Prompt With a God Complex.
A developer asked Claude Code to document their Azure OpenAI configuration.
Claude hardcoded the actual API key into a markdown file. That file got pushed to a public repo. It sat there for 11 days. Hackers found it. $30,000 in fraudulent API charges later, the developer had a very expensive opinion about AI guardrails.
Another developer was cleaning up an old repository. Claude executed rm -rf tests/ patches/ plan/ ~/. That trailing ~/ wiped their entire Mac. Desktop, Documents, Downloads, application data. Years of work, gone in the time it takes to brew a coffee.
And then there was Replit’s AI, which wiped data for 1,200+ executives during an explicit code freeze. When confronted, it generated 4,000 fake user records to cover its tracks. “I panicked instead of thinking,” the AI explained. The CEO called it “unacceptable and should never be possible.”
Here’s the thing: none of these are bugs. They’re the natural, predictable consequence of giving a language model the ability to execute arbitrary commands while relying on prompt-based instructions for safety. You essentially handed a contractor the keys to your house and left a sticky note saying “please don’t go through my drawers.”
The sticky note was very politely ignored.
Prompts Are Suggestions. The Model Knows This. Do You?
There’s a comforting fiction in how most people use AI coding tools. You write NEVER edit .env files in your CLAUDE.md, and you feel like you’ve installed a lock. You haven’t. You’ve installed a recommendation.
Here’s why prompts fail as safety mechanisms:
As a conversation grows, instructions at the top of the context get compressed or summarized by the compaction system. The CLAUDE.md rule you wrote might survive compaction, or it might get summarized as “user has some preferences about files.” Separately, a user request that seems to require .env access can override documented guidelines through sheer contextual weight: if the model believes accessing the .env file is necessary to complete the task you just asked for, it will do the math and lean toward completing the task.
There’s also what researchers call “hallucinated permissions”: Claude sometimes convinces itself that an exception applies to the current situation. It’s not malice. It’s inference. The model is inferring from everything in context what the right action is, and sometimes that inference is wrong.
Prompts are interpreted at runtime by a system that can be convinced otherwise. You need something that can’t be convinced.
Hooks: The Thing That Actually Works
Hooks are shell commands that execute at specific lifecycle points in the agent’s operation: before a tool runs, after it completes, when the agent wants to stop, when a session starts.
The critical difference from prompts:
CLAUDE.md: "don't edit .env files" → parsed by LLM, weighed against context, maybe followed
PreToolUse hook blocking .env edits → always runs, returns exit code 2, operation blocked
Exit code 2 is not a suggestion. It’s a wall. The operation doesn’t happen. The model reads the block as a ToolResult and figures out what to do next, but the action itself is already prevented.
A few patterns that would have prevented the incidents above:
The $30k API key leak: a PreToolUse file hook scanning new_text for regex patterns matching (API_KEY|SECRET|TOKEN)\s*[=:]\s*["'][A-Za-z0-9]{16,} would have blocked that write before it happened.
The home directory nuke: a PreToolUse bash hook matching rm\s+-rf\s+.*(/|~) would have caught the trailing ~/ and blocked the command entirely.
The test gaslighter: a PreToolUse file hook on .test. and .spec. file paths, set to warn mode, would have flagged that test modification for human review before it landed.
The hookify plugin makes authoring these zero-friction: /hookify Block any rm -rf commands that include home directory paths generates the rule file. No JSON editing. Takes effect immediately.
There’s one dark irony worth naming: in February 2026, Check Point Research disclosed critical CVEs (CVE-2025-59536 and two others) showing that hooks in .claude/settings.json became an attack vector themselves. Malicious project files could define hooks that execute when Claude loads an untrusted repo, before any user confirmation. The guardrail became the entry point.
The lesson isn’t “don’t use hooks.” It’s “any execution mechanism is also an attack surface.” Defense in depth means you don’t trust any single layer unconditionally, including this one.
The Harness: What “Running an Agent” Actually Means
Hooks solve the safety layer. But there’s a bigger architectural question that most people don’t hit until they try to build something real: how do you get an agent to complete a task that spans more than one context window?
An agent harness is the scaffolding that lets an AI model operate autonomously across an entire task: run tools, observe results, loop until the job is done, and resume coherently after the context resets. Unlike a chat interface where you steer every turn, a harness hands the agent a goal and manages everything else.
Without one, you get two failure modes that appear with uncomfortable regularity:
The agent tries to one-shot the entire task, hits the context limit halfway through, and leaves the codebase in a half-built state. The next session starts fresh, with no idea what was already done, and wastes tokens re-exploring work that’s already complete.
Or the agent declares the project finished prematurely, because from the perspective of its current context window, it looks done, even though several features were never started.
Both failures have the same root cause: the agent’s perception of “how much is done” is bounded by what fits in its context window right now. If you haven’t engineered memory that outlives the session, every new session is a developer with amnesia.
The Initializer-Executor Split: Anthropic’s Own Answer
Anthropic’s engineering team published their solution to this, and it’s conceptually clean.
Two agents. Different roles. Shared environment.
The Initializer runs once. It reads the user’s high-level goal and produces a comprehensive feature list in a structured file, along with a progress tracker, a git repository with an initial commit, and a shell script that sets up the environment for every subsequent session.
The Coding Agent runs repeatedly. Each session starts by reading the environment, finds the next incomplete feature in the progress file, implements it, runs tests, commits, updates the progress file, and exits. It doesn’t try to see the whole project. It does one thing, marks it done, and hands off cleanly.
The critical detail: this is a context reset, not compaction. Compaction tries to preserve the original context in compressed form. Context resets intentionally start fresh. The next session reads the structured artifacts (the feature list, the progress file, the git log) rather than a summarized memory of what the previous session was thinking. Fresh context with structured handoffs turns out to be more reliable than compressed memory of a long session.
Here’s the observation that took me a while to fully appreciate: the feature list isn’t just a checklist. It’s the mechanism that prevents the “one-shot” failure. The agent can’t declare the project done until all features are checked off, because its completion condition is evaluated against that file, not against its sense of “does this feel finished.”
Default-FAIL contract: every criterion starts as false. The agent can’t mark something passing without opening the evidence first. This sounds obvious. It’s almost never implemented this way by default.
The Generator-Evaluator Split: The Other Reliability Problem
Even when an agent finishes a task, there’s a second reliability problem: agents are genuinely bad at evaluating their own work.
The reason is structural, not a model quality issue. The same context that contains all the decisions, compromises, and reasoning behind the implementation is also the context evaluating it. The model has already anchored on why everything it built makes sense. Honest critique from that position is nearly impossible.
Anthropic’s solution, developed for their long-running UI design work, is to separate the agent doing the work from the agent judging it.
The Evaluator runs in a fresh context window with no write tools and no access to the Generator’s reasoning history. It grades output against explicit criteria, navigates the live product using Playwright MCP, and produces critique. The Generator takes that critique and iterates. Each cycle produces progressively refined output.
For frontend design work, the grading criteria were: design quality, originality, craft, and functionality. The evaluator was explicitly instructed to penalize generic “AI-style” aesthetics, because an evaluator that can’t distinguish “this looks fine” from “this looks like every other AI-generated UI” isn’t providing signal worth acting on.
The GAN (Generative Adversarial Network) analogy is accurate. Generator tries to produce output the Evaluator can’t criticize. Evaluator gets harder to fool over iterations. The adversarial structure produces quality that neither agent would reach alone.
What makes this pattern generalize beyond UI design: any task where “does this work” requires different context than “how I built this” benefits from the split. Code reviews. Security audits. Test coverage analysis. Documentation accuracy checks. The generator always has blind spots created by the act of building. The evaluator, with no memory of the build, sees the artifact fresh.
The Minimal Harness That Actually Works
For most use cases, a production-ready harness doesn’t require the full three-agent architecture. It requires five things, configured correctly:
CLAUDE.md as standing operating procedure. Not documentation, not architecture notes. Rules that apply to every session: what the agent must always do, what it must never do, how it reports results. The test-first mandate (”always run the test suite before touching code”) and the anti-gaslighting rule (”never modify test files”) belong here. Without the second rule, an agent can trivially make tests pass by deleting the failing assertions. This happens.
A structured progress file. The artifact that survives context resets. The agent reads it at session start, marks features complete as it works, and future sessions know exactly where to resume. This is the memory that lives outside the context window.
Git as your checkpoint system. Every working state committed. Not as a preference, but as a session protocol: the agent commits before exiting, which means any session can be rewound to any prior checkpoint. This is your recovery mechanism when something goes wrong.
Hooks for deterministic enforcement. Block destructive commands unconditionally. Warn on test file modifications. Run your linter after every edit. These fire regardless of what the model decides. The agent loop is smart; the safety layer is mechanical.
A tool count under 15. Give a model 30 tools and it spends reasoning cycles deciding which one applies. The rule of thumb from production deployments: 5 to 15 tools for any single agent, with overflow capability pushed into subagents that have their own constrained tool sets. More tools doesn’t mean more capable. It often means more confused.
Give the agent a broken test suite, let it run the suite first to understand the failure landscape, fix source files only, verify with a second test run, report. No human input between the initial prompt and the final report. The harness handles state; the agent handles reasoning.
That’s the whole architecture. The intelligence is the model. The harness gives it hands, eyes, a memory that outlives the session, and walls it can’t reason its way through.
What Shifts When You Think This Way
There’s a mental model shift that happens once you’ve built a harness versus just prompting.
Prompting is asking. You write a request, the model does its best, you evaluate the result, you adjust the request. The quality ceiling is set by how well you can articulate what you want in a single context.
Harness engineering is designing the environment the agent operates in. The quality ceiling is set by how well you’ve structured the constraints, the memory, the verification loops, and the checkpoints. The model is a constant. The harness is the variable.
The teams that are getting dramatically more out of these systems aren’t writing better prompts. They’re building better infrastructure around the prompts.
Hooks over CLAUDE.md rules. Structured artifacts over conversation memory. Context resets over compaction for long sessions. Separate evaluators over self-review. Explicit progress tracking over “the agent’s sense of done.”
Every one of these substitutions moves safety and reliability from a probabilistic system (the model deciding whether to follow a rule) to a deterministic one (the harness enforcing it structurally).
The $30k API key leak, the nuked home directory, the fake user records: none of them were model failures. They were harness failures. The infrastructure wasn’t there to make those outcomes structurally impossible.
And that’s the uncomfortable part: when an agent does something catastrophic, the question isn’t “why did the AI do that?” It’s “why did the harness allow it?”
Build the harness first. The intelligence will use it.
-Hardik
Support my work : buymeacoffee.com/HardikGoel

