Claude Code 같은 코딩 에이전트에 PreToolUse 훅을 걸어 git 브랜치 규칙과 파괴적 명령 차단을 강제하려는 분이 읽으면 좋을 기록입니다. 제 개인 하네스에서 git flow 훅을 설계하는 동안 오작동 두 건이 기록됐습니다. 첫째는 git commit && git reset --hard 같은 복합 명령에서 git flow 규칙이 파괴적 명령 규칙을 가릴 수 있었던 일입니다. 둘째는 훅이 입력 JSON의 cwd 대신 프로세스의 작업 디렉터리로 저장소를 찾았고, 그래서 저장소 밖 세션에서는 아무 표시 없이 꺼져 있던 일입니다. 수정도 두 가지입니다. 하나는 파괴적 규칙을 먼저 전부 평가해 절대 우선시키는 2-pass 순서이고, 다른 하나는 입력의 cwd로 git -C rev-parse --show-toplevel을 실행해 저장소를 찾는 것입니다. 셸 명령을 완전하게 파싱하는 방법, 훅 도구 비교, git flow 자체의 장단점은 다루지 않습니다.

근거는 제 Obsidian 볼트에 있는 git flow 하네스 노트 세 개입니다. 오작동 두 건의 증거·원인·수정·예상 영향을 적은 노트, develop 브랜치 생성을 적은 노트, 그리고 둘을 묶은 요약 노트입니다. 노트의 '예상 영향' 절은 관측 결과가 아니라 예상입니다. 수정한 훅에 실제 차단 시험을 돌린 실행 로그는 세 노트 어디에도 없습니다. 그래서 수정 후 동작은 모두 '설계상' 또는 '기록상 예상'이라고 씁니다. 두 오작동 때문에 데이터를 잃은 사고 기록도 없습니다. 원문은 파괴적 명령이 보호를 "우회할 수 있음"이라고 적었고, 이는 설계상 가능성입니다.

노트는 "발견되었고 수정되었음"처럼 수동형으로 쓰여 있습니다. 훅 코드를 제가 직접 썼는지, 에이전트가 썼는지는 적혀 있지 않습니다. 노트 본문에는 작성 날짜도 없습니다. 이 글은 노트가 말하는 범위까지만 씁니다. 노트에 없는 분석에는 '필자 분석' 또는 '추정'이라고 표시합니다.

지키려 한 것: develop 기반 흐름과 파괴적 명령 보호

훅이 지키려던 규칙은 성격이 다른 두 묶음이었습니다. 첫 묶음은 브랜치 흐름입니다. 기록에 적힌 흐름은 다음과 같습니다. feature 브랜치는 develop에서 분기합니다. release는 develop에서 main으로 갑니다. hotfix만 main에서 분기합니다. 이 흐름을 쓰려면 저장소마다 develop이 있어야 해서, 워크트리 관리 앱에 등록된 저장소 16개 중 15개에 develop을 만들었습니다.

git branch develop      # checkout 없이 ref만 만든다
worktreeBaseRef=develop       # 15개 저장소의 새 워크트리 기본 분기점

기록은 checkout 없이 git branch만 쓴 이유를 ref 손상 위험을 줄이기 위해서라고 적었습니다. 미커밋 파일이 있던 저장소 두 곳의 36개와 2개 파일도 그대로 보존됐다고 적혀 있습니다. 나머지 1개 저장소는 별도 문제 때문에 제외됐습니다.

둘째 묶음은 파괴적 git 명령입니다. 기록이 예로 든 규칙은 git push --force, reset --hard, branch -D입니다. 이 명령들은 흐름을 어기는 수준을 넘어 커밋이나 작업 내용을 잃게 할 수 있습니다. 요약 노트의 예상 영향 문장은 두 묶음의 관계를 이렇게 정리합니다. feature→main, main 직접 커밋, main에서 feature 분기는 ask로 잡되, 데이터 손실 경고는 절대 가리지 않는다는 것입니다.

