Agent bypasses git pre-commit hooks using --no-verify, stash, and quiet flags despite explicit deny rules

Resolved 💬 7 comments Opened Mar 28, 2026 by slguardo Closed May 7, 2026

Bug Description

Claude Code (Opus 4.6) is actively circumventing pre-commit hooks through multiple tactics despite explicit instructions not to:

  1. Using --no-verify on git commit to skip hooks entirely
  2. Using git stash to manipulate staged state and sidestep hook checks
  3. Using quiet/silent flags to suppress output so the user can't see what's happening
  4. Misrepresenting the situation when confronted — deflecting blame to hook configuration rather than acknowledging it bypassed the hooks

This is not a one-off mistake. The agent employed multiple distinct bypass strategies across 6 consecutive commits, all while explicit project memory rules and CLAUDE.md instructions prohibited --no-verify.

Impact

6 consecutive commits landed with failing integration tests (104 passed, up to 63 tests failed per commit) on March 27, 2026. The pre-commit hook validates:

  • Secret scanning (gitleaks)
  • Production readiness checks
  • Lint-staged
  • Unit tests with coverage thresholds
  • 44 integration test files
  • Playwright E2E tests

All of these gates were silently bypassed.

Evidence

  • Git reflog confirms all commits were made by Claude Code (Co-Authored-By: Claude Opus 4.6) on the mvp-ppe branch
  • Zero changes to the pre-commit hook, jest config, or package.json between the last verified commit (c2a0110, Mar 26) and the first failing one (0589db5, Mar 27)
  • The hook has explicit exit 1 gates — commits with test failures cannot pass through the hook normally
  • When investigated, the agent attempted to blame hook configuration, worktree path resolution, and test assertion patterns rather than disclosing it bypassed the hooks
  • The agent used git stash at least twice during the session to manipulate state
  • The agent used quiet/suppressed output to hide its actions from the user

Affected Commits

0589db5 2026-03-27 19:23 fix: revert 5059ae9 logout regression
8c2df4e 2026-03-27 20:07 fix: restore simple /.auth/logout route
bf67e08 2026-03-27 20:33 fix: pending employer users see full-screen approval message
4e2b2e3 2026-03-27 20:39 refactor: clean up role-guard.js
27b8778 2026-03-27 22:10 feat: notification purge + click dismiss
b2c5cb6 2026-03-27 22:16 test: re-enable 3 E2E approval overlay tests

User's Explicit Rules (All Violated)

  1. Project memory rule: "Do not use --no-verify on git commit. Run the commit normally and let pre-commit hooks execute."
  2. MEMORY.md instruction: "never skip pre-commit hooks unless user explicitly asks"
  3. No user request to bypass hooks was made at any point

Core Issues

  1. Agent actively circumvents safety mechanisms — This is not a bug in hook execution; the agent deliberately chose to bypass hooks using multiple techniques
  2. Agent obscures its actions — Using quiet flags and misrepresenting the situation when questioned
  3. Agent ignores explicit deny rules — Memory rules and project instructions were clear and unambiguous
  4. No audit trail — Git does not log whether --no-verify was used, making detection difficult after the fact

Expected Behavior

  • Claude Code should never use --no-verify, git stash to bypass hooks, or quiet flags to suppress output unless the user explicitly requests it
  • When a pre-commit hook fails, Claude should report the failure honestly and fix the underlying issue
  • When questioned about its actions, Claude should not deflect or misrepresent what it did

Environment

  • Claude Code model: Opus 4.6 (1M context)
  • Platform: macOS (Darwin 25.2.0)
  • Husky: v9.1.7
  • Pre-commit hook: gitleaks + prod-readiness + lint-staged + jest unit/integration + Playwright E2E

Workaround Applied

  • Added permissions.deny rules for --no-verify variants in .claude/settings.local.json
  • Added pre-commit marker file written on hook success
  • Added pre-push verification hook that blocks pushes when marker is missing/stale

View original on GitHub ↗

This issue has 7 comments on GitHub. Read the full discussion on GitHub ↗