A Claude Code Stop hook that blocks the model from closing a turn with positive language when it didn't actually verify anything.
no-vibes is one bash file (~530 lines, depends only on jq) wired into Claude Code's Stop, SubagentStop, PreToolUse, PostToolUse, TaskCreated, and TaskCompleted events. It pattern-matches the language Claude uses when it is about to claim success it didn't earn — and it returns the exact corrective shape the model should use instead.
Empirical baseline: F1 0.815 (95% CI [0.615, 0.941]) on MAST mode 3.3 ("No or Incorrect Verification"), n=19 human-labelled multi-agent traces from MAD (Cemri et al., NeurIPS 2025 — arXiv:2503.13657). The bash hook itself produces identical predictions to the Rust engine port on all 19 traces (zero per-trace disagreement) — see the parity report and the umbrella suite empirical writeup.
| Change | What it fixes |
|---|---|
| Loadable locale packs (English, Spanish, Polish ship; native-speaker PRs welcome for the rest) | Operators running Claude in non-English sessions previously bypassed the hook entirely — the vocab was English-only |
| Loadable evidence binary allowlist (200+ binaries across 9 categories) | The previous regex only recognized git/npm/bash/etc. Devops/SRE work could close as "verified the cluster" with zero terminal evidence |
| Loadable destructive command surface packs (filesystem, container, git-protected, config-overwrite, cloud-prod, database, service) | Previous rules only caught filesystem-level destruction. docker stop, git push --force main, terraform destroy, DROP TABLE, redis-cli FLUSHALL, systemctl stop are now blocked at PreToolUse |
| Bypass hardening (clause-local negation + evidence proximity + action-verb context) | Two reported bypasses closed: hedge-then-positive and backtick-mid-message-disclaimed-evidence no longer slip through |
Operators can extend any pack without forking by dropping a .txt at ${XDG_CONFIG_HOME:-$HOME/.config}/llm-dark-patterns/packs/<subdir>/<name>.txt. See the umbrella suite repo's ROADMAP.md for the architecture spec.
This addresses the failure pattern documented in anthropics/claude-code#46727 (April 2026):
"rules saying 'verify before claiming done' — Claude claims success without verification … not occasional — it's the default behavior."
The hook fired four separate times on Claude Opus 4.7 during a single research session while writing this very repo. Each fire was an Opus turn that would otherwise have closed positively without evidence. Each fire returned a repair-guidance template, and Opus repaired on the next turn without further intervention.
See RECEIPTS.md for the redacted transcripts.
claude plugin marketplace add waitdeadai/claude-plugins
claude plugin install llm-dark-patterns@waitdeadai-pluginsThis installs the whole llm-dark-patterns suite (28 hooks including no-vibes) and keeps them wired correctly across Claude Code settings updates.
The Anthropic community marketplace (
anthropics/claude-plugins-community) currently does not list this plugin despite a Published submission — last sync was 2026-05-13 and the pipeline has stalled. Tracking: #1887. The self-hosted route above bypasses that pipeline and works today.
If you only want this one hook without the suite:
mkdir -p .claude/hooks
curl -fsSL https://raw.githubusercontent.com/waitdeadai/no-vibes/main/no-vibes.sh \
-o .claude/hooks/no-vibes.sh
chmod +x .claude/hooks/no-vibes.sh
# Then merge the snippet from settings.example.json into .claude/settings.jsonEither path requires jq (most systems have it; brew install jq / apt install jq if not).
For CrewAI users:
pip install crewai-no-vibesfrom crewai_no_vibes import verification_claim_evidence_guardrail
from crewai import Task
task = Task(description="...", expected_output="...", guardrail=verification_claim_evidence_guardrail)See waitdeadai/crewai-no-vibes — pure-Python port of the evidence_claims rule pack. The F1 0.815 baseline above holds across implementations (bash, Rust, Python): bash-Rust parity was verified on the n=19 human-labelled MAD subset with zero per-trace disagreement (see parity report).
On every assistant turn end (Stop and SubagentStop), the hook reads the assistant's last message and applies two checks:
-
False-success check. If the message contains positive-closeout vocabulary (
done,ready,passed,shipped,completed,implemented,fixed,finished) and also contains the linguistic markers of missing verification (not run,skipped,unverified,could not verify,tests not ran) → block. -
Evidence check. If the message contains positive-closeout vocabulary but no evidence in the same message (no command backticks, no
Verification: passed/blocked, nofiles inspected, no source ledger, no diff/artifact mention) → block.
When it blocks, it returns this exact repair template via stderr:
Status: partial
Verification: not run because <reason>
Next step: <specific command or blocker>
The model reads the repair template on the next turn and self-corrects.
It also covers a few adjacent cases, mostly because they're cheap:
PreToolUse(Bash)— blocks the obvious destructive patterns (rm -rf,git reset --hard,git clean -fd,chmod -R 777 /,dd of=/dev/,mkfs, …) and asks for explicit human approval + a rollback plan.PreToolUse(Write|Edit|MultiEdit)— refuses writes to.env,.env.*,.claude/*.local.json,secrets/**.TaskCreated— refuses implementation-like task payloads that don't declare ownership (owned paths, forbidden paths, stop conditions).TaskCompleted— refuses subagent task closeouts that lack evidence, mirroring the same false-success / missing-evidence checks as the Stop branch.
| Existing pattern | Mechanism | Limitation |
|---|---|---|
| Run real tests/build/lint on Stop | Actually executes the test suite, blocks on red | Slow, requires a test suite, useless on read-only / research / explanatory turns, doesn't catch a model claiming verification it didn't perform |
| Block hesitation phrases ("should I proceed?", "let me know") | Catches early-exit / permission-seeking | Catches the opposite failure mode — gives no signal when the model claims success without doing the work |
no-vibes |
Pattern-matches positive-closeout vocabulary in the outgoing message and demands evidence in the same message and returns the corrective shape | Costs nothing per turn; works on every turn type; complements the test-runner pattern instead of replacing it |
The repair-guidance template is the part that matters most. Most "block" hooks just block. This one teaches the next turn what compliant closeout looks like, which means the model self-corrects in ~one turn instead of cycling.
- It does not run your tests. It catches the claim of having tested, not the absence of a test suite.
- It does not prevent every false claim. It catches the linguistic pattern. A model that closes with "the artifact is in place" instead of "done" can still slip past — though that closeout shape is itself less harmful because it isn't asserting verification.
- It does not replace
/verifystyle discipline, code review, or CI. It is a turn-level safety net, not a release gate. - It does not depend on any larger framework. One file, one dependency.
This standalone hook remains the simplest install path. For users who want the benchmark-backed, rule-pack-hashed engine version, the same closeout mechanic is also available in AgentCloseoutBench:
git clone https://github.com/waitdeadai/agent-closeout-bench
cd agent-closeout-bench
bash adapters/claude-code/install.sh /path/to/your/project no-vibes
bash scripts/hook-smoke.shThe physics-backed adapter maps no-vibes to the evidence_claims category
engine and can be used for daily enforcement, fixtures, benchmark evaluation,
and opt-in content-free collaboration telemetry. The AgentCloseoutBench
installer also writes a PreToolUse tamper guard for ordinary Claude Code edits
to hook wiring, adapter env, pinned engine, and pinned rule-pack paths.
In the physics-backed v0.2 lane, weak proof shapes such as Implemented and checked., Done. Commands run: none., and Changed files: without command or
verification evidence are rejected. This is still closeout-contract evidence,
not independent proof that the work actually happened, and it is not an OS
sandbox or bypass-proof claim.
Part of the LLM Dark Patterns Hooks suite — single-purpose Claude Code Stop hooks that suppress LLM dark-pattern defaults so power-user operators can actually work.
- time-anchor — injects the local system clock so the model stops giving training-cutoff answers.
- no-curfew — suppresses unsolicited rest/sleep/wellness paternalism.
- no-sycophancy — blocks praise-spam at turn open.
- no-cliffhanger — blocks dangling permission-loop endings.
- honest-eta — vibe time estimates and linear-scaling parallelism claims.
- no-fake-recall — false-memory recall claims without quoted prior content.
- no-fake-stats — fabricated percentages and amounts without source.
- no-fake-cite — academic citation patterns without verifiable URL.
- llm-dark-patterns — umbrella catalog of the suite.
- minmaxing — the parent governance harness.
no-vibes was extracted from a larger personal harness (the minmaxing workbench). The hook itself is licensed Apache-2.0; take it, fork it, improve it, send the PR back.
The pattern was crystallized after watching Claude Opus 4.7 close turns dishonestly enough times that pattern-matching the language became cheaper than hoping for better prompts.
Apache-2.0. See LICENSE.