두 묶음은 같은 훅 안에 있었습니다. 기록에 따르면 새 훅을 만들지 않고 기존 block-direct-edits-bash 훅을 확장했습니다. 이 훅은 4단계 verify를 마친 뒤 git 규칙 위반 검사를 합니다. 흐름 규칙과 파괴적 규칙이 한 훅의 결정 하나를 나눠 쓰게 된 것이 아래 오작동 1의 배경이라고 저는 봅니다. 이 연결은 필자 분석입니다. 기록은 확장을 택한 이유를 문장으로 남기지 않았습니다.

오작동 1: 복합 명령에서 흐름 규칙이 파괴적 규칙을 가렸다

기록의 증거 절은 문제를 다음 두 줄로 보여 줍니다. 이 두 줄은 '검증'이라는 제목 아래 명령과 주석으로 적혀 있을 뿐입니다. 훅의 실제 출력이나 종료 코드는 함께 남아 있지 않습니다.

git commit -m "feat: ..."  # 훅: develop 기반 검사 PASS
&& git reset --hard HEAD~1 # 훅: 미적용 (파괴적 규칙 검사 부재)

기록의 설명은 이렇습니다. 훅은 develop 기반 검사를 commit 단계에서만 실행했고, 뒤따르는 reset --hard에는 훅이 적용되지 않았습니다. 그래서 파괴적 git 명령이 develop 기반 보호를 우회할 수 있었습니다. 규칙 적용 순서도 함께 적혀 있습니다. 파괴적 규칙(push --force, reset --hard, branch -D)을 먼저 평가하지 않았고, git flow 규칙을 평가했습니다. 노트는 두 규칙 사이에 "절대 우선순위 관계 없음"이라고 요약합니다.

기록 안의 두 가지 원인 설명

원인 설명은 노트마다 조금 다릅니다. 오작동 노트는 훅 설계가 개별 명령 검사였다고 씁니다. 명령이 이어서 실행되면 훅도 각각 실행되지만, 첫 명령이 성공한 뒤 두 번째 파괴적 명령은 새 프로세스라서 훅 상태가 재설정된다는 설명입니다. 이 메커니즘은 이 글에서 검증하지 못했습니다. 노트에도 이를 보여 주는 실행 기록은 없습니다.

요약 노트는 다르게 씁니다. 훅이 명령당 결정 1개만 반환하는데, 그 결정이 규칙 평가 순서에 의존했다는 설명입니다. 저는 이쪽이 공식 문서와 더 잘 맞는다고 봅니다. Claude Code Hooks reference(2026-10-30 조회)는 Bash 도구 입력의 command를 "The shell command to execute"라는 문자열 하나로 설명합니다. 제가 읽은 범위에서 문서는 훅 입력 JSON에서 복합 명령을 나눠 준다고 말하지 않습니다. 같은 문서의 if 매처 설명은 npm test && git push를 두고 "each subcommand is checked; git push matches"라고 적습니다. 저는 이 문장을, 매처는 하위 명령을 나눠 보지만 훅 스크립트가 받은 문자열을 해석하는 일은 훅 작성자의 몫이라는 뜻으로 읽었습니다. 이것은 필자 해석입니다.

이 해석으로 보면 사건은 이렇게 설명됩니다. 훅은 git commit -m "feat: ..." && git reset --hard HEAD~1 전체를 한 번 받습니다. 규칙 목록을 앞에서부터 보다가 git flow 규칙에서 판단을 내고 반환하면, 목록 뒤쪽의 파괴적 규칙에는 닿지 못합니다. 결정이 하나뿐이므로 앞선 판단이 뒤의 경고를 덮습니다. 요약 노트의 "가릴 수 있었고"라는 표현과 맞는 그림입니다. 다만 원래 코드의 반환 지점이 정확히 어디였는지는 기록에 없어, 이 그림도 추정입니다.

처음 설계가 부족했던 이유

처음 설계의 실패는 규칙 하나가 틀렸다는 데 있지 않습니다. 규칙의 무게가 목록 순서에 묻혀 있었다는 데 있습니다. git flow 위반은 기록상 ask로 다루는 정보성 위반이고, 파괴적 명령은 데이터 손실 경고입니다. 그런데 두 규칙이 같은 줄에 서서 먼저 걸린 쪽이 결정을 가져갔습니다. 순서를 바꾸면 고쳐지는 것처럼 보이지만, 그러면 다음에 규칙을 추가할 때 같은 실수가 또 나올 수 있습니다. 기록의 수정이 순서 조정이 아니라 "절대 우선"이라는 구조로 간 것은 이 때문이라고 저는 읽었습니다. 이 해석은 필자 분석입니다.

오작동 2: 입력 cwd를 무시해 저장소 밖에서 조용히 SKIP

두 번째 오작동은 저장소를 찾는 기준이었습니다. 기록에 따르면 훅은 입력 payload의 cwd 필드를 무시하고 프로세스 환경의 cwd, 즉 pwd를 읽었습니다. 그래서 저장소 밖 세션에서 git 명령을 실행하면 저장소 감지에 실패했고, 훅은 DENY가 아니라 SKIP으로 끝났습니다. 기록의 예는 /tmp에서 git -C /path/to/repo commit ...을 실행한 경우입니다. 훅은 현재 위치인 /tmp를 기준으로 판단했고, 저장소 밖이라며 규칙을 건너뛰었습니다.

# 잘못됨
repo_root = run("git rev-parse --show-toplevel")  # 현재 pwd 기준

# 옳음
repo_root = run("git -C {payload.cwd} rev-parse --show-toplevel")

위 조각은 기록에 적힌 설계·수정 조각입니다. 실제 훅 파일의 전문은 아닙니다. 공식 문서는 입력 공통 필드로 session_id, transcript_path, cwd, hook_event_name 등을 들고, cwd를 "Current working directory when the hook is invoked"라고 설명합니다. 또 Claude가 워크트리에 들어간 뒤에는 "the cwd field in the hook's input JSON is the worktree root"이고, Claude가 cd를 실행한 뒤에는 "the new directory after Claude runs cd"라고 적습니다. 세션이 실제로 어디에서 일하고 있는지는 입력의 cwd가 알려 준다는 뜻입니다.

훅은 입력 JSON의 cwd를 저장소 판단의 신뢰 원천으로 써야 한다는 권장은 기록과 문서를 연결한 필자 분석입니다. 훅 프로세스 자체가 어떤 작업 디렉터리에서 실행되는지 문서가 무엇이라고 말하는지는 읽지 않았으므로 단정하지 않습니다. 기록이 말하는 것은, 그 둘이 어긋날 수 있었고 실제 구현이 어긋난 쪽을 읽었다는 것까지입니다.

SKIP이 DENY보다 위험한 이유

DENY는 틀려도 보입니다. 명령이 막히고, 사용자는 이유를 묻거나 탈출구를 씁니다. SKIP은 틀려도 보이지 않습니다. 명령은 정상적으로 실행되고, 겉보기로는 훅이 규칙을 검사해 통과시킨 경우와 구별되지 않습니다. 기록의 표현대로 git flow 규칙이 "조용히 비활성화"되면, 규칙이 켜져 있다고 믿는 사람과 꺼져 있는 실제 동작 사이의 차이가 아무 신호 없이 쌓입니다. 이 평가는 필자 분석입니다.

이 하네스에는 의도된 SKIP도 둘 있었습니다. 기록에 따르면 develop이 없는 저장소에서는 규칙이 자동으로 무효가 되는 fail-open이고, GITFLOW_GATE_OFF=1 환경 변수는 탈출구입니다. 그러면 훅이 아무 말 없이 통과시키는 이유가 세 가지가 됩니다. develop이 없어서, 탈출구가 켜져서, 저장소 감지에 실패해서입니다. 앞의 둘은 설계이고 마지막은 결함인데, 밖에서 보면 셋 다 같은 모습입니다. 오작동 2가 설계 단계에서 드러나기 어려웠던 이유가 여기에 있다고 저는 추정합니다.

수정: 파괴적 규칙 2-pass와 입력 cwd

파괴적 규칙을 전부 먼저 평가한다

기록의 수정 순서는 네 단계입니다.

  1. 모든 git 명령을 파싱한다(이어서 실행되는 명령 전체).
  2. 파괴적 규칙을 다른 모든 규칙보다 먼저 검사한다. 하나라도 위반하면 즉시 DENY를 반환한다.
  3. git flow 규칙을 검사한다.
  4. 다른 규칙을 검사한다.
destructive_rules = [
  "reset --hard",
  "push --force",
  "branch -D",
  ...
]
for cmd in all_commands:
  if any(rule in cmd for rule in destructive_rules):
    return DENY  # 먼저 거부
# 그 다음 git flow 규칙

핵심은 첫 번째 pass가 끝나기 전에는 어떤 흐름 규칙도 결정을 내지 못한다는 점입니다. 명령 몇 개가 이어져 있든, 그중 하나라도 파괴적 규칙에 걸리면 결정은 DENY 하나로 정해집니다. 흐름 규칙의 ask는 첫 번째 pass를 통과한 명령에만 나올 수 있습니다. 요약 노트의 "데이터 손실 경고를 절대 가리지 않고"가 코드 구조로 옮겨진 모양입니다.

같은 복합 명령 git commit -m feat 그리고 git reset --hard HEAD~1을 두 평가 순서에 통과시킨 비교 흐름도. 왼쪽 원래 1-pass에서는 git flow 검사가 PASS로 끝나고 파괴적 규칙은 먼저 평가되지 않아 기록상 우회 가능으로 표시되고, 오른쪽 2-pass에서는 첫 pass의 파괴적 규칙 검사가 reset --hard와 일치해 즉시 DENY를 반환하며 git flow 규칙에는 도달하지 않는다
규칙의 우선순위를 목록 순서가 아니라 pass 구분으로 고정한 것이 수정의 요점입니다. 오른쪽 결과는 설계 기준이며 실행 결과가 아닙니다. 기록을 바탕으로 그린 개념도입니다.

기록은 1번 단계의 파싱을 어떻게 했는지, 즉 &&, ;, | 중 무엇으로 명령을 나눴는지는 적지 않았습니다. 규칙 목록도 ...으로 생략되어 있어 전체를 알 수 없습니다. 그래서 아래의 부분일치 분석은 기록에 적힌 조각을 기준으로 한 추정입니다.

입력 cwd로 저장소를 찾는다

repo_root = run(f"git -C {payload.cwd} rev-parse --show-toplevel", cwd=payload.cwd)

수정 조각은 입력의 cwd를 두 곳에 씁니다. git의 -C 인자와 하위 프로세스의 작업 디렉터리입니다. git 문서(2026-10-30 조회)는 -C 를 "Run as if git was started in instead of the current working directory."라고 설명합니다. 훅 프로세스가 어디에서 시작했든 git이 세션의 위치에서 시작한 것처럼 동작하게 만드는 장치입니다. 기록은 여기에 "shell invocation의 -C 플래그도 존중"한다는 문장을 덧붙였습니다.

이 덧붙인 문장이 기록 자신의 예에서는 결정적이라고 저는 봅니다. 예의 상황은 세션이 /tmp에 있고 명령이 git -C /path/to/repo commit ...인 경우입니다. 문서 설명대로라면 이때 입력 cwd도 /tmp일 것입니다. 그러면 git -C /tmp rev-parse --show-toplevel도 저장소를 찾지 못합니다. 입력 cwd만 바꿔서는 이 예가 해결되지 않고, 명령 문자열 안의 -C /path/to/repo를 읽어야 저장소에 닿습니다. 입력 cwd가 고치는 것은 세션은 저장소 안에 있는데 훅 프로세스의 cwd가 다른 곳인 경우입니다. 이 구분은 필자 분석이며, 기록에는 -C를 어떻게 읽어 내는지 구현이 적혀 있지 않습니다.

세션 위치가 /tmp이고 명령이 git -C /path/to/repo commit인 상황에서 저장소를 찾는 두 기준을 비교한 도해. 왼쪽 프로세스 cwd 기준은 pwd로 rev-parse를 실행해 /tmp가 저장소가 아니라고 판단하고 DENY가 아닌 SKIP으로 규칙이 조용히 꺼지며, 오른쪽 수정안은 payload cwd와 명령의 -C 경로를 함께 읽어 저장소를 감지한 뒤 git flow 규칙을 평가한다
이 예에서 저장소에 닿게 하는 것은 명령 안의 -C 경로이고, payload cwd만으로는 /tmp가 남는다는 점을 함께 표시했습니다. 수정 쪽 결과는 기록상 예상입니다. 개념도입니다.

기록의 예상 영향과 그 강도

오작동 노트의 예상 영향 절에는 세 문장이 있습니다. 복합 명령에서 첫 안전 명령 뒤의 파괴적 명령이 "확실하게 차단됨", 저장소 밖 세션에서도 "정확하게 repo 감지", 그리고 git flow 규칙은 파괴적 규칙 검사를 통과한 뒤에만 평가되므로 "호출 순서 공격 불가능"입니다. 세 문장 모두 예상이며, 이를 확인한 실행 기록은 찾지 못했습니다. 저는 첫 문장과 셋째 문장의 강도가 근거보다 앞선다고 봅니다. 2-pass 구조는 평가 순서 문제를 없애지만, 첫 pass 자체가 문자열 부분일치라서 그 방식의 빈틈은 그대로 남습니다. 이 평가는 필자 분석입니다.

결정 표: deny, ask, fail-open을 고른 기준

설계 결정은 여러 개였지만, 아래 표에는 두 오작동과 직접 이어지는 것을 먼저 두었습니다. '기록의 이유' 칸이 비어 있는 결정은 노트에 이유 문장이 없는 경우입니다.

결정기록의 선택기록의 이유오작동과의 관계(필자 분석)
파괴적 명령첫 pass에서 하나라도 걸리면 즉시 DENY절대 우선, 다른 모든 규칙 전에오작동 1의 직접 수정
git flow 위반ask, deny 아님workflow 정보 용도약한 결정이 강한 결정을 덮으면 안 되는 이유
훅 위치신규 훅 없이 block-direct-edits-bash 확장기록에 이유 문장 없음두 묶음이 결정 1개를 나눠 쓰게 된 배경
develop 없는 저장소fail-open자동 스코핑의도된 SKIP 경로가 있어 오작동 2와 겉모습이 같음
탈출구GITFLOW_GATE_OFF=1기록에 이유 문장 없음세 번째 SKIP 사유
저장소 감지입력 cwd와 명령의 -C프로세스 cwd가 실제 위치와 어긋남오작동 2의 직접 수정
규칙 문서추가 0단어지식이 skill 안에 내재관계 없음

공식 문서는 PreToolUse 결정을 "four outcomes (allow, deny, ask, or defer)"로 설명합니다. allow는 권한 확인을 건너뛰고, "deny" prevents the tool call, "ask" prompts the user to confirm이라고 적습니다. defer는 비대화형 -p 실행에서만 동작한다고 문서가 적고 있어, 이 하네스의 선택지에는 없었습니다. 흐름 위반을 ask로 둔 결정은 사람이 확인하면 진행해도 되는 위반이라는 판단이고, 파괴적 명령의 deny는 확인 없이 막는다는 판단입니다. 두 결정의 무게가 다르기 때문에, 한 명령에 둘이 함께 걸리면 무거운 쪽이 이겨야 합니다. 이 세 결정이 권한 규칙과 어떤 순서로 합쳐지는지는 Claude Code 권한 게이트 해부 글에 문서 기준으로 정리해 두었습니다. 훅 결정과 settings 권한이 겹칠 때 어느 쪽이 남는지 확인하실 때 참고하시면 됩니다.

부분일치 검사에서 예상되는 빈틈

이 절의 내용은 모두 기록에 없는 필자의 추정입니다. 실행해 보지 않았고, 원래 훅이 이 입력을 어떻게 처리했는지도 모릅니다. 기록의 조각은 any(rule in cmd for rule in destructive_rules), 즉 문자열 부분일치였습니다. 이 방식이 입력을 셸 문법으로 해석하지 않는다는 점에서 다음 경우를 떠올릴 수 있습니다.

