| description | Automated git worktree lifecycle management with branch strategy enforcement, workspace isolation, configuration replication, health checks, and cleanup procedures. | |||||
|---|---|---|---|---|---|---|
| allowed-tools |
|
|||||
| author | Engineering Standards Committee | |||||
| version | 1.0 |
Automate the complete lifecycle of git worktrees for multi-agent development workflows: creation with standardized branch naming, configuration replication, isolation validation, health monitoring, and safe cleanup after task completion.
- Starting multi-agent feature development (spawn-agents command)
- Creating isolated development environments per task
- Enforcing consistent branch naming conventions across team
- Managing multiple parallel development streams
- Scaling development from single to multi-worktree workflows
- Decommissioning completed worktrees post-merge
- Git repository with clean working directory on main branch
- At least one agent task from MULTI_AGENT_PLAN.md to process
- Sufficient disk space for worktrees (2-3x repo size)
- Read/write permissions on repository directory
- Optional: Repository configuration files to replicate (.gitignore, .editorconfig, etc)
To use this template in your project:
- Copy to
.claude/commands/worktree-setup.md - Customize branch naming convention in Step 2.1 to match team standards
- Update configuration files list in Step 3.1 to replicate project-specific configs
- Adjust health check thresholds in Step 5 based on project requirements
- Modify cleanup retention policy in Step 7 if needed
- Set environment variables in
.env.worktreefor automation
Configuration File (.env.worktree):
# Branch naming strategy: {prefix}/{role}/{task-id}/{description}
BRANCH_PREFIX="feat"
WORKTREE_BASE_PATH="${PROJECT_ROOT}/.worktrees"
# Health check configuration
HEALTH_CHECK_MAX_UNCOMMITTED=100
HEALTH_CHECK_MAX_DISK_USAGE_PCT=80
HEALTH_CHECK_TIMEOUT_MINUTES=120
# Cleanup configuration
CLEANUP_AFTER_MERGE_DAYS=1
ARCHIVE_COMPLETED_WORKTREES=true
ARCHIVE_PATH="${PROJECT_ROOT}/.worktree-archive"Step 1.1: Validate Repository State
# Ensure main branch is up-to-date and clean
git fetch origin main
# Check for uncommitted changes
UNCOMMITTED=$(git status --porcelain | wc -l)
if [ "$UNCOMMITTED" -gt 0 ]; then
echo "ERROR: Working directory not clean. Commit or stash changes first."
exit 1
fi
# Verify no orphaned worktrees from previous runs
ORPHANED=$(git worktree list --porcelain | grep -v "^prune" | wc -l)
if [ "$ORPHANED" -gt 20 ]; then
echo "WARNING: Found $ORPHANED worktrees. Consider cleanup."
fi
# Show current state
git status --short
git log --oneline -5Step 1.2: Load Task Configuration
Load task definition from MULTI_AGENT_PLAN.md:
- Task ID: {{TASK_ID}}
- Agent Role: {{AGENT_ROLE}} (architect|builder|validator|scribe|devops|researcher)
- Task Description: {{TASK_DESCRIPTION}}
- Base Branch: {{BASE_BRANCH}} (default: main)
- Dependencies: {{TASK_DEPENDENCIES}} (list of prerequisite tasks)Step 1.3: Verify Task Dependencies
# Check if all prerequisite tasks are completed
DEPENDENCIES="{{TASK_DEPENDENCIES}}"
for dep_task in $DEPENDENCIES; do
STATUS=$(grep "^| $dep_task |" MULTI_AGENT_PLAN.md | grep -o "✅\|⏳\|❌")
if [ "$STATUS" != "✅" ]; then
echo "ERROR: Dependency $dep_task not complete. Status: $STATUS"
exit 1
fi
done
echo "All dependencies satisfied ✓"Step 2.1: Generate Branch Name
# Standardized naming: {prefix}/{role}/{task-id}/{kebab-case-description}
BRANCH_PREFIX="${BRANCH_PREFIX:-feat}"
AGENT_ROLE="{{AGENT_ROLE}}"
TASK_ID="{{TASK_ID}}"
TASK_DESC="{{TASK_DESCRIPTION}}"
# Sanitize description to kebab-case
SANITIZED_DESC=$(echo "$TASK_DESC" | \
tr '[:upper:]' '[:lower:]' | \
sed 's/[^a-z0-9]/-/g' | \
sed 's/-\+/-/g' | \
sed 's/^-\|-$//g' | \
cut -c1-30)
BRANCH_NAME="${BRANCH_PREFIX}/${AGENT_ROLE}/${TASK_ID}/${SANITIZED_DESC}"
echo "Generated branch: $BRANCH_NAME"
# Validate branch name (no spaces, special chars, etc)
if ! [[ "$BRANCH_NAME" =~ ^[a-z0-9]([a-z0-9._/-]*[a-z0-9])?$ ]]; then
echo "ERROR: Invalid branch name: $BRANCH_NAME"
exit 1
fi
# Check if branch already exists
if git show-ref --quiet --verify "refs/heads/$BRANCH_NAME"; then
echo "ERROR: Branch already exists: $BRANCH_NAME"
exit 1
fiStep 2.2: Validate Naming Convention Compliance
# Enforce team naming conventions
VALID_PREFIXES="feat|fix|refactor|docs|test|chore"
VALID_ROLES="architect|builder|validator|scribe|devops|researcher"
if ! [[ "$BRANCH_PREFIX" =~ ^($VALID_PREFIXES)$ ]]; then
echo "ERROR: Invalid prefix '$BRANCH_PREFIX'. Allowed: $VALID_PREFIXES"
exit 1
fi
if ! [[ "$AGENT_ROLE" =~ ^($VALID_ROLES)$ ]]; then
echo "ERROR: Invalid role '$AGENT_ROLE'. Allowed: $VALID_ROLES"
exit 1
fi
if ! [[ "$TASK_ID" =~ ^[A-Z]+-[0-9]+$ ]]; then
echo "WARNING: Task ID '$TASK_ID' doesn't follow pattern PROJ-123. Proceeding anyway."
fi
echo "Branch naming convention validated ✓"Step 3.1: Create Worktree Directory
# Create worktree base directory
WORKTREE_BASE="${WORKTREE_BASE_PATH:-./.worktrees}"
WORKTREE_DIR="${WORKTREE_BASE}/${BRANCH_NAME}"
mkdir -p "$WORKTREE_BASE"
# Create git worktree (lightweight clone of repo)
git worktree add --track -b "$BRANCH_NAME" "$WORKTREE_DIR" origin/main
if [ $? -ne 0 ]; then
echo "ERROR: Failed to create worktree at $WORKTREE_DIR"
rm -rf "$WORKTREE_DIR"
exit 1
fi
echo "Created worktree at: $WORKTREE_DIR"
cd "$WORKTREE_DIR"Step 3.2: Replicate Configuration Files
# Copy essential configuration files to maintain consistency
CONFIG_FILES=(
".gitignore"
".editorconfig"
".prettierrc"
".eslintrc.json"
"tsconfig.json"
"package.json"
"Dockerfile"
".env.example"
".github/workflows"
"pytest.ini"
"pyproject.toml"
"Makefile"
)
REPO_ROOT="$(git rev-parse --show-toplevel)"
for config_file in "${CONFIG_FILES[@]}"; do
src="${REPO_ROOT}/${config_file}"
dst="$(pwd)/${config_file}"
if [ -e "$src" ] && [ ! -e "$dst" ]; then
if [ -d "$src" ]; then
cp -r "$src" "$dst"
echo "Copied directory: $config_file"
else
cp "$src" "$dst"
echo "Copied file: $config_file"
fi
fi
done
# Verify core config files exist
if [ ! -f ".gitignore" ]; then
cp "${REPO_ROOT}/.gitignore" . 2>/dev/null || echo "WARNING: .gitignore not copied"
fiStep 3.3: Setup Worktree Metadata
# Create worktree context file for agent reference
cat > .worktree-context.md << 'CONTEXT_EOF'
# Worktree Context
- **Worktree ID**: {{WORKTREE_ID}}
- **Branch**: {{BRANCH_NAME}}
- **Agent Role**: {{AGENT_ROLE}}
- **Task ID**: {{TASK_ID}}
- **Created**: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
- **Base Branch**: {{BASE_BRANCH}}
- **Parent PID**: {{PARENT_PID}}
## Task Description
{{TASK_DESCRIPTION}}
## Handoff Instructions
1. Complete task according to acceptance criteria in MULTI_AGENT_PLAN.md
2. Commit regularly with clear messages
3. Push to remote branch: `git push -u origin {{BRANCH_NAME}}`
4. Create pull request and mark task as complete
5. Upon merge, cleanup worktree using `worktree-setup --cleanup`
## Environment
```bash
cd "$(pwd)"
git statusCONTEXT_EOF
echo "Created worktree metadata: .worktree-context.md"
### Phase 4: Configuration Integrity Validation
**Step 4.1: Verify Worktree Integrity**
```bash
# Ensure worktree is properly initialized
git status > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "ERROR: Worktree git status failed"
exit 1
fi
# Check branch is correct
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [ "$CURRENT_BRANCH" != "$BRANCH_NAME" ]; then
echo "ERROR: On wrong branch. Expected $BRANCH_NAME, got $CURRENT_BRANCH"
exit 1
fi
# Verify tracking branch
TRACKING=$(git config --local branch.${BRANCH_NAME}.remote)
if [ -z "$TRACKING" ]; then
echo "WARNING: Branch not tracking remote. Setting up..."
git branch -u origin/"$BRANCH_NAME" || true
fi
echo "Worktree integrity verified ✓"
Step 4.2: Validate Configuration Replication
# Verify essential files were replicated
REQUIRED_CONFIGS=(".gitignore" "package.json")
MISSING=()
for config in "${REQUIRED_CONFIGS[@]}"; do
if [ ! -e "$config" ]; then
MISSING+=("$config")
fi
done
if [ ${#MISSING[@]} -gt 0 ]; then
echo "WARNING: Missing configs: ${MISSING[*]}"
echo "Install dependencies manually or copy from parent repo"
else
echo "Configuration files verified ✓"
fiStep 5.1: Initial Health Assessment
# Check disk usage
DISK_USAGE=$(du -s . | awk '{print $1}')
MAX_DISK=$(df . | awk 'NR==2 {print $4}')
USAGE_PCT=$((100 * DISK_USAGE / MAX_DISK))
echo "Disk usage: ${USAGE_PCT}% (${DISK_USAGE}K / ${MAX_DISK}K)"
if [ "$USAGE_PCT" -gt "${HEALTH_CHECK_MAX_DISK_USAGE_PCT:-80}" ]; then
echo "WARNING: Disk usage high. Consider cleanup."
fi
# Check git object database integrity
git fsck --full > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "Git object database healthy ✓"
else
echo "WARNING: Git object database integrity check failed"
fi
# Verify remote connectivity
git fetch --dry-run origin > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "Remote connectivity verified ✓"
else
echo "ERROR: Cannot reach remote repository"
exit 1
fiStep 5.2: Generate Health Report
# Create health check report
cat > .worktree-health.json << 'HEALTH_EOF'
{
"check_timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
"worktree_id": "{{WORKTREE_ID}}",
"branch": "{{BRANCH_NAME}}",
"status": "healthy",
"checks": {
"git_status": "pass",
"branch_tracking": "pass",
"remote_connectivity": "pass",
"disk_usage_pct": {{USAGE_PCT}},
"object_database": "pass"
},
"metadata": {
"created": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
"last_check": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
"agent_role": "{{AGENT_ROLE}}",
"task_id": "{{TASK_ID}}"
}
}
HEALTH_EOF
echo "Health report generated: .worktree-health.json"Step 6.1: Lock Acquisition
# Prevent concurrent modifications to shared resources
LOCK_DIR="${REPO_ROOT}/.worktree-locks"
mkdir -p "$LOCK_DIR"
WORKTREE_LOCK="${LOCK_DIR}/${BRANCH_NAME}.lock"
exec 200>"$WORKTREE_LOCK"
flock -n 200
if [ $? -ne 0 ]; then
echo "ERROR: Worktree already in use by another process"
exit 1
fi
echo "$$" > "$WORKTREE_LOCK"
echo "Lock acquired for worktree: $BRANCH_NAME"
# Register cleanup trap to release lock
trap "rm -f '$WORKTREE_LOCK'" EXITStep 6.2: Dependency Conflict Detection
# Check for other worktrees with conflicting dependencies
EXISTING_WORKTREES=$(git worktree list --porcelain | grep -v "^prune" | awk '{print $2}' | wc -l)
if [ "$EXISTING_WORKTREES" -gt 10 ]; then
echo "WARNING: Found $EXISTING_WORKTREES active worktrees. Cleanup stale ones."
git worktree prune
fi
# List all active worktrees for reference
echo ""
echo "Active worktrees:"
git worktree list --porcelain | grep -v "^prune" | while read line; do
PATH=$(echo $line | awk '{print $2}')
BRANCH=$(echo $line | awk '{print $3}' | sed 's/\[//; s/\]//')
echo " $BRANCH @ $PATH"
doneStep 7.1: Mark Worktree for Cleanup (Post-Merge)
# After PR is merged, mark worktree for cleanup
# Run this command when task completion is confirmed
REPO_ROOT="$(git rev-parse --show-toplevel)"
BRANCH_NAME="{{BRANCH_NAME}}"
WORKTREE_PATH="${REPO_ROOT}/.worktrees/${BRANCH_NAME}"
# Verify branch is merged
MERGED=$(git branch -r --merged origin/main | grep "$BRANCH_NAME")
if [ -z "$MERGED" ]; then
echo "ERROR: Branch not merged to main. Push PR or merge first."
exit 1
fi
echo "✓ Branch merged to main"
# Archive metadata before cleanup
if [ -d "$WORKTREE_PATH" ]; then
ARCHIVE_PATH="${REPO_ROOT}/.worktree-archive"
mkdir -p "$ARCHIVE_PATH"
if [ -f "$WORKTREE_PATH/.worktree-context.md" ]; then
cp "$WORKTREE_PATH/.worktree-context.md" \
"$ARCHIVE_PATH/${BRANCH_NAME}-context.md"
fi
if [ -f "$WORKTREE_PATH/.worktree-health.json" ]; then
cp "$WORKTREE_PATH/.worktree-health.json" \
"$ARCHIVE_PATH/${BRANCH_NAME}-health.json"
fi
fiStep 7.2: Remove Worktree Safely
# Remove worktree and delete branch
git worktree remove "$WORKTREE_PATH" --force
if [ $? -eq 0 ]; then
echo "✓ Removed worktree: $WORKTREE_PATH"
else
echo "ERROR: Failed to remove worktree. Manual cleanup may be needed."
exit 1
fi
# Delete local branch
git branch -D "$BRANCH_NAME"
# Delete remote branch (optional, usually done via PR merge)
git push origin --delete "$BRANCH_NAME" 2>/dev/null || \
echo "INFO: Remote branch already deleted or requires different permissions"
echo "✓ Cleanup complete for: $BRANCH_NAME"Step 7.3: Batch Cleanup (Orphaned Worktrees)
# Run periodically to clean up abandoned worktrees
REPO_ROOT="$(git rev-parse --show-toplevel)"
WORKTREE_BASE="${REPO_ROOT}/.worktrees"
THRESHOLD_DAYS=7
echo "Scanning for orphaned worktrees older than $THRESHOLD_DAYS days..."
if [ ! -d "$WORKTREE_BASE" ]; then
echo "No worktree directory found"
exit 0
fi
CURRENT_TIME=$(date +%s)
CLEANUP_COUNT=0
for worktree_dir in "$WORKTREE_BASE"/*; do
if [ ! -d "$worktree_dir" ]; then
continue
fi
MODIFIED_TIME=$(stat -f%m "$worktree_dir" 2>/dev/null || stat -c%Y "$worktree_dir")
AGE_DAYS=$(((CURRENT_TIME - MODIFIED_TIME) / 86400))
if [ "$AGE_DAYS" -gt "$THRESHOLD_DAYS" ]; then
BRANCH_NAME=$(basename "$worktree_dir")
echo "Removing orphaned worktree (${AGE_DAYS}d old): $BRANCH_NAME"
git worktree remove "$worktree_dir" --force 2>/dev/null
((CLEANUP_COUNT++))
fi
done
echo "✓ Cleanup complete: Removed $CLEANUP_COUNT orphaned worktrees"Worktree creation fails:
# Clean up partial worktree
git worktree remove <path> --force
# Ensure branch doesn't exist: git branch -D <branch-name>
# Retry worktree creationLock file conflicts:
# Check if process is still running
ps -p $(cat .worktree-locks/branch-name.lock) 2>/dev/null
# If not running, safely remove lock
rm -f .worktree-locks/branch-name.lockDisk space exhausted:
# Run batch cleanup immediately
git worktree prune
# Remove abandoned worktrees older than 1 day
find .worktrees -maxdepth 1 -type d -mtime +1 -exec rm -rf {} \;Remote tracking issues:
# Reset tracking relationship
git branch -u origin/branch-name
git fetch origin branch-name:refs/remotes/origin/branch-name- File Permissions: Worktrees inherit parent repository permissions
- Lock Files: Use file locking to prevent concurrent modifications
- Configuration Isolation: Each worktree maintains separate git config
- Cleanup: Always cleanup after task completion to prevent disk bloat
- Archive Policy: Archive metadata before deletion for audit trails
- Branch Protection: Enforce branch protection rules on main/develop
- Worktree Reuse: Consider worktree pooling for frequent tasks
- Disk I/O: Monitor disk usage, cleanup orphaned worktrees regularly
- Lock Contention: Keep lock duration minimal
- Shallow Clones: For large repos, consider
--depth 1option in worktree add
# 1. Initialize session with task configuration
task_id="PROJ-123"
agent_role="builder"
branch_name="feat/builder/PROJ-123/add-user-authentication"
# 2. Create worktree
git worktree add --track -b "$branch_name" \
".worktrees/${branch_name}" origin/main
# 3. Enter worktree
cd ".worktrees/${branch_name}"
# 4. Install dependencies (if needed)
npm install # or: pip install -r requirements.txt
# 5. Begin work
# ... make changes, commit regularly ...
# 6. Push and create PR
git push -u origin "$branch_name"
# 7. After merge, cleanup
git worktree remove . --force
cd - && git branch -D "$branch_name"From spawn-agents command:
# spawn-agents creates task with isolation="worktree"
# → worktree-setup creates isolated workspace
# → agent begins work in isolated context
# → Upon completion, worktree-setup --cleanup removes environmentFrom MULTI_AGENT_PLAN.md:
| Task ID | Agent | Status | Worktree | Branch |
|---------|-------|--------|----------|--------|
| PROJ-1 | Builder | ✅ | .worktrees/feat/builder/PROJ-1/... | feat/builder/PROJ-1/... |
| PROJ-2 | Validator | ⏳ | .worktrees/feat/validator/PROJ-2/... | feat/validator/PROJ-2/... |- Initial release
- Git worktree lifecycle automation
- Branch naming convention enforcement
- Configuration replication
- Worktree health checks
- Multi-worktree conflict prevention
- Cleanup automation
- Lock-based concurrency control
- Archive policy support