feat(issue-21): loop runner --worktree/--base/--review-only with a fail-closed empty change-set guard; control plane stays in the main checkout
- --worktree <path> (CLI over MAM_LOOP_WORKTREE): the source root. Only the
diff, base, head and the empty-set check use it; the wrapper, registry,
checkpoint, loop guard, promotion and cleanup stay on the control root. The
collector is the control root's diff_collect.sh (never the worktree's
unreviewed copy), its arguments shlex-quoted. Validation: an existing
directory, the top of its working tree, the same git common dir as the
control root (".git" is resolved against the directory it came from).
- --base <ref> (CLI only, no env - a stale sha would silently widen the next
range): resolved to a full sha, must be an ancestor of the source HEAD.
- --review-only: requires --base, refuses --plan; Phase 2 is skipped and
logged; correction and comply jobs still go to the creator.
- With --worktree or --base every delegated prompt starts with a context
block (path, branch, base and its origin, head, base..head commit count,
uncommitted changes); without them prompts are byte-identical. All call
sites in run() go through one _dispatch helper.
- Fail-closed guard (only with --worktree/--base): before each review round,
self-review included, an empty change set or one git cannot determine
(exit codes, never output parsing) ends the loop with rc 1 instead of
asking for reviews. This closes the fail-open where a loop started after
an out-of-loop commit sent reviewers "(no changes since base commit)".
- The runner honours MAM_JOBS_DIR and passes --registry-dir to the wrapper.
- run_loop.sh accepts the three options; the legacy shell loop
(MAM_LOOP_PY=0) refuses them before the freeze and the loop lock.
Tests T2-1..T2-14.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017R3Eg7MEpSWnm1oB9ZPP6G
This commit is contained in:
1 parent
d3d3a76c83
commit
d907359746
4 files changed
+539
-37
No files matched your search
@@ -32,6 +32,9 @@ TARGET_AGENT=""
|
||||
TASK=""
|
||||
REVIEWER_LIST=""
|
||||
PLANNER_SESSION_OVERRIDE=""
|
||||
LOOP_WORKTREE=""
|
||||
LOOP_BASE=""
|
||||
LOOP_REVIEW_ONLY=0
|
||||
|
||||
# Print usage instructions
|
||||
usage() {
|
||||
@@ -47,6 +50,9 @@ usage() {
|
||||
echo " --max-rebut N Max rebuttal attempts per review iteration (default: 1, 0: disabled)"
|
||||
echo " --verbose Print detailed execution timeline traces"
|
||||
echo " --cleanup Purge temporary job directories upon success"
|
||||
echo " --worktree <path> Source worktree for diff/base/head (control plane stays here)"
|
||||
echo " --base <ref> Pin the review diff base (must be an ancestor of the source HEAD)"
|
||||
echo " --review-only Skip implementation; review <base>..HEAD (requires --base)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -83,6 +89,9 @@ while [[ "$#" -gt 0 ]]; do
|
||||
;;
|
||||
--planner) PLANNER_SESSION_OVERRIDE="$2"; shift 2 ;;
|
||||
--task) TASK="$2"; shift 2 ;;
|
||||
--worktree) LOOP_WORKTREE="$2"; shift 2 ;;
|
||||
--base) LOOP_BASE="$2"; shift 2 ;;
|
||||
--review-only) LOOP_REVIEW_ONLY=1; shift ;;
|
||||
-h|--help) usage ;;
|
||||
*) echo "Unknown option: $1"; usage ;;
|
||||
esac
|
||||
@@ -104,6 +113,15 @@ if [ -n "${PLANNER_SESSION_OVERRIDE:-}" ] && [ "$PLAN_MODE" = false ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Issue #21 (D12): the legacy shell loop assumes a cwd-relative .mam/jobs and
|
||||
# diffs the checkout it runs in; the worktree/base options are Python-only.
|
||||
# Checked before the freeze and the loop lock (log_* are not defined yet).
|
||||
if [ "${MAM_LOOP_PY:-1}" = "0" ] && { [ -n "$LOOP_WORKTREE" ] || [ -n "$LOOP_BASE" ] \
|
||||
|| [ "$LOOP_REVIEW_ONLY" = "1" ] || [ -n "${MAM_LOOP_WORKTREE:-}" ]; }; then
|
||||
echo "ERROR: --worktree/--base/--review-only require the Python loop (MAM_LOOP_PY=1)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Anchor Python interpreter before freeze so freeze child processes find the project venv (O-4)
|
||||
if [ -z "${AGENT_PYTHON_BIN:-}" ]; then
|
||||
if [ -x "$REPO_ROOT/.venv/bin/python" ]; then
|
||||
|
||||
Reference in new issue
Block a user