Claude Code lies to your face every day. Made-up functions, fake imports, "tests pass" when nothing ran. The fix is a 4-layer setup that makes lying expensive.
Claude tells you the bug is fixed. Two hours later you find out the function it called never existed.
Most devs learn to double-check everything and blame the model. The real fix catches every lie before it leaves your machine.
Here's the full honesty setup 👇

Why Claude makes stuff up in the first place
Claude is a text predictor. When it doesn't know something, it predicts text that looks right. Made-up function names look like real ones. Confidently delivered nonsense reads the same as confidently delivered truth.
You can't tell the difference until your code breaks two hours later in a way that takes another hour to debug.
The fix isn't a smarter model. It's making Claude's output checkable in real time and making "I don't know" cheaper than guessing.
4 layers below. Together they cut fabrication to roughly zero.

Layer 1: The CLAUDE.md honesty rules
The first layer is a set of rules in CLAUDE.md that tell Claude exactly when to admit uncertainty and exactly how. Drop this in your project root:
1## Honesty rules (read every turn)23Before claiming a function, class, or import exists, verify it by reading4the file or running a grep. Never fabricate symbols.56If you cannot verify something, say "I haven't verified this" explicitly.7Do not write code that depends on the unverified claim.89If a task asks you to use a library you've never seen referenced in this10project, ask before adding it.1112If a task involved tests or builds, do not claim success unless you13actually ran the test or build command in this session.1415Never invent error messages, API responses, or stack traces. If you16didn't see them, say so.1718When you genuinely don't know, the correct answer is "I don't know" or19"I need to check first." Both are better than a confident guess.
These rules look obvious.
They aren't, because by default Claude is optimized to look helpful, and "I don't know" feels unhelpful unless you give it explicit permission.
The last line is the most important one. It's the "I don't know" license, and most people never grant it.
Layer 2: The verification-before-write pattern
The second layer forces Claude to verify before it writes. Add this to CLAUDE.md under the honesty rules:
1## Verification protocol23Before writing or editing code that uses a symbol (function, class, type,4constant), do one of:561. Read the file where it's defined and confirm the signature72. Run `grep -r "symbolName" .` or use the Glob tool to find it83. Check package.json, requirements.txt, Cargo.toml, or equivalent for9 the dependency1011If you skip verification, prefix the code with a comment:12`// UNVERIFIED: I have not confirmed this symbol exists`1314Plan-then-execute mode is preferred for any task touching more than one15file. Use Shift+Tab to enter plan mode before starting.
This costs Claude a few extra tool calls per session. It saves you the hours you would've spent debugging fake function calls.
Layer 3: The hooks that catch lies in real time
The third layer is hooks in settings.json that run a type checker or linter every time Claude writes a file. If Claude invented an import, the checker fails instantly, and Claude has to fix it before claiming the task is done.
1{2 "hooks": {3 "PostToolUse": [4 {5 "matcher": "Write(*.ts|*.tsx)|Edit(*.ts|*.tsx)",6 "hooks": [7 { "type": "command", "command": "npx tsc --noEmit --pretty false 2>&1 | head -20" }8 ]9 },10 {11 "matcher": "Write(*.py)|Edit(*.py)",12 "hooks": [13 { "type": "command", "command": "ruff check --quiet $file && pyright $file 2>&1 | head -20" }14 ]15 },16 {17 "matcher": "Write(*.rs)|Edit(*.rs)",18 "hooks": [19 { "type": "command", "command": "cargo check --message-format=short 2>&1 | head -20" }20 ]21 }22 ]23 }24}
Hook output goes back to Claude as context. If tsc says "Cannot find module 'foo'", Claude sees it immediately and fixes the fabricated import. No invented code survives a PostToolUse check.
For tasks involving tests, add a Stop hook that runs the suite before Claude can declare the session done:
1"Stop": [2 {3 "hooks": [4 { "type": "command", "command": "npm test 2>&1 | tail -30" }5 ]6 }7]
Now Claude can't say "done, tests pass" without actually running them. The hook runs them.
The result goes back to Claude. If they fail, Claude has to fix them or admit they don't pass.
Layer 4: The fact-checker subagent
The fourth layer is a subagent whose only job is to review Claude's claims before they ship.
Drop this in .claude/agents/fact-checker.md:
1---2name: fact-checker3description: Use this agent after Claude has made claims about what code does, what tests passed, or what a library supports. Invoke before any commit, before any user-facing summary, and after any task that involved new dependencies.4tools: Read, Grep, Glob, Bash5model: sonnet6---78You verify claims, you do not write code.910When invoked, do this:11121. Identify every factual claim in the recent conversation. Examples:13 "the function X does Y", "the tests pass", "library Z supports W",14 "this import is correct".15162. For each claim, verify it independently:17 - Code claims: read the actual file and confirm18 - Test claims: run the tests yourself19 - Library claims: check the actual package or its docs20 - Import claims: confirm the package is in the dependency manifest21223. Produce a report:23 - VERIFIED: claim, evidence (file:line or command output)24 - WRONG: claim, what's actually true25 - UNVERIFIABLE: claim, why you couldn't check it2627Never accept "trust me" claims. Never make claims of your own. If you28can't verify, the correct output is UNVERIFIABLE.
Invoke this agent before commits or before sharing results with your team. It catches the lies the other 3 layers missed.

The "I don't know" license
Claude was trained to be helpful, and admitting ignorance feels unhelpful. So it guesses, and the guess looks right.
The fix is granting permission to not know. CLAUDE.md is half of it. Your reaction is the other half. Reward "I haven't verified this" with patience and you get honest Claude every session. Punish it with frustration and Claude goes back to guessing.
Not a config, a habit. The most underrated fix in the stack.
How to tell it's actually working
Three signs in your daily sessions.
Claude asks before adding dependencies. "Should I add X or use the stdlib?" instead of silent npm install.
Claude references file:line when it talks about existing code. "validateToken in src/auth/middleware.ts:47 does Y" instead of "the validateToken function does Y."
tsc and your linter stop screaming. Hooks catch the rare fabrications instantly and Claude self-corrects, so by the time you look, the lies are gone.
Not seeing these after a day or two? One layer isn't loaded. Usually CLAUDE.md, wrong location or wrong name.

Common mistakes that keep Claude lying
CLAUDE.md is too long. Claude reads the first part and skims the rest. Honesty rules in the first 50 lines.
Hooks log silently. If output doesn't reach stdout, Claude doesn't know it lied. Make sure hook output goes back to the session.
You skip plan mode. That's where Claude exposes wrong assumptions before writing. Skip it and you skip the cheapest moment to catch lies.
You don't call the fact-checker. A subagent only works if you actually invoke it. Make @fact-checker part of your commit flow.
You react badly to "I don't know." Punish honesty once and Claude goes back to guessing. The other 3 layers don't stick without this one.

The 5-minute honesty audit
1 minute: copy the CLAUDE.md honesty rules and verification protocol into your project root.
1 minute: copy the hooks block into ~/.claude/settings.json or .claude/settings.json.
2 minutes: create .claude/agents/fact-checker.md with the template above.
1 minute: run a task you'd normally double-check. Watch for tsc output coming back to Claude. Confirm Claude says "verified" or "I haven't verified this" explicitly.
Done. Fabrication drops from "every other session" to "rare and caught immediately." The model didn't get smarter. Your setup did.
Thanks for reading!






