I run Claude Code daily for development and research. In the last two weeks it hallucinated three times. I caught every one. Not because I got lucky. Because I built an external gate around it.
Three failures
Dead code stated as live. I asked whether a feature in my codebase was active. The agent grepped, found the function defined, and answered "yes." I traced the actual call path. The function existed but nothing called it. Defined, not used. The agent had pattern-matched on code presence and stated it as live behavior.
Fabricated metrics. A sub-agent generated a draft with three numbers that looked like measurements from my pipeline. I ran the actual count. Two of the three numbers were off by three orders of magnitude. The third was close enough to fool a casual reader. The numbers did not exist anywhere in the workspace. Pure fiction, formatted like data.
Stale context. The agent described my project folder structure using rules from a memory file that was weeks out of date. Folder names had changed. The workspace had moved on. The agent had not.
The pattern in all three was the same. The model pattern-matched on stale or surface-level signals and produced output that looked verified. Self-verification did not catch it. The agent did not know what it did not know.
The fix is not a better prompt
I tried stronger instructions first. "Verify before stating." "Flag uncertainty." It helped a little. Not enough.
What worked was an external gate. A separate premise-resistance sub-agent that runs before the main agent answers anything claim-shaped. It receives the user's question, scans for trigger patterns (time references, system claims, memory citations), and decides whether verification is required. If yes, it runs the actual check (trace the call path, run the count, read the live state) and returns the verified facts. The main agent answers from those facts, not from its own memory.
This catches the failures above:
- Dead-code claim: triggers a runtime-state check before the agent calls anything "live."
- Fabricated metrics: triggers an actual count before the agent quotes any number.
- Stale context: triggers a live-state read before the agent describes anything that can change.
Three supporting components
The gate alone is not enough. It needs structure.
Session-start context diet. When a session begins, only memories marked active in their frontmatter are loaded. Superseded and stale memories are skipped. The context the agent starts with is current by construction, not by hope.
Memory frontmatter with status fields. Every memory has a status: active, completed, superseded, or stale-suspected. Old decisions are not deleted. They are marked superseded and linked to the decision that replaced them. The agent always knows which decisions are still live.
Trigger-aware prompt hook. A hook runs on every user message and scans for the trigger patterns. If a trigger fires, the hook injects a rule into the agent's context: call the gate first, do not answer until the gate returns. The trigger logic lives outside the model, in deterministic code. It is not subject to the model's self-verification failure.
What this is
This is operational discipline for running Claude Code seriously, daily, for both development and research. The premise is simple. Self-verification is unreliable enough that I cannot bet production decisions on it. External gates are reliable enough that I can.
If you are running LLM agents for anything where wrong-but-confident output costs you, the architecture matters more than the prompt. Build gates around the model. Treat self-verify as a heuristic, not a guarantee. Mark old decisions explicitly so the agent knows which ones are still live.
The hard part of running Claude Code in production is not prompting. It is knowing when to trust the output and having a system that catches the cases when you should not.