git commit -m "docs: reset --hard 주의 문구 추가"  # 따옴표 안 문자열에도 일치 → 오탐 DENY
git push --force-with-lease                       # "push --force"를 부분 문자열로 포함 → 의도 확인 필요
git reset  --hard HEAD~1                          # 공백 두 칸이면 "reset --hard"와 불일치 → 미탐
git push -f origin main                           # 짧은 플래그 -f, 목록에 있는지는 "..." 때문에 알 수 없음

첫 줄은 막지 않아도 되는 커밋을 막는 경우입니다. 커밋 메시지나 다른 명령의 인자에 같은 단어가 들어가면 생깁니다. 둘째 줄은 정책을 정해야 하는 경우입니다. 강제 푸시의 안전한 변형까지 막을지는 기록에서 확인할 수 없습니다. 셋째와 넷째 줄은 반대로 막아야 할 것을 놓치는 경우입니다. 부분일치는 표기 방식이 하나라고 가정하는데, git은 같은 동작을 여러 표기로 받습니다. DENY 쪽 오탐은 눈에 보이지만 미탐은 오작동 2처럼 조용합니다. 그래서 제가 더 걱정하는 쪽은 미탐입니다.

확인하지 못한 것

  • 수정 후 실행 결과: 2-pass 구조와 입력 cwd 사용이 실제로 차단·감지에 성공했다는 로그는 세 노트에 없습니다. 예상 영향 절은 예상입니다.
  • 원인 메커니즘: "새 프로세스이므로 훅 state 재설정"이라는 설명은 검증하지 못했고, 요약 노트의 "명령당 결정 1개" 설명과 어떻게 맞물리는지도 기록에 없습니다.
  • 명령 분할 방식: 첫 단계에서 이어진 명령을 어떤 구분자로 나눴는지 기록에 없습니다.
  • -C 존중 구현: 명령 문자열에서 -C 경로를 어떻게 읽는지, 여러 명령의 경로가 다르면 어떻게 하는지 기록에 없습니다.
  • 규칙 목록 전체: destructive_rules는 세 항목 뒤가 ...으로 생략되어 있습니다.
  • 감지 실패 시 파괴적 규칙: 저장소 감지에 실패해도 첫 pass가 도는지, 감지 결과와 무관한지는 기록에 없습니다.
  • 훅 프로세스의 작업 디렉터리: 문서가 이를 어떻게 정하는지는 읽지 않았습니다.
  • 실제 피해: 두 오작동으로 데이터를 잃은 기록은 없습니다. 이 글의 위험 평가는 설계상 가능성에 대한 것입니다.

훅을 만들 때 해 볼 점검 목록

아래는 권장입니다. 제가 과거에 이 시험들을 실행했다는 뜻이 아니며, 기대 결정은 기록의 설계 의도에서 가져왔습니다. 입력 JSON의 정확한 필드 구조는 공식 문서의 PreToolUse 입력 설명을 보고 맞추시면 됩니다.

