에이전트 워크트리 격리, 왜 리다이렉트 있는 for문을 막을까
Claude Code 서브에이전트를 git worktree로 격리했을 때 복합 bash 명령이 실제로 어떻게 막히는지, 오늘 직접 받은 에러 메시지로 원인과 대응을 정리한다.
블로그 자동화 작업을 이어가려고 이 레포에서 for f in ...; do ...; done | tail -300 형태의 셸 명령을 몇 번 돌렸다. 카테고리별 최근 글 목록을 뽑아 유사도 게이트를 확인하려던 것뿐인데, 두 번째 시도부터 명령이 실행되지 않고 이런 문구가 돌아왔다. “This agent is isolated in the worktree /Users/…/worktrees/agent-af673f2a2a553578f, but this command is too complex to verify that it stays inside the worktree; break it into plain, separate commands. Refusing to run it — a worktree-isolated agent’s git operations must target its own worktree.”
사실: 무엇이 막혔는가
git worktree list로 확인해 보니, 지금 작업 디렉터리는 별도 워크트리였다. 원본 레포는 /Users/minhyuk/Antigravity/blog에서 cursor/add-7-category-blog-posts 브랜치를 물고 있고, 내가 서 있는 곳은 /Users/minhyuk/Antigravity/blog/.claude/worktrees/agent-af673f2a2a553578f에서 worktree-agent-af673f2a2a553578f 브랜치를 물고 있었다. 커밋 해시는 둘 다 0a3e643로 같았다. 즉 워크트리는 같은 히스토리 위에서 갈라진 별도 작업 트리다.
막힌 명령의 공통점을 추려 보면 세 가지였다. 파이프(|)로 여러 프로세스를 연결한 것, for 반복문 안에 리다이렉트(>)나 명령을 여러 개 쌓은 것, 그리고 cd 뒤에 상대 경로로 다른 명령을 잇댄 것. 반대로 curl -s -o /dev/null -w "%{http_code}\n" -I <url>처럼 단일 프로그램 한 번 호출은 인자가 길어도 그대로 통과했다. grep -l "category: xxx" src/content/posts/*.md도 단일 명령이라 문제없이 실행됐다.
이유: 격리가 검증하려는 것
에러 메시지가 스스로 이유를 밝히고 있다. “복합 명령이라 워크트리 밖으로 나가지 않는다는 걸 검증하기 너무 어렵다”는 것이다. 셸 명령을 정적으로 분석해 “이 명령이 실행 중 다른 디렉터리를 건드리지 않는다”를 보장하려면, 단일 바이너리 호출 하나를 보는 것과 파이프·서브셸·반복문이 얽힌 스크립트를 보는 것은 난이도가 다르다. 후자는 cd ../../other-repo && rm -rf .처럼 워크트리 경계를 넘는 부수효과를 셸 파서 수준에서 배제하기가 사실상 불가능에 가깝다. 그래서 정책은 “복잡하면 아예 거부”라는 보수적인 규칙을 택한 것으로 보인다. 특히 git 관련 동작에서 이 규칙이 강조된 건, 워크트리 자체가 .git 메타데이터를 공유하는 구조라 잘못된 명령이 다른 워크트리의 상태까지 건드릴 여지가 있기 때문일 것이다.
평가: 실무에서 어떻게 우회하나
우회는 간단했다. 반복문을 쓰고 싶으면 각 항목을 개별 Bash 호출로 쪼갰다. 예를 들어 히어로 이미지 6개의 URL을 한 번에 검증하려던 for u in ...; do curl -I "$u"; done 대신, URL마다 curl -s -o /dev/null -w "%{http_code}\n" -I "$url"을 여섯 번 따로 호출했다. 느리지만 실패하지는 않는다. 파일 목록을 뽑을 때도 for f in *.md; do head -12 "$f" | grep ...; done 대신 grep -l "category: xxx" *.md처럼 grep 한 번으로 끝나는 형태를 우선 시도했다. 정말 반복이 필요하면, 셸 반복문 대신 에이전트 쪽에서 파일 목록을 받아 하나씩 도구를 호출하는 방식으로 바꿨다.
이 정책의 장점은 명확하다. 서브에이전트가 실수로건 프롬프트 인젝션으로건 워크트리 밖 파일을 건드릴 가능성을 셸 단계에서 원천 차단한다. 단점도 있다. 정상적인 배치 작업(로그 여러 개 훑기, 여러 URL 상태 확인)까지 호출 횟수가 늘어난다. 오늘 작업만 봐도 curl 검증이 6번의 개별 도구 호출로 나뉘었다. 자동화 스크립트를 많이 쓰는 워크플로라면 이 비용을 감안해 설계해야 한다.
부연: 어디까지가 안전한 패턴인가
경험적으로 통과한 패턴과 막힌 패턴을 정리하면 이렇다. 통과: 단일 프로그램 호출(옵션·인자 많아도 무방), git status/git diff/git log 단독 호출, grep/ls/cat 단독 호출. 막힘: 파이프로 두 개 이상 프로그램 연결, &&나 ;로 여러 명령 연결, for/while 반복문 안에 명령 여러 줄, 서브셸 $(...) 안에 복합 로직. 다만 이건 이번 세션에서 관찰한 경계일 뿐, 정책 자체는 버전에 따라 더 엄격해지거나 완화될 수 있다.
이 정책을 다른 팀에 적용한다면, 워크트리 격리를 쓰는 에이전트에는 “반복 작업은 도구를 여러 번 호출하는 식으로 설계하라”는 안내를 미리 박아 두는 편이 낫다. 에이전트가 매번 시행착오로 이 규칙을 알아내게 두면, 똑같이 두세 번 실패한 뒤에야 단일 명령으로 쪼개는 패턴을 학습한다. 이번 글도 그 시행착오 과정 자체를 기록해 둔 것이다. 다음에 워크트리 격리 환경에서 작업할 때는 처음부터 파이프·반복문을 피하고, 필요하면 파일별로 도구를 나눠 호출하는 쪽으로 설계를 시작할 생각이다.
한 가지 더 확인해 둘 점은, 이 제약이 “권한 부족”이 아니라 “명령 복잡도” 문제라는 것이다. 실제로 같은 디렉터리 안에서 단일 명령으로는 파일 쓰기·git add·git commit·npm run build가 모두 정상 동작했다. 즉 워크트리 경계 밖으로 나가는지 검증하기 쉬운 형태의 명령이면 권한 자체는 충분히 열려 있다. 문제는 오직 “정적으로 안전을 증명하기 어려운 형태의 명령”이라는 점을 구분해서 봐야, 이 에러를 권한 이슈로 오인해 불필요하게 설정을 뒤지는 삽질을 피할 수 있다.
출처
- 내부 실측: 이 레포(
/Users/minhyuk/Antigravity/blog/.claude/worktrees/agent-af673f2a2a553578f)에서 2026-08-04 세션 중git worktree list,npm run build, 복합for/파이프 명령 시도 시 반환된 실제 도구 에러 메시지를 그대로 인용 - 내부 실측: 단일 명령(
grep -l,curl -I개별 호출)은 정상 실행되어npm run build가92 page(s) built in 642ms로 정상 완료됨을 확인