AI 에이전트 여러 세션이 서로 다른 Git 워크트리에서 작업하도록 해도, 주변 도구까지 자동으로 격리되지는 않았다. 내가 사용하는 개발 하네스에서는 한 세션이 다른 세션의 리뷰 의무 때문에 종료를 막혔다. 반대로 워크트리에서 코드를 수정했는데 필요한 검사 표시가 전혀 생기지 않는 경우도 있었다.
하네스는 도구 사용 전후와 작업 종료 시점에 검사하는 훅, 그리고 아직 수행해야 할 리뷰·설명 등의 상태를 관리하는 장치다. 나는 상태의 소유 단위와 루트 판정 방식을 결정했고, 구현·회귀 시험·리뷰에는 AI 에이전트를 활용했다. 이 글은 2026년 10월에 적용한 세션 격리와 루트 고정 두 변경을 다룬다.
코드 디렉터리와 검사 상태의 경계가 달랐다
Git 워크트리는 같은 저장소의 여러 브랜치를 별도 작업 디렉터리에 펼칠 수 있게 한다. 다만 저장소 정보 중 일부는 공유하며, Git 바깥에 만든 상태 파일까지 분리해 주지는 않는다. Git worktree 문서
당시 환경에서는 워크트리 세션에도 프로젝트 디렉터리 값으로 메인 체크아웃이 전달됐다. 하네스는 그 값으로 상태 폴더와 파일 분류 기준을 함께 정했다. 서로 다른 두 문제의 원인이 같은 가정에 있었다.
| 실패 | 원인 | 실제 영향 |
|---|---|---|
| 다른 세션의 의무를 읽음 | 모든 세션이 같은 상태 폴더 사용 | 다른 작업의 산출물 때문에 종료가 3회 차단 |
| 읽기 작업이 변경으로 잡힘 | Bash 변경 감지 시간 창을 공유 | 체크아웃 파일 197개가 리뷰 대상으로 기록 |
| 워크트리 수정이 빠짐 | 메인 기준 상대경로로만 파일 분류 | 실제 코드가 하네스 내부 경로처럼 보여 검사 누락 |
검사 횟수를 더 늘려도 이 문제는 해결되지 않는다. 먼저 어떤 세션의 상태인지, 어느 체크아웃의 파일인지 구분해야 했다.
상태는 세션별로, 기존 상태는 그대로
훅 입력에 session_id가 있으면 세션별 상태 디렉터리를 사용하도록 바꿨다. 키는 세션 ID의 SHA-256 결과 앞 32자리, 즉 128비트를 사용했다. 처음 검토한 32비트 체크섬에서는 충돌 사례가 확인되어 더 넓은 키로 바꿨다. 충돌 가능성이 수학적으로 0이라는 의미는 아니다.
리뷰 필요 표시뿐 아니라 Bash 변경 감지 창과 종료 차단 카운터도 이 경계에 포함했다. 해당 환경에서 서브에이전트 도구 호출은 부모와 같은 세션 ID를 전달하는 것도 확인했다. 따라서 이 변경은 에이전트마다 상태를 나누는 설계가 아니라, 같은 작업 세션의 의무를 함께 관리하는 설계다.
세션 ID가 없는 입력은 기존 공유 경로로 돌아가도록 호환성을 남겼다. 반대로 세션 ID가 있는 경로에서는 옛 공유 플래그를 읽거나 지우지 않는다. 새 세션이 오래된 의무를 임의로 떠안거나, 다른 실행이 필요한 상태를 지우지 않도록 한 결정이다.
루트는 첫 훅에서 정하고 세션 동안 유지
상태 폴더를 나눠도 파일 분류 기준이 메인 체크아웃이면 워크트리 편집은 계속 빠진다. 두 번째 변경에서는 첫 훅 호출의 현재 디렉터리로 작업 루트를 정해 세션 상태에 기록하고, 이후 cd로 이동해도 그 루트는 바꾸지 않았다.
매 도구 호출마다 루트를 바꾸는 방식도 가능하지만, 그러면 앞서 생긴 리뷰 의무와 나중에 수행하는 검사가 서로 다른 체크아웃을 가리킬 수 있다. 한 세션에 기준 루트를 하나 유지하는 쪽을 선택했다.
다른 체크아웃의 파일을 수정하거나 삭제하는 경우까지 무시하지는 않았다. 해당 파일이 속한 체크아웃 기준으로 코드 여부를 분류하고, 의무에는 절대경로를 기록했다. 작업 루트와 변경 파일의 소속을 별개로 다룬 것이다.
이 과정에서 기본 브랜치 커밋 가드를 넓히자, 관련 없는 별도 저장소의 정상 커밋까지 막는 회귀가 발생했다. 범위를 같은 Git 공통 디렉터리를 사용하는 체크아웃으로 좁히고, Git 조회 자체가 실패한 경우와 실제로 다른 저장소인 경우를 구분했다. --show-toplevel과 --git-common-dir의 의미는 git rev-parse 문서에 정리돼 있다. 워크트리의 .git이 디렉터리가 아니라 실제 Git 디렉터리를 가리키는 파일일 수도 있다는 점도 고려해야 한다. Git 저장소 구조 문서
정상 통과뿐 아니라 잘못된 구현을 잡는지도 확인
| 검증 대상 | 당시 검증 기록 |
|---|---|
| 세션 상태 격리 | 회귀 14케이스, 의도적으로 망가뜨린 변이 8개 검출 |
| 워크트리 루트 판정 | 초기 회귀 54케이스에 후속 오류 경로 회귀 추가 |
| 루트 판정 변이 시험 | 24개 중 23개 검출 |
| 실제 적용 범위 | 메인 체크아웃과 워크트리 4곳에 로컬 적용 |
변이 시험은 상태 공유나 판정 오류를 의도적으로 되살린 코드에서 시험이 실패하는지 확인하는 방식이다. 원본에서 시험이 통과하는 것만으로는, 시험이 결함을 놓치고 있는지 알기 어렵기 때문이다.
루트 판정 변이는 23/24였으므로 모든 잘못된 구현을 검출했다고 쓰지 않았다. 10월 2일 로컬 적용 뒤, 두 변경은 10월 12일 템플릿 main에 머지됐다. 이 수치는 개발 속도가 몇 퍼센트 좋아졌다는 측정이 아니라, 특정 누락과 상태 간섭을 막는 회귀 근거다. 저장소가 비공개이므로 여기서는 개인 경로와 내부 원문을 제외하고 구조와 검증 범위를 정리했다.
세션 격리가 해결하지 않는 것
첫 훅이 잘못된 폴더에서 시작하면 그 루트가 세션 동안 유지된다. 다른 체크아웃의 편집 의무를 기록해도 그 체크아웃의 타입 검사·스타일 검사·push 확인까지 모두 수행하는 것은 아니다. 일부 작업 산출물과 스윕 시간 창도 여전히 공유한다.
세션이 바뀌면 이전 세션의 남은 의무를 자동 승계하지 않는다. 루트 기록에는 하드 링크를 이용한 경쟁 제어를 사용했지만, 이를 사용할 수 없는 파일시스템의 대체 경로까지 같은 원자성을 보장하지 않는다. 악의적 우회를 막는 보안 격리 장치로 설명할 수 없는 이유다.
이 개선에서 중심이 된 것은 검사 규칙의 개수보다 상태의 소유자와 검사 대상의 일치였다. 워크트리를 늘려 병렬로 개발할 때는 코드 충돌뿐 아니라, 그 작업을 검사하는 도구가 무엇을 공유하고 무엇을 분리하는지도 함께 설계해야 한다.