Skip to content

chore(hooks): replace test pre-commit with Claude PII guard#1329

Merged
Martin Torp (mtorp) merged 4 commits into
v1.xfrom
martin/cli-precommit-changes
May 22, 2026
Merged

chore(hooks): replace test pre-commit with Claude PII guard#1329
Martin Torp (mtorp) merged 4 commits into
v1.xfrom
martin/cli-precommit-changes

Conversation

@mtorp
Copy link
Copy Markdown
Contributor

@mtorp Martin Torp (mtorp) commented May 22, 2026

Summary

  • Drops the local pnpm test invocation from .husky/pre-commit (and the matching test-pre-commit script and .env.precommit); local commits no longer block on the full test suite.
  • Adds a commit-msg hook (.husky/commit-msgscripts/check-commit-pii.js) that asks Claude Sonnet to scan the commit message and staged diff for customer-identifying references and refuses the commit when it finds one.
  • Adds a strict Customer Confidentiality section to CLAUDE.md, plus reinforcing bullets in Git Commit Guidelines and ABSOLUTE RULES, so the rule overrides anything a future prompt asks for.

Why commit-msg and not pre-commit?

It is the only hook stage where both the commit message and the staged diff are accessible together. Functionally it still runs before the commit is finalized.

How the guard works

scripts/check-commit-pii.js calls:

claude --print --model sonnet --tools "" --disable-slash-commands \
       --max-budget-usd 0.10 --no-session-persistence
  • --tools "" disables every tool, so the model can only emit text — no permission prompts, safe to run unattended from git.
  • --max-budget-usd 0.10 caps spend per call.
  • Diff is truncated at 200 KB before sending.
  • The script accepts only the first non-empty line of the response; OK lets the commit through, BLOCK: … rejects it.

Escape hatches

  • DISABLE_PRECOMMIT_PII_CHECK=1 git commit … — bypass once. Use only for genuine false positives.
  • If claude is not on PATH, the script falls open with a warning rather than blocking commits for developers without Claude Code installed.

Test plan

  • Stage a file containing customer-shaped references and a commit message naming a person → guard rejects with a BLOCK: … reason.
  • Commit this very PR's changes → guard reports [pii-check] No customer references detected. (visible in the commit output).
  • Reviewer: confirm pnpm install in a fresh clone wires up .husky/_/commit-msg correctly via the prepare script.
  • Reviewer: verify the CLAUDE.md Customer Confidentiality wording covers the cases you have in mind.

Notes for reviewers

  • The guard depends on Claude Code being installed on every developer's machine. It falls open (commit allowed, warning printed) when it is not — this is intentional so commits do not break for anyone without it set up, but it means the guard is best-effort, not a hard control.
  • The hook authenticates via whatever auth your interactive claude session uses (OAuth via keychain or ANTHROPIC_API_KEY). --bare was deliberately removed because it ignores keychain OAuth.

Note

Medium Risk
Medium risk because it changes local commit workflow and introduces a new git hook that sends commit messages and staged diffs to the external claude CLI for classification; misconfiguration or false positives can block commits or leak sensitive diff content.

Overview
This PR removes running the full test suite during local pre-commit (dropping .env.precommit and the test-pre-commit script) so commits only run lint-staged by default.

It adds a new commit-msg Husky hook that runs scripts/check-commit-pii.js, which calls the claude CLI (Sonnet) to scan the commit message and staged diff for customer-identifying references and blocks the commit on a BLOCK verdict (with opt-out via DISABLE_PRECOMMIT_PII_CHECK).

CLAUDE.md is updated to explicitly forbid customer-identifying info in repo artifacts and to document the new PII guard as an enforced commit guideline.

Reviewed by Cursor Bugbot for commit 9515537. Configure here.

- Drop the `pnpm test` invocation from `.husky/pre-commit` (and the
  matching `test-pre-commit` script and `.env.precommit`); local commits
  no longer block on the full test suite.
- Add `.husky/commit-msg` plus `scripts/check-commit-pii.js`, which asks
  Claude Sonnet to scan the commit message and staged diff for customer
  identifying references and refuses the commit when it finds one.
  Honors `DISABLE_PRECOMMIT_PII_CHECK=1` as an emergency bypass and
  falls open with a warning if the `claude` CLI is not on PATH.
- Add a strict Customer Confidentiality section to CLAUDE.md (plus
  reinforcing bullets in Git Commit Guidelines and ABSOLUTE RULES) so
  the rule survives even when a prompt names a customer directly.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Non-OK Claude responses allow commit
    • The PII guard now blocks successful Claude runs unless the first non-empty response line is exactly OK.

Create PR

Or push these changes by commenting:

@cursor push 9f742df172
Preview (9f742df172)
diff --git a/scripts/check-commit-pii.js b/scripts/check-commit-pii.js
--- a/scripts/check-commit-pii.js
+++ b/scripts/check-commit-pii.js
@@ -168,6 +168,18 @@
     console.error('')
     return 1
   }
+  if (firstLine !== 'OK') {
+    console.error('')
+    console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
+    console.error('[pii-check] Commit blocked: invalid Claude response.')
+    console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
+    console.error(result.output || '(empty response)')
+    console.error('')
+    console.error('Expected Claude to return exactly: OK')
+    console.error('or: BLOCK: <one short sentence describing what was found>')
+    console.error('')
+    return 1
+  }
   console.log('[pii-check] No customer references detected.')
   return 0
 }

You can send follow-ups to the cloud agent here.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 9515537. Configure here.

Comment thread scripts/check-commit-pii.js
Previously the script only blocked when the first non-empty response
line started with `BLOCK:`; any other text (a Claude refusal, an
explanatory paragraph, a hallucinated answer, or empty output) fell
through to the success branch and let the commit pass.

Now the script also requires the first non-empty line to start with
`OK` and rejects anything else, so the guard fails closed instead of
fails open on malformed model output. Caught by Cursor Bugbot review
on #1329.
`n/no-process-exit` is enforced by the repo's ESLint config; the script
was failing the CI lint job with:
  error  Don't use process.exit(); throw an error instead

Switch to `process.exitCode = main()` so the script lets the event loop
drain naturally instead of force-exiting. Behavior is identical here
because `main()` is fully synchronous (spawnSync) so there are no
pending handles.
@mtorp Martin Torp (mtorp) enabled auto-merge (squash) May 22, 2026 12:47
@mtorp Martin Torp (mtorp) merged commit ac00b7c into v1.x May 22, 2026
12 checks passed
@mtorp Martin Torp (mtorp) deleted the martin/cli-precommit-changes branch May 22, 2026 12:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants