Skip to content

Commit 3bb3191

Browse files
committed
chore(claude): add spec-drift pre-commit reminder hook
Adds .claude/hooks/pre-commit-spec.sh which blocks every commit attempt and prompts the agent to deep-read specs/ and decide whether any spec needs updating for the staged change. Mandatory touch-flag escape (/tmp/claude-spec-confirmed-<session-id>) prevents the hook from looping forever — the agent must consciously acknowledge 'specs are still accurate' once per commit attempt. Wired into .claude/settings.json as the first PreToolUse hook for 'git commit', running before the existing format/lint/test hook.
1 parent bd6447e commit 3bb3191

2 files changed

Lines changed: 33 additions & 0 deletions

File tree

.claude/hooks/pre-commit-spec.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
# Pre-commit hook: spec reminder.
3+
# Always blocks the first commit attempt and tells the agent to reconsider
4+
# whether specs/ needs updating. The touch-flag escape is the only way out,
5+
# so the agent must consciously confirm "specs are aligned" each commit.
6+
# Without the flag the hook would loop forever.
7+
8+
set -uo pipefail
9+
10+
HOOK_INPUT=$(cat)
11+
SESSION_ID=$(printf '%s' "$HOOK_INPUT" | jq -r '.session_id // ""')
12+
FLAG_FILE="/tmp/claude-spec-confirmed${SESSION_ID:+-$SESSION_ID}"
13+
14+
# Acknowledged → consume flag and pass.
15+
if [ -f "$FLAG_FILE" ]; then
16+
rm -f "$FLAG_FILE"
17+
exit 0
18+
fi
19+
20+
REASON="Before committing: deep-read specs/ and reconsider whether any spec needs updating for this change.\n\n"
21+
REASON+=" • If a spec is now WRONG → update it, then git add specs/<file>, then retry.\n"
22+
REASON+=" • If a changed area has NO spec → add a new specs/NN-<topic>.md or extend an existing spec, then git add specs/<file>, then retry.\n"
23+
REASON+=" • If specs are still accurate AND coverage is intentional → in a SEPARATE Bash tool call run:\n touch $FLAG_FILE\n then retry the commit.\n"
24+
REASON+="\nDeep-read means open the relevant spec files and verify against the new code — do not skim or trust the diff alone."
25+
26+
printf '{"decision": "block", "reason": %s}' "$(printf '%s' "$REASON" | jq -Rs .)"

.claude/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44
{
55
"matcher": "Bash",
66
"hooks": [
7+
{
8+
"type": "command",
9+
"if": "Bash(*git commit*)",
10+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-commit-spec.sh",
11+
"timeout": 30,
12+
"statusMessage": "Checking spec drift..."
13+
},
714
{
815
"type": "command",
916
"if": "Bash(*git commit*)",

0 commit comments

Comments
 (0)