Worktree Isolation μ›Œν¬νŠΈλ¦¬ 격리

Developer Deep Dive 개발자 심측 κ°€μ΄λ“œ

● Overview κ°œμš”

ROACH PI uses temporary git worktrees to isolate subagent and team worker edits from the main checkout. Every subagent or team worker that requests worktree isolation runs inside a detached worktree β€” a separate working directory backed by the same repository object store.

ROACH PIλŠ” μ„œλΈŒμ—μ΄μ „νŠΈμ™€ νŒ€ μ›Œμ»€μ˜ νŽΈμ§‘μ„ 메인 μ²΄ν¬μ•„μ›ƒμ—μ„œ κ²©λ¦¬ν•˜κΈ° μœ„ν•΄ μž„μ‹œ git μ›Œν¬νŠΈλ¦¬λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€. μ›Œν¬νŠΈλ¦¬ 격리λ₯Ό μš”μ²­ν•˜λŠ” λͺ¨λ“  μ„œλΈŒμ—μ΄μ „νŠΈλ‚˜ νŒ€ μ›Œμ»€λŠ” λ””νƒœμΉ˜λœ μ›Œν¬νŠΈλ¦¬ β€” λ™μΌν•œ μ €μž₯μ†Œ 객체 μ €μž₯μ†Œλ₯Ό 기반으둜 ν•˜λŠ” λ³„λ„μ˜ μž‘μ—… 디렉토리 β€” μ—μ„œ μ‹€ν–‰λ©λ‹ˆλ‹€.

After the run completes, a complete diff artifact is captured before the worktree is cleaned up. This gives you a full record of every change the agent made β€” files added, modified, or deleted β€” without any risk of polluting your working tree.

싀행이 μ™„λ£Œλœ ν›„, μ›Œν¬νŠΈλ¦¬κ°€ μ •λ¦¬λ˜κΈ° 전에 μ™„μ „ν•œ diff μ•„ν‹°νŒ©νŠΈκ°€ μΊ‘μ²˜λ©λ‹ˆλ‹€. μž‘μ—… 트리λ₯Ό μ˜€μ—Όμ‹œν‚¬ μœ„ν—˜ 없이 μ—μ΄μ „νŠΈκ°€ μˆ˜ν–‰ν•œ λͺ¨λ“  λ³€κ²½ β€” μΆ”κ°€, μˆ˜μ • λ˜λŠ” μ‚­μ œλœ 파일 β€” 에 λŒ€ν•œ 전체 기둝을 얻을 수 μžˆμŠ΅λ‹ˆλ‹€.

● Key Benefit ● 핡심 이점

Even if a subagent makes destructive changes (deletes files, breaks builds), your main checkout remains untouched. The diff artifact preserves the full change set for review or replay.

μ„œλΈŒμ—μ΄μ „νŠΈκ°€ 파괴적인 변경을 μˆ˜ν–‰ν•˜λ”λΌλ„(파일 μ‚­μ œ, λΉŒλ“œ μ‹€νŒ¨), 메인 체크아웃은 영ν–₯을 λ°›μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. Diff μ•„ν‹°νŒ©νŠΈλŠ” κ²€ν†  λ˜λŠ” μž¬μƒμ„ μœ„ν•œ 전체 λ³€κ²½ μ„ΈνŠΈλ₯Ό λ³΄μ‘΄ν•©λ‹ˆλ‹€.

β–Ά How It Works μž‘λ™ 방식

The worktree lifecycle follows a strict sequence of steps:

μ›Œν¬νŠΈλ¦¬ 라이프사이클은 λ‹€μŒ μ—„κ²©ν•œ 단계 μˆœμ„œλ₯Ό λ”°λ¦…λ‹ˆλ‹€:

  1. git rev-parse --show-toplevel β€” Find git root β€” Git 루트 μ°ΎκΈ°

    Resolve the repository root directory. All subsequent git operations use this path.

    μ €μž₯μ†Œ 루트 디렉토리λ₯Ό ν™•μΈν•©λ‹ˆλ‹€. 이후 λͺ¨λ“  git μž‘μ—…μ€ 이 경둜λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

  2. mkdtemp(tmpdir(), pi-subagent-worktree-${runId}-) β€” Create temp directory β€” μž„μ‹œ 디렉토리 생성

    Allocate a unique temporary directory in the system temp folder. The runId ensures no collisions between concurrent runs.

    μ‹œμŠ€ν…œ μž„μ‹œ 폴더에 κ³ μœ ν•œ μž„μ‹œ 디렉토리λ₯Ό ν• λ‹Ήν•©λ‹ˆλ‹€. runIdλŠ” λ™μ‹œ μ‹€ν–‰ κ°„ μΆ©λŒμ„ λ°©μ§€ν•©λ‹ˆλ‹€.

  3. git worktree add --detach <path> HEAD β€” Create detached worktree β€” λ””νƒœμΉ˜λœ μ›Œν¬νŠΈλ¦¬ 생성

    Create a detached worktree at the current HEAD commit. No branch is created β€” the worktree starts as a clean mirror of the current repository state.

    ν˜„μž¬ HEAD μ»€λ°‹μ—μ„œ λ””νƒœμΉ˜λœ μ›Œν¬νŠΈλ¦¬λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. λΈŒλžœμΉ˜λŠ” μƒμ„±λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€ β€” μ›Œν¬νŠΈλ¦¬λŠ” ν˜„μž¬ μ €μž₯μ†Œ μƒνƒœμ˜ κΉ¨λ—ν•œ 미러둜 μ‹œμž‘ν•©λ‹ˆλ‹€.

  4. Subagent/team worker cwd mapped into worktree μ„œλΈŒμ—μ΄μ „νŠΈ/νŒ€ μ›Œμ»€ cwdλ₯Ό μ›Œν¬νŠΈλ¦¬μ— λ§€ν•‘

    The agent's working directory is translated from the main checkout path to the equivalent path inside the worktree. All file reads, writes, and command executions happen in isolation.

    μ—μ΄μ „νŠΈμ˜ μž‘μ—… 디렉토리가 메인 체크아웃 κ²½λ‘œμ—μ„œ μ›Œν¬νŠΈλ¦¬ λ‚΄λΆ€μ˜ ν•΄λ‹Ή 경둜둜 λ³€ν™˜λ©λ‹ˆλ‹€. λͺ¨λ“  파일 읽기, μ“°κΈ° 및 λͺ…λ Ή 싀행이 격리된 μƒνƒœμ—μ„œ μ΄λ£¨μ–΄μ§‘λ‹ˆλ‹€.

  5. Capture diff artifacts Diff μ•„ν‹°νŒ©νŠΈ 캑처

    After the agent finishes: capture git status --short, git diff --stat, git diff, and git diff --cached from the worktree.

    μ—μ΄μ „νŠΈ μ™„λ£Œ ν›„: μ›Œν¬νŠΈλ¦¬μ—μ„œ git status --short, git diff --stat, git diff, git diff --cachedλ₯Ό μΊ‘μ²˜ν•©λ‹ˆλ‹€.

  6. Write worktree.diff.md to artifact directory μ•„ν‹°νŒ©νŠΈ 디렉토리에 worktree.diff.md μž‘μ„±

    All captured output is written to <artifactDir>/worktree.diff.md in a structured markdown format.

    캑처된 λͺ¨λ“  좜λ ₯이 κ΅¬μ‘°ν™”λœ λ§ˆν¬λ‹€μš΄ ν˜•μ‹μœΌλ‘œ <artifactDir>/worktree.diff.md에 μž‘μ„±λ©λ‹ˆλ‹€.

  7. git worktree remove --force <path> β€” Cleanup worktree β€” μ›Œν¬νŠΈλ¦¬ 정리

    Remove the temporary worktree. The --force flag handles uncommitted changes and locked files. The temp directory is then deleted.

    μž„μ‹œ μ›Œν¬νŠΈλ¦¬λ₯Ό μ œκ±°ν•©λ‹ˆλ‹€. --force ν”Œλž˜κ·ΈλŠ” μ»€λ°‹λ˜μ§€ μ•Šμ€ λ³€κ²½κ³Ό 잠긴 νŒŒμΌμ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€. κ·Έ ν›„ μž„μ‹œ 디렉토리가 μ‚­μ œλ©λ‹ˆλ‹€.

β–  Diff Capture Format Diff 캑처 ν˜•μ‹

The diff artifact is written as a structured markdown file. This is the exact format produced at <artifactDir>/worktree.diff.md:

Diff μ•„ν‹°νŒ©νŠΈλŠ” κ΅¬μ‘°ν™”λœ λ§ˆν¬λ‹€μš΄ 파일둜 μž‘μ„±λ©λ‹ˆλ‹€. <artifactDir>/worktree.diff.md에 μƒμ„±λ˜λŠ” μ •ν™•ν•œ ν˜•μ‹μž…λ‹ˆλ‹€:

# Worktree Diff Worktree: <path> Git root: <gitRoot> Max captured diff buffer: 1048576 bytes ## Status <git status --short output> ## Diff Stat <git diff --stat output> ## Unstaged Diff <git diff output> ## Staged Diff <git diff --cached output>
● Buffer Limit ● 버퍼 μ œν•œ

The total diff output is capped at 1,048,576 bytes (1 MB). If the combined unstaged and staged diff exceeds this limit, the output is truncated. The header always indicates the max buffer size so consumers know truncation is possible.

κ²°ν•©λœ diff 좜λ ₯은 1,048,576 λ°”μ΄νŠΈ(1 MB)둜 μ œν•œλ©λ‹ˆλ‹€. κ²°ν•©λœ unstaged 및 staged diffκ°€ 이 ν•œλ„λ₯Ό μ΄ˆκ³Όν•˜λ©΄ 좜λ ₯이 μž˜λ¦½λ‹ˆλ‹€. 헀더에 항상 μ΅œλŒ€ 버퍼 크기가 ν‘œμ‹œλ˜μ–΄ μ†ŒλΉ„μžκ°€ 잘림 κ°€λŠ₯성을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.

● Cleanup States 정리 μƒνƒœ

The worktree cleanup process tracks its outcome as one of four states:

μ›Œν¬νŠΈλ¦¬ 정리 ν”„λ‘œμ„ΈμŠ€λŠ” κ²°κ³Όλ₯Ό λ‹€μŒ λ„€ κ°€μ§€ μƒνƒœ 쀑 ν•˜λ‚˜λ‘œ μΆ”μ ν•©λ‹ˆλ‹€:

Stateμƒνƒœ Whenλ°œμƒ μ‹œμ 
not-needed Worktree was never created (worktree isolation disabled or creation was skipped). μ›Œν¬νŠΈλ¦¬κ°€ μƒμ„±λ˜μ§€ μ•ŠμŒ(μ›Œν¬νŠΈλ¦¬ 격리 λΉ„ν™œμ„±ν™” λ˜λŠ” 생성 μƒλž΅).
removed Successfully cleaned up via git worktree remove --force followed by temp directory deletion. git worktree remove --force 및 μž„μ‹œ 디렉토리 μ‚­μ œλ‘œ μ„±κ³΅μ μœΌλ‘œ 정리됨.
failed git worktree remove --force failed. The worktree and temp directory may still exist on disk. git worktree remove --force μ‹€νŒ¨. μ›Œν¬νŠΈλ¦¬μ™€ μž„μ‹œ 디렉토리가 λ””μŠ€ν¬μ— λ‚¨μ•„μžˆμ„ 수 있음.
kept Diff capture required the worktree to be preserved (e.g. the diff artifact needs the worktree to remain accessible for post-processing). Diff 캑처둜 인해 μ›Œν¬νŠΈλ¦¬ 보쑴이 ν•„μš”ν•¨(예: diff μ•„ν‹°νŒ©νŠΈκ°€ ν›„μ²˜λ¦¬λ₯Ό μœ„ν•΄ μ›Œν¬νŠΈλ¦¬μ— μ ‘κ·Ό κ°€λŠ₯ν•΄μ•Ό 함).

β˜… Using with Subagents μ„œλΈŒμ—μ΄μ „νŠΈμ™€ ν•¨κ»˜ μ‚¬μš©

Enable worktree isolation for individual subagent calls by setting worktree: true in the subagent tool options:

μ„œλΈŒμ—μ΄μ „νŠΈ 도ꡬ μ˜΅μ…˜μ— worktree: trueλ₯Ό μ„€μ •ν•˜μ—¬ κ°œλ³„ μ„œλΈŒμ—μ΄μ „νŠΈ ν˜ΈμΆœμ— λŒ€ν•΄ μ›Œν¬νŠΈλ¦¬ 격리λ₯Ό ν™œμ„±ν™”ν•©λ‹ˆλ‹€:

subagent({ agent: 'worker', task: 'Refactor the auth module', worktree: true, // enable worktree isolation output: 'artifacts/' // diff written to artifacts/worktree.diff.md })

The subagent runs entirely inside the worktree. All file edits, new files, and deletions are isolated. When the subagent finishes, the diff is automatically captured to the specified artifactDir.

μ„œλΈŒμ—μ΄μ „νŠΈλŠ” μ›Œν¬νŠΈλ¦¬ λ‚΄λΆ€μ—μ„œ μ™„μ „νžˆ μ‹€ν–‰λ©λ‹ˆλ‹€. λͺ¨λ“  파일 νŽΈμ§‘, μƒˆ 파일 및 μ‚­μ œκ°€ κ²©λ¦¬λ©λ‹ˆλ‹€. μ„œλΈŒμ—μ΄μ „νŠΈκ°€ μ™„λ£Œλ˜λ©΄ diffκ°€ μ§€μ •λœ artifactDir에 μžλ™μœΌλ‘œ μΊ‘μ²˜λ©λ‹ˆλ‹€.

β—† Using with Team Mode νŒ€ λͺ¨λ“œμ™€ ν•¨κ»˜ μ‚¬μš©

In team mode, set the worktree policy via the /team command:

νŒ€ λͺ¨λ“œμ—μ„œλŠ” /team λͺ…령을 톡해 μ›Œν¬νŠΈλ¦¬ 정책을 μ„€μ •ν•©λ‹ˆλ‹€:

Policyμ •μ±… Behaviorλ™μž‘
on Always use worktree isolation for every team worker. Diffs are captured per-worker. λͺ¨λ“  νŒ€ μ›Œμ»€μ— λŒ€ν•΄ 항상 μ›Œν¬νŠΈλ¦¬ 격리 μ‚¬μš©. Diffκ°€ μ›Œμ»€λ³„λ‘œ 캑처됨.
auto Use worktree isolation if available (git repository detected, worktree creation succeeds). Falls back to no isolation gracefully. μ‚¬μš© κ°€λŠ₯ν•œ 경우 μ›Œν¬νŠΈλ¦¬ 격리 μ‚¬μš©(git μ €μž₯μ†Œ 감지, μ›Œν¬νŠΈλ¦¬ 생성 성곡). 격리 없이 μš°μ•„ν•˜κ²Œ 폴백.
off No worktree isolation. Team workers edit directly in the main checkout. μ›Œν¬νŠΈλ¦¬ 격리 μ—†μŒ. νŒ€ μ›Œμ»€κ°€ 메인 μ²΄ν¬μ•„μ›ƒμ—μ„œ 직접 νŽΈμ§‘.
/team worktree-policy=on refactor-auth implement-tests

Each team worker gets its own isolated worktree. Diffs are captured to per-worker artifact directories, so you can review each worker's changes independently.

각 νŒ€ μ›Œμ»€λŠ” 자체 격리된 μ›Œν¬νŠΈλ¦¬λ₯Ό κ°–μŠ΅λ‹ˆλ‹€. Diffκ°€ μ›Œμ»€λ³„ μ•„ν‹°νŒ©νŠΈ 디렉토리에 μΊ‘μ²˜λ˜λ―€λ‘œ 각 μ›Œμ»€μ˜ 변경을 λ…λ¦½μ μœΌλ‘œ κ²€ν† ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

● Edge Cases μ—£μ§€ μΌ€μ΄μŠ€

Worktree Creation Failure μ›Œν¬νŠΈλ¦¬ 생성 μ‹€νŒ¨

If git worktree add fails (e.g. the repository is in a state that doesn't support worktrees, or the temp directory is unavailable), the system falls back to no isolation. The agent runs in the main checkout as usual. This is logged but does not fail the run.

git worktree addκ°€ μ‹€νŒ¨ν•˜λ©΄(예: μ €μž₯μ†Œκ°€ μ›Œν¬νŠΈλ¦¬λ₯Ό μ§€μ›ν•˜μ§€ μ•ŠλŠ” μƒνƒœμ΄κ±°λ‚˜ μž„μ‹œ 디렉토리λ₯Ό μ‚¬μš©ν•  수 μ—†μŒ), μ‹œμŠ€ν…œμ€ 격리 없이 ν΄λ°±ν•©λ‹ˆλ‹€. μ—μ΄μ „νŠΈκ°€ ν‰μ†Œμ²˜λŸΌ 메인 μ²΄ν¬μ•„μ›ƒμ—μ„œ μ‹€ν–‰λ©λ‹ˆλ‹€. μ΄λŠ” λ‘œκ·Έμ— κΈ°λ‘λ˜μ§€λ§Œ 싀행을 μ‹€νŒ¨μ‹œν‚€μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

Diff Buffer Overflow Diff 버퍼 μ˜€λ²„ν”Œλ‘œμš°

The combined diff output is capped at 1,048,576 bytes (1 MB). If the agent generates a diff larger than this, the output is truncated at the buffer boundary. The artifact header always records the max buffer size so consumers can detect potential truncation.

κ²°ν•©λœ diff 좜λ ₯은 1,048,576 λ°”μ΄νŠΈ(1 MB)둜 μ œν•œλ©λ‹ˆλ‹€. μ—μ΄μ „νŠΈκ°€ 이보닀 큰 diffλ₯Ό μƒμ„±ν•˜λ©΄ 좜λ ₯이 버퍼 κ²½κ³„μ—μ„œ μž˜λ¦½λ‹ˆλ‹€. μ•„ν‹°νŒ©νŠΈ 헀더에 항상 μ΅œλŒ€ 버퍼 크기가 κΈ°λ‘λ˜μ–΄ μ†ŒλΉ„μžκ°€ 잠재적 μž˜λ¦Όμ„ 감지할 수 μžˆμŠ΅λ‹ˆλ‹€.

Cleanup Failure 정리 μ‹€νŒ¨

If git worktree remove --force fails (e.g. a process still holds a file handle in the worktree), the cleanup state is set to failed. The failure is logged but does not fail the run. The temp directory may remain on disk and will be cleaned up by the OS temp directory reaper.

git worktree remove --forceκ°€ μ‹€νŒ¨ν•˜λ©΄(예: ν”„λ‘œμ„ΈμŠ€κ°€ μ—¬μ „νžˆ μ›Œν¬νŠΈλ¦¬μ˜ 파일 핸듀을 보유 쀑), 정리 μƒνƒœκ°€ failed둜 μ„€μ •λ©λ‹ˆλ‹€. μ‹€νŒ¨λŠ” λ‘œκ·Έμ— κΈ°λ‘λ˜μ§€λ§Œ 싀행을 μ‹€νŒ¨μ‹œν‚€μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μž„μ‹œ 디렉토리가 λ””μŠ€ν¬μ— 남을 수 있으며 OS μž„μ‹œ 디렉토리 정리기에 μ˜ν•΄ μ •λ¦¬λ©λ‹ˆλ‹€.

Concurrent Worktrees λ™μ‹œ μ›Œν¬νŠΈλ¦¬

Each run uses a unique temp directory suffixed with the runId, so concurrent subagents or team workers never conflict. Git itself supports multiple worktrees on the same repository natively.

각 싀행은 runIdκ°€ μ ‘λ―Έμ‚¬λ‘œ 뢙은 κ³ μœ ν•œ μž„μ‹œ 디렉토리λ₯Ό μ‚¬μš©ν•˜λ―€λ‘œ λ™μ‹œ μ„œλΈŒμ—μ΄μ „νŠΈλ‚˜ νŒ€ μ›Œμ»€κ°€ μΆ©λŒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. Git μžμ²΄κ°€ λ™μΌν•œ μ €μž₯μ†Œμ—μ„œ μ—¬λŸ¬ μ›Œν¬νŠΈλ¦¬λ₯Ό 기본적으둜 μ§€μ›ν•©λ‹ˆλ‹€.