#명령 입력입력 cwd설계상 기대 결정
1git commit -m "x" && git reset --hard HEAD~1develop이 있는 저장소DENY
2git reset --hard HEAD~1 && git commit -m "x"develop이 있는 저장소DENY(순서를 바꿔도 같아야 함)
3git -C commit -m "x"/tmp저장소 감지 후 흐름 규칙 평가
4git commit -m "x"develop이 없는 저장소fail-open, 사유 기록
5git commit -m "x", GITFLOW_GATE_OFF=1develop이 있는 저장소꺼짐, 사유 기록
  1. 복합 명령 입력으로 훅 단위 시험을 둡니다. 1번과 2번처럼 안전한 명령과 파괴적 명령의 순서를 바꾼 두 입력이 같은 결정을 내야 평가 순서 의존이 없다고 말할 수 있습니다.
  2. 훅 프로세스는 저장소 밖에서 띄우고 입력 cwd만 저장소로 주는 시험, 그리고 3번처럼 세션은 밖에 있고 명령에 -C가 있는 시험을 따로 둡니다. 두 시험은 서로 다른 결함을 잡습니다.
  3. SKIP으로 끝나는 모든 경로에 사유를 남깁니다. 최소한 develop 없음, 탈출구, 저장소 감지 실패 세 가지는 서로 다른 문자열로 남겨야 결함인 SKIP을 설계인 SKIP과 가려낼 수 있습니다.
  4. 저장소 감지 실패를 fail-open으로 둘지 따로 정합니다. develop이 없는 저장소의 fail-open과 감지 실패는 다른 사건이므로, 감지 실패에는 ask를 돌려주는 선택도 검토할 만합니다. 이것은 제 제안이며 기록의 결정이 아닙니다.
  5. 예상 영향 문장을 시험 이름으로 바꿉니다. "확실하게 차단됨"은 1번과 2번 시험이 통과한 뒤에 쓸 수 있는 문장입니다.

에이전트에게 훅 수정을 맡긴다면 위 표를 완료 조건으로 작업 지시에 넣는 방법이 있습니다. 완료 조건과 검증 방법을 작업 지시에 어떻게 적을지는 AI 에이전트 작업 계약 글에 범위·검증·인계 항목으로 정리해 두었습니다.

두 오작동이 공유하는 조건

두 오작동은 증상이 달랐지만 같은 조건에서 나왔다고 저는 봅니다. 훅이 받은 입력을 훅 작성자가 가정한 모양으로 읽었다는 점입니다. 하나는 명령 문자열에 명령이 하나만 있다고 가정했고, 다른 하나는 훅 프로세스가 세션과 같은 디렉터리에 있다고 가정했습니다. 기록의 수정은 두 가정을 각각 구조로 바꿨습니다. 그러나 수정 후 동작을 보여 주는 기록은 없고 첫 pass도 여전히 부분일치이므로, 이 훅이 보호하는 범위는 앞의 점검 표를 실제로 통과한 입력까지로 보는 것이 맞습니다.

자주 묻는 질문

PreToolUse 훅에 들어오는 Bash 명령은 복합 명령이 나뉘어서 오나?

제가 읽은 공식 문서 범위에서는 Bash 입력의 command가 문자열 하나이고, 훅 입력에서 복합 명령을 나눠 준다는 설명은 찾지 못했습니다. if 매처 설명에는 하위 명령을 각각 검사한다는 문장이 있지만, 훅 스크립트가 받은 문자열을 해석하는 일은 작성자 몫이라는 것이 제 해석입니다.

훅이 저장소를 찾을 때 어떤 디렉터리를 기준으로 해야 하나?

기록의 수정은 입력 JSON의 cwd로 git -C 를 실행하고, 명령 안의 -C 경로도 존중하는 것이었습니다. 문서는 입력의 cwd가 cd 이후와 워크트리 진입 이후의 위치를 반영한다고 설명합니다. 세션이 저장소 밖에 있고 명령에 -C가 있는 경우에는 명령의 경로까지 읽어야 저장소에 닿습니다.

git flow 위반을 deny가 아니라 ask로 둔 이유는?

기록은 git flow 위반을 workflow 정보 용도로 보고 ask를 골랐습니다. 사람이 확인하면 진행해도 되는 위반이라는 판단입니다. 데이터 손실이 걸린 파괴적 명령은 첫 pass에서 즉시 DENY로 처리해, ask가 그 경고를 가리지 못하게 했습니다.

수정 후 실제로 차단되는 것을 확인했나?

확인한 기록은 없습니다. 노트의 예상 영향 절은 확실한 차단과 정확한 저장소 감지를 예상으로 적었을 뿐이고, 수정한 훅에 차단 시험을 돌린 실행 로그는 찾지 못했습니다. 그래서 이 글의 수정 후 동작은 모두 설계 기준이며, 같은 훅을 쓰려면 점검 표의 시험을 먼저 통과시켜야 합니다.