fix(ultraplan-local): CRITICAL — worktree isolation for parallel sessions
Phase 2.6 previously launched parallel claude -p sessions in the same working directory, causing git race conditions and repository corruption. Changes: - Add Phase 2.55 (pre-flight safety checks): clean tree, plan file tracking, scope fence overlap validation, stale worktree cleanup - Rewrite Phase 2.6 with git worktree isolation: each parallel session gets its own worktree and branch, merged back sequentially - Add merge conflict detection and abort (no silent data loss) - Add unconditional worktree cleanup (even on failure) - Add hard rules 11-13 (worktree mandatory, cleanup, sequential merge) - Session-scoped progress file naming for --session mode - Update headless launch template with worktree support and cleanup trap - Bump version to 1.5.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c747ab6ee6
commit
5dd7e8447c
4 changed files with 356 additions and 29 deletions
|
|
@ -17,23 +17,55 @@ set -euo pipefail
|
|||
# Prevent accidental API billing — remove this line if you intend to use API credits
|
||||
unset ANTHROPIC_API_KEY
|
||||
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||
PLAN_DIR="{session_dir}"
|
||||
LOG_DIR="{session_dir}/logs"
|
||||
mkdir -p "$LOG_DIR"
|
||||
WORKTREE_BASE="{session_dir}/worktrees"
|
||||
mkdir -p "$LOG_DIR" "$WORKTREE_BASE"
|
||||
|
||||
echo "=== Ultraplan Headless Execution ==="
|
||||
# Cleanup trap — always remove worktrees on exit (success or failure)
|
||||
cleanup_worktrees() {
|
||||
echo ""
|
||||
echo "=== Cleaning up worktrees ==="
|
||||
cd "$REPO_ROOT"
|
||||
for wt in "$WORKTREE_BASE"/session-*; do
|
||||
[ -d "$wt" ] && git worktree remove "$wt" --force 2>/dev/null && echo "Removed: $wt"
|
||||
done
|
||||
git worktree prune
|
||||
git branch --list "ultraplan/{slug}/*" | while read b; do
|
||||
git branch -D "$b" 2>/dev/null
|
||||
done
|
||||
rmdir "$WORKTREE_BASE" 2>/dev/null
|
||||
echo "Cleanup complete."
|
||||
}
|
||||
trap cleanup_worktrees EXIT
|
||||
|
||||
# Pre-flight: verify clean working tree
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "ERROR: Working tree is not clean. Commit or stash changes before parallel execution."
|
||||
git status --short
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== Ultraplan Headless Execution (Worktree-Isolated) ==="
|
||||
echo "Plan: {plan_path}"
|
||||
echo "Sessions: {total_sessions}"
|
||||
echo "Repo root: $REPO_ROOT"
|
||||
echo ""
|
||||
|
||||
# --- Wave {N}: Parallel sessions (no dependencies) ---
|
||||
echo "--- Wave {N}: {description} ---"
|
||||
|
||||
{# For each parallel session in this wave: }
|
||||
claude -p "$(cat "$PLAN_DIR/session-{n}-{slug}.md")" \
|
||||
{# For each parallel session in this wave, create worktree: }
|
||||
git worktree add -b "ultraplan/{slug}/session-{n}" "$WORKTREE_BASE/session-{n}" HEAD
|
||||
echo "Worktree created: session-{n} (branch: ultraplan/{slug}/session-{n})"
|
||||
|
||||
{# Launch session in its worktree: }
|
||||
cd "$WORKTREE_BASE/session-{n}" && claude -p "$(cat "$PLAN_DIR/session-{n}-{slug}.md")" \
|
||||
--dangerously-skip-permissions \
|
||||
> "$LOG_DIR/session-{n}.log" 2>&1 &
|
||||
PID_{n}=$!
|
||||
cd "$REPO_ROOT"
|
||||
echo "Started session {n}: {title} (PID $PID_{n})"
|
||||
|
||||
{# After all parallel sessions in this wave: }
|
||||
|
|
@ -42,6 +74,25 @@ wait $PID_{n1} $PID_{n2}
|
|||
echo "Wave {N} complete."
|
||||
echo ""
|
||||
|
||||
# --- Merge wave results (sequential) ---
|
||||
echo "--- Merging Wave {N} ---"
|
||||
cd "$REPO_ROOT"
|
||||
{# For each session in the wave, merge its branch: }
|
||||
git merge --no-ff "ultraplan/{slug}/session-{n}" \
|
||||
-m "merge: ultraplan session {n} — {title}"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "MERGE CONFLICT: session {n}. Conflicting files:"
|
||||
git diff --name-only --diff-filter=U
|
||||
git merge --abort
|
||||
echo "Aborting. Earlier sessions in this wave are already merged."
|
||||
exit 1
|
||||
fi
|
||||
git worktree remove "$WORKTREE_BASE/session-{n}" --force
|
||||
git branch -d "ultraplan/{slug}/session-{n}"
|
||||
echo "Merged and cleaned: session {n}"
|
||||
|
||||
git worktree prune
|
||||
|
||||
# --- Verify wave results ---
|
||||
echo "--- Verifying Wave {N} ---"
|
||||
{# For each session in the wave, run its exit condition commands }
|
||||
|
|
@ -78,3 +129,16 @@ When generating a launch script from this template:
|
|||
10. **Billing preamble.** Prepend `unset ANTHROPIC_API_KEY` with a comment at
|
||||
the top of the script to prevent accidental API billing. Users who intend
|
||||
to use API credits can remove this line.
|
||||
11. **Worktree isolation is mandatory.** Every parallel wave MUST use git
|
||||
worktrees. Each session gets its own worktree and branch. Never launch
|
||||
parallel `claude -p` sessions in the same working directory.
|
||||
12. **Cleanup trap on EXIT.** The generated script MUST include a `trap` on
|
||||
EXIT that removes all worktrees (`git worktree remove --force`) and prunes
|
||||
branches, even if the script fails or is interrupted.
|
||||
13. **Sequential merge after each wave.** After all sessions in a wave complete,
|
||||
merge their branches back to the main branch one at a time. Abort on merge
|
||||
conflict — do not force-resolve.
|
||||
14. **Clean working tree before worktrees.** Add a `git status --porcelain`
|
||||
check at the top of the script. Fail if the working tree is dirty.
|
||||
15. **Absolute paths for logs.** Log file paths must be absolute (resolved from
|
||||
`$REPO_ROOT`), not relative to any worktree.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue