diff --git a/scripts/agent-branch-finish.sh b/scripts/agent-branch-finish.sh index e97cd7d..d63539c 100755 --- a/scripts/agent-branch-finish.sh +++ b/scripts/agent-branch-finish.sh @@ -165,7 +165,11 @@ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then fi repo_root="$(git rev-parse --show-toplevel)" -current_worktree="$(pwd -P)" +# The physical cwd may be a subdirectory inside the source worktree. Cleanup +# decisions need the enclosing worktree root, otherwise finishing from `src/` +# can delete the caller's cwd and turn a successful merge into a false shell +# failure. +current_worktree="$repo_root" common_git_dir_raw="$(git -C "$repo_root" rev-parse --git-common-dir)" if [[ "$common_git_dir_raw" == /* ]]; then common_git_dir="$common_git_dir_raw" @@ -846,10 +850,12 @@ if [[ "$CLEANUP_AFTER_MERGE" -eq 1 ]]; then echo "[agent-branch-finish] You can run manual cleanup: gx cleanup --base ${BASE_BRANCH}" >&2 fi - echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via ${merge_status} flow and cleaned source branch/worktree." if [[ "$source_worktree" == "$current_worktree" && "$source_worktree" == "${agent_worktree_root}"/* && -d "$source_worktree" ]]; then + echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via ${merge_status} flow and cleaned source branch/remote." echo "[agent-branch-finish] Current worktree '${source_worktree}' still exists because it is the active shell cwd." >&2 echo "[agent-branch-finish] Leave this directory, then run: gx cleanup --base ${BASE_BRANCH}" >&2 + else + echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via ${merge_status} flow and cleaned source branch/worktree." fi else pivot_to_repo_root_before_prune diff --git a/templates/scripts/agent-branch-finish.sh b/templates/scripts/agent-branch-finish.sh index e97cd7d..d63539c 100755 --- a/templates/scripts/agent-branch-finish.sh +++ b/templates/scripts/agent-branch-finish.sh @@ -165,7 +165,11 @@ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then fi repo_root="$(git rev-parse --show-toplevel)" -current_worktree="$(pwd -P)" +# The physical cwd may be a subdirectory inside the source worktree. Cleanup +# decisions need the enclosing worktree root, otherwise finishing from `src/` +# can delete the caller's cwd and turn a successful merge into a false shell +# failure. +current_worktree="$repo_root" common_git_dir_raw="$(git -C "$repo_root" rev-parse --git-common-dir)" if [[ "$common_git_dir_raw" == /* ]]; then common_git_dir="$common_git_dir_raw" @@ -846,10 +850,12 @@ if [[ "$CLEANUP_AFTER_MERGE" -eq 1 ]]; then echo "[agent-branch-finish] You can run manual cleanup: gx cleanup --base ${BASE_BRANCH}" >&2 fi - echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via ${merge_status} flow and cleaned source branch/worktree." if [[ "$source_worktree" == "$current_worktree" && "$source_worktree" == "${agent_worktree_root}"/* && -d "$source_worktree" ]]; then + echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via ${merge_status} flow and cleaned source branch/remote." echo "[agent-branch-finish] Current worktree '${source_worktree}' still exists because it is the active shell cwd." >&2 echo "[agent-branch-finish] Leave this directory, then run: gx cleanup --base ${BASE_BRANCH}" >&2 + else + echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via ${merge_status} flow and cleaned source branch/worktree." fi else pivot_to_repo_root_before_prune diff --git a/test/finish.test.js b/test/finish.test.js index 09fb408..0691c30 100644 --- a/test/finish.test.js +++ b/test/finish.test.js @@ -734,6 +734,8 @@ test('agent-branch-finish cleanup succeeds from active agent worktree when base assert.equal(result.status, 0, result.stderr || result.stdout); result = runCmd('git', ['push', '-u', 'origin', 'agent/test-active-worktree-cleanup'], agentWorktreePath); assert.equal(result.status, 0, result.stderr || result.stdout); + const agentSubdir = path.join(agentWorktreePath, 'nested', 'cwd'); + fs.mkdirSync(agentSubdir, { recursive: true }); const { fakePath: fakeGhPath } = createFakeGhScript(` if [[ "$1" == "pr" && "$2" == "create" ]]; then @@ -756,13 +758,13 @@ exit 1 const finish = runBranchFinish( ['--branch', 'agent/test-active-worktree-cleanup', '--base', 'dev', '--mode', 'pr', '--cleanup'], - agentWorktreePath, + agentSubdir, { GUARDEX_GH_BIN: fakeGhPath }, ); assert.equal(finish.status, 0, finish.stderr || finish.stdout); assert.match( finish.stdout, - /Merged 'agent\/test-active-worktree-cleanup' into 'dev' via pr flow and cleaned source branch\/worktree\./, + /Merged 'agent\/test-active-worktree-cleanup' into 'dev' via pr flow and cleaned source branch\/remote\./, ); assert.match(finish.stderr, /Current worktree '.+' still exists because it is the active shell cwd/); diff --git a/test/metadata.test.js b/test/metadata.test.js index 3c04d34..d5de329 100644 --- a/test/metadata.test.js +++ b/test/metadata.test.js @@ -235,6 +235,7 @@ test('critical runtime helper scripts and active-agents sources stay in sync wit test('agent-branch-finish pivots out of active agent cwd before every prune path', () => { const script = fs.readFileSync(path.join(repoRoot, 'scripts', 'agent-branch-finish.sh'), 'utf8'); + assert.match(script, /current_worktree="\$repo_root"/); assert.match(script, /pivot_to_repo_root_before_prune\(\) \{\n\s+if \[\[ "\$current_worktree" == "\$source_worktree"/); assert.match(script, /cd "\$repo_root" 2>\/dev\/null \|\| true/); assert.match(