ktg-plugin-marketplace/plugins/ultraplan-local/templates/headless-launch-template.md
Kjell Tore Guttormsen d1befac35a feat(ultraplan-local): v1.7.0 — self-verifying plan chain
Wave 1 of a 6-session parallel build revealed three failure modes:
(1) hallucinated completion (status=completed after 2/5 steps, last
tool call was an arbitrary file review), (2) fail-late bash (3/6
sessions had push blocked inside sub-agent sandbox after all work
was done), (3) no objective verification (plans were prose).

v1.7 closes all three by making the plan an executable contract.

Per-step YAML manifest (expected_paths, commit_message_pattern,
bash_syntax_check, forbidden_paths, must_contain) is the objective
completion predicate. Plan-critic dimension 10 (Manifest quality)
is a hard gate. Session decomposer propagates manifests verbatim
and emits an obligatory Step 0 pre-flight (git push --dry-run,
exit 77 sentinel) in every session spec.

ultraexecute-local gets Phase 7.5 (independent manifest audit from
git log + filesystem, ignoring agent bookkeeping) and Phase 7.6
(bounded recovery dispatch, recovery_depth ≤ 2). Hard Rule 17
forbids marking a step passed without manifest verification. Hard
Rule 18 forbids ending on an arbitrary tool call before reporting.

Division of labor is made explicit:
- /ultraresearch-local gathers context (no build decisions)
- /ultraplan-local produces an executable contract (manifests,
  plan-critic gate)
- /ultraexecute-local executes disciplined (does NOT compensate
  for weak plans — escalates)

Code complete. Docs partial (Arbeidsdeling table + manifest section
added to plugin + marketplace READMEs). Verification tests
(10-sequence) pending — see REMEMBER.md.

Backward compat: v1.6 plans without plan_version marker get
legacy mode with synthesized manifests and legacy_plan: true in
progress file. Plan-critic emits advisory, not block.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 07:38:16 +02:00

6.6 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

# Pre-flight: verify remote push permissions (catches credential/auth issues
# BEFORE spawning sessions). Sub-agent bash sandbox may have different
# credentials than the launching shell — Step 0 in each session spec handles
# the sandbox-side detection. Set ULTRAEXECUTE_SKIP_PREFLIGHT=1 for offline
# or air-gapped testing.
if [ "${ULTRAEXECUTE_SKIP_PREFLIGHT:-0}" != "1" ]; then
  if ! git push --dry-run origin HEAD >/tmp/push-dryrun-launch.log 2>&1; then
    echo "ERROR: git push --dry-run failed. Sessions will be unable to push."
    cat /tmp/push-dryrun-launch.log
    echo ""
    echo "Fix remote credentials before running parallel execution, or set"
    echo "ULTRAEXECUTE_SKIP_PREFLIGHT=1 to bypass (offline/air-gapped only)."
    exit 1
  fi
  if grep -qE "(rejected|denied|forbidden|permission)" /tmp/push-dryrun-launch.log; then
    echo "ERROR: git push --dry-run reports rejection. Sessions will fail at commit time."
    cat /tmp/push-dryrun-launch.log
    exit 1
  fi
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")" \
  --allowedTools "Read,Write,Edit,Bash,Glob,Grep" \
  --permission-mode bypassPermissions \
  > "$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 --allowedTools "Read,Write,Edit,Bash,Glob,Grep" with --permission-mode bypassPermissions for child sessions. This limits the tool surface to what the executor needs and prevents agent spawning, MCP access, and external web requests in headless sessions.
  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.