feat: worktree-based run isolation for parallel loops
Each /agent-loop:run now creates a git worktree for the feature branch before generating stories. This provides full isolation: - Multiple loops can run in parallel on different specs in the same project - Main working directory stays on main, always available - Each worktree has its own .loop/ state, tmux session, and branch - Completed runs are archived to main's .loop/archive/ with runs.log Changes: - setup.sh: add --init-worktree mode for initializing worktree .loop/ - archive.sh: add archive_from_worktree() for cross-directory archiving - loop.sh: replace branch checkout with validation (worktree is pre-checked-out) - agents/planner.md: accept absolute path prefix for worktree .loop/ writes - skills/run/SKILL.md: full rewrite — worktree creation in Phase 2, launch in Phase 3, archive on completion, .active-worktree tracking file - skills/stories/SKILL.md: worktree-aware, defer to /run for full flow Bump to 0.12.0.
This commit is contained in:
@@ -150,6 +150,54 @@ print(json.load(open(os.environ['LOOP_PRD'])).get('branchName', ''), end='')
|
||||
echo "[archive] .loop/ reset — ready for new stories"
|
||||
}
|
||||
|
||||
# Archive a completed run from a worktree back to the main project's .loop/archive/.
|
||||
# Called by the /run skill's completion handler after the loop finishes in a worktree.
|
||||
#
|
||||
# Usage: archive_from_worktree <worktree_loop_dir> <main_loop_dir>
|
||||
# worktree_loop_dir: absolute path to the worktree's .loop/ (source)
|
||||
# main_loop_dir: absolute path to the main project's .loop/ (destination)
|
||||
archive_from_worktree() {
|
||||
local wt_loop_dir="$1"
|
||||
local main_loop_dir="$2"
|
||||
local wt_prd="$wt_loop_dir/prd.json"
|
||||
|
||||
[ -f "$wt_prd" ] || { echo "[archive] WARNING: No prd.json in worktree — nothing to archive"; return 1; }
|
||||
|
||||
# Read branch name from worktree's prd.json
|
||||
local branch_name=""
|
||||
if command -v jq &>/dev/null; then
|
||||
branch_name=$(jq -r '.branchName // empty' "$wt_prd" 2>/dev/null)
|
||||
elif command -v python3 &>/dev/null; then
|
||||
branch_name=$(LOOP_PRD="$wt_prd" python3 -c "
|
||||
import json, os
|
||||
print(json.load(open(os.environ['LOOP_PRD'])).get('branchName', ''), end='')
|
||||
" 2>/dev/null)
|
||||
fi
|
||||
|
||||
local feature_name
|
||||
feature_name=$(echo "${branch_name:-unknown}" | sed 's|.*/||')
|
||||
|
||||
local archive_dir="$main_loop_dir/archive/$(date +%Y-%m-%d)-${feature_name}"
|
||||
mkdir -p "$archive_dir"
|
||||
|
||||
# Copy artifacts from worktree
|
||||
[ -f "$wt_prd" ] && cp "$wt_prd" "$archive_dir/"
|
||||
[ -f "$wt_loop_dir/progress.md" ] && cp "$wt_loop_dir/progress.md" "$archive_dir/"
|
||||
[ -f "$wt_loop_dir/progress-archive.md" ] && cp "$wt_loop_dir/progress-archive.md" "$archive_dir/"
|
||||
[ -d "$wt_loop_dir/contracts" ] && cp -r "$wt_loop_dir/contracts" "$archive_dir/"
|
||||
[ -d "$wt_loop_dir/triage" ] && cp -r "$wt_loop_dir/triage" "$archive_dir/"
|
||||
|
||||
# Verify archive has content
|
||||
if ! find "$archive_dir" -maxdepth 1 -type f | read -r; then
|
||||
echo "[archive] WARNING: Archive directory is empty — copy may have failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
append_runs_log "$branch_name" "$archive_dir"
|
||||
|
||||
echo "[archive] Archived worktree run to $archive_dir"
|
||||
}
|
||||
|
||||
# Append a one-line summary to the runs log.
|
||||
append_runs_log() {
|
||||
local branch_name="$1"
|
||||
|
||||
Reference in New Issue
Block a user