Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

feat: worktree isolation for task mode workers #226

Copy link
Copy link
@rocklambros

Description

@rocklambros
Issue body actions

Summary

Task mode (the default execution mode for /zerg:rush) currently has no git worktree isolation. All subagent workers edit files in the same working directory. Conflict prevention relies entirely on:

  1. Design-time validationvalidate_file_ownership() ensures no file appears in two tasks' files.create or files.modify lists
  2. Prompt-based discipline — each worker prompt includes "IMPORTANT: Only touch files listed above. Other files are owned by other tasks."
  3. Level sequencing — all Level N tasks complete before Level N+1 starts

Container and subprocess modes already use full git worktree isolation via WorktreeManager. This issue tracks adding optional worktree isolation to task mode for stronger conflict guarantees.

Current Architecture

Task Mode (no isolation)

/zerg:rush (default, --mode task)
  → Orchestrator uses Task tool for parallelization
  → All subagents share the same working directory
  → File ownership enforced by prompt instructions only
  → No merge phase needed — direct commits to shared checkout

Relevant prompt template (rush.core.md:159-199):

## Files
- **Create**: {files.create}
- **Modify**: {files.modify}
- **Read (context only)**: {files.read}

IMPORTANT: Only touch files listed above. Other files are owned by other tasks.

## On Completion
1. Stage ONLY your owned files: git add {files.create + files.modify}
2. Commit with message: "feat({FEATURE}): {TASK_ID} - {title}"

Container/Subprocess Mode (full isolation)

/zerg:rush --mode container|subprocess
  → WorkerManager.spawn_worker(worker_id)
    → WorktreeManager.create(feature, worker_id)
      → git worktree add .zerg-worktrees/{feature}/worker-{id} zerg/{feature}/worker-{id}
  → Each worker operates in isolated worktree on dedicated branch
  → Merge phase reconciles branches after each level

Relevant code (rush.core.md:238-262):

for i in $(seq 0 $((WORKERS - 1))); do
  BRANCH="zerg/$FEATURE/worker-$i"
  WORKTREE="../.zerg-worktrees/$FEATURE/worker-$i"
  git worktree add "$WORKTREE" "$BRANCH"
done

Problem Statement

While prompt-based file ownership works in practice (LLM workers rarely edit files they aren't told to), there is no runtime enforcement. Failure modes include:

  1. Accidental file touches — A worker modifies a shared config file, utility, or __init__.py that it reads but doesn't own. Common with auto-formatters or IDE-style cleanup behaviors.
  2. Implicit side effects — Running ruff format or similar tools in the shared directory reformats files beyond the worker's ownership scope.
  3. Race conditions on git operations — Two workers running git add + git commit concurrently in the same checkout can produce corrupted commits or include unintended files.
  4. No rollback granularity — If one worker in a level fails, its partial changes are already committed to the shared branch. There's no clean way to revert just that worker's changes without reverting the entire level.

Proposed Enhancement

Add optional worktree isolation to task mode, controllable via a flag:

/zerg:rush --isolate          # Enable worktree isolation in task mode
/zerg:rush --isolate=worktree # Explicit (same as above)
/zerg:rush                    # Default: no isolation (current behavior)

Implementation Approach Options

Option A: Claude Code Native Worktree Isolation

Claude Code's Agent tool supports isolation: "worktree" natively. Each Task tool call could use this:

# In rush.core.md subagent prompt generation
# Add isolation parameter to each Task tool call
Agent(
    prompt="You are ZERG Worker executing task {TASK_ID}...",
    subagent_type="general-purpose",
    isolation="worktree"  # <-- Claude Code creates/manages worktree automatically
)

Pros:

  • Zero custom worktree management needed — Claude Code handles creation and cleanup
  • Each subagent gets an auto-managed worktree with its own branch
  • Minimal changes to ZERG codebase

Cons:

  • Merge strategy is opaque — Claude Code manages the worktree lifecycle, not ZERG
  • Changes made in the worktree may need manual merging back
  • Less control over branch naming, cleanup timing, and merge order
  • Need to verify Claude Code worktree behavior with concurrent agents

Option B: Reuse Existing WorktreeManager

Extend the existing WorktreeManager (zerg/worktree.py) to task mode:

# Before launching task-mode subagents at each level:
worktree_mgr = WorktreeManager(repo_path)
for task in level_tasks:
    wt_path = worktree_mgr.create(feature, task.id, base_branch="main")
    # Pass worktree path to subagent prompt
    # Subagent uses: cd {wt_path} && <do work>

# After level completes:
for task in level_tasks:
    merge_worker_branch(task)
    worktree_mgr.delete(wt_path)

Pros:

  • Full control over worktree lifecycle, branch naming, merge order
  • Consistent with container/subprocess modes
  • Reuses existing, tested WorktreeManager API
  • Enables ZERG's existing merge coordinator for conflict resolution

Cons:

  • Requires adding a merge phase to task mode (currently has none)
  • Significant complexity increase — task mode's simplicity is a feature
  • Disk space overhead (full checkout per worker per level)
  • Slower execution (worktree creation + merge per level)

Option C: Hybrid — Lightweight File Locking

Instead of full worktree isolation, add runtime file-level enforcement:

# Before task execution, create lock files for owned files
for file in task.files.create + task.files.modify:
    lock_path = f".zerg/locks/{hashlib.sha256(file.encode()).hexdigest()}"
    # Attempt exclusive lock creation
    # Fail if lock already held by another task

# After task completion, release locks

Pros:

  • Minimal overhead (no worktree creation, no merge phase)
  • Catches accidental cross-ownership edits at runtime
  • Preserves task mode's simplicity

Cons:

  • Doesn't prevent git staging race conditions
  • Lock management adds complexity
  • Subagents may not honor lock checks (prompt-based again)

Recommended Approach

Option A (Claude Code native isolation: "worktree") for initial implementation:

  • Lowest implementation cost
  • Leverages platform-native isolation
  • Can upgrade to Option B later if more control is needed

Option B as a follow-up if Option A proves insufficient for merge control or branch naming requirements.

Key Files

File Role Lines
zerg/data/commands/rush.core.md Task mode execution flow (Step 2) 95-228
zerg/data/commands/rush.core.md Container/subprocess worktree setup (Step 3) 232-262
zerg/worktree.py WorktreeManager — create, delete, list, sync 30-340
zerg/worker_manager.py WorkerManager — calls worktree create/delete on spawn/terminate
zerg/launcher_configurator.py Mode detection and launcher creation 117-126
zerg/validation.py validate_file_ownership() — design-time conflict detection
zerg/data/commands/merge.core.md Merge coordinator (container/subprocess only)

Acceptance Criteria

  • New --isolate flag parsed by /zerg:rush
  • When enabled, each task-mode subagent runs in an isolated worktree
  • Worker branches follow naming convention: zerg/{feature}/task-{id}
  • Merge phase added to task mode when --isolate is active
  • Worktrees cleaned up after level completion (or on failure)
  • validate_file_ownership() still runs at design time (defense in depth)
  • Prompt-based file ownership instructions preserved (belt and suspenders)
  • /zerg:status reports worktree state when isolation is active
  • /zerg:cleanup handles task-mode worktrees
  • Documentation updated: rush command reference, architecture docs
  • Integration test: two parallel tasks with --isolate, verify no cross-contamination

Risks & Mitigations

Risk Impact Mitigation
Merge conflicts between worker branches Tasks fail at merge phase File ownership validation prevents overlapping modifications by design
Disk space (N worktrees × full repo) CI/local runs hit disk limits Shallow worktrees, cleanup after each level, document disk requirements
Performance regression (worktree creation overhead) Slower task-mode execution Measure overhead, make isolation opt-in (not default)
Claude Code isolation: "worktree" behavior changes Breaking changes upstream Pin to known-good behavior, add integration tests
Subagents can't find spec files in worktree Task execution fails Copy or symlink .zerg/specs/ into worktree, or use absolute paths

Non-Goals

  • Making isolation the default — task mode's simplicity is valuable; this is opt-in
  • Replacing container/subprocess modes — those modes serve different deployment needs
  • Runtime file locking — may be a separate enhancement (Option C above)
  • Cross-level worktree persistence — worktrees are per-level, not per-feature

Related Issues

Reactions are currently unavailable

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low priorityLow priorityarchitectureArchitectural improvementsArchitectural improvementsenhancementNew feature or requestNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.