Skip to content

Worktree isolation for sub-agents #968

@bug-ops

Description

@bug-ops

Part of #974

Background

Claude Code supports isolation: worktree in agent definitions. When set, the agent runs inside a temporary git worktree created from the current HEAD. If the agent makes no changes, the worktree is automatically cleaned up. This prevents unintended modifications to the working tree during exploratory or experimental tasks.

Current state in Zeph

All sub-agents share the same working directory as the parent process. There is no isolation mechanism. SubAgentPermissions has a background: bool flag but no worktree concept.

Implementation

  1. Add isolation: IsolationMode to AgentFileSpec and SubAgentDef:

    pub enum IsolationMode {
        None,
        Worktree,
    }
  2. Implement WorktreeManager in zeph-core/src/subagent/worktree.rs:

    • create(base_path: &Path, agent_id: &AgentId) -> Result<WorktreeHandle>
    • Creates worktree via git worktree add --detach /tmp/zeph-agent-{id} HEAD
    • WorktreeHandle contains path and implements Drop for cleanup
    • cleanup(): runs git worktree remove --force {path} then std::fs::remove_dir_all
  3. On agent spawn with IsolationMode::Worktree:

    • Create worktree before first tool call
    • Set agent's working directory to the worktree path
    • On completion: if git status --porcelain in worktree is empty, auto-remove; otherwise log path and leave for user inspection
  4. Expose worktree path in AgentHandle for status reporting.

  5. Config key: agent.worktree_base_dir (default: std::env::temp_dir()).

Acceptance criteria

  • isolation: worktree creates a git worktree before agent execution.
  • Worktree is placed under a configurable base directory.
  • Clean worktree (no changes) is automatically removed after agent completion.
  • Dirty worktree is left on disk and its path is logged at info level.
  • Agent's shell tool calls execute in the worktree path.
  • WorktreeHandle::drop is idempotent (no panic if already removed).
  • Unit test: mock git commands, verify worktree create/remove sequence.
  • cargo nextest run -p zeph-core passes.

Technical notes

  • Use tokio::process::Command to invoke git — keep it async.
  • Guard WorktreeManager::create behind a check that the base path is inside a git repo (git rev-parse --git-dir).
  • If git is not available, log a warning and fall back to IsolationMode::None.
  • WorktreeHandle uses std::sync::Arc<WorktreeDrop> where WorktreeDrop implements Drop for the sync cleanup path (std::process::Command).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions