ktg-plugin-marketplace/plugins/ultraplan-local/templates/headless-launch-template.md
Kjell Tore Guttormsen 5dd7e8447c 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>
2026-04-07 22:12:53 +02:00

5.5 KiB

Headless Launch Script Template

This template is used by the session-decomposer agent to generate a launch script for headless execution of decomposed sessions.

Template

#!/usr/bin/env bash
# Headless launch script — generated by ultraplan-local
# Master plan: {plan_path}
# Generated: {date}
# Sessions: {total_sessions} ({parallel_count} parallel, {sequential_count} sequential)

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"
WORKTREE_BASE="{session_dir}/worktrees"
mkdir -p "$LOG_DIR" "$WORKTREE_BASE"

# 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, 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: }
echo "Waiting for Wave {N} to complete..."
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 }
{verify_commands}

# --- Wave {N+1}: Sequential sessions (depends on previous wave) ---
{# Repeat wave pattern for dependent sessions }

echo ""
echo "=== All sessions complete ==="
echo "Review logs in $LOG_DIR/"
echo "Run final verification: {final_verify_command}"

Rules for the session-decomposer

When generating a launch script from this template:

  1. Group sessions into waves by dependency. Sessions with no dependencies or whose dependencies are all in earlier waves can run in the same wave.
  2. Each wave waits for completion before the next wave starts.
  3. Verification runs after each wave — if verification fails, the script stops and reports which session failed.
  4. Log each session to a separate file for debugging.
  5. Use claude -p with the session spec file as the prompt.
  6. Use --dangerously-skip-permissions rather than --allowedTools — the executor needs flexible tool access and enumerating every tool is fragile.
  7. Final verification at the end runs the master plan's verification section.
  8. Never include secrets in the generated script.
  9. Wave verification must be independent. After each wave completes, run verification commands fresh via Bash — never parse session log files as proof of success. Log files contain executor self-reporting, not ground truth. The command's exit code is the only authoritative verification signal.
  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.