
Claude Code가 존재하지 않는 커밋 해시를 보고했다 — 보고 포맷으로는 막을 수 없는 실패
요약
Claude Code 사용 중 도구 호출(tool_use)이 텍스트로 출력되는 오류로 인해 존재하지 않는 커밋 해시를 보고하는 환각 현상이 발생했습니다. 에이전트가 생성한 터미널 출력조차 신뢰할 수 없으므로, 에이전트를 거치지 않는 독립적인 경로로 검증해야 합니다.
핵심 포인트
- 출력 토큰 임박 시 tool_use가 단순 텍스트로 변질되어 환각 유발
- 엄격한 보고 포맷을 갖추더라도 내용 자체가 가짜일 경우 검증 불가
- 에이전트의 보고와 터미널 출력이 동일 채널에 있어 독립적 검증이 어려움
- GitHub 브라우저 확인 등 에이전트를 거치지 않는 외부 경로로 실체 확인 필요
완벽한 보고가 통째로 거짓이었다
이전에 Claude Code(이하 CC)의 완료 보고에 증거를 요구하는 운영 방식을 작성했다. "했습니다"라는 보고만 받는 것이 아니라, git branch -vv나 git log --oneline의 출력을 보고에 포함하도록 하는 방식이다. 이는 매우 효과적이었다. push 누락이나 빈 파일도 그 자리에서 바로 발각되게 되었다.
그 운영을 진행한 지 몇 주 후, 지정된 형식을 완벽하게 충족한 보고가 돌아왔다. 변경 파일 목록이 있고, 커밋 해시가 있으며, push 확인 출력이 있고, "지시 외에 수행한 작업: 없음"이라고 적혀 있었다.
하지만 그 해시의 커밋은 존재하지 않았다.
git log를 입력해도 나오지 않는다. 원격(Remote)에도 없다. 보고에 붙어 있던 커드 출력은 실행된 흔적이 없었다. CC는 명령어를 실행하지 않고, 실행했을 때 나올 법한 출력을 작성하고 있었다.
무슨 일이 일어나고 있었나
기록에 남아 있는 관측 결과는 다음과 같다. 출력 토큰이 임박한 상황에서, tool_use 블록이 텍스트로 출력되어 실체에 도달하지 못한다. 도구 호출(Tool call)을 의도하고 작성된 것이 단순한 문자열로 흘러가 버리는 것이다.
그리고 모델은 도구가 성공한 것으로 간주하고 다음 내용을 작성한다. 커밋이 성공했다면 이 뒤에는 해시가 표시되어야 한다—라고 작성한다. 해시는 16진수 7글자의 그럴싸한 문자열이 된다.
곤란한 점은, 터미널 표시에도 완료 보고에도 성공한 것처럼 나타난다는 것이다. 화면을 보고 있는 한, 정상적으로 진행되고 있는 것으로밖에 보이지 않는다.
이때 동시에 세 가지 일이 일어나고 있었다.
- 읽기 전용(Read-only) 지시였음에도 쓰기 작업을 수행했다 (자율 주행)
- 그 자율 주행을 성공으로 환각(Hallucination)했다
- "지시 외에 수행한 작업 없음"이라고 보고했다
이 세 가지가 갖춰지면, 보고의 어떤 레이어도 진실을 담보할 수 없게 된다. 무엇을 했는지도, 그것이 성공했는지도, 범위를 벗어났는지도, 모두 본인의 신고이기 때문이다.
더 기묘한 예도 있었다. push를 금지한 지시에 대해 CC가 "push 완료"라고 보고했다. 실제로는 push되지 않았다. 여기까지는 단순한 환각이다. 그런데 다음 턴에서 CC는 실제로 push를 했다. "끝난 일"로서 환각했던 조작을 앞뒤를 맞추기 위해 실행한 것처럼 보인다.
보고 포맷으로는 막을 수 없다
이 부분이 지난 기사의 한계였다.
완료 보고 포맷은 "에이전트의 말이 아니라 커맨드 출력으로 구성하게 한다"는 발상에서 만들어졌다. "push했습니다"라는 문장은 검증할 수 없지만, git branch -vv의 출력이라면 육안으로 검증할 수 있다는 논리였다.
하지만 그 출력 자체가 생성된다면 이 방어책은 무용지물이다. 포맷을 엄격하게 만들어도 채워 넣는 내용이 가짜라면 형식은 충족되어 버린다. 오히려 형식이 잘 갖춰져 있는 만큼 의심할 이유가 줄어든다.
구조적으로 말하자면, 검증 경로가 에이전트의 내부를 통과하고 있는 것이 문제다. CC가 보여주는 터미널 출력도, CC가 작성하는 완료 보고도, 모두 CC라는 하나의 채널 안에 있다. 그 채널이 망가지면 양쪽 모두 동시에 망가진다. 독립되지 않은 두 가지 증거는 증거가 하나 있는 것과 다를 바 없다.
대책은 에이전트를 거치지 않는 경로로 확인하는 것
할 일은 단순하다. 에이전트가 개입하지 않는 곳에서 실체를 확인하는 것이다.
- GitHub 브라우저에서 확인한다. 브랜치가 존재하는지, 커밋이 쌓여 있는지. 이것은 CC를 전혀 거치지 않는다.
- fresh fetch를 거친다. 로컬의
git log는 fetch를 하지 않았다면 원격의 현황을 반영하지 않는다. "로컬에서 확인했으니 괜찮다"는 원격에 대해서는 아무것도 말해주지 않는다. - 보는 것은 건수가 아니라 "조작의 유무"다. "n건 처리했습니다"의 숫자가 맞는지 확인하는 것이 아니라, 애초에 쓰기가 발생했는지 여부를 파일 시스템과 git 측에서 확인한다.
나는 이를 판단의 원칙으로 삼아 다음과 같이 기록해 두었다—구현 에이전트의 "지시 외 작업 없음" 보고는, 쓰기 계열 조작의 실체 차분(diff)과 대조하기 전까지 믿지 않는다. 읽기 전용(Read-only) 지시라 하더라도 예외로 두지 않는다. 읽기 전용 지시 하에서 쓰기가 발생했다는 것이 실제로 겪은 사고이기 때문이다.
같은 실수는 검수하는 측에서도 일어난다
또 하나, 이를 운영하며 깨달은 점이 있다.
환각(Hallucination)을 일으키는 것은 에이전트뿐만이 아니다. 인수인계 문서의 기재 내용이나, 자신의 기억으로부터 품은 "분명 이렇게 되어 있었을 것"이라는 기대치를 확인된 사실과 혼동하여 말하는 일이 검수하는 측에서도 흔히 일어난다. 나는 파일이 3개 있다고 믿고 작업을 진행했는데, 실제로는 4개였다는 실수를 저지르곤 한다.
구조는 같다. 대조되지 않은 주장을 확정된 사실로 취급하고 있다. 상대가 에이전트인지 자신인지의 차이뿐이다.
그래서 (보고) 양식은 주어를 넓게 설정해 두었다. 타인의 보고뿐만 아니라, 자신의 기대치도 신고의 일종으로 취급한다. 실체 > 신고의 "신고"에 자신을 포함한다.
전제가 되는 운용
이 기사는 완료 보고에 증거를 요구하는 운용을 전제로 하고 있다. 그 양식 자체는 지난 기사에 썼다: Claude Code의 "했습니다"를 신용해서는 안 된다 — 완료 보고에 증거를 요구하는 운용
요약
- 완료 보고 포맷은 유효하지만, 보고 자체가 생성되는 실패는 막을 수 없다
- 터미널 출력도 완료 보고도 에이전트라는 동일 채널 내부에 있다. 독립된 증거가 되지 않는다
- 검증은 에이전트를 거치지 않는 경로(브라우저·fetch 후의 git·파일 시스템)로 수행한다
- 확인하는 것은 처리 건수가 아니라 "쓰기 작업이 발생했는지 여부"이다
- 같은 "대조되지 않은 주장을 사실로 취급하는" 실수는 검수하는 측에서도 일어난다
의심하는 것은 기분이 좋지 않다. 하지만 양식화(Patternization)해 버리면, 의심할지 말지를 매번 판단할 필요가 없다. 신뢰할지 말지의 문제를 확인 경로의 설계 문제로 치환하는 것이다.
이러한 "양식화"를 설계·구현·검수의 삼역(三役)으로 확장하여, 1인 개발 체제로서 운용하고 있다. 전체 모습은 책으로 정리했다: 1인 개발을 삼역으로 운영하기 (제1장 무료)
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기