| name | head-1-diff-shows-only-the-last-commit |
| description | "무엇이 바뀌었나"를 `git diff HEAD~1 HEAD` 로 만들어 LLM 판사·리뷰봇·알림·딜리버러블에 넣으면, 에이전트가 커밋을 두 번 이상 한 순간 **첫 커밋들이 통째로 사라진다.** 파일 목록은 맞는데 내용이 0바이트라 "파일이 없다/검증할 수 없다"는 오판이 나온다. 트리거 - `git diff HEAD~1`, `HEAD^..HEAD`, 판사/리뷰어에게 보낼 컨텍스트 조립, "코드를 볼 수 없습니다" 반복 거절, 커밋 여러 개 만드는 에이전트 루프. |
HEAD~1..HEAD 는 "이 작업의 변경"이 아니라 "마지막 커밋"이다
Problem
에이전트/파이프라인이 만든 변경을 사람이나 LLM 에게 보여줄 때 흔히 이렇게 쓴다:
result = await box.exec(f"git -C {mount} diff HEAD~1 HEAD -- {path}")
- 증상: 판사·리뷰어가 "실제 파일 내용이 캡처되지 않았습니다", "코드 수준에서
검증할 수 없습니다" 라며 반복 거절한다. 그런데 파일은 분명히 있고,
같은 레코드의 다른 검사(scope 등)는 파일 8개를 정상적으로 세고 있다.
- 근본 원인: 에이전트 루프는 턴마다 커밋한다.
HEAD~1..HEAD 는 그중
마지막 하나만 보여준다. 첫 커밋에서 만든 파일은 diff 가 0 바이트다.
실측 (prod 워크스페이스, 커밋 3개짜리 런):
| 파일 | HEAD~1..HEAD | 실제 변경(base..HEAD) |
|---|
stream_consumers.py (새 모듈 전체) | 0 B | 5,752 B |
test_..._decomp.py (요구사항 본체) | 0 B | 2,390 B |
run.py (마지막 커밋에서 손댐) | 714 B | 714 B |
작업의 본체 두 개가 존재하지 않는 것처럼 보인다. 곁가지 하나만 보인다.
- 흔한 오해 1: "blob 폴백이 있으니 괜찮다." → 폴백은 diff 가 하나도 안
나올 때만 돈다. 한 경로라도 diff 가 나오면 나머지는 조용히 빠진 채 반환된다.
- 흔한 오해 2: "거절이 반복되는 건 모델이 까다로워서다." → 아니다.
판정 문제가 아니라 관측 문제다. 판사를 바꾸거나 프롬프트를 달래도 안 고쳐진다.
Solution
- 작업 시작 시점의 base 를 한 번 잡아 저장하라. 에이전트가 커밋하면 HEAD 가
움직여서 나중에는 복구 불가다.
res = await box.exec("git rev-parse HEAD")
baseline = res.stdout.strip() or None
- diff 는 그 base 대비로.
git diff <baseline> HEAD -- <path>.
base 를 못 잡았으면 diff 를 건너뛰고 blob 폴백으로 — 틀린 창을 쓰느니 없는 게 낫다.
- 부분 성공을 성공으로 반환하지 마라. 요청한 N 경로 중 M(<N) 만 내용이
나왔으면 그 사실을 컨텍스트에 명시하라. 조용히 빠지면 소비자는 "그 파일은
안 바뀌었다"로 읽는다.
- 못 봤을 때는 판정하지 말고
cannot_determine 으로. 단, 이건 증상 봉합이다.
왜 못 봤는지를 따로 고쳐라 — 정직한 무판정이 관측 결함을 없애주지는 않는다.
진단법 — 코드만 읽지 말고 그 코드가 남긴 데이터로 반증하라
(no file content captured) 를 grep 해서 "written_paths 가 빌 때만 나온다"고
결론냈다가, 같은 검증 레코드의 scope.candidates = 8 에 반증당했다.
경로는 있었고 판사에게 가는 경로만 비어 있었다.
- 같은 레코드 안의 다른 소비자가 같은 입력을 어떻게 봤는지 비교하라.
한쪽은 보고 한쪽은 못 봤다면, 입력이 아니라 그 소비자의 조립 경로가 범인이다.
- 그리고 읽는 코드가 현재 버전인지 확인하라. 사건 당시 배포된 커밋과
지금 main 이 다르면 코드 판독은 사건을 설명하지 못한다.
Key Insights
- "파일 목록은 맞는데 내용이 비었다"는 거의 항상 diff 창(revision range) 문제다.
파일 추적(written_paths) 은 멀쩡한데 렌더링만 굶는다.
- 에이전트 루프는 사람처럼 한 번 커밋하지 않는다. 사람 기준으로 짠 리비전
범위는 에이전트 앞에서 무너진다. 커밋 개수를 가정하지 마라.
- 인프라가 에이전트를 눈멀게 하면 에이전트는 우회하지 않고 같은 실패를 무한
반복한다. 한 런이 46번 거절당한 원인이 이것이었다.