fix: Extract lib/worktree.sh — create, recover, cleanup (#797)

Extract reusable worktree management into lib/worktree.sh:
- worktree_create: git worktree add + checkout + submodules
- worktree_recover: detect existing worktree, reuse or recreate
- worktree_cleanup: remove worktree + clear Claude Code project cache
- worktree_cleanup_stale: scan /tmp for orphaned worktrees, skip preserved
- worktree_preserve: mark worktree for debugging (skip stale cleanup)

Update callers:
- dev-agent.sh: use worktree_create/worktree_recover/worktree_cleanup
- action-agent.sh: use worktree_cleanup/worktree_preserve
- formula-session.sh: delegate cleanup_stale_crashed_worktrees, use worktree_preserve
- All formula agents source lib/worktree.sh

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
openhands 2026-03-27 19:06:31 +00:00
parent 1c5970f4bf
commit c5c24cda67
11 changed files with 187 additions and 88 deletions

View file

@ -25,6 +25,7 @@ source "$(dirname "$0")/../lib/env.sh"
source "$(dirname "$0")/../lib/ci-helpers.sh"
source "$(dirname "$0")/../lib/agent-session.sh"
source "$(dirname "$0")/../lib/formula-session.sh"
source "$(dirname "$0")/../lib/worktree.sh"
# shellcheck source=./phase-handler.sh
source "$(dirname "$0")/phase-handler.sh"
@ -98,12 +99,7 @@ PR_NUMBER=""
# --- Cleanup helpers ---
cleanup_worktree() {
cd "$REPO_ROOT"
git worktree remove "$WORKTREE" --force 2>/dev/null || true
rm -rf "$WORKTREE"
# Clear Claude Code session history for this worktree to prevent hallucinated "already done"
CLAUDE_PROJECT_DIR="$HOME/.claude/projects/$(echo "$WORKTREE" | sed 's|/|-|g; s|^-||')"
rm -rf "$CLAUDE_PROJECT_DIR" 2>/dev/null || true
worktree_cleanup "$WORKTREE"
}
cleanup_labels() {
@ -449,29 +445,13 @@ export FORGE_REMOTE # used by phase-handler.sh
log "forge remote: ${FORGE_REMOTE} (FORGE_URL=${FORGE_URL})"
if [ "$RECOVERY_MODE" = true ]; then
git fetch "${FORGE_REMOTE}" "$BRANCH" 2>/dev/null
# Reuse existing worktree if on the right branch (preserves session context)
REUSE_WORKTREE=false
if [ -d "$WORKTREE/.git" ] || [ -f "$WORKTREE/.git" ]; then
WT_BRANCH=$(cd "$WORKTREE" && git rev-parse --abbrev-ref HEAD 2>/dev/null || true)
if [ "$WT_BRANCH" = "$BRANCH" ]; then
log "reusing existing worktree (preserves session)"
cd "$WORKTREE"
git pull --ff-only "${FORGE_REMOTE}" "$BRANCH" 2>/dev/null || git reset --hard "${FORGE_REMOTE}/${BRANCH}" 2>/dev/null || true
REUSE_WORKTREE=true
fi
if ! worktree_recover "$WORKTREE" "$BRANCH" "$FORGE_REMOTE"; then
log "ERROR: worktree recovery failed"
cleanup_labels
exit 1
fi
if [ "$REUSE_WORKTREE" = false ]; then
cleanup_worktree
git worktree add "$WORKTREE" "${FORGE_REMOTE}/${BRANCH}" -B "$BRANCH" 2>&1 || {
log "ERROR: worktree creation failed for recovery"
cleanup_labels
exit 1
}
cd "$WORKTREE"
git submodule update --init --recursive 2>/dev/null || true
if [ "$_WORKTREE_REUSED" = true ]; then
log "reusing existing worktree (preserves session)"
fi
else
# Normal mode: create fresh worktree from primary branch
@ -489,16 +469,11 @@ else
git fetch "${FORGE_REMOTE}" "${PRIMARY_BRANCH}" 2>/dev/null
git pull --ff-only "${FORGE_REMOTE}" "${PRIMARY_BRANCH}" 2>/dev/null || true
cleanup_worktree
git worktree add "$WORKTREE" "${FORGE_REMOTE}/${PRIMARY_BRANCH}" -B "$BRANCH" 2>&1 || {
if ! worktree_create "$WORKTREE" "$BRANCH" "${FORGE_REMOTE}/${PRIMARY_BRANCH}"; then
log "ERROR: worktree creation failed"
git worktree add "$WORKTREE" "${FORGE_REMOTE}/${PRIMARY_BRANCH}" -B "$BRANCH" 2>&1 | while read -r wt_line; do log " $wt_line"; done || true
cleanup_labels
exit 1
}
cd "$WORKTREE"
git checkout -B "$BRANCH" "${FORGE_REMOTE}/${PRIMARY_BRANCH}" 2>/dev/null
git submodule update --init --recursive 2>/dev/null || true
fi
# Symlink lib node_modules from main repo (submodule init doesn't run npm install)
for lib_dir in "$REPO_ROOT"/onchain/lib/*